mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-25 14:56:20 +00:00
a247fd4e90
It contains some rare options that we don't currently make editable, and we don't set it when creating new image nodes. We could change our code to always set it, and consider it required, but that would theoretically be a break in backwards-compatibility. Bug: T198660 Change-Id: I6e77cce257f733f0f8f6e896b967177ff01658c6
174 lines
4.7 KiB
JavaScript
174 lines
4.7 KiB
JavaScript
/*!
|
|
* VisualEditor ContentEditable MWImageNode class.
|
|
*
|
|
* @copyright 2011-2018 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' } );
|
|
};
|
|
|
|
/* 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.
|
|
*
|
|
* @method
|
|
* @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 = $.Deferred();
|
|
|
|
// 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 id, src, pages = ve.getProp( response, 'query', 'pages' );
|
|
for ( id in pages ) {
|
|
if ( pages[ id ].imageinfo ) {
|
|
src = pages[ id ].imageinfo[ 0 ].thumburl;
|
|
}
|
|
}
|
|
if ( src ) {
|
|
deferred.resolve( src );
|
|
} 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();
|
|
};
|