From 5d060c34ccb52909e7b4af4284a884a46fba0648 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Mon, 23 May 2022 15:18:20 +0100 Subject: [PATCH] GalleryImageNode: Handle broken images Most of this logic copied from MWBlockImageNode. Change-Id: I92c860c53741c5ee48c8bce216987d97a92bc05f --- .../ve-mw/dm/nodes/ve.dm.MWBlockImageNode.js | 2 +- .../dm/nodes/ve.dm.MWGalleryImageNode.js | 35 +++++++---- modules/ve-mw/tests/dm/ve.dm.mwExample.js | 62 +++++++++++++++++-- .../ve-mw/ui/dialogs/ve.ui.MWGalleryDialog.js | 9 ++- .../ui/widgets/ve.ui.MWGalleryItemWidget.js | 1 + 5 files changed, 88 insertions(+), 21 deletions(-) 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 || {};