2016-11-19 23:11:06 +00:00
|
|
|
/*!
|
|
|
|
* VisualEditor DataModel MWGalleryImageNode class.
|
|
|
|
*
|
|
|
|
* @copyright 2016 VisualEditor Team and others; see AUTHORS.txt
|
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DataModel MediaWiki gallery image node.
|
|
|
|
*
|
|
|
|
* @class
|
|
|
|
* @extends ve.dm.BranchNode
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @param {Object} [element] Reference to element in linear model
|
|
|
|
* @param {ve.dm.Node[]} [children]
|
|
|
|
*/
|
|
|
|
ve.dm.MWGalleryImageNode = function VeDmMWGalleryImageNode() {
|
|
|
|
// Parent constructor
|
|
|
|
ve.dm.MWGalleryImageNode.super.apply( this, arguments );
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Inheritance */
|
|
|
|
|
|
|
|
OO.inheritClass( ve.dm.MWGalleryImageNode, ve.dm.BranchNode );
|
|
|
|
|
|
|
|
/* Static members */
|
|
|
|
|
|
|
|
ve.dm.MWGalleryImageNode.static.name = 'mwGalleryImage';
|
|
|
|
|
|
|
|
ve.dm.MWGalleryImageNode.static.matchTagNames = [ 'li' ];
|
|
|
|
|
2018-04-03 18:33:00 +00:00
|
|
|
ve.dm.MWGalleryImageNode.static.childNodeTypes = [ 'mwGalleryImageCaption' ];
|
|
|
|
|
2016-11-19 23:11:06 +00:00
|
|
|
ve.dm.MWGalleryImageNode.static.matchFunction = function ( element ) {
|
|
|
|
var parentTypeof = ( element.parentNode && element.parentNode.getAttribute( 'typeof' ) ) || '';
|
|
|
|
return element.getAttribute( 'class' ) === 'gallerybox' &&
|
2018-10-02 20:48:25 +00:00
|
|
|
parentTypeof.trim().split( /\s+/ ).indexOf( 'mw:Extension/gallery' ) !== -1;
|
2016-11-19 23:11:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
ve.dm.MWGalleryImageNode.static.parentNodeTypes = [ 'mwGallery' ];
|
|
|
|
|
|
|
|
ve.dm.MWGalleryImageNode.static.toDataElement = function ( domElements, converter ) {
|
|
|
|
// TODO: Improve handling of missing files. See 'isError' in MWBlockImageNode#toDataElement
|
2021-10-13 12:57:45 +00:00
|
|
|
var li = domElements[ 0 ];
|
|
|
|
var img = li.querySelector( 'img,audio,video,span[resource]' );
|
2022-05-31 18:35:52 +00:00
|
|
|
var container = img.parentNode.parentNode;
|
2016-11-19 23:11:06 +00:00
|
|
|
|
|
|
|
// Get caption (may be missing for mode="packed-hover" galleries)
|
2021-10-13 12:57:45 +00:00
|
|
|
var captionNode = li.querySelector( '.gallerytext' );
|
2018-08-01 02:35:49 +00:00
|
|
|
if ( captionNode ) {
|
|
|
|
captionNode = captionNode.cloneNode( true );
|
2016-11-19 23:11:06 +00:00
|
|
|
// If showFilename is 'yes', the filename is also inside the caption, so throw this out
|
2021-10-13 12:57:45 +00:00
|
|
|
var filename = captionNode.querySelector( '.galleryfilename' );
|
2016-11-19 23:11:06 +00:00
|
|
|
if ( filename ) {
|
|
|
|
filename.remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add a checkbox to use the image caption as the alt text for galleries
The need for something like this was anticipated in
I2bf43c7e83283f43e047229eb53c244918fcbb0c.
As of version 2.5.0 of Parsoid's output, if alternate text is missing
for an image but a caption is present and image isn't displaying the
caption (ie. it isn't a thumb or frame), then the text content of the
caption will be set as the alt attribute. Parsoid will then drop the
alt attribute when serializing if it matches the caption text, since
it's unnecessary.
However, if the caption is modified and the alt text isn't, the alt will
be serialized. This is likely to be unexpected to editor. They may
have missed that the both the caption and alt are populated in VE and
only edited one place.
Since all of the above is happening only for images where the caption
isn't visible, it doesn't appear to be a much used feature since, at
least for inline images, the experience of caption editing was already
less than optimal.
However, because of a quirk in how galleries are rendered in Parsoid,
this affects gallery caption editing, which is visible and presumably
used more often. See T268250 for a discussion on an improved gallery
structure. But for now, gallery images are effectively inline and set
the alternate text, thus subject to the above.
Here we add a checkbox so that the default is to ignore the alt if it's
the same as the caption. And only make use of it if it differed
originally or was explicitly unchecked to modify.
Bug: T311677
Change-Id: Idf297d8a98995971c5835b0cea56c3317a3626e2
2022-07-04 21:01:40 +00:00
|
|
|
// FIXME: This should match Parsoid's WTUtils::textContentFromCaption,
|
|
|
|
// which drops <ref>s
|
|
|
|
var altFromCaption = captionNode ? captionNode.textContent.trim() : '';
|
|
|
|
var altTextSame = img.hasAttribute( 'alt' ) && altFromCaption &&
|
|
|
|
( img.getAttribute( 'alt' ).trim() === altFromCaption );
|
|
|
|
|
2021-10-13 12:57:45 +00:00
|
|
|
var caption;
|
2018-08-01 02:35:49 +00:00
|
|
|
if ( captionNode ) {
|
|
|
|
caption = converter.getDataFromDomClean( captionNode, { type: 'mwGalleryImageCaption' } );
|
2016-11-19 23:11:06 +00:00
|
|
|
} else {
|
2018-08-01 02:35:49 +00:00
|
|
|
caption = [
|
|
|
|
{ type: 'mwGalleryImageCaption' },
|
|
|
|
{ type: 'paragraph', internal: { generated: 'wrapper' } },
|
|
|
|
{ type: '/paragraph' },
|
|
|
|
{ type: '/mwGalleryImageCaption' }
|
|
|
|
];
|
2016-11-19 23:11:06 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 18:35:52 +00:00
|
|
|
var typeofAttrs = container.getAttribute( 'typeof' ).trim().split( /\s+/ );
|
2022-05-23 14:18:20 +00:00
|
|
|
var errorIndex = typeofAttrs.indexOf( 'mw:Error' );
|
|
|
|
var isError = errorIndex !== -1;
|
2023-02-16 02:28:35 +00:00
|
|
|
var errorText = isError ? img.textContent : null;
|
2022-05-23 14:18:20 +00:00
|
|
|
var width = img.getAttribute( isError ? 'data-width' : 'width' );
|
|
|
|
var height = img.getAttribute( isError ? 'data-height' : 'height' );
|
2022-05-21 12:59:31 +00:00
|
|
|
|
2022-05-30 23:17:46 +00:00
|
|
|
if ( isError ) {
|
|
|
|
typeofAttrs.splice( errorIndex, 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
var types = ve.dm.MWImageNode.static.rdfaToTypes[ typeofAttrs[ 0 ] ];
|
|
|
|
|
2021-10-13 12:57:45 +00:00
|
|
|
var dataElement = {
|
2016-11-19 23:11:06 +00:00
|
|
|
type: this.name,
|
|
|
|
attributes: {
|
2022-05-30 23:17:46 +00:00
|
|
|
mediaClass: types.mediaClass,
|
|
|
|
mediaTag: img.nodeName.toLowerCase(),
|
Parse relative hrefs on image nodes like on regular links (try 2)
Previous, reverted attempt: da9b6fffbdba9007cfe893b1eafd1ccb42dbb537.
This attempt also includes 6037fefbe044fbf61e7734923f3b625f10e910a1,
and fixes minor conflicts with other changes.
* In normal images, parse relative 'href' attributes instead of
expanding them to absolute. This resolves Parsoid generating
|link= options for copy-pasted images (T193253).
Keep them in the underscore-form to avoid causing dirty diffs like
T237040 again. Unlike in the previous attempt, we don't need to be
super-careful about the 'resource' attribute, thanks to the Parsoid
changes in T108504.
* In gallery images stuff, prefix the 'resource' attribute with './',
same as normal images do. This causes no functional changes, but it
makes updating tests easier, and the consistency is probably good.
* Update test examples to also prefix 'resource' and relative 'href'
attributes with './', like the real Parsoid does.
Bug: T193253
Change-Id: I91131728a87c9406bf069d46d3c94c9a8905a003
2019-09-10 18:57:05 +00:00
|
|
|
resource: './' + mw.libs.ve.normalizeParsoidResourceName( img.getAttribute( 'resource' ) ),
|
2016-11-19 23:11:06 +00:00
|
|
|
altText: img.getAttribute( 'alt' ),
|
Add a checkbox to use the image caption as the alt text for galleries
The need for something like this was anticipated in
I2bf43c7e83283f43e047229eb53c244918fcbb0c.
As of version 2.5.0 of Parsoid's output, if alternate text is missing
for an image but a caption is present and image isn't displaying the
caption (ie. it isn't a thumb or frame), then the text content of the
caption will be set as the alt attribute. Parsoid will then drop the
alt attribute when serializing if it matches the caption text, since
it's unnecessary.
However, if the caption is modified and the alt text isn't, the alt will
be serialized. This is likely to be unexpected to editor. They may
have missed that the both the caption and alt are populated in VE and
only edited one place.
Since all of the above is happening only for images where the caption
isn't visible, it doesn't appear to be a much used feature since, at
least for inline images, the experience of caption editing was already
less than optimal.
However, because of a quirk in how galleries are rendered in Parsoid,
this affects gallery caption editing, which is visible and presumably
used more often. See T268250 for a discussion on an improved gallery
structure. But for now, gallery images are effectively inline and set
the alternate text, thus subject to the above.
Here we add a checkbox so that the default is to ignore the alt if it's
the same as the caption. And only make use of it if it differed
originally or was explicitly unchecked to modify.
Bug: T311677
Change-Id: Idf297d8a98995971c5835b0cea56c3317a3626e2
2022-07-04 21:01:40 +00:00
|
|
|
altTextSame: altTextSame,
|
2018-04-05 21:14:16 +00:00
|
|
|
// 'src' for images, 'poster' for video/audio
|
|
|
|
src: img.getAttribute( 'src' ) || img.getAttribute( 'poster' ),
|
2022-05-21 12:59:31 +00:00
|
|
|
width: width !== null && width !== '' ? +width : null,
|
|
|
|
height: height !== null && height !== '' ? +height : null,
|
2023-02-16 02:28:35 +00:00
|
|
|
isError: isError,
|
|
|
|
errorText: errorText
|
2016-11-19 23:11:06 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return [ dataElement ]
|
|
|
|
.concat( caption )
|
|
|
|
.concat( { type: '/' + this.name } );
|
|
|
|
};
|
|
|
|
|
|
|
|
ve.dm.MWGalleryImageNode.static.toDomElements = function ( data, doc ) {
|
|
|
|
// ImageNode:
|
2019-12-31 00:17:40 +00:00
|
|
|
// <li> li (gallerybox)
|
2018-11-21 12:33:32 +00:00
|
|
|
// <div> thumbDiv
|
2022-05-31 18:35:52 +00:00
|
|
|
// <span> container
|
2018-11-21 12:33:32 +00:00
|
|
|
// <a> a
|
2022-05-23 14:18:20 +00:00
|
|
|
// <img> img (or span if error)
|
2016-11-19 23:11:06 +00:00
|
|
|
var model = data,
|
2022-05-23 14:18:20 +00:00
|
|
|
attributes = model.attributes,
|
2016-11-19 23:11:06 +00:00
|
|
|
li = doc.createElement( 'li' ),
|
|
|
|
thumbDiv = doc.createElement( 'div' ),
|
2022-05-31 18:35:52 +00:00
|
|
|
container = doc.createElement( 'span' ),
|
2016-11-19 23:11:06 +00:00
|
|
|
a = doc.createElement( 'a' ),
|
2022-05-30 23:17:46 +00:00
|
|
|
img = doc.createElement( attributes.isError ? 'span' : ( attributes.mediaTag || 'img' ) ),
|
2022-05-23 14:18:20 +00:00
|
|
|
alt = attributes.altText;
|
2016-11-19 23:11:06 +00:00
|
|
|
|
2022-05-30 23:17:46 +00:00
|
|
|
// FIXME: attributes.mediaTag and attributes.mediaClass aren't set after edit
|
|
|
|
|
2016-11-19 23:11:06 +00:00
|
|
|
li.classList.add( 'gallerybox' );
|
|
|
|
thumbDiv.classList.add( 'thumb' );
|
2022-05-30 23:17:46 +00:00
|
|
|
container.setAttribute( 'typeof', ve.dm.MWImageNode.static.getRdfa(
|
|
|
|
( attributes.mediaClass || 'File' ), 'none', attributes.isError
|
|
|
|
) );
|
2016-11-19 23:11:06 +00:00
|
|
|
|
|
|
|
// TODO: Support editing the link
|
2022-03-09 19:33:42 +00:00
|
|
|
// FIXME: Dropping the href causes Parsoid to mark the node as wrapper modified,
|
|
|
|
// making the whole gallery subtree edited, preventing selser. When fixing,
|
|
|
|
// preserving the imgWrapperClassAttr, as in the MW*ImageNodes, will also be
|
|
|
|
// necessary.
|
2022-05-23 14:18:20 +00:00
|
|
|
// a.setAttribute( 'href', attributes.src );
|
2016-11-19 23:11:06 +00:00
|
|
|
|
2022-05-23 14:18:20 +00:00
|
|
|
img.setAttribute( 'resource', attributes.resource );
|
|
|
|
if ( attributes.isError ) {
|
|
|
|
img.classList.add( 'mw-broken-media' );
|
|
|
|
var filename = mw.libs.ve.normalizeParsoidResourceName( attributes.resource || '' );
|
2023-02-16 02:28:35 +00:00
|
|
|
img.appendChild( doc.createTextNode( attributes.errorText ? attributes.errorText : filename ) );
|
2022-05-23 14:18:20 +00:00
|
|
|
} else {
|
2022-05-30 23:17:46 +00:00
|
|
|
var srcAttr = ve.dm.MWImageNode.static.tagsToSrcAttrs[ img.nodeName.toLowerCase() ];
|
|
|
|
img.setAttribute( srcAttr, attributes.src );
|
2022-05-23 14:18:20 +00:00
|
|
|
}
|
|
|
|
img.setAttribute( attributes.isError ? 'data-width' : 'width', attributes.width );
|
|
|
|
img.setAttribute( attributes.isError ? 'data-height' : 'height', attributes.height );
|
Add a checkbox to use the image caption as the alt text for galleries
The need for something like this was anticipated in
I2bf43c7e83283f43e047229eb53c244918fcbb0c.
As of version 2.5.0 of Parsoid's output, if alternate text is missing
for an image but a caption is present and image isn't displaying the
caption (ie. it isn't a thumb or frame), then the text content of the
caption will be set as the alt attribute. Parsoid will then drop the
alt attribute when serializing if it matches the caption text, since
it's unnecessary.
However, if the caption is modified and the alt text isn't, the alt will
be serialized. This is likely to be unexpected to editor. They may
have missed that the both the caption and alt are populated in VE and
only edited one place.
Since all of the above is happening only for images where the caption
isn't visible, it doesn't appear to be a much used feature since, at
least for inline images, the experience of caption editing was already
less than optimal.
However, because of a quirk in how galleries are rendered in Parsoid,
this affects gallery caption editing, which is visible and presumably
used more often. See T268250 for a discussion on an improved gallery
structure. But for now, gallery images are effectively inline and set
the alternate text, thus subject to the above.
Here we add a checkbox so that the default is to ignore the alt if it's
the same as the caption. And only make use of it if it differed
originally or was explicitly unchecked to modify.
Bug: T311677
Change-Id: Idf297d8a98995971c5835b0cea56c3317a3626e2
2022-07-04 21:01:40 +00:00
|
|
|
|
|
|
|
if ( typeof alt === 'string' && !attributes.altTextSame ) {
|
2016-11-19 23:11:06 +00:00
|
|
|
img.setAttribute( 'alt', alt );
|
|
|
|
}
|
|
|
|
|
|
|
|
a.appendChild( img );
|
2022-05-31 18:35:52 +00:00
|
|
|
container.appendChild( a );
|
|
|
|
thumbDiv.appendChild( container );
|
2019-12-31 00:17:40 +00:00
|
|
|
li.appendChild( thumbDiv );
|
2016-11-19 23:11:06 +00:00
|
|
|
|
|
|
|
return [ li ];
|
|
|
|
};
|
|
|
|
|
2018-04-04 16:28:26 +00:00
|
|
|
ve.dm.MWGalleryImageNode.static.describeChange = function ( key ) {
|
2022-07-12 15:59:02 +00:00
|
|
|
if ( key === 'altText' ) {
|
2022-07-01 12:14:49 +00:00
|
|
|
// Parent method
|
|
|
|
return ve.dm.MWGalleryImageNode.super.static.describeChange.apply( this, arguments );
|
2018-04-04 16:28:26 +00:00
|
|
|
}
|
2022-07-12 15:59:02 +00:00
|
|
|
// All other attributes are computed, or result in nodes being incomparable (`resource`)
|
2022-07-01 12:14:49 +00:00
|
|
|
return null;
|
2018-04-04 16:28:26 +00:00
|
|
|
};
|
|
|
|
|
2022-05-19 00:28:10 +00:00
|
|
|
ve.dm.MWGalleryImageNode.static.isDiffComparable = function ( element, other ) {
|
|
|
|
// Images with different src's shouldn't be diffed
|
|
|
|
return element.type === other.type && element.attributes.resource === other.attributes.resource;
|
|
|
|
};
|
|
|
|
|
2016-11-19 23:11:06 +00:00
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the image's caption node.
|
|
|
|
*
|
|
|
|
* @return {ve.dm.MWImageCaptionNode|null} Caption node, if present
|
|
|
|
*/
|
|
|
|
ve.dm.MWGalleryImageNode.prototype.getCaptionNode = function () {
|
|
|
|
return this.children.length > 0 ? this.children[ 0 ] : null;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Registration */
|
|
|
|
|
|
|
|
ve.dm.modelRegistry.register( ve.dm.MWGalleryImageNode );
|