mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 02:23:58 +00:00
Support the upcoming mw:File typeof
The "mediaClass" property now only serves to capture the original class found on the media so that it can be roundtripped without causing dirty diffs. In the 2.4.0 version of Parsoid's output, that will still be the usual Image/Audio/Video. As of 2.5.0, it will always be File and the mediaClass property can be dropped. Parsoid is currently forward compatible with serializing mw:File, so edited or new media can use that type already. The contextmenu item for media has been updated to make use of the "mediaTag" instead of mediaClass to continue distinguishing media types. That was the only place a grep of mediaClass turned up any use. Bug: T273505 Change-Id: If5dc6b794dacd6973d3b2093e6b385591b91d539
This commit is contained in:
parent
03a2523501
commit
0533f49fd5
|
@ -130,7 +130,8 @@ ve.dm.MWImageModel.static.createImageNode = function ( attributes, imageType ) {
|
|||
.thumbLimits[ mw.user.options.get( 'thumbsize' ) ];
|
||||
|
||||
var attrs = ve.extendObject( {
|
||||
mediaClass: 'Image',
|
||||
mediaClass: 'File',
|
||||
mediaTag: 'img',
|
||||
type: 'thumb',
|
||||
align: 'default',
|
||||
width: defaultThumbSize,
|
||||
|
@ -562,7 +563,8 @@ ve.dm.MWImageModel.prototype.getUpdatedAttributes = function () {
|
|||
}
|
||||
|
||||
var attrs = {
|
||||
mediaClass: this.getMediaClass(),
|
||||
mediaClass: 'File',
|
||||
mediaTag: this.getMediaTag(),
|
||||
type: this.getType(),
|
||||
width: currentDimensions.width,
|
||||
height: currentDimensions.height,
|
||||
|
@ -798,20 +800,20 @@ ve.dm.MWImageModel.prototype.getMediaType = function () {
|
|||
};
|
||||
|
||||
/**
|
||||
* Get Parsoid media class: Image, Video or Audio
|
||||
* Get media tag: img, video or audio
|
||||
*
|
||||
* @return {string} Media class
|
||||
* @return {string} Tag name
|
||||
*/
|
||||
ve.dm.MWImageModel.prototype.getMediaClass = function () {
|
||||
ve.dm.MWImageModel.prototype.getMediaTag = function () {
|
||||
var mediaType = this.getMediaType();
|
||||
|
||||
if ( mediaType === 'VIDEO' ) {
|
||||
return 'Video';
|
||||
return 'video';
|
||||
}
|
||||
if ( mediaType === 'AUDIO' ) {
|
||||
return 'Audio';
|
||||
return 'audio';
|
||||
}
|
||||
return 'Image';
|
||||
return 'img';
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -95,6 +95,7 @@ ve.dm.MWBlockImageNode.static.toDataElement = function ( domElements, converter
|
|||
|
||||
var attributes = {
|
||||
mediaClass: types.mediaClass,
|
||||
mediaTag: img.nodeName.toLowerCase(),
|
||||
type: types.frameType,
|
||||
src: img.getAttribute( 'src' ) || img.getAttribute( 'poster' ),
|
||||
href: href,
|
||||
|
@ -162,16 +163,15 @@ ve.dm.MWBlockImageNode.static.toDataElement = function ( domElements, converter
|
|||
ve.dm.MWBlockImageNode.static.toDomElements = function ( data, doc, converter ) {
|
||||
var dataElement = data[ 0 ],
|
||||
attributes = dataElement.attributes,
|
||||
mediaClass = attributes.mediaClass,
|
||||
figure = doc.createElement( 'figure' ),
|
||||
imgWrapper = doc.createElement( attributes.href ? 'a' : 'span' ),
|
||||
img = doc.createElement( attributes.isError ? 'span' : this.typesToTags[ mediaClass ] ),
|
||||
img = doc.createElement( attributes.isError ? 'span' : attributes.mediaTag ),
|
||||
wrapper = doc.createElement( 'div' ),
|
||||
classAttr = this.getClassAttrFromAttributes( attributes ),
|
||||
captionData = data.slice( 1, -1 );
|
||||
|
||||
// RDFa type
|
||||
figure.setAttribute( 'typeof', this.getRdfa( mediaClass, attributes.type, attributes.isError ) );
|
||||
figure.setAttribute( 'typeof', this.getRdfa( attributes.mediaClass, attributes.type, attributes.isError ) );
|
||||
if ( !ve.isEmptyObject( attributes.mw ) ) {
|
||||
figure.setAttribute( 'data-mw', JSON.stringify( attributes.mw ) );
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ ve.dm.MWBlockImageNode.static.toDomElements = function ( data, doc, converter )
|
|||
}
|
||||
}
|
||||
|
||||
var srcAttr = this.typesToSrcAttrs[ mediaClass ];
|
||||
var srcAttr = this.tagsToSrcAttrs[ img.nodeName.toLowerCase() ];
|
||||
if ( srcAttr && !attributes.isError ) {
|
||||
img.setAttribute( srcAttr, attributes.src );
|
||||
}
|
||||
|
|
|
@ -75,9 +75,17 @@ ve.dm.MWGalleryImageNode.static.toDataElement = function ( domElements, converte
|
|||
var width = img.getAttribute( isError ? 'data-width' : 'width' );
|
||||
var height = img.getAttribute( isError ? 'data-height' : 'height' );
|
||||
|
||||
if ( isError ) {
|
||||
typeofAttrs.splice( errorIndex, 1 );
|
||||
}
|
||||
|
||||
var types = ve.dm.MWImageNode.static.rdfaToTypes[ typeofAttrs[ 0 ] ];
|
||||
|
||||
var dataElement = {
|
||||
type: this.name,
|
||||
attributes: {
|
||||
mediaClass: types.mediaClass,
|
||||
mediaTag: img.nodeName.toLowerCase(),
|
||||
resource: './' + mw.libs.ve.normalizeParsoidResourceName( img.getAttribute( 'resource' ) ),
|
||||
altText: img.getAttribute( 'alt' ),
|
||||
// 'src' for images, 'poster' for video/audio
|
||||
|
@ -106,12 +114,16 @@ ve.dm.MWGalleryImageNode.static.toDomElements = function ( data, doc ) {
|
|||
thumbDiv = doc.createElement( 'div' ),
|
||||
container = doc.createElement( 'span' ),
|
||||
a = doc.createElement( 'a' ),
|
||||
img = doc.createElement( attributes.isError ? 'span' : 'img' ),
|
||||
img = doc.createElement( attributes.isError ? 'span' : ( attributes.mediaTag || 'img' ) ),
|
||||
alt = attributes.altText;
|
||||
|
||||
// FIXME: attributes.mediaTag and attributes.mediaClass aren't set after edit
|
||||
|
||||
li.classList.add( 'gallerybox' );
|
||||
thumbDiv.classList.add( 'thumb' );
|
||||
container.setAttribute( 'typeof', 'mw:Image' );
|
||||
container.setAttribute( 'typeof', ve.dm.MWImageNode.static.getRdfa(
|
||||
( attributes.mediaClass || 'File' ), 'none', attributes.isError
|
||||
) );
|
||||
|
||||
// TODO: Support editing the link
|
||||
// FIXME: Dropping the href causes Parsoid to mark the node as wrapper modified,
|
||||
|
@ -126,7 +138,8 @@ ve.dm.MWGalleryImageNode.static.toDomElements = function ( data, doc ) {
|
|||
var filename = mw.libs.ve.normalizeParsoidResourceName( attributes.resource || '' );
|
||||
img.appendChild( doc.createTextNode( filename ) );
|
||||
} else {
|
||||
img.setAttribute( 'src', attributes.src );
|
||||
var srcAttr = ve.dm.MWImageNode.static.tagsToSrcAttrs[ img.nodeName.toLowerCase() ];
|
||||
img.setAttribute( srcAttr, attributes.src );
|
||||
}
|
||||
img.setAttribute( attributes.isError ? 'data-width' : 'width', attributes.width );
|
||||
img.setAttribute( attributes.isError ? 'data-height' : 'height', attributes.height );
|
||||
|
|
|
@ -55,7 +55,7 @@ OO.mixinClass( ve.dm.MWImageNode, ve.dm.ResizableNode );
|
|||
ve.dm.MWImageNode.static.rdfaToTypes = ( function () {
|
||||
var rdfaToType = {};
|
||||
|
||||
[ 'Image', 'Video', 'Audio' ].forEach( function ( mediaClass ) {
|
||||
[ 'File', 'Image', 'Video', 'Audio' ].forEach( function ( mediaClass ) {
|
||||
rdfaToType[ 'mw:' + mediaClass ] = { mediaClass: mediaClass, frameType: 'none' };
|
||||
rdfaToType[ 'mw:' + mediaClass + '/Frameless' ] = { mediaClass: mediaClass, frameType: 'frameless' };
|
||||
// Block image only:
|
||||
|
@ -70,7 +70,7 @@ ve.dm.MWImageNode.static.rdfaToTypes = ( function () {
|
|||
* Get RDFa type
|
||||
*
|
||||
* @static
|
||||
* @param {string} mediaClass Media class, one of 'Image', 'Video' or 'Audio'
|
||||
* @param {string} mediaClass Media class, one of 'File', 'Image', 'Video' or 'Audio'
|
||||
* @param {string} frameType Frame type, one of 'none', 'frameless', 'thumb' or 'frame'
|
||||
* @param {boolean} isError Whether the included media file is missing
|
||||
* @return {string} RDFa type
|
||||
|
@ -86,25 +86,15 @@ ve.dm.MWImageNode.static.getRdfa = function ( mediaClass, frameType, isError ) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Map media types to tag names
|
||||
* Map media tags to source attributes
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
ve.dm.MWImageNode.static.typesToTags = {
|
||||
Image: 'img',
|
||||
Audio: 'audio',
|
||||
Video: 'video'
|
||||
};
|
||||
|
||||
/**
|
||||
* Map media types to source attributes
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
ve.dm.MWImageNode.static.typesToSrcAttrs = {
|
||||
Image: 'src',
|
||||
Audio: null,
|
||||
Video: 'poster'
|
||||
ve.dm.MWImageNode.static.tagsToSrcAttrs = {
|
||||
img: 'src',
|
||||
audio: null,
|
||||
video: 'poster',
|
||||
span: null
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,7 +54,7 @@ ve.dm.MWInlineImageNode.static.toDataElement = function ( domElements, converter
|
|||
// Malformed figure, alienate (T267282)
|
||||
return null;
|
||||
}
|
||||
var img = imgWrapper.children[ 0 ]; // <img>, <video> or <audio>
|
||||
var img = imgWrapper.children[ 0 ]; // <img>, <video>, <audio>, or <span> if mw:Error
|
||||
var typeofAttrs = ( container.getAttribute( 'typeof' ) || '' ).trim().split( /\s+/ );
|
||||
var mwDataJSON = container.getAttribute( 'data-mw' );
|
||||
var mwData = mwDataJSON ? JSON.parse( mwDataJSON ) : {};
|
||||
|
@ -83,6 +83,7 @@ ve.dm.MWInlineImageNode.static.toDataElement = function ( domElements, converter
|
|||
|
||||
var attributes = {
|
||||
mediaClass: types.mediaClass,
|
||||
mediaTag: img.nodeName.toLowerCase(),
|
||||
type: types.frameType,
|
||||
src: img.getAttribute( 'src' ) || img.getAttribute( 'poster' ),
|
||||
href: href,
|
||||
|
@ -142,9 +143,8 @@ ve.dm.MWInlineImageNode.static.toDataElement = function ( domElements, converter
|
|||
|
||||
ve.dm.MWInlineImageNode.static.toDomElements = function ( dataElement, doc, converter ) {
|
||||
var attributes = dataElement.attributes,
|
||||
mediaClass = attributes.mediaClass,
|
||||
container = doc.createElement( 'span' ),
|
||||
img = doc.createElement( attributes.isError ? 'span' : this.typesToTags[ mediaClass ] ),
|
||||
img = doc.createElement( attributes.isError ? 'span' : attributes.mediaTag ),
|
||||
classes = [],
|
||||
originalClasses = attributes.originalClasses;
|
||||
|
||||
|
@ -158,7 +158,7 @@ ve.dm.MWInlineImageNode.static.toDomElements = function ( dataElement, doc, conv
|
|||
img.setAttribute( attributes.isError ? 'data-width' : 'height', height );
|
||||
}
|
||||
|
||||
var srcAttr = this.typesToSrcAttrs[ mediaClass ];
|
||||
var srcAttr = this.tagsToSrcAttrs[ img.nodeName.toLowerCase() ];
|
||||
if ( srcAttr && !attributes.isError ) {
|
||||
img.setAttribute( srcAttr, attributes.src );
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ ve.dm.MWInlineImageNode.static.toDomElements = function ( dataElement, doc, conv
|
|||
}
|
||||
|
||||
// RDFa type
|
||||
container.setAttribute( 'typeof', this.getRdfa( mediaClass, attributes.type, attributes.isError ) );
|
||||
container.setAttribute( 'typeof', this.getRdfa( attributes.mediaClass, attributes.type, attributes.isError ) );
|
||||
if ( !ve.isEmptyObject( attributes.mw ) ) {
|
||||
container.setAttribute( 'data-mw', JSON.stringify( attributes.mw ) );
|
||||
}
|
||||
|
|
|
@ -283,6 +283,7 @@ ve.dm.mwExample.MWBlockImage = {
|
|||
imageClassAttr: null,
|
||||
imgWrapperClassAttr: null,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
src: ve.ce.minImgDataUri,
|
||||
width: 1,
|
||||
height: 2,
|
||||
|
@ -321,6 +322,7 @@ ve.dm.mwExample.MWInlineImage = {
|
|||
imageClassAttr: null,
|
||||
imgWrapperClassAttr: null,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
width: 135,
|
||||
height: 155,
|
||||
alt: 'alt text',
|
||||
|
@ -353,6 +355,7 @@ ve.dm.mwExample.MWInlineImageWithWrapperClass = {
|
|||
imageClassAttr: null,
|
||||
imgWrapperClassAttr: 'mw-file-description',
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
width: 135,
|
||||
height: 155,
|
||||
alt: 'alt text',
|
||||
|
@ -876,6 +879,8 @@ ve.dm.mwExample.domToDataCases = {
|
|||
{
|
||||
type: 'mwGalleryImage',
|
||||
attributes: {
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
altText: null,
|
||||
width: 120,
|
||||
height: 120,
|
||||
|
@ -922,6 +927,8 @@ ve.dm.mwExample.domToDataCases = {
|
|||
{
|
||||
type: 'mwGalleryImage',
|
||||
attributes: {
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'span',
|
||||
altText: null,
|
||||
width: 120,
|
||||
height: 120,
|
||||
|
@ -944,8 +951,8 @@ ve.dm.mwExample.domToDataCases = {
|
|||
{ type: 'internalList' },
|
||||
{ type: '/internalList' }
|
||||
],
|
||||
normalizedBody: '<ul class="gallery mw-gallery-packed-hover" typeof="mw:Extension/gallery" data-mw=\'{"attrs":{"mode":"packed-hover"},"body":{"extsrc":""},"name":"gallery"}\'><li class="gallerybox" style="width: 122px;"><div class="thumb"><span typeof="mw:Image"><a><span class="mw-broken-media" resource="./Foo" data-height="120" data-width="120">Foo</span></a></span></div><div class="gallerytext"></div></li></ul>',
|
||||
fromDataBody: '<ul typeof="mw:Extension/gallery" data-mw=\'{"attrs":{"mode":"packed-hover"},"body":{"extsrc":""},"name":"gallery"}\'><li class="gallerybox"><div class="thumb"><span typeof="mw:Image"><a><span class="mw-broken-media" resource="./Foo" data-height="120" data-width="120">Foo</span></a></span></div><div class="gallerytext"></div></li></ul>'
|
||||
normalizedBody: '<ul class="gallery mw-gallery-packed-hover" typeof="mw:Extension/gallery" data-mw=\'{"attrs":{"mode":"packed-hover"},"body":{"extsrc":""},"name":"gallery"}\'><li class="gallerybox" style="width: 122px;"><div class="thumb"><span typeof="mw:Error mw:Image"><a><span class="mw-broken-media" resource="./Foo" data-height="120" data-width="120">Foo</span></a></span></div><div class="gallerytext"></div></li></ul>',
|
||||
fromDataBody: '<ul typeof="mw:Extension/gallery" data-mw=\'{"attrs":{"mode":"packed-hover"},"body":{"extsrc":""},"name":"gallery"}\'><li class="gallerybox"><div class="thumb"><span typeof="mw:Error mw:Image"><a><span class="mw-broken-media" resource="./Foo" data-height="120" data-width="120">Foo</span></a></span></div><div class="gallerytext"></div></li></ul>'
|
||||
},
|
||||
'mwGalleryImage (empty caption in DOM)': {
|
||||
body: '<ul class="gallery mw-gallery-packed" typeof="mw:Extension/gallery" data-mw=\'{"attrs":{"mode":"packed"},"body":{"extsrc":""},"name":"gallery"}\'><li class="gallerybox" style="width: 122px;"><div class="thumb" style="width: 120px;"><span typeof="mw:Image"><a href="./Foo"><img resource="./Foo" src="' + ve.ce.minImgDataUri + '" height="120" width="120"/></a></span></div><div class="gallerytext"></div></li></ul>',
|
||||
|
@ -968,6 +975,8 @@ ve.dm.mwExample.domToDataCases = {
|
|||
{
|
||||
type: 'mwGalleryImage',
|
||||
attributes: {
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
altText: null,
|
||||
width: 120,
|
||||
height: 120,
|
||||
|
@ -1014,6 +1023,8 @@ ve.dm.mwExample.domToDataCases = {
|
|||
{
|
||||
type: 'mwGalleryImage',
|
||||
attributes: {
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
altText: null,
|
||||
width: 120,
|
||||
height: 120,
|
||||
|
@ -1060,6 +1071,8 @@ ve.dm.mwExample.domToDataCases = {
|
|||
{
|
||||
type: 'mwGalleryImage',
|
||||
attributes: {
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
altText: null,
|
||||
width: 120,
|
||||
height: 120,
|
||||
|
@ -1095,6 +1108,8 @@ ve.dm.mwExample.domToDataCases = {
|
|||
{
|
||||
type: 'mwGalleryImage',
|
||||
attributes: {
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
altText: null,
|
||||
width: 120,
|
||||
height: 120,
|
||||
|
@ -1126,6 +1141,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imgWrapperClassAttr: null,
|
||||
isError: false,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
mw: {},
|
||||
resource: './Foo',
|
||||
src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
|
||||
|
@ -1162,6 +1178,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imgWrapperClassAttr: null,
|
||||
isError: false,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
mw: {},
|
||||
resource: './Foo',
|
||||
src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
|
||||
|
@ -1197,6 +1214,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imgWrapperClassAttr: null,
|
||||
isError: false,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
mw: {},
|
||||
resource: './Foo',
|
||||
src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
|
||||
|
@ -1232,6 +1250,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imgWrapperClassAttr: null,
|
||||
isError: false,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
mw: {},
|
||||
resource: './Foo',
|
||||
src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
|
||||
|
@ -1258,6 +1277,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imgWrapperClassAttr: null,
|
||||
isError: false,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
mw: {},
|
||||
resource: './Foo',
|
||||
src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
|
||||
|
@ -2015,6 +2035,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imageClassAttr: 'mw-broken-media',
|
||||
imgWrapperClassAttr: null,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'span',
|
||||
src: null,
|
||||
defaultSize: true,
|
||||
width: 220,
|
||||
|
@ -2070,6 +2091,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imageClassAttr: 'mw-broken-media',
|
||||
imgWrapperClassAttr: null,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'span',
|
||||
src: null,
|
||||
width: 200,
|
||||
height: null,
|
||||
|
@ -2131,6 +2153,7 @@ ve.dm.mwExample.domToDataCases = {
|
|||
imageClassAttr: null,
|
||||
imgWrapperClassAttr: null,
|
||||
mediaClass: 'Image',
|
||||
mediaTag: 'img',
|
||||
src: ve.ce.minImgDataUri,
|
||||
width: 1,
|
||||
height: 2,
|
||||
|
|
|
@ -22,19 +22,23 @@ ve.ui.MWMediaContextItem = function VeUiMWMediaContextItem( context, model ) {
|
|||
// Initialization
|
||||
this.$element.addClass( 've-ui-mwMediaContextItem' );
|
||||
|
||||
var mediaClass = model.getAttribute( 'mediaClass' ) || 'Image';
|
||||
var mediaTag = model.getAttribute( 'mediaTag' ) || 'img';
|
||||
|
||||
this.setIcon( model.getAttribute( 'isError' ) ? 'imageBroken' : {
|
||||
Image: 'image',
|
||||
this.setIcon( {
|
||||
img: 'image',
|
||||
span: 'imageBroken',
|
||||
// TODO: Better icons for audio/video
|
||||
Audio: 'play',
|
||||
Video: 'play'
|
||||
}[ mediaClass ] );
|
||||
audio: 'play',
|
||||
video: 'play'
|
||||
}[ mediaTag ] );
|
||||
|
||||
var messagePostfix = ( mediaTag === 'audio' || mediaTag === 'video' ) ? mediaTag : 'image';
|
||||
|
||||
// The following messages are used here:
|
||||
// * visualeditor-media-title-audio
|
||||
// * visualeditor-media-title-image
|
||||
// * visualeditor-media-title-video
|
||||
this.setLabel( ve.msg( 'visualeditor-media-title-' + mediaClass.toLowerCase() ) );
|
||||
this.setLabel( ve.msg( 'visualeditor-media-title-' + messagePostfix ) );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
|
Loading…
Reference in a new issue