mediawiki-extensions-Visual.../modules/ve-mw/ce/nodes/ve.ce.MWBlockImageNode.js
Timo Tijhof 7db65f386c Rename @emits to @fires so we're forward compatible with JSDuck 5
Instead of using @emits in both, use our custom @fires in
production (JSDuck 4), and in the future it'll just naturally
use the native one.

This way we can also index oojs without issues, which seems to
have started using @fires already.

Change-Id: I7c3b56dd112626d57fa87ab995d205fb782a0149
2013-10-22 19:11:16 +00:00

246 lines
6.5 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.MWImageNode
*
* @constructor
* @param {ve.dm.MWBlockImageNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWBlockImageNode = function VeCeMWBlockImageNode( model, config ) {
var captionModel, captionView, type;
// Parent constructor
ve.ce.BranchNode.call( this, model, config );
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 );
this.$inner = this.$$( '<div>' ).addClass( 've-ce-mwBlockImageNode-inner' );
if ( type === 'none' || type === 'frameless' ) {
this.$thumb.addClass(
this.getCssClass( 'none', this.model.getAttribute( 'align' ) )
);
this.$a.appendTo( this.$thumb );
// For centered images, this.$thumb is full width, so wrap
// this.$image in another div and use that for selection
this.$inner
.append( this.$image )
.appendTo( this.$a );
} 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 );
// For centered images, this.$thumb is full width, so wrap
// this.$thumbInner in another div and use that for selection
this.$inner
.append( this.$thumbInner )
.appendTo( this.$thumb );
}
// Mixin constructors
ve.ce.MWImageNode.call( this, this.$inner, 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 );
}
}
// Events
this.model.connect( this, { 'attributeChange': 'onAttributeChange' } );
};
/* Inheritance */
OO.inheritClass( ve.ce.MWBlockImageNode, ve.ce.BranchNode );
// Need to mixin base class as well
OO.mixinClass( ve.ce.MWBlockImageNode, ve.ce.GeneratedContentNode );
OO.mixinClass( ve.ce.MWBlockImageNode, ve.ce.MWImageNode );
/* 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'
},
'none': {
'left': 'floatleft',
'right': 'floatright',
'center' : 'floatnone',
'none' : 'floatnone'
}
};
/* Methods */
/**
* Get the right CSS class to use for alignment
* @param {string} type 'none' or 'default'
* @param {string} alignment 'left', 'right', 'center', 'none' or 'default'
*/
ve.ce.MWBlockImageNode.prototype.getCssClass = function ( type, alignment ) {
// TODO use this.model.getAttribute( 'type' ) etc., see bug 52065
// Default is different between RTL and LTR wikis:
if ( type === 'default' && alignment === 'default' ) {
if ( this.$.css( 'direction' ) === 'rtl' ) {
return 'tleft';
} else {
return 'tright';
}
} else {
return this.constructor.static.cssClasses[type][alignment];
}
};
/**
* 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' );
ve.ce.BranchNode.prototype.onSetup.call( this );
if ( type !== 'none' && type !=='frameless' ) {
this.$thumb.addClass( this.getCssClass( 'default', this.model.getAttribute( 'align' ) ) );
}
};
/**
* Update the rendering of the 'align', src', 'width' and 'height' attributes when they change
* in the model.
*
* @method
* @param {string} key Attribute key
* @param {string} from Old value
* @param {string} to New value
* @fires setup
*/
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( this.getCssClass( 'none', from ) );
this.$thumb.addClass( this.getCssClass( 'none', to ) );
} else {
this.$thumb.removeClass( this.getCssClass( 'default', from ) );
this.$thumb.addClass( this.getCssClass( '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.onResizableResizing = function ( dimensions ) {
if ( !this.outline ) {
ve.ce.ResizableNode.prototype.onResizableResizing.call( this, dimensions );
this.$thumbInner.css( 'width', dimensions.width + 2 );
}
};
/** */
ve.ce.MWBlockImageNode.prototype.setupSlugs = function () {
// Intentionally empty
};
/** */
ve.ce.MWBlockImageNode.prototype.onSplice = function () {
// Intentionally empty
};
/* Registration */
ve.ce.nodeFactory.register( ve.ce.MWBlockImageNode );