mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-29 00:30:44 +00:00
96fa010f58
This removes the initial alignment (either left or right) from new inserted images, letting the wiki decide the default positioning. Also, it makes sure that VisualEditor positions the image properly (according to wiki defaults tright/tleft for ltr/rtl) when editing. Bug: 51851 Change-Id: I25b966cf6f2736437509ea7e70bfda1bdbc79021
210 lines
5.6 KiB
JavaScript
210 lines
5.6 KiB
JavaScript
/*!
|
|
* VisualEditor ContentEditable MWBlockImageNode class.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* ContentEditable MediaWiki image node.
|
|
*
|
|
* @class
|
|
* @extends ve.ce.BranchNode
|
|
* @mixins ve.ce.ProtectedNode
|
|
* @mixins ve.ce.FocusableNode
|
|
* @mixins ve.ce.RelocatableNode
|
|
* @mixins ve.ce.MWResizableNode
|
|
*
|
|
* @constructor
|
|
* @param {ve.dm.MWBlockImageNode} model Model to observe
|
|
* @param {Object} [config] Config options
|
|
*/
|
|
ve.ce.MWBlockImageNode = function VeCeMWBlockImageNode( model, config ) {
|
|
var captionModel, captionView, type;
|
|
|
|
// Parent constructor
|
|
ve.ce.BranchNode.call( this, model, config );
|
|
|
|
// Mixin constructors
|
|
ve.ce.ProtectedNode.call( this );
|
|
ve.ce.FocusableNode.call( this );
|
|
ve.ce.RelocatableNode.call( this );
|
|
ve.ce.MWResizableNode.call( this );
|
|
|
|
type = this.model.getAttribute( 'type' );
|
|
|
|
if ( this.model.getAttribute( 'align' ) === 'center' ) {
|
|
this.$.addClass( 'center' );
|
|
this.$thumb = this.$$( '<div>' ).appendTo( this.$ );
|
|
} else {
|
|
this.$thumb = this.$;
|
|
}
|
|
|
|
this.$thumbInner = this.$$( '<div>' )
|
|
.addClass( 'thumbinner' )
|
|
.css( 'width', parseInt( this.model.getAttribute( 'width' ), 10 ) + 2 );
|
|
|
|
this.$a = this.$$( '<a>' )
|
|
.addClass( 'image' )
|
|
.attr( 'src', this.model.getAttribute( 'href' ) );
|
|
|
|
this.$image = this.$$( '<img>' )
|
|
.attr( 'src', this.model.getAttribute( 'src' ) )
|
|
.attr( 'width', this.model.getAttribute( 'width' ) )
|
|
.attr( 'height', this.model.getAttribute( 'height' ) )
|
|
.appendTo( this.$a );
|
|
|
|
if ( type === 'none' || type ==='frameless' ) {
|
|
this.$thumb.addClass(
|
|
ve.ce.MWBlockImageNode.static.cssClasses.none[ this.model.getAttribute( 'align' ) ]
|
|
);
|
|
this.$a.appendTo( this.$thumb );
|
|
} else {
|
|
// Type "frame", "thumb" and the default
|
|
this.$image.addClass( 'thumbimage' );
|
|
this.$thumb
|
|
.addClass( 'thumb' );
|
|
this.$a.appendTo( this.$thumbInner );
|
|
this.$thumbInner.appendTo( this.$thumb );
|
|
}
|
|
|
|
this.$resizable = this.$image;
|
|
|
|
// I smell a caption!
|
|
if ( type !== 'none' && type !== 'frameless' && this.model.children.length === 1 ) {
|
|
captionModel = this.model.children[0];
|
|
captionView = ve.ce.nodeFactory.create( captionModel.getType(), captionModel );
|
|
captionModel.connect( this, { 'update': 'onModelUpdate' } );
|
|
this.children.push( captionView );
|
|
captionView.attach( this );
|
|
captionView.$.appendTo( this.$thumbInner );
|
|
if ( this.live !== captionView.isLive() ) {
|
|
captionView.setLive( this.live );
|
|
}
|
|
}
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
ve.inheritClass( ve.ce.MWBlockImageNode, ve.ce.BranchNode );
|
|
|
|
ve.mixinClass( ve.ce.MWBlockImageNode, ve.ce.ProtectedNode );
|
|
|
|
ve.mixinClass( ve.ce.MWBlockImageNode, ve.ce.FocusableNode );
|
|
|
|
ve.mixinClass( ve.ce.MWBlockImageNode, ve.ce.RelocatableNode );
|
|
|
|
// Need to mixin base class as well
|
|
ve.mixinClass( ve.ce.MWBlockImageNode, ve.ce.ResizableNode );
|
|
|
|
ve.mixinClass( ve.ce.MWBlockImageNode, ve.ce.MWResizableNode );
|
|
|
|
/* Static Properties */
|
|
|
|
ve.ce.MWBlockImageNode.static.name = 'mwBlockImage';
|
|
|
|
ve.ce.MWBlockImageNode.static.tagName = 'div';
|
|
|
|
ve.ce.MWBlockImageNode.static.renderHtmlAttributes = false;
|
|
|
|
ve.ce.MWBlockImageNode.static.transition = false;
|
|
|
|
ve.ce.MWBlockImageNode.static.cssClasses = {
|
|
'default': {
|
|
'left': 'tleft',
|
|
'right': 'tright',
|
|
'center' : 'tnone',
|
|
'none' : 'tnone',
|
|
// Default is different between RTL and LTR wikis:
|
|
'default': ['tright', 'tleft']
|
|
},
|
|
'none': {
|
|
'left': 'floatleft',
|
|
'right': 'floatright',
|
|
'center' : 'floatnone',
|
|
'none' : 'floatnone'
|
|
}
|
|
};
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Override the default onSetup to add direction-dependent
|
|
* classes to the image thumbnail.
|
|
*
|
|
* @method
|
|
*/
|
|
ve.ce.MWBlockImageNode.prototype.onSetup = function ( ) {
|
|
var type = this.model.getAttribute( 'type' ),
|
|
isRTL;
|
|
|
|
if ( type !== 'none' && type !=='frameless' ) {
|
|
// get the proper alignment for the image inside the editor
|
|
isRTL = ( this.$.css( 'direction' ) === 'rtl' ) ? 1 : 0;
|
|
this.$thumb
|
|
.addClass(
|
|
ve.ce.MWBlockImageNode.static.cssClasses[ 'default' ][ this.model.getAttribute( 'align' ) ][ isRTL ]
|
|
);
|
|
}
|
|
|
|
};
|
|
|
|
ve.ce.MWBlockImageNode.prototype.onAttributeChange = function ( key, from, to ) {
|
|
var $element, type;
|
|
|
|
if ( key === 'height' || key === 'width' ) {
|
|
to = parseInt( to, 10 );
|
|
}
|
|
|
|
if ( from !== to ) {
|
|
switch ( key ) {
|
|
case 'align':
|
|
if ( to === 'center' || from === 'center' ) {
|
|
this.emit( 'teardown' );
|
|
if ( to === 'center' ) {
|
|
$element = this.$$( '<div>' ).addClass( 'center' );
|
|
this.$thumb = this.$;
|
|
this.$.replaceWith( $element );
|
|
this.$ = $element;
|
|
this.$.append( this.$thumb );
|
|
} else {
|
|
this.$.replaceWith( this.$thumb );
|
|
this.$ = this.$thumb;
|
|
}
|
|
this.emit( 'setup' );
|
|
}
|
|
type = this.model.getAttribute( 'type' );
|
|
if ( type === 'none' || type === 'frameless' ) {
|
|
this.$thumb.removeClass( ve.ce.MWBlockImageNode.static.cssClasses.none[ from ] );
|
|
this.$thumb.addClass( ve.ce.MWBlockImageNode.static.cssClasses.none[ to ] );
|
|
} else {
|
|
this.$thumb.removeClass( ve.ce.MWBlockImageNode.static.cssClasses[ 'default' ][ from ] );
|
|
this.$thumb.addClass( ve.ce.MWBlockImageNode.static.cssClasses[ 'default' ][ to ] );
|
|
}
|
|
break;
|
|
case 'src':
|
|
this.$image.attr( 'src', to );
|
|
break;
|
|
case 'width':
|
|
this.$thumbInner.css( 'width', to + 2 );
|
|
this.$image.css( 'width', to );
|
|
break;
|
|
case 'height':
|
|
this.$image.css( 'height', to );
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
ve.ce.MWBlockImageNode.prototype.setupSlugs = function () {
|
|
// Intentionally empty
|
|
};
|
|
|
|
ve.ce.MWBlockImageNode.prototype.onSplice = function () {
|
|
// Intentionally empty
|
|
};
|
|
|
|
/* Registration */
|
|
|
|
ve.ce.nodeFactory.register( ve.ce.MWBlockImageNode );
|