2013-04-25 00:47:17 +00:00
|
|
|
/*!
|
|
|
|
* VisualEditor DataModel MWBlockImageNode class.
|
|
|
|
*
|
2014-01-05 12:05:05 +00:00
|
|
|
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
|
2013-04-25 00:47:17 +00:00
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DataModel MediaWiki image node.
|
|
|
|
*
|
|
|
|
* @class
|
|
|
|
* @extends ve.dm.BranchNode
|
2013-10-15 12:18:11 +00:00
|
|
|
* @mixins ve.dm.MWImageNode
|
2013-04-25 00:47:17 +00:00
|
|
|
* @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 ) {
|
2013-10-15 12:18:11 +00:00
|
|
|
// Parent constructor
|
2013-04-25 00:47:17 +00:00
|
|
|
ve.dm.BranchNode.call( this, 0, element );
|
2013-10-15 12:18:11 +00:00
|
|
|
|
|
|
|
// Mixin constructors
|
|
|
|
ve.dm.MWImageNode.call( this );
|
2013-04-25 00:47:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Inheritance */
|
|
|
|
|
2013-10-11 21:44:09 +00:00
|
|
|
OO.inheritClass( ve.dm.MWBlockImageNode, ve.dm.BranchNode );
|
2013-04-25 00:47:17 +00:00
|
|
|
|
2013-10-15 12:18:11 +00:00
|
|
|
// Need to mixin base class as well
|
2013-10-11 21:44:09 +00:00
|
|
|
OO.mixinClass( ve.dm.MWBlockImageNode, ve.dm.GeneratedContentNode );
|
2013-10-15 12:18:11 +00:00
|
|
|
|
2013-10-11 21:44:09 +00:00
|
|
|
OO.mixinClass( ve.dm.MWBlockImageNode, ve.dm.MWImageNode );
|
2013-10-15 12:18:11 +00:00
|
|
|
|
2013-04-25 00:47:17 +00:00
|
|
|
/* Static Properties */
|
|
|
|
|
2013-09-05 20:39:12 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.rdfaToType = {
|
|
|
|
'mw:Image/Thumb': 'thumb',
|
|
|
|
'mw:Image/Frame': 'frame',
|
|
|
|
'mw:Image/Frameless': 'frameless',
|
|
|
|
'mw:Image': 'none'
|
|
|
|
};
|
|
|
|
|
2013-05-28 11:31:41 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.name = 'mwBlockImage';
|
2013-04-25 00:47:17 +00:00
|
|
|
|
2013-06-07 23:04:45 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.storeHtmlAttributes = {
|
|
|
|
'blacklist': [ 'typeof', 'class', 'src', 'resource', 'width', 'height', 'href', 'rel' ]
|
|
|
|
};
|
2013-05-20 23:54:03 +00:00
|
|
|
|
2013-04-25 00:47:17 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.handlesOwnChildren = true;
|
|
|
|
|
2013-05-28 11:31:41 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.childNodeTypes = [ 'mwImageCaption' ];
|
2013-04-25 00:47:17 +00:00
|
|
|
|
2013-06-19 21:08:45 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.matchTagNames = [ 'figure' ];
|
2013-09-19 18:03:04 +00:00
|
|
|
|
|
|
|
ve.dm.MWBlockImageNode.static.blacklistedAnnotationTypes = [ 'link' ];
|
2013-06-19 21:08:45 +00:00
|
|
|
|
2013-09-05 20:39:12 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.getMatchRdfaTypes = function () {
|
|
|
|
return Object.keys( this.rdfaToType );
|
|
|
|
};
|
2013-04-25 00:47:17 +00:00
|
|
|
|
|
|
|
ve.dm.MWBlockImageNode.static.toDataElement = function ( domElements, converter ) {
|
2013-10-15 12:18:11 +00:00
|
|
|
var dataElement,
|
|
|
|
$figure = $( domElements[0] ),
|
2013-08-15 19:36:49 +00:00
|
|
|
// images with link='' have a span wrapper instead
|
2013-08-16 19:03:46 +00:00
|
|
|
$imgWrapper = $figure.children( 'a, span' ).eq( 0 ),
|
|
|
|
$img = $imgWrapper.children( 'img' ).eq( 0 ),
|
2013-04-25 00:47:17 +00:00
|
|
|
$caption = $figure.children( 'figcaption' ).eq( 0 ),
|
2013-05-14 23:39:13 +00:00
|
|
|
typeofAttr = $figure.attr( 'typeof' ),
|
2013-05-15 23:23:42 +00:00
|
|
|
classes = $figure.attr( 'class' ),
|
2013-06-28 00:24:05 +00:00
|
|
|
recognizedClasses = [],
|
2013-05-14 23:39:13 +00:00
|
|
|
attributes = {
|
2013-09-05 20:39:12 +00:00
|
|
|
type: this.rdfaToType[typeofAttr],
|
2013-08-16 19:03:46 +00:00
|
|
|
href: $imgWrapper.attr( 'href' ) || '',
|
2013-05-14 23:39:13 +00:00
|
|
|
src: $img.attr( 'src' ),
|
2013-06-24 19:34:15 +00:00
|
|
|
resource: $img.attr( 'resource' ),
|
|
|
|
originalClasses: classes
|
2013-10-15 16:50:01 +00:00
|
|
|
},
|
|
|
|
width = $img.attr( 'width' ),
|
2013-12-28 10:29:35 +00:00
|
|
|
height = $img.attr( 'height' ),
|
|
|
|
altText = $img.attr( 'alt' );
|
|
|
|
|
|
|
|
if ( altText !== undefined ) {
|
|
|
|
attributes.alt = altText;
|
|
|
|
}
|
2013-10-15 16:50:01 +00:00
|
|
|
|
|
|
|
attributes.width = width !== undefined && width !== '' ? Number( width ) : null;
|
|
|
|
attributes.height = height !== undefined && height !== '' ? Number( height ) : null;
|
2013-05-14 23:39:13 +00:00
|
|
|
|
2013-05-15 23:23:42 +00:00
|
|
|
// Extract individual classes
|
2013-06-28 00:24:05 +00:00
|
|
|
classes = typeof classes === 'string' ? classes.trim().split( /\s+/ ) : [];
|
2013-05-15 23:23:42 +00:00
|
|
|
|
2013-05-14 23:39:13 +00:00
|
|
|
// Horizontal alignment
|
|
|
|
if ( classes.indexOf( 'mw-halign-left' ) !== -1 ) {
|
|
|
|
attributes.align = 'left';
|
2013-06-28 00:24:05 +00:00
|
|
|
recognizedClasses.push( 'mw-halign-left' );
|
2013-05-14 23:39:13 +00:00
|
|
|
} else if ( classes.indexOf( 'mw-halign-right' ) !== -1 ) {
|
|
|
|
attributes.align = 'right';
|
2013-06-28 00:24:05 +00:00
|
|
|
recognizedClasses.push( 'mw-halign-right' );
|
2013-05-14 23:39:13 +00:00
|
|
|
} else if ( classes.indexOf( 'mw-halign-center' ) !== -1 ) {
|
|
|
|
attributes.align = 'center';
|
2013-06-28 00:24:05 +00:00
|
|
|
recognizedClasses.push( 'mw-halign-center' );
|
2013-05-14 23:39:13 +00:00
|
|
|
} else if ( classes.indexOf( 'mw-halign-none' ) !== -1 ) {
|
|
|
|
attributes.align = 'none';
|
2013-06-28 00:24:05 +00:00
|
|
|
recognizedClasses.push( 'mw-halign-none' );
|
2013-05-14 23:39:13 +00:00
|
|
|
} else {
|
2013-06-05 21:34:53 +00:00
|
|
|
attributes.align = 'default';
|
2013-05-14 23:39:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Default-size
|
2013-06-06 20:49:29 +00:00
|
|
|
if ( classes.indexOf( 'mw-default-size' ) !== -1 ) {
|
2013-05-14 23:39:13 +00:00
|
|
|
attributes.defaultSize = true;
|
2013-06-28 00:24:05 +00:00
|
|
|
recognizedClasses.push( 'mw-default-size' );
|
2013-05-14 23:39:13 +00:00
|
|
|
}
|
|
|
|
|
2013-06-28 00:24:05 +00:00
|
|
|
// Store unrecognized classes so we can restore them on the way out
|
2013-10-23 01:26:50 +00:00
|
|
|
attributes.unrecognizedClasses = OO.simpleArrayDifference( classes, recognizedClasses );
|
2013-06-28 00:24:05 +00:00
|
|
|
|
2013-10-15 12:18:11 +00:00
|
|
|
dataElement = { 'type': this.name, 'attributes': attributes };
|
|
|
|
|
|
|
|
this.storeGeneratedContents( dataElement, dataElement.attributes.src, converter.getStore() );
|
|
|
|
|
2013-05-14 23:39:13 +00:00
|
|
|
if ( $caption.length === 0 ) {
|
2013-05-23 05:47:11 +00:00
|
|
|
return [
|
2013-10-15 12:18:11 +00:00
|
|
|
dataElement,
|
2013-05-28 11:31:41 +00:00
|
|
|
{ 'type': 'mwImageCaption' },
|
|
|
|
{ 'type': '/mwImageCaption' },
|
2013-09-05 20:39:12 +00:00
|
|
|
{ 'type': '/' + this.name }
|
2013-05-23 05:47:11 +00:00
|
|
|
];
|
2013-05-14 23:39:13 +00:00
|
|
|
} else {
|
2013-10-15 12:18:11 +00:00
|
|
|
return [ dataElement ].
|
2013-12-05 21:48:44 +00:00
|
|
|
concat( converter.getDataFromDomClean( $caption[0], { 'type': 'mwImageCaption' } ) ).
|
2013-09-05 20:39:12 +00:00
|
|
|
concat( [ { 'type': '/' + this.name } ] );
|
2013-05-14 23:39:13 +00:00
|
|
|
}
|
2013-04-25 00:47:17 +00:00
|
|
|
};
|
|
|
|
|
2013-06-05 21:34:53 +00:00
|
|
|
// 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.
|
2013-04-25 00:47:17 +00:00
|
|
|
ve.dm.MWBlockImageNode.static.toDomElements = function ( data, doc, converter ) {
|
|
|
|
var dataElement = data[0],
|
|
|
|
figure = doc.createElement( 'figure' ),
|
2013-08-16 19:03:46 +00:00
|
|
|
imgWrapper = doc.createElement( dataElement.attributes.href !== '' ? 'a' : 'span' ),
|
2013-04-25 00:47:17 +00:00
|
|
|
img = doc.createElement( 'img' ),
|
2013-06-24 19:34:15 +00:00
|
|
|
wrapper = doc.createElement( 'div' ),
|
|
|
|
classes = [],
|
2013-06-25 00:22:03 +00:00
|
|
|
originalClasses = dataElement.attributes.originalClasses,
|
2013-09-05 20:39:12 +00:00
|
|
|
captionData = data.slice( 1, -1 ),
|
|
|
|
rdfa;
|
|
|
|
|
|
|
|
if ( !this.typeToRdfa ) {
|
|
|
|
this.typeToRdfa = {};
|
|
|
|
for ( rdfa in this.rdfaToType ) {
|
|
|
|
this.typeToRdfa[this.rdfaToType[rdfa]] = rdfa;
|
|
|
|
}
|
|
|
|
}
|
2013-06-05 21:34:53 +00:00
|
|
|
|
|
|
|
// Type
|
2013-09-05 20:39:12 +00:00
|
|
|
figure.setAttribute( 'typeof', this.typeToRdfa[dataElement.attributes.type] );
|
2013-06-05 21:34:53 +00:00
|
|
|
|
|
|
|
// Default-size
|
|
|
|
if ( dataElement.attributes.defaultSize === true ) {
|
2013-06-24 19:34:15 +00:00
|
|
|
classes.push( 'mw-default-size' );
|
2013-06-05 21:34:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Horizontal alignment
|
|
|
|
switch ( dataElement.attributes.align ) {
|
|
|
|
case 'left':
|
2013-06-24 19:34:15 +00:00
|
|
|
classes.push( 'mw-halign-left' );
|
2013-06-05 21:34:53 +00:00
|
|
|
break;
|
|
|
|
case 'right':
|
2013-06-24 19:34:15 +00:00
|
|
|
classes.push( 'mw-halign-right' );
|
2013-06-05 21:34:53 +00:00
|
|
|
break;
|
|
|
|
case 'center':
|
2013-06-24 19:34:15 +00:00
|
|
|
classes.push( 'mw-halign-center' );
|
2013-06-05 21:34:53 +00:00
|
|
|
break;
|
|
|
|
case 'none':
|
2013-06-24 19:34:15 +00:00
|
|
|
classes.push( 'mw-halign-none' );
|
2013-06-05 21:34:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-06-28 00:24:05 +00:00
|
|
|
if ( dataElement.attributes.unrecognizedClasses ) {
|
2013-10-23 01:26:50 +00:00
|
|
|
classes = OO.simpleArrayUnion( classes, dataElement.attributes.unrecognizedClasses );
|
2013-06-28 00:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
originalClasses &&
|
|
|
|
ve.compare( originalClasses.trim().split( /\s+/ ).sort(), classes.sort() )
|
|
|
|
) {
|
2013-06-24 19:34:15 +00:00
|
|
|
figure.className = originalClasses;
|
|
|
|
} else if ( classes.length > 0 ) {
|
|
|
|
figure.className = classes.join( ' ' );
|
|
|
|
}
|
2013-08-16 19:03:46 +00:00
|
|
|
if ( dataElement.attributes.href !== '' ) {
|
|
|
|
imgWrapper.setAttribute( 'href', dataElement.attributes.href );
|
|
|
|
}
|
2013-04-25 00:47:17 +00:00
|
|
|
img.setAttribute( 'src', dataElement.attributes.src );
|
|
|
|
img.setAttribute( 'width', dataElement.attributes.width );
|
|
|
|
img.setAttribute( 'height', dataElement.attributes.height );
|
|
|
|
img.setAttribute( 'resource', dataElement.attributes.resource );
|
2013-12-28 10:29:35 +00:00
|
|
|
if ( dataElement.attributes.alt !== undefined ) {
|
|
|
|
img.setAttribute( 'alt', dataElement.attributes.alt );
|
|
|
|
}
|
2013-08-16 19:03:46 +00:00
|
|
|
figure.appendChild( imgWrapper );
|
|
|
|
imgWrapper.appendChild( img );
|
2013-04-25 00:47:17 +00:00
|
|
|
|
2013-06-25 00:22:03 +00:00
|
|
|
// 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 );
|
|
|
|
}
|
2013-04-25 00:47:17 +00:00
|
|
|
}
|
|
|
|
return [ figure ];
|
|
|
|
};
|
|
|
|
|
2013-06-18 22:58:10 +00:00
|
|
|
/* Methods */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the caption node of the image.
|
|
|
|
*
|
|
|
|
* @method
|
2013-07-31 22:53:29 +00:00
|
|
|
* @returns {ve.dm.MWImageCaptionNode|null} Caption node, if present
|
2013-06-18 22:58:10 +00:00
|
|
|
*/
|
2013-12-06 02:34:44 +00:00
|
|
|
ve.dm.MWBlockImageNode.prototype.getCaptionNode = function () {
|
2013-06-18 22:58:10 +00:00
|
|
|
var node = this.children[0];
|
|
|
|
return node instanceof ve.dm.MWImageCaptionNode ? node : null;
|
|
|
|
};
|
|
|
|
|
2013-04-25 00:47:17 +00:00
|
|
|
/* Registration */
|
|
|
|
|
2013-06-06 20:49:29 +00:00
|
|
|
ve.dm.modelRegistry.register( ve.dm.MWBlockImageNode );
|