mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-24 22:35:41 +00:00
9119e57a2e
ve.dm.MWImageNode: * Define sensible scalable properties for audio files. They are now scalable to any width but have a fixed height. (Ideally they would have no concept of height, but that would require many more changes.) This prevents them from resetting to 0x0 when resized. ve.ce.MWBlockImageNode: * Remove override for #isResizable, audio files can be resized now. * Move #updateMediaType to MWImageNode mixin so it applies to MWInlineImageNode as well. ve.ce.MWImageNode: * Add #updateMediaType from MWBlockImageNode. * Hide the real image 'src' using CSS rather than changing the attribute. It seems the previous solution depended on the order in which methods are called, because it stopped working when I moved the code here. (This depends on VE/VE change If5b1b5b5d.) audioPlayer.svg: * Make the file nicely resizeable. The dimensions of the "play" icon and time are fixed, the bar adjusts to the width of the container. Bug: T206022 Change-Id: Ia0f38ca11e0d55a5b725fd9aeb6c79ec1345376d
182 lines
4.9 KiB
JavaScript
182 lines
4.9 KiB
JavaScript
/*!
|
|
* VisualEditor ContentEditable MWImageNode class.
|
|
*
|
|
* @copyright 2011-2020 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* ContentEditable MediaWiki image node.
|
|
*
|
|
* @class
|
|
* @abstract
|
|
* @extends ve.ce.GeneratedContentNode
|
|
* @mixins ve.ce.FocusableNode
|
|
* @mixins ve.ce.MWResizableNode
|
|
*
|
|
* @constructor
|
|
* @param {jQuery} $focusable Focusable part of the node
|
|
* @param {jQuery} $image Image part of the node
|
|
* @param {Object} [config] Configuration options
|
|
*/
|
|
ve.ce.MWImageNode = function VeCeMWImageNode( $focusable, $image, config ) {
|
|
config = ve.extendObject( {
|
|
enforceMax: false,
|
|
minDimensions: { width: 1, height: 1 },
|
|
$bounding: this.$element
|
|
}, config );
|
|
|
|
// Properties
|
|
this.$image = $image;
|
|
// Parent constructor triggers render so this must precede it
|
|
this.renderedDimensions = null;
|
|
|
|
// Parent constructor
|
|
ve.ce.GeneratedContentNode.call( this );
|
|
|
|
// Mixin constructors
|
|
ve.ce.FocusableNode.call( this, $focusable, config );
|
|
ve.ce.MWResizableNode.call( this, this.$image, config );
|
|
|
|
// Events
|
|
this.model.connect( this, { attributeChange: 'onAttributeChange' } );
|
|
|
|
// Initialization
|
|
this.updateMediaType();
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
OO.inheritClass( ve.ce.MWImageNode, ve.ce.GeneratedContentNode );
|
|
|
|
OO.mixinClass( ve.ce.MWImageNode, ve.ce.FocusableNode );
|
|
|
|
// Need to mixin base class as well (T92540)
|
|
OO.mixinClass( ve.ce.MWImageNode, ve.ce.ResizableNode );
|
|
|
|
OO.mixinClass( ve.ce.MWImageNode, ve.ce.MWResizableNode );
|
|
|
|
/* Static Properties */
|
|
|
|
ve.ce.MWImageNode.static.primaryCommandName = 'media';
|
|
|
|
/* Static Methods */
|
|
|
|
/**
|
|
* @inheritdoc ve.ce.Node
|
|
*/
|
|
ve.ce.MWImageNode.static.getDescription = function ( model ) {
|
|
var title = new mw.Title( model.getFilename() );
|
|
return title.getMainText();
|
|
};
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Update the rendering of the 'align', src', 'width' and 'height' attributes
|
|
* when they change in the model.
|
|
*
|
|
* @param {string} key Attribute key
|
|
* @param {string} from Old value
|
|
* @param {string} to New value
|
|
*/
|
|
ve.ce.MWImageNode.prototype.onAttributeChange = function () {
|
|
this.update();
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc ve.ce.GeneratedContentNode
|
|
*/
|
|
ve.ce.MWImageNode.prototype.onGeneratedContentNodeUpdate = function () {
|
|
// Do nothing to avoid re-rendering every time the caption is changed.
|
|
// Call update inside onAttributeChange instead.
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc ve.ce.GeneratedContentNode
|
|
*/
|
|
ve.ce.MWImageNode.prototype.generateContents = function () {
|
|
var xhr, params,
|
|
model = this.getModel(),
|
|
width = model.getAttribute( 'width' ),
|
|
height = model.getAttribute( 'height' ),
|
|
mwData = model.getAttribute( 'mw' ) || {},
|
|
deferred = ve.createDeferred();
|
|
|
|
// If the current rendering is larger don't fetch a new image, just let the browser resize
|
|
if ( this.renderedDimensions && this.renderedDimensions.width > width ) {
|
|
return deferred.reject().promise();
|
|
}
|
|
|
|
if ( mwData.thumbtime !== undefined ) {
|
|
params = 'seek=' + mwData.thumbtime;
|
|
} else if ( mwData.page !== undefined ) {
|
|
params = 'page' + mwData.page + '-' + width + 'px';
|
|
// Don't send width twice
|
|
width = undefined;
|
|
}
|
|
|
|
xhr = ve.init.target.getContentApi( this.getModel().getDocument() ).get( {
|
|
action: 'query',
|
|
prop: 'imageinfo',
|
|
iiprop: 'url',
|
|
iiurlwidth: width,
|
|
iiurlheight: height,
|
|
iiurlparam: params,
|
|
titles: this.getModel().getFilename()
|
|
} )
|
|
.done( this.onParseSuccess.bind( this, deferred ) )
|
|
.fail( this.onParseError.bind( this, deferred ) );
|
|
|
|
return deferred.promise( { abort: xhr.abort } );
|
|
};
|
|
|
|
/**
|
|
* Handle a successful response from the parser for the image src.
|
|
*
|
|
* @param {jQuery.Deferred} deferred The Deferred object created by generateContents
|
|
* @param {Object} response Response data
|
|
*/
|
|
ve.ce.MWImageNode.prototype.onParseSuccess = function ( deferred, response ) {
|
|
var thumburl = ve.getProp( response.query.pages[ 0 ], 'imageinfo', 0, 'thumburl' );
|
|
if ( thumburl ) {
|
|
deferred.resolve( thumburl );
|
|
} else {
|
|
deferred.reject();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @inheritdoc ve.ce.GeneratedContentNode
|
|
*/
|
|
ve.ce.MWImageNode.prototype.render = function ( generatedContents ) {
|
|
this.$image.attr( 'src', generatedContents );
|
|
// As we only re-render when the image is larger than last rendered size
|
|
// this will always be the largest ever rendering
|
|
this.renderedDimensions = ve.copy( this.model.getScalable().getCurrentDimensions() );
|
|
if ( this.live ) {
|
|
this.afterRender();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle an unsuccessful response from the parser for the image src.
|
|
*
|
|
* @param {jQuery.Deferred} deferred The promise object created by generateContents
|
|
* @param {Object} response Response data
|
|
*/
|
|
ve.ce.MWImageNode.prototype.onParseError = function ( deferred ) {
|
|
deferred.reject();
|
|
};
|
|
|
|
/**
|
|
* Update rendering when media type changes
|
|
*/
|
|
ve.ce.MWImageNode.prototype.updateMediaType = function () {
|
|
if ( this.model.getMediaType() === 'AUDIO' ) {
|
|
this.$image.addClass( 've-ce-mwImageNode-audioPlayer' );
|
|
} else {
|
|
this.$image.removeClass( 've-ce-mwImageNode-audioPlayer' );
|
|
}
|
|
};
|