diff --git a/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js b/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js
index f243a78070..e50ea73ee6 100644
--- a/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js
+++ b/modules/ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js
@@ -225,7 +225,7 @@ ve.dm.MWBlockImageNode.static.toDomElements = function ( data, doc, converter )
img.setAttribute( attributes.isError ? 'data-width' : 'width', width );
}
if ( height !== null ) {
- img.setAttribute( attributes.isError ? 'data-width' : 'height', height );
+ img.setAttribute( attributes.isError ? 'data-height' : 'height', height );
}
img.setAttribute( 'resource', attributes.resource );
diff --git a/modules/ve-mw/dm/nodes/ve.dm.MWGalleryImageNode.js b/modules/ve-mw/dm/nodes/ve.dm.MWGalleryImageNode.js
index 0887717a28..dd684c23ad 100644
--- a/modules/ve-mw/dm/nodes/ve.dm.MWGalleryImageNode.js
+++ b/modules/ve-mw/dm/nodes/ve.dm.MWGalleryImageNode.js
@@ -69,8 +69,11 @@ ve.dm.MWGalleryImageNode.static.toDataElement = function ( domElements, converte
];
}
- var width = img.getAttribute( 'width' );
- var height = img.getAttribute( 'height' );
+ var typeofAttrs = figureInline.getAttribute( 'typeof' ).trim().split( /\s+/ );
+ var errorIndex = typeofAttrs.indexOf( 'mw:Error' );
+ var isError = errorIndex !== -1;
+ var width = img.getAttribute( isError ? 'data-width' : 'width' );
+ var height = img.getAttribute( isError ? 'data-height' : 'height' );
var dataElement = {
type: this.name,
@@ -81,7 +84,8 @@ ve.dm.MWGalleryImageNode.static.toDataElement = function ( domElements, converte
src: img.getAttribute( 'src' ) || img.getAttribute( 'poster' ),
width: width !== null && width !== '' ? +width : null,
height: height !== null && height !== '' ? +height : null,
- tagName: figureInline.nodeName.toLowerCase()
+ tagName: figureInline.nodeName.toLowerCase(),
+ isError: isError
}
};
@@ -96,14 +100,15 @@ ve.dm.MWGalleryImageNode.static.toDomElements = function ( data, doc ) {
//
thumbDiv
//
innerDiv
// a
- // img
+ // img (or span if error)
var model = data,
+ attributes = model.attributes,
li = doc.createElement( 'li' ),
thumbDiv = doc.createElement( 'div' ),
- innerDiv = doc.createElement( model.attributes.tagName || 'span' ),
+ innerDiv = doc.createElement( attributes.tagName || 'span' ),
a = doc.createElement( 'a' ),
- img = doc.createElement( 'img' ),
- alt = model.attributes.altText;
+ img = doc.createElement( attributes.isError ? 'span' : 'img' ),
+ alt = attributes.altText;
li.classList.add( 'gallerybox' );
thumbDiv.classList.add( 'thumb' );
@@ -114,12 +119,18 @@ ve.dm.MWGalleryImageNode.static.toDomElements = function ( data, doc ) {
// making the whole gallery subtree edited, preventing selser. When fixing,
// preserving the imgWrapperClassAttr, as in the MW*ImageNodes, will also be
// necessary.
- // a.setAttribute( 'href', model.attributes.src );
+ // a.setAttribute( 'href', attributes.src );
- img.setAttribute( 'resource', model.attributes.resource );
- img.setAttribute( 'src', model.attributes.src );
- img.setAttribute( 'width', model.attributes.width );
- img.setAttribute( 'height', model.attributes.height );
+ img.setAttribute( 'resource', attributes.resource );
+ if ( attributes.isError ) {
+ img.classList.add( 'mw-broken-media' );
+ var filename = mw.libs.ve.normalizeParsoidResourceName( attributes.resource || '' );
+ img.appendChild( doc.createTextNode( filename ) );
+ } else {
+ img.setAttribute( 'src', attributes.src );
+ }
+ img.setAttribute( attributes.isError ? 'data-width' : 'width', attributes.width );
+ img.setAttribute( attributes.isError ? 'data-height' : 'height', attributes.height );
if ( typeof alt === 'string' ) {
img.setAttribute( 'alt', alt );
}
diff --git a/modules/ve-mw/tests/dm/ve.dm.mwExample.js b/modules/ve-mw/tests/dm/ve.dm.mwExample.js
index e46689f7c3..c466bcbfaa 100644
--- a/modules/ve-mw/tests/dm/ve.dm.mwExample.js
+++ b/modules/ve-mw/tests/dm/ve.dm.mwExample.js
@@ -883,7 +883,8 @@ ve.dm.mwExample.domToDataCases = {
height: 120,
resource: './Foo',
src: ve.ce.minImgDataUri,
- tagName: 'span'
+ tagName: 'span',
+ isError: false
}
},
{ type: 'mwGalleryImageCaption' },
@@ -903,6 +904,53 @@ ve.dm.mwExample.domToDataCases = {
normalizedBody: '',
fromDataBody: ''
},
+ 'mwGalleryImage (broken image)': {
+ body: '',
+ data: [
+ {
+ type: 'mwGallery',
+ attributes: {
+ mw: {
+ attrs: {
+ mode: 'packed-hover'
+ },
+ body: {
+ extsrc: ''
+ },
+ name: 'gallery'
+ },
+ originalMw: '{"attrs":{"mode":"packed-hover"},"body":{"extsrc":""},"name":"gallery"}'
+ }
+ },
+ {
+ type: 'mwGalleryImage',
+ attributes: {
+ altText: null,
+ width: 120,
+ height: 120,
+ resource: './Foo',
+ src: null,
+ tagName: 'span',
+ isError: true
+ }
+ },
+ { type: 'mwGalleryImageCaption' },
+ {
+ type: 'paragraph',
+ internal: {
+ generated: 'wrapper'
+ }
+ },
+ { type: '/paragraph' },
+ { type: '/mwGalleryImageCaption' },
+ { type: '/mwGalleryImage' },
+ { type: '/mwGallery' },
+ { type: 'internalList' },
+ { type: '/internalList' }
+ ],
+ normalizedBody: '',
+ fromDataBody: ''
+ },
'mwGalleryImage (empty caption in DOM)': {
body: '',
data: [
@@ -929,7 +977,8 @@ ve.dm.mwExample.domToDataCases = {
height: 120,
resource: './Foo',
src: ve.ce.minImgDataUri,
- tagName: 'span'
+ tagName: 'span',
+ isError: false
}
},
{ type: 'mwGalleryImageCaption' },
@@ -975,7 +1024,8 @@ ve.dm.mwExample.domToDataCases = {
height: 120,
resource: './Foo',
src: ve.ce.minImgDataUri,
- tagName: 'span'
+ tagName: 'span',
+ isError: false
}
},
{ type: 'mwGalleryImageCaption' },
@@ -1021,7 +1071,8 @@ ve.dm.mwExample.domToDataCases = {
height: 120,
resource: './Foo',
src: ve.ce.minImgDataUri,
- tagName: 'span'
+ tagName: 'span',
+ isError: false
}
},
{ type: '/mwGalleryImage' },
@@ -1056,7 +1107,8 @@ ve.dm.mwExample.domToDataCases = {
height: 120,
resource: './Foo',
src: ve.ce.minImgDataUri,
- tagName: 'span'
+ tagName: 'span',
+ isError: false
}
},
{ type: 'mwGalleryImageCaption' },
diff --git a/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js b/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js
index 7826060177..4c1186704d 100644
--- a/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js
+++ b/modules/ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js
@@ -424,7 +424,8 @@ ve.ui.MWGalleryDialog.prototype.getSetupProcess = function ( data ) {
height: image.getAttribute( 'height' ),
width: image.getAttribute( 'width' ),
captionDocument: this.createCaptionDocument( imageCaptionNode ),
- tagName: image.getAttribute( 'tagName' )
+ tagName: image.getAttribute( 'tagName' ),
+ isError: image.getAttribute( 'isError' )
} );
}
@@ -664,7 +665,8 @@ ve.ui.MWGalleryDialog.prototype.onRequestImagesSuccess = function ( response ) {
height: thumbUrls[ title ].height,
width: thumbUrls[ title ].width,
thumbUrl: thumbUrls[ title ].thumbUrl,
- captionDocument: this.createCaptionDocument( null )
+ captionDocument: this.createCaptionDocument( null ),
+ isError: false
}, config ) );
delete this.selectedFilenames[ title ];
}
@@ -992,7 +994,8 @@ ve.ui.MWGalleryDialog.prototype.insertOrUpdateNode = function () {
src: galleryItem.src,
height: size.height,
width: size.width,
- tagName: galleryItem.tagName
+ tagName: galleryItem.tagName,
+ isError: galleryItem.isError
};
return [
diff --git a/modules/ve-mw/ui/widgets/ve.ui.MWGalleryItemWidget.js b/modules/ve-mw/ui/widgets/ve.ui.MWGalleryItemWidget.js
index 4c91107dd0..1f2d2a32b6 100644
--- a/modules/ve-mw/ui/widgets/ve.ui.MWGalleryItemWidget.js
+++ b/modules/ve-mw/ui/widgets/ve.ui.MWGalleryItemWidget.js
@@ -30,6 +30,7 @@ ve.ui.MWGalleryItemWidget = function VeUiMWGalleryItemWidget( imageInfo, config
this.captionDocument = imageInfo.captionDocument;
this.highlighted = false;
this.tagName = imageInfo.tagName;
+ this.isError = imageInfo.isError;
// Configuration initialization
config = config || {};