mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-12-02 01:46:47 +00:00
89aecd54ba
There are several conditions to defaultSize behavior of thumbnails and frameless images and other images when it comes to default size. In the same principle is 'border' which is not quite a type despite the fact it 'behaves' as such in wikitext (and has a unique identifier that comes instead of the other types. This commit aims to organize this behavior for the user in an understandable manner. * Add 'basic' image type for images that have no specified type ('none') * Handle the difference in 'default' size behavior between basic images and thumbnails/frameless. The thumb/frameless images have the default wiki size. Other images' default size is their original dimensions. * Force wiki-configured default size for thumbnails and frameless images in the DM. This is done because at the moment Parsoid's output is of Wikipedia's default size rather than the local wiki's. The size is adapted if needed, directly in the DM. * Added 'border' as a pseudo-type checkbox flag that sets css class 'mw-image-border' is for parsoid rendering on save. * Add 'make full size' to the size widget select and treat it as a faux default button for basic and frame images. Bug: 62013 Bug: 62024 Bug: 61155 Bug: 61059 Bug: 61282 Change-Id: I6778705306f0dd6bb96afeb91383089a4ddab7ed
268 lines
8.1 KiB
JavaScript
268 lines
8.1 KiB
JavaScript
/*!
|
|
* VisualEditor DataModel MWBlockImageNode class.
|
|
*
|
|
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
/*global mw */
|
|
|
|
/**
|
|
* DataModel MediaWiki image node.
|
|
*
|
|
* @class
|
|
* @extends ve.dm.BranchNode
|
|
* @mixins ve.dm.MWImageNode
|
|
* @constructor
|
|
* @param {number} [length] Length of content data in document
|
|
* @param {Object} [element] Reference to element in linear model
|
|
*/
|
|
ve.dm.MWBlockImageNode = function VeDmMWBlockImageNode( length, element ) {
|
|
// Parent constructor
|
|
ve.dm.BranchNode.call( this, 0, element );
|
|
|
|
// Mixin constructors
|
|
ve.dm.MWImageNode.call( this );
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
OO.inheritClass( ve.dm.MWBlockImageNode, ve.dm.BranchNode );
|
|
|
|
// Need to mixin base class as well
|
|
OO.mixinClass( ve.dm.MWBlockImageNode, ve.dm.GeneratedContentNode );
|
|
|
|
OO.mixinClass( ve.dm.MWBlockImageNode, ve.dm.MWImageNode );
|
|
|
|
/* Static Properties */
|
|
|
|
ve.dm.MWBlockImageNode.static.rdfaToType = {
|
|
'mw:Image/Thumb': 'thumb',
|
|
'mw:Image/Frame': 'frame',
|
|
'mw:Image/Frameless': 'frameless',
|
|
'mw:Image': 'none'
|
|
};
|
|
|
|
ve.dm.MWBlockImageNode.static.name = 'mwBlockImage';
|
|
|
|
ve.dm.MWBlockImageNode.static.storeHtmlAttributes = {
|
|
'blacklist': [ 'typeof', 'class', 'src', 'resource', 'width', 'height', 'href', 'rel' ]
|
|
};
|
|
|
|
ve.dm.MWBlockImageNode.static.handlesOwnChildren = true;
|
|
|
|
ve.dm.MWBlockImageNode.static.childNodeTypes = [ 'mwImageCaption' ];
|
|
|
|
ve.dm.MWBlockImageNode.static.matchTagNames = [ 'figure' ];
|
|
|
|
ve.dm.MWBlockImageNode.static.blacklistedAnnotationTypes = [ 'link' ];
|
|
|
|
ve.dm.MWBlockImageNode.static.getMatchRdfaTypes = function () {
|
|
return ve.getObjectKeys( this.rdfaToType );
|
|
};
|
|
|
|
ve.dm.MWBlockImageNode.static.toDataElement = function ( domElements, converter ) {
|
|
var dataElement,
|
|
$figure = $( domElements[0] ),
|
|
// images with link='' have a span wrapper instead
|
|
$imgWrapper = $figure.children( 'a, span' ).eq( 0 ),
|
|
$img = $imgWrapper.children( 'img' ).eq( 0 ),
|
|
$caption = $figure.children( 'figcaption' ).eq( 0 ),
|
|
typeofAttr = $figure.attr( 'typeof' ),
|
|
classes = $figure.attr( 'class' ),
|
|
recognizedClasses = [],
|
|
attributes = {
|
|
type: this.rdfaToType[typeofAttr],
|
|
href: $imgWrapper.attr( 'href' ) || '',
|
|
src: $img.attr( 'src' ),
|
|
resource: $img.attr( 'resource' ),
|
|
originalClasses: classes
|
|
},
|
|
width = $img.attr( 'width' ),
|
|
height = $img.attr( 'height' ),
|
|
altText = $img.attr( 'alt' ),
|
|
defaultSizeBoundingBox = mw.config.get( 'wgVisualEditorConfig' )
|
|
.defaultUserOptions.defaultthumbsize;
|
|
|
|
if ( altText !== undefined ) {
|
|
attributes.alt = altText;
|
|
}
|
|
|
|
// Extract individual classes
|
|
classes = typeof classes === 'string' ? classes.trim().split( /\s+/ ) : [];
|
|
|
|
// Deal with border flag
|
|
if ( classes.indexOf( 'mw-image-border' ) !== -1 ) {
|
|
attributes.borderImage = true;
|
|
recognizedClasses.push( 'mw-image-border' );
|
|
}
|
|
|
|
// Horizontal alignment
|
|
if ( classes.indexOf( 'mw-halign-left' ) !== -1 ) {
|
|
attributes.align = 'left';
|
|
recognizedClasses.push( 'mw-halign-left' );
|
|
} else if ( classes.indexOf( 'mw-halign-right' ) !== -1 ) {
|
|
attributes.align = 'right';
|
|
recognizedClasses.push( 'mw-halign-right' );
|
|
} else if ( classes.indexOf( 'mw-halign-center' ) !== -1 ) {
|
|
attributes.align = 'center';
|
|
recognizedClasses.push( 'mw-halign-center' );
|
|
} else if ( classes.indexOf( 'mw-halign-none' ) !== -1 ) {
|
|
attributes.align = 'none';
|
|
recognizedClasses.push( 'mw-halign-none' );
|
|
} else {
|
|
attributes.align = 'default';
|
|
}
|
|
|
|
attributes.width = width !== undefined && width !== '' ? Number( width ) : null;
|
|
attributes.height = height !== undefined && height !== '' ? Number( height ) : null;
|
|
|
|
// Default-size
|
|
if ( classes.indexOf( 'mw-default-size' ) !== -1 ) {
|
|
// Flag as default size
|
|
attributes.defaultSize = true;
|
|
recognizedClasses.push( 'mw-default-size' );
|
|
// Force wiki-default size for thumb and frameless
|
|
if (
|
|
attributes.type === 'thumb' ||
|
|
attributes.type === 'frameless'
|
|
) {
|
|
// Parsoid hands us images with default Wikipedia dimensions
|
|
// rather than default MediaWiki configuration dimensions.
|
|
// We must force local wiki default in edit mode for default
|
|
// size images.
|
|
if ( attributes.width > attributes.height ) {
|
|
if ( attributes.height !== null ) {
|
|
attributes.height = ( attributes.height / attributes.width ) * defaultSizeBoundingBox;
|
|
}
|
|
attributes.width = defaultSizeBoundingBox;
|
|
} else {
|
|
if ( attributes.width !== null ) {
|
|
attributes.width = ( attributes.width / attributes.height ) * defaultSizeBoundingBox;
|
|
}
|
|
attributes.height = defaultSizeBoundingBox;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store unrecognized classes so we can restore them on the way out
|
|
attributes.unrecognizedClasses = OO.simpleArrayDifference( classes, recognizedClasses );
|
|
|
|
dataElement = { 'type': this.name, 'attributes': attributes };
|
|
|
|
this.storeGeneratedContents( dataElement, dataElement.attributes.src, converter.getStore() );
|
|
|
|
if ( $caption.length === 0 ) {
|
|
return [
|
|
dataElement,
|
|
{ 'type': 'mwImageCaption' },
|
|
{ 'type': '/mwImageCaption' },
|
|
{ 'type': '/' + this.name }
|
|
];
|
|
} else {
|
|
return [ dataElement ].
|
|
concat( converter.getDataFromDomClean( $caption[0], { 'type': 'mwImageCaption' } ) ).
|
|
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.MWBlockImageNode.static.toDomElements = function ( data, doc, converter ) {
|
|
var dataElement = data[0],
|
|
figure = doc.createElement( 'figure' ),
|
|
imgWrapper = doc.createElement( dataElement.attributes.href !== '' ? 'a' : 'span' ),
|
|
img = doc.createElement( 'img' ),
|
|
wrapper = doc.createElement( 'div' ),
|
|
classes = [],
|
|
originalClasses = dataElement.attributes.originalClasses,
|
|
captionData = data.slice( 1, -1 ),
|
|
rdfa;
|
|
|
|
if ( !this.typeToRdfa ) {
|
|
this.typeToRdfa = {};
|
|
for ( rdfa in this.rdfaToType ) {
|
|
this.typeToRdfa[this.rdfaToType[rdfa]] = rdfa;
|
|
}
|
|
}
|
|
|
|
// Type
|
|
figure.setAttribute( 'typeof', this.typeToRdfa[dataElement.attributes.type] );
|
|
if ( dataElement.attributes.borderImage === true ) {
|
|
classes.push( 'mw-image-border' );
|
|
}
|
|
|
|
// Apply classes if size is default
|
|
if ( dataElement.attributes.defaultSize === true ) {
|
|
classes.push( 'mw-default-size' );
|
|
}
|
|
|
|
// Horizontal alignment
|
|
switch ( dataElement.attributes.align ) {
|
|
case 'left':
|
|
classes.push( 'mw-halign-left' );
|
|
break;
|
|
case 'right':
|
|
classes.push( 'mw-halign-right' );
|
|
break;
|
|
case 'center':
|
|
classes.push( 'mw-halign-center' );
|
|
break;
|
|
case 'none':
|
|
classes.push( 'mw-halign-none' );
|
|
break;
|
|
}
|
|
|
|
if ( dataElement.attributes.unrecognizedClasses ) {
|
|
classes = OO.simpleArrayUnion( classes, dataElement.attributes.unrecognizedClasses );
|
|
}
|
|
|
|
if (
|
|
originalClasses &&
|
|
ve.compare( originalClasses.trim().split( /\s+/ ).sort(), classes.sort() )
|
|
) {
|
|
figure.className = originalClasses;
|
|
} else if ( classes.length > 0 ) {
|
|
figure.className = classes.join( ' ' );
|
|
}
|
|
if ( dataElement.attributes.href !== '' ) {
|
|
imgWrapper.setAttribute( 'href', dataElement.attributes.href );
|
|
}
|
|
img.setAttribute( 'src', dataElement.attributes.src );
|
|
img.setAttribute( 'width', dataElement.attributes.width );
|
|
img.setAttribute( 'height', dataElement.attributes.height );
|
|
img.setAttribute( 'resource', dataElement.attributes.resource );
|
|
if ( dataElement.attributes.alt !== undefined ) {
|
|
img.setAttribute( 'alt', dataElement.attributes.alt );
|
|
}
|
|
figure.appendChild( imgWrapper );
|
|
imgWrapper.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.MWImageCaptionNode|null} Caption node, if present
|
|
*/
|
|
ve.dm.MWBlockImageNode.prototype.getCaptionNode = function () {
|
|
var node = this.children[0];
|
|
return node instanceof ve.dm.MWImageCaptionNode ? node : null;
|
|
};
|
|
|
|
/* Registration */
|
|
|
|
ve.dm.modelRegistry.register( ve.dm.MWBlockImageNode );
|