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

Change subject: Basic block image (figure/figcaption) support
......................................................................


Basic block image (figure/figcaption) support

Change-Id: Iddb3a5f57fd0ab965db00c2b148ab4f5e89b923d
---
M .docs/eg-iframe.html
M build/modules.json
M demos/ve/desktop.html
M demos/ve/mobile.html
M demos/ve/pages/image.html
A src/ce/nodes/ve.ce.BlockImageCaptionNode.js
A src/ce/nodes/ve.ce.BlockImageNode.js
A src/dm/nodes/ve.dm.BlockImageCaptionNode.js
A src/dm/nodes/ve.dm.BlockImageNode.js
M tests/dm/ve.dm.example.js
M tests/index.html
11 files changed, 413 insertions(+), 30 deletions(-)

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



diff --git a/.docs/eg-iframe.html b/.docs/eg-iframe.html
index 4255d78..20d3ad6 100644
--- a/.docs/eg-iframe.html
+++ b/.docs/eg-iframe.html
@@ -163,7 +163,6 @@
                <script src="../src/dm/nodes/ve.dm.DivNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.DocumentNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.HeadingNode.js"></script>
-               <script src="../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../src/dm/nodes/ve.dm.InternalItemNode.js"></script>
                <script 
src="../src/dm/nodes/ve.dm.InternalListNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.ListItemNode.js"></script>
@@ -176,6 +175,9 @@
                <script src="../src/dm/nodes/ve.dm.TableRowNode.js"></script>
                <script 
src="../src/dm/nodes/ve.dm.TableSectionNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.TextNode.js"></script>
+               <script src="../src/dm/nodes/ve.dm.BlockImageNode.js"></script>
+               <script 
src="../src/dm/nodes/ve.dm.BlockImageCaptionNode.js"></script>
+               <script src="../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../src/dm/annotations/ve.dm.LanguageAnnotation.js"></script>
                <script 
src="../src/dm/annotations/ve.dm.LinkAnnotation.js"></script>
                <script 
src="../src/dm/annotations/ve.dm.TextStyleAnnotation.js"></script>
@@ -225,7 +227,6 @@
                <script src="../src/ce/nodes/ve.ce.DivNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.DocumentNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.HeadingNode.js"></script>
-               <script src="../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../src/ce/nodes/ve.ce.InternalItemNode.js"></script>
                <script 
src="../src/ce/nodes/ve.ce.InternalListNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.ListItemNode.js"></script>
@@ -238,6 +239,9 @@
                <script src="../src/ce/nodes/ve.ce.TableRowNode.js"></script>
                <script 
src="../src/ce/nodes/ve.ce.TableSectionNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.TextNode.js"></script>
+               <script src="../src/ce/nodes/ve.ce.BlockImageNode.js"></script>
+               <script 
src="../src/ce/nodes/ve.ce.BlockImageCaptionNode.js"></script>
+               <script src="../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../src/ce/annotations/ve.ce.LanguageAnnotation.js"></script>
                <script 
src="../src/ce/annotations/ve.ce.LinkAnnotation.js"></script>
                <script 
src="../src/ce/annotations/ve.ce.TextStyleAnnotation.js"></script>
diff --git a/build/modules.json b/build/modules.json
index 2158754..40fd4d4 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -185,7 +185,6 @@
                        "src/dm/nodes/ve.dm.DivNode.js",
                        "src/dm/nodes/ve.dm.DocumentNode.js",
                        "src/dm/nodes/ve.dm.HeadingNode.js",
-                       "src/dm/nodes/ve.dm.InlineImageNode.js",
                        "src/dm/nodes/ve.dm.InternalItemNode.js",
                        "src/dm/nodes/ve.dm.InternalListNode.js",
                        "src/dm/nodes/ve.dm.ListItemNode.js",
@@ -198,6 +197,9 @@
                        "src/dm/nodes/ve.dm.TableRowNode.js",
                        "src/dm/nodes/ve.dm.TableSectionNode.js",
                        "src/dm/nodes/ve.dm.TextNode.js",
+                       "src/dm/nodes/ve.dm.BlockImageNode.js",
+                       "src/dm/nodes/ve.dm.BlockImageCaptionNode.js",
+                       "src/dm/nodes/ve.dm.InlineImageNode.js",
                        "src/dm/annotations/ve.dm.LanguageAnnotation.js",
                        "src/dm/annotations/ve.dm.LinkAnnotation.js",
                        "src/dm/annotations/ve.dm.TextStyleAnnotation.js",
@@ -247,7 +249,6 @@
                        "src/ce/nodes/ve.ce.DivNode.js",
                        "src/ce/nodes/ve.ce.DocumentNode.js",
                        "src/ce/nodes/ve.ce.HeadingNode.js",
-                       "src/ce/nodes/ve.ce.InlineImageNode.js",
                        "src/ce/nodes/ve.ce.InternalItemNode.js",
                        "src/ce/nodes/ve.ce.InternalListNode.js",
                        "src/ce/nodes/ve.ce.ListItemNode.js",
@@ -260,6 +261,9 @@
                        "src/ce/nodes/ve.ce.TableRowNode.js",
                        "src/ce/nodes/ve.ce.TableSectionNode.js",
                        "src/ce/nodes/ve.ce.TextNode.js",
+                       "src/ce/nodes/ve.ce.BlockImageNode.js",
+                       "src/ce/nodes/ve.ce.BlockImageCaptionNode.js",
+                       "src/ce/nodes/ve.ce.InlineImageNode.js",
                        "src/ce/annotations/ve.ce.LanguageAnnotation.js",
                        "src/ce/annotations/ve.ce.LinkAnnotation.js",
                        "src/ce/annotations/ve.ce.TextStyleAnnotation.js",
diff --git a/demos/ve/desktop.html b/demos/ve/desktop.html
index c51677d..3379a4c 100644
--- a/demos/ve/desktop.html
+++ b/demos/ve/desktop.html
@@ -175,7 +175,6 @@
                <script src="../../src/dm/nodes/ve.dm.DivNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.DocumentNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.HeadingNode.js"></script>
-               <script 
src="../../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../../src/dm/nodes/ve.dm.InternalItemNode.js"></script>
                <script 
src="../../src/dm/nodes/ve.dm.InternalListNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.ListItemNode.js"></script>
@@ -188,6 +187,9 @@
                <script src="../../src/dm/nodes/ve.dm.TableRowNode.js"></script>
                <script 
src="../../src/dm/nodes/ve.dm.TableSectionNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.TextNode.js"></script>
+               <script 
src="../../src/dm/nodes/ve.dm.BlockImageNode.js"></script>
+               <script 
src="../../src/dm/nodes/ve.dm.BlockImageCaptionNode.js"></script>
+               <script 
src="../../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../../src/dm/annotations/ve.dm.LanguageAnnotation.js"></script>
                <script 
src="../../src/dm/annotations/ve.dm.LinkAnnotation.js"></script>
                <script 
src="../../src/dm/annotations/ve.dm.TextStyleAnnotation.js"></script>
@@ -237,7 +239,6 @@
                <script src="../../src/ce/nodes/ve.ce.DivNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.DocumentNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.HeadingNode.js"></script>
-               <script 
src="../../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../../src/ce/nodes/ve.ce.InternalItemNode.js"></script>
                <script 
src="../../src/ce/nodes/ve.ce.InternalListNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.ListItemNode.js"></script>
@@ -250,6 +251,9 @@
                <script src="../../src/ce/nodes/ve.ce.TableRowNode.js"></script>
                <script 
src="../../src/ce/nodes/ve.ce.TableSectionNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.TextNode.js"></script>
+               <script 
src="../../src/ce/nodes/ve.ce.BlockImageNode.js"></script>
+               <script 
src="../../src/ce/nodes/ve.ce.BlockImageCaptionNode.js"></script>
+               <script 
src="../../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../../src/ce/annotations/ve.ce.LanguageAnnotation.js"></script>
                <script 
src="../../src/ce/annotations/ve.ce.LinkAnnotation.js"></script>
                <script 
src="../../src/ce/annotations/ve.ce.TextStyleAnnotation.js"></script>
diff --git a/demos/ve/mobile.html b/demos/ve/mobile.html
index eddce77..9f8f2fa 100644
--- a/demos/ve/mobile.html
+++ b/demos/ve/mobile.html
@@ -176,7 +176,6 @@
                <script src="../../src/dm/nodes/ve.dm.DivNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.DocumentNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.HeadingNode.js"></script>
-               <script 
src="../../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../../src/dm/nodes/ve.dm.InternalItemNode.js"></script>
                <script 
src="../../src/dm/nodes/ve.dm.InternalListNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.ListItemNode.js"></script>
@@ -189,6 +188,9 @@
                <script src="../../src/dm/nodes/ve.dm.TableRowNode.js"></script>
                <script 
src="../../src/dm/nodes/ve.dm.TableSectionNode.js"></script>
                <script src="../../src/dm/nodes/ve.dm.TextNode.js"></script>
+               <script 
src="../../src/dm/nodes/ve.dm.BlockImageNode.js"></script>
+               <script 
src="../../src/dm/nodes/ve.dm.BlockImageCaptionNode.js"></script>
+               <script 
src="../../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../../src/dm/annotations/ve.dm.LanguageAnnotation.js"></script>
                <script 
src="../../src/dm/annotations/ve.dm.LinkAnnotation.js"></script>
                <script 
src="../../src/dm/annotations/ve.dm.TextStyleAnnotation.js"></script>
@@ -238,7 +240,6 @@
                <script src="../../src/ce/nodes/ve.ce.DivNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.DocumentNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.HeadingNode.js"></script>
-               <script 
src="../../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../../src/ce/nodes/ve.ce.InternalItemNode.js"></script>
                <script 
src="../../src/ce/nodes/ve.ce.InternalListNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.ListItemNode.js"></script>
@@ -251,6 +252,9 @@
                <script src="../../src/ce/nodes/ve.ce.TableRowNode.js"></script>
                <script 
src="../../src/ce/nodes/ve.ce.TableSectionNode.js"></script>
                <script src="../../src/ce/nodes/ve.ce.TextNode.js"></script>
+               <script 
src="../../src/ce/nodes/ve.ce.BlockImageNode.js"></script>
+               <script 
src="../../src/ce/nodes/ve.ce.BlockImageCaptionNode.js"></script>
+               <script 
src="../../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../../src/ce/annotations/ve.ce.LanguageAnnotation.js"></script>
                <script 
src="../../src/ce/annotations/ve.ce.LinkAnnotation.js"></script>
                <script 
src="../../src/ce/annotations/ve.ce.TextStyleAnnotation.js"></script>
diff --git a/demos/ve/pages/image.html b/demos/ve/pages/image.html
index 860cf9c..3d363b8 100644
--- a/demos/ve/pages/image.html
+++ b/demos/ve/pages/image.html
@@ -1,4 +1,8 @@
-<p>This is a local image: <img alt="VisualEditor logo" 
src="VisualEditor-logo.svg" width="320" height="112">foo</p>
+<figure style="float: right;">
+       <img alt="VisualEditor logo" src="VisualEditor-logo.svg" width="160" 
height="56">
+       <figcaption>Block image with <i>caption</i></figcaption>
+</figure>
+<p>This is a local image: <img alt="VisualEditor logo" 
src="VisualEditor-logo.svg" width="160" height="56">foo</p>
 <p>Remote image outside of paragraph:</p>
 <img alt="Wikipedia Logo" 
src="//upload.wikimedia.org/wikipedia/commons/b/b3/Wikipedia-logo-v2-en.svg" 
width="135" height="155">
 <p>Image without dimensions attributes: <img 
src="//upload.wikimedia.org/wikipedia/commons/0/03/Dialog-information.svg" 
alt="Lightbulb"></p>
\ No newline at end of file
diff --git a/src/ce/nodes/ve.ce.BlockImageCaptionNode.js 
b/src/ce/nodes/ve.ce.BlockImageCaptionNode.js
new file mode 100644
index 0000000..9ad647b
--- /dev/null
+++ b/src/ce/nodes/ve.ce.BlockImageCaptionNode.js
@@ -0,0 +1,34 @@
+/*!
+ * VisualEditor ContentEditable block image caption node class.
+ *
+ * @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * ContentEditable block image caption item node.
+ *
+ * @class
+ * @extends ve.ce.BranchNode
+ * @constructor
+ * @param {ve.dm.BlockImageCaptionNode} model Model to observe
+ * @param {Object} [config] Configuration options
+ */
+ve.ce.BlockImageCaptionNode = function VeCeBlockImageCaptionNode( model, 
config ) {
+       // Parent constructor
+       ve.ce.BranchNode.call( this, model, config );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ce.BlockImageCaptionNode, ve.ce.BranchNode );
+
+/* Static Properties */
+
+ve.ce.BlockImageCaptionNode.static.name = 'imageCaption';
+
+ve.ce.BlockImageCaptionNode.static.tagName = 'figcaption';
+
+/* Registration */
+
+ve.ce.nodeFactory.register( ve.ce.BlockImageCaptionNode );
diff --git a/src/ce/nodes/ve.ce.BlockImageNode.js 
b/src/ce/nodes/ve.ce.BlockImageNode.js
new file mode 100644
index 0000000..822cf09
--- /dev/null
+++ b/src/ce/nodes/ve.ce.BlockImageNode.js
@@ -0,0 +1,101 @@
+/*!
+ * VisualEditor ContentEditable block image node class.
+ *
+ * @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * ContentEditable block image node.
+ *
+ * @class
+ * @extends ve.ce.BranchNode
+ * @mixins ve.ce.FocusableNode
+ * @mixins ve.ce.ResizableNode
+ *
+ * @constructor
+ * @param {ve.dm.BlockImageNode} model Model to observe
+ * @param {Object} [config] Configuration options
+ */
+ve.ce.BlockImageNode = function VeCeBlockImageNode( model, config ) {
+       config = ve.extendObject( {
+               minDimensions: { width: 1, height: 1 }
+       }, config );
+
+       // Parent constructor
+       ve.ce.BranchNode.call( this, model, config );
+
+       // Build DOM
+       this.$image = this.$( '<img>' )
+               .attr( 'src', this.getResolvedAttribute( 'src' ) )
+               .prependTo( this.$element );
+
+       // Mixin constructors
+       ve.ce.FocusableNode.call( this );
+       ve.ce.ResizableNode.call( this, this.$image, config );
+
+       // Events
+       this.$image.on( 'load', ve.bind( this.onLoad, this ) );
+       this.model.connect( this, { attributeChange: 'onAttributeChange' } );
+
+       // Initialization
+       this.$element.addClass( 've-ce-imageNode' );
+       this.$image
+               .attr( {
+                       alt: this.model.getAttribute( 'alt' ),
+                       src: this.getResolvedAttribute( 'src' )
+               } )
+               .css( {
+                       width: this.model.getAttribute( 'width' ),
+                       height: this.model.getAttribute( 'height' )
+               } );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ce.BlockImageNode, ve.ce.BranchNode );
+
+OO.mixinClass( ve.ce.BlockImageNode, ve.ce.FocusableNode );
+OO.mixinClass( ve.ce.BlockImageNode, ve.ce.ResizableNode );
+
+/* Static Properties */
+
+ve.ce.BlockImageNode.static.name = 'blockImage';
+
+ve.ce.BlockImageNode.static.tagName = 'figure';
+
+/* Methods */
+
+/**
+ * Update the rendering of the 'src', 'width' and 'height' attributes when 
they change in the model.
+ *
+ * @method
+ * @param {string} key Attribute key
+ * @param {string} from Old value
+ * @param {string} to New value
+ */
+ve.ce.BlockImageNode.prototype.onAttributeChange = function ( key, from, to ) {
+       if ( key === 'src' ) {
+               this.$image.attr( 'src', this.getResolvedAttribute( 'src' ) );
+       }
+       if ( key === 'width' || key === 'height' ) {
+               this.$image.css( key, to !== null ? to : '' );
+       }
+};
+
+/**
+ * Handle the image load
+ *
+ * @method
+ * @param {jQuery.Event} e Load event
+ */
+ve.ce.BlockImageNode.prototype.onLoad = function () {
+       this.setOriginalDimensions( {
+               width: this.$image.prop( 'naturalWidth' ),
+               height: this.$image.prop( 'naturalHeight' )
+       } );
+};
+
+/* Registration */
+
+ve.ce.nodeFactory.register( ve.ce.BlockImageNode );
diff --git a/src/dm/nodes/ve.dm.BlockImageCaptionNode.js 
b/src/dm/nodes/ve.dm.BlockImageCaptionNode.js
new file mode 100644
index 0000000..068c552
--- /dev/null
+++ b/src/dm/nodes/ve.dm.BlockImageCaptionNode.js
@@ -0,0 +1,37 @@
+/*!
+ * VisualEditor DataModel block image caption node class.
+ *
+ * @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * DataModel block image caption node.
+ *
+ * @class
+ * @extends ve.dm.BranchNode
+ *
+ * @constructor
+ * @param {Object} [element] Reference to element in linear model
+ * @param {ve.dm.Node[]} [children]
+ */
+ve.dm.BlockImageCaptionNode = function VeDmBlockImageCaptionNode() {
+       // Parent constructor
+       ve.dm.BranchNode.apply( this, arguments );
+};
+
+OO.inheritClass( ve.dm.BlockImageCaptionNode, ve.dm.BranchNode );
+
+ve.dm.BlockImageCaptionNode.static.name = 'imageCaption';
+
+ve.dm.BlockImageCaptionNode.static.matchTagNames = [];
+
+ve.dm.BlockImageCaptionNode.static.parentNodeTypes = [ 'blockImage' ];
+
+ve.dm.BlockImageCaptionNode.static.toDomElements = function ( dataElement, doc 
) {
+       return [ doc.createElement( 'figcaption' ) ];
+};
+
+/* Registration */
+
+ve.dm.modelRegistry.register( ve.dm.BlockImageCaptionNode );
diff --git a/src/dm/nodes/ve.dm.BlockImageNode.js 
b/src/dm/nodes/ve.dm.BlockImageNode.js
new file mode 100644
index 0000000..6015ad9
--- /dev/null
+++ b/src/dm/nodes/ve.dm.BlockImageNode.js
@@ -0,0 +1,146 @@
+/*!
+ * VisualEditor DataModel BlockImageNode class.
+ *
+ * @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+/**
+ * DataModel block image node.
+ *
+ * @class
+ * @extends ve.dm.BranchNode
+ * @mixins ve.dm.ResizableNode
+ *
+ * @constructor
+ * @param {Object} [element] Reference to element in linear model
+ * @param {ve.dm.Node[]} [children]
+ */
+ve.dm.BlockImageNode = function VeDmBlockImageNode() {
+       // Parent constructor
+       ve.dm.BranchNode.apply( this, arguments );
+
+       // Mixin constructor
+       ve.dm.ResizableNode.call( this );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.dm.BlockImageNode, ve.dm.BranchNode );
+
+OO.mixinClass( ve.dm.BlockImageNode, ve.dm.ResizableNode );
+
+/* Static Properties */
+
+ve.dm.BlockImageNode.static.name = 'blockImage';
+
+ve.dm.BlockImageNode.static.storeHtmlAttributes = {
+       blacklist: [ 'typeof', 'class', 'src', 'resource', 'width', 'height', 
'href', 'rel' ]
+};
+
+ve.dm.BlockImageNode.static.handlesOwnChildren = true;
+
+ve.dm.BlockImageNode.static.childNodeTypes = [ 'imageCaption' ];
+
+ve.dm.BlockImageNode.static.matchTagNames = [ 'figure' ];
+
+//ve.dm.BlockImageNode.static.blacklistedAnnotationTypes = [ 'link' ];
+
+ve.dm.BlockImageNode.static.toDataElement = function ( domElements, converter 
) {
+       var dataElement,
+               $figure = $( domElements[0] ),
+               $img = $figure.children( 'img' ).eq( 0 ),
+               $caption = $figure.children( 'figcaption' ).eq( 0 ),
+               attributes = {
+                       src: $img.attr( 'src' )
+               },
+               width = $img.attr( 'width' ),
+               height = $img.attr( 'height' ),
+               altText = $img.attr( 'alt' );
+
+       if ( altText !== undefined ) {
+               attributes.alt = altText;
+       }
+
+       attributes.width = width !== undefined && width !== '' ? Number( width 
) : null;
+       attributes.height = height !== undefined && height !== '' ? Number( 
height ) : null;
+
+       dataElement = { type: this.name, attributes: attributes };
+
+       if ( $caption.length === 0 ) {
+               return [
+                       dataElement,
+                       { type: 'imageCaption' },
+                       { type: 'imageCaption' },
+                       { type: '/' + this.name }
+               ];
+       } else {
+               return [ dataElement ].
+                       concat( converter.getDataFromDomClean( $caption[0], { 
type: 'imageCaption' } ) ).
+                       concat( [ { type: '/' + this.name } ] );
+       }
+};
+
+// TODO: Consider using jQuery instead of pure JS.
+// TODO: At this moment node is not resizable but when it will be then adding 
defaultSize class
+// should be more conditional.
+ve.dm.BlockImageNode.static.toDomElements = function ( data, doc, converter ) {
+       var dataElement = data[0],
+               width = dataElement.attributes.width,
+               height = dataElement.attributes.height,
+               figure = doc.createElement( 'figure' ),
+               img = doc.createElement( 'img' ),
+               wrapper = doc.createElement( 'div' ),
+               captionData = data.slice( 1, -1 );
+
+       img.setAttribute( 'src', dataElement.attributes.src );
+       img.setAttribute( 'width', width );
+       img.setAttribute( 'height', height );
+       if ( dataElement.attributes.alt !== undefined ) {
+               img.setAttribute( 'alt', dataElement.attributes.alt );
+       }
+       figure.appendChild( img );
+
+       // If length of captionData is smaller or equal to 2 it means that 
there is no caption or that
+       // it is empty - in both cases we are going to skip appending 
<figcaption>.
+       if ( captionData.length > 2 ) {
+               converter.getDomSubtreeFromData( data.slice( 1, -1 ), wrapper );
+               while ( wrapper.firstChild ) {
+                       figure.appendChild( wrapper.firstChild );
+               }
+       }
+       return [ figure ];
+};
+
+/* Methods */
+
+/**
+ * Get the caption node of the image.
+ *
+ * @method
+ * @returns {ve.dm.BlockImageCaptionNode|null} Caption node, if present
+ */
+ve.dm.BlockImageNode.prototype.getCaptionNode = function () {
+       var node = this.children[0];
+       return node instanceof ve.dm.BlockImageCaptionNode ? node : null;
+};
+
+/**
+ * @inheritdoc
+ */
+ve.dm.BlockImageNode.prototype.createScalable = function () {
+       return new ve.dm.Scalable( {
+               currentDimensions: {
+                       width: this.getAttribute( 'width' ),
+                       height: this.getAttribute( 'height' )
+               },
+               minDimensions: {
+                       width: 1,
+                       height: 1
+               }
+       } );
+};
+
+/* Registration */
+
+ve.dm.modelRegistry.register( ve.dm.BlockImageNode );
diff --git a/tests/dm/ve.dm.example.js b/tests/dm/ve.dm.example.js
index dbbc5bc..13ec868 100644
--- a/tests/dm/ve.dm.example.js
+++ b/tests/dm/ve.dm.example.js
@@ -197,6 +197,40 @@
        }
 };
 
+ve.dm.example.blockImage = {
+       html: '<figure><img src="' + ve.dm.example.imgSrc + '" alt="Example" 
width="100" height="50"><figcaption>caption</figcaption></figure>',
+       data: [
+               {
+                       type: 'blockImage',
+                       attributes: {
+                               src: ve.dm.example.imgSrc,
+                               alt: 'Example',
+                               width: 100,
+                               height: 50
+                       },
+                       htmlAttributes: [
+                               {
+                                       values: {},
+                                       children: [
+                                               {
+                                                       values: {
+                                                               alt: 'Example'
+                                                       }
+                                               },
+                                               { values: {} }
+                                       ]
+                               }
+                       ]
+               },
+               { type: 'imageCaption' },
+               { type: 'paragraph', internal: { generated: 'wrapper' } },
+               'c', 'a', 'p', 't', 'i', 'o', 'n',
+               { type: '/paragraph' },
+               { type: '/imageCaption' },
+               { type: '/blockImage' }
+       ]
+};
+
 /**
  * Serialized HTML.
  *
@@ -1097,6 +1131,13 @@
                        { type: '/internalList' }
                ]
        },
+       'block image': {
+               body: ve.dm.example.blockImage.html,
+               data: ve.dm.example.blockImage.data.concat( [
+                       { type: 'internalList' },
+                       { type: '/internalList' }
+               ] )
+       },
        'paragraph with alienInline inside': {
                body: '<p>a<foobar class="foo">b</foobar>c</p>',
                data: [
@@ -1114,14 +1155,14 @@
                ]
        },
        'paragraphs with an alienBlock between them': {
-               body: '<p>abc</p><figure>abc</figure><p>def</p>',
+               body: '<p>abc</p><div rel="ve:Alien">abc</div><p>def</p>',
                data: [
                        { type: 'paragraph' },
                        'a',
                        'b',
                        'c',
                        { type: '/paragraph' },
-                       { type: 'alienBlock', attributes: { domElements: $( 
'<figure>abc</figure>' ).toArray() } },
+                       { type: 'alienBlock', attributes: { domElements: $( 
'<div rel="ve:Alien">abc</div>' ).toArray() } },
                        { type: '/alienBlock' },
                        { type: 'paragraph' },
                        'd',
@@ -1335,14 +1376,14 @@
                ]
        },
        'wrapping of bare content with block alien': {
-               body: '1<figure class="bar">baz</figure>2',
+               body: '1<div rel="ve:Alien" class="bar">baz</div>2',
                data: [
                        { type: 'paragraph', internal: { generated: 'wrapper' } 
},
                        '1',
                        { type: '/paragraph' },
                        {
                                type: 'alienBlock',
-                               attributes: { domElements: $( '<figure 
class="bar">baz</figure>' ).toArray() }
+                               attributes: { domElements: $( '<div 
rel="ve:Alien" class="bar">baz</div>' ).toArray() }
                        },
                        { type: '/alienBlock' },
                        { type: 'paragraph', internal: { generated: 'wrapper' } 
},
@@ -2001,12 +2042,12 @@
                ]
        },
        'whitespace preservation with aliens': {
-               body: ' <figure>  <br>   </figure>    
<p>\tFoo\t\t<foobar>\t\t\tBar\t\t\t\t</foobar>\nBaz\n\n<foobar>\n\n\nQuux\n\n\n\n</foobar>
 \tWhee \n</p>\t\n<figure>\n\tYay \t </figure> \n ',
+               body: ' <div rel="ve:Alien">  <br>   </div>    
<p>\tFoo\t\t<foobar>\t\t\tBar\t\t\t\t</foobar>\nBaz\n\n<foobar>\n\n\nQuux\n\n\n\n</foobar>
 \tWhee \n</p>\t\n<div rel="ve:Alien">\n\tYay \t </div> \n ',
                data: [
                        {
                                type: 'alienBlock',
                                attributes: {
-                                       domElements: $( '<figure>  <br>   
</figure>' ).toArray()
+                                       domElements: $( '<div rel="ve:Alien">  
<br>   </div>' ).toArray()
                                },
                                internal: {
                                        whitespace: [ ' ', undefined, 
undefined, '    ' ]
@@ -2044,7 +2085,7 @@
                        {
                                type: 'alienBlock',
                                attributes: {
-                                       domElements: $( '<figure>\n\tYay \t 
</figure>' ).toArray()
+                                       domElements: $( '<div 
rel="ve:Alien">\n\tYay \t </div>' ).toArray()
                                },
                                internal: {
                                        whitespace: [ '\t\n', undefined, 
undefined, ' \n ' ]
@@ -2533,27 +2574,27 @@
                ]
        },
        'about grouping': {
-               body: '<figure about="#mwt1">Foo</figure>' +
-                       '<figure about="#mwt1">Bar</figure>' +
-                       '<figure about="#mwt2">Baz</figure>' +
+               body: '<div rel="ve:Alien" about="#mwt1">Foo</div>' +
+                       '<div rel="ve:Alien" about="#mwt1">Bar</div>' +
+                       '<div rel="ve:Alien" about="#mwt2">Baz</div>' +
                        '<foobar about="#mwt2">Quux</foobar>' +
                        '<p>Whee</p>' +
                        '<foobar about="#mwt2">Yay</foobar>' +
-                       '<figure about="#mwt2">Blah</figure>' +
+                       '<div rel="ve:Alien" about="#mwt2">Blah</div>' +
                        '<foobar about="#mwt3">Meh</foobar>',
                data: [
                        {
                                type: 'alienBlock',
                                attributes: {
-                                       domElements: $( '<figure 
about="#mwt1">Foo</figure>' +
-                                               '<figure 
about="#mwt1">Bar</figure>' ).toArray()
+                                       domElements: $( '<div rel="ve:Alien" 
about="#mwt1">Foo</div>' +
+                                               '<div rel="ve:Alien" 
about="#mwt1">Bar</div>' ).toArray()
                                }
                        },
                        { type: '/alienBlock' },
                        {
                                type: 'alienBlock',
                                attributes: {
-                                       domElements: $( '<figure 
about="#mwt2">Baz</figure>' +
+                                       domElements: $( '<div rel="ve:Alien" 
about="#mwt2">Baz</div>' +
                                                '<foobar 
about="#mwt2">Quux</foobar>' ).toArray()
                                }
                        },
@@ -2568,7 +2609,7 @@
                                type: 'alienBlock',
                                attributes: {
                                        domElements: $( '<foobar 
about="#mwt2">Yay</foobar>' +
-                                               '<figure 
about="#mwt2">Blah</figure>' ).toArray()
+                                               '<div rel="ve:Alien" 
about="#mwt2">Blah</div>' ).toArray()
                                }
                        },
                        { type: '/alienBlock' },
@@ -2586,14 +2627,14 @@
                ]
        },
        'whitespace preservation with an about group': {
-               body: ' <figure about="#mwt1">\tFoo\t\t</figure>\t\t\t' +
-                       '<figure about="#mwt1">  Bar   </figure>    ',
+               body: ' <div rel="ve:Alien" 
about="#mwt1">\tFoo\t\t</div>\t\t\t' +
+                       '<div rel="ve:Alien" about="#mwt1">  Bar   </div>    ',
                data: [
                        {
                                type: 'alienBlock',
                                attributes: {
-                                       domElements: $( '<figure 
about="#mwt1">\tFoo\t\t</figure>\t\t\t' +
-                                               '<figure about="#mwt1">  Bar   
</figure>' ).toArray()
+                                       domElements: $( '<div rel="ve:Alien" 
about="#mwt1">\tFoo\t\t</div>\t\t\t' +
+                                               '<div rel="ve:Alien" 
about="#mwt1">  Bar   </div>' ).toArray()
                                },
                                internal: {
                                        whitespace: [ ' ', undefined, 
undefined, '    ' ]
diff --git a/tests/index.html b/tests/index.html
index fef2646..f969b27 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -124,7 +124,6 @@
                <script src="../src/dm/nodes/ve.dm.DivNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.DocumentNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.HeadingNode.js"></script>
-               <script src="../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../src/dm/nodes/ve.dm.InternalItemNode.js"></script>
                <script 
src="../src/dm/nodes/ve.dm.InternalListNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.ListItemNode.js"></script>
@@ -137,6 +136,9 @@
                <script src="../src/dm/nodes/ve.dm.TableRowNode.js"></script>
                <script 
src="../src/dm/nodes/ve.dm.TableSectionNode.js"></script>
                <script src="../src/dm/nodes/ve.dm.TextNode.js"></script>
+               <script src="../src/dm/nodes/ve.dm.BlockImageNode.js"></script>
+               <script 
src="../src/dm/nodes/ve.dm.BlockImageCaptionNode.js"></script>
+               <script src="../src/dm/nodes/ve.dm.InlineImageNode.js"></script>
                <script 
src="../src/dm/annotations/ve.dm.LanguageAnnotation.js"></script>
                <script 
src="../src/dm/annotations/ve.dm.LinkAnnotation.js"></script>
                <script 
src="../src/dm/annotations/ve.dm.TextStyleAnnotation.js"></script>
@@ -186,7 +188,6 @@
                <script src="../src/ce/nodes/ve.ce.DivNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.DocumentNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.HeadingNode.js"></script>
-               <script src="../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../src/ce/nodes/ve.ce.InternalItemNode.js"></script>
                <script 
src="../src/ce/nodes/ve.ce.InternalListNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.ListItemNode.js"></script>
@@ -199,6 +200,9 @@
                <script src="../src/ce/nodes/ve.ce.TableRowNode.js"></script>
                <script 
src="../src/ce/nodes/ve.ce.TableSectionNode.js"></script>
                <script src="../src/ce/nodes/ve.ce.TextNode.js"></script>
+               <script src="../src/ce/nodes/ve.ce.BlockImageNode.js"></script>
+               <script 
src="../src/ce/nodes/ve.ce.BlockImageCaptionNode.js"></script>
+               <script src="../src/ce/nodes/ve.ce.InlineImageNode.js"></script>
                <script 
src="../src/ce/annotations/ve.ce.LanguageAnnotation.js"></script>
                <script 
src="../src/ce/annotations/ve.ce.LinkAnnotation.js"></script>
                <script 
src="../src/ce/annotations/ve.ce.TextStyleAnnotation.js"></script>

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Iddb3a5f57fd0ab965db00c2b148ab4f5e89b923d
Gerrit-PatchSet: 8
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <esand...@wikimedia.org>
Gerrit-Reviewer: Catrope <roan.katt...@gmail.com>
Gerrit-Reviewer: Esanders <esand...@wikimedia.org>
Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org>
Gerrit-Reviewer: Mooeypoo <mor...@gmail.com>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to