mediawiki-extensions-Visual.../modules/ve-mw/ui/widgets/ve.ui.MWMediaResultWidget.js
Moriel Schottlender aa9eb95455 Refactor MWMediaSearchWidget to use a queue and providers
Change the media search widget to work with resource queues and
providers. Create providers based on the user's filerepo settings
and aggregate their responses with the media queue. Stop asking
for more results from providers that are depleted.

Also fixes a rather nasty infinite-loop bug where the API returns
only very few images, and the UI keeps asking for more.

Bug: T78161
Bug: T88764
Change-Id: I65aed3446cd1f056476c56e6e04522c70e49e595
2015-02-06 16:45:56 -08:00

263 lines
7.3 KiB
JavaScript

/*!
* VisualEditor UserInterface MWMediaResultWidget class.
*
* @copyright 2011-2015 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Creates an ve.ui.MWMediaResultWidget object.
*
* @class
* @extends OO.ui.OptionWidget
*
* @constructor
* @param {Object} [config] Configuration options
* @cfg {number} [size] Media thumbnail size
*/
ve.ui.MWMediaResultWidget = function VeUiMWMediaResultWidget( config ) {
// Configuration initialization
config = config || {};
// Parent constructor
OO.ui.OptionWidget.call( this, config );
// Properties
this.initialSize = config.size || 150;
this.maxWidth = config.maxWidth || this.initialSize * 2;
this.expanded = false;
this.dimensions = {};
this.$thumb = this.buildThumbnail();
this.$overlay = this.$( '<div>' );
this.row = null;
// Store the thumbnail url
this.thumbUrl = this.data.thumburl;
this.src = null;
// Get wiki default thumbnail size
this.defaultThumbSize = mw.config.get( 'wgVisualEditorConfig' )
.defaultUserOptions.defaultthumbsize;
// Initialization
this.setLabel( new mw.Title( this.data.title ).getNameText() );
this.$label.addClass( 've-ui-mwMediaResultWidget-nameLabel' );
this.$overlay.addClass( 've-ui-mwMediaResultWidget-overlay' );
this.$element
.addClass( 've-ui-mwMediaResultWidget ve-ui-texture-pending' )
.prepend( this.$thumb, this.$overlay );
// Adjust wrapper padding
this.$element.css( $.extend( this.dimensions, this.calculateWrapperPadding( this.dimensions, this.initialSize ) ) );
};
/* Inheritance */
OO.inheritClass( ve.ui.MWMediaResultWidget, OO.ui.OptionWidget );
/* Methods */
/** */
ve.ui.MWMediaResultWidget.prototype.onThumbnailLoad = function () {
this.$thumb.first().addClass( 've-ui-texture-transparency' );
this.$element
.addClass( 've-ui-mwMediaResultWidget-done' )
.removeClass( 've-ui-texture-pending' );
};
/** */
ve.ui.MWMediaResultWidget.prototype.onThumbnailError = function () {
this.$thumb.last()
.css( 'background-image', '' )
.addClass( 've-ui-texture-alert' );
this.$element
.addClass( 've-ui-mwMediaResultWidget-error' )
.removeClass( 've-ui-texture-pending' );
};
/**
* Build a thumbnail.
*
* @method
* @returns {jQuery} Thumbnail element
*/
ve.ui.MWMediaResultWidget.prototype.buildThumbnail = function () {
var imageDimensions,
info = this.data,
$thumb = this.$( '<img>' );
// Preload image
$thumb
.addClass( 've-ui-mwMediaResultWidget-thumbnail' )
.on( {
load: this.onThumbnailLoad.bind( this ),
error: this.onThumbnailError.bind( this )
} );
if ( info.mediatype === 'AUDIO' ) {
// HACK: We are getting the wrong information from the
// API about audio files. Set their thumbnail to square
imageDimensions = {
width: this.initialSize,
height: this.initialSize
};
} else {
if ( info.height < this.initialSize && info.width < this.maxWidth ) {
// Define dimensions with original size
imageDimensions = {
width: info.width,
height: info.height
};
} else {
// Resize dimensions to be a fixed height
imageDimensions = ve.dm.Scalable.static.getDimensionsFromValue(
{ height: this.initialSize },
info.thumbwidth / info.thumbheight
);
}
}
// Resize the wrapper
this.dimensions = this.calculateThumbDimensions( imageDimensions );
// Resize the image
$thumb.css( this.dimensions );
return $thumb;
};
/**
* Replace the empty .src attribute of the image with the
* actual src.
*/
ve.ui.MWMediaResultWidget.prototype.lazyLoad = function () {
if ( !this.hasSrc() ) {
this.src = this.thumbUrl;
this.$thumb.attr( 'src', this.thumbUrl );
}
};
/**
* Retrieve the store dimensions object
* @return {Object} Thumb dimensions
*/
ve.ui.MWMediaResultWidget.prototype.getDimensions = function () {
return this.dimensions;
};
/**
* Resize thumbnail and element according to the resize factor
* @param {number} resizeFactor New resizing factor, multiplying the
* current dimensions of the thumbnail
*/
ve.ui.MWMediaResultWidget.prototype.resizeThumb = function ( resizeFactor ) {
var wrapperCss, imageDimensions,
currWidth = this.$thumb.width(),
currHeight = this.$thumb.height();
imageDimensions = {
width: currWidth * resizeFactor,
height: currHeight * resizeFactor
};
// Resize thumb wrapper
this.$thumb.css( imageDimensions );
// Adjust wrapper padding - this is done so the image is placed in the center
wrapperCss = $.extend( imageDimensions, this.calculateWrapperPadding( imageDimensions, imageDimensions.height ) );
this.$element.css( wrapperCss );
};
/**
* Calculate thumbnail dimensions
* @param {Object} imageDimensions Image dimensions
* @return {Object} Thumbnail dimensions
*/
ve.ui.MWMediaResultWidget.prototype.calculateThumbDimensions = function ( imageDimensions ) {
var dimensions,
maxWidth = this.maxWidth,
ratio = imageDimensions.width / imageDimensions.height;
// Rules of resizing:
// (1) Images must have height = this.initialSize
// (2) If after resize image width is larger than maxWidth
// the image is scaled down to width = 3*this.width
// (3) Smaller images do not scale up
// * If image height < this.initialSize, add padding and center
// the image vertically
// * If image width < this.initialSize, add a fixed padding to both
// sides.
// This is done in 'calculateWrapperPadding'
// First scale all images based on height = this.initialSize
dimensions = ve.dm.MWImageNode.static.resizeToBoundingBox(
// Image thumb size
imageDimensions,
// Bounding box
{
width: maxWidth,
height: this.initialSize
}
);
// Check if image width is larger than maxWidth
if ( dimensions.width > maxWidth ) {
// Resize again to fit maxWidth
dimensions = ve.dm.Scalable.static.getDimensionsFromValue( { width: maxWidth }, ratio );
}
return dimensions;
};
/**
* Adjust the wrapper padding for small images
* @param {Object} thumbDimensions Thumbnail dimensions
* @return {Object} Left and right padding
*/
ve.ui.MWMediaResultWidget.prototype.calculateWrapperPadding = function ( thumbDimensions, rowHeight ) {
var padding,
minWidthRatioForPadding = 2 / 3 * rowHeight,
paddingWidth = {},
paddingHeight = {},
minWidth = 0.5 * rowHeight;
// Check if the image fits the row height
if ( thumbDimensions.height < rowHeight ) {
// Set up top/bottom padding
paddingHeight = {
'padding-top': ( rowHeight - thumbDimensions.height ) / 2,
'padding-bottom': ( rowHeight - thumbDimensions.height ) / 2
};
}
// Check if the image is too thin so we can make a bit of space around it
if ( thumbDimensions.width < minWidth ) {
// Make the padding so that the total width is a 1/3 of the line height
padding = rowHeight - minWidthRatioForPadding - thumbDimensions.width;
paddingWidth = {
'padding-left': padding / 2,
'padding-right': padding / 2
};
}
return $.extend( {}, paddingWidth, paddingHeight );
};
/**
* Set the row this result is in.
* @param {number} row Row number
*/
ve.ui.MWMediaResultWidget.prototype.setRow = function ( row ) {
this.row = row;
};
/**
* Get the row this result is in.
* @return {number} row Row number
*/
ve.ui.MWMediaResultWidget.prototype.getRow = function () {
return this.row;
};
/**
* Check if the image has a src attribute already
* @returns {boolean} Thumbnail has its source attribute set
*/
ve.ui.MWMediaResultWidget.prototype.hasSrc = function () {
return !!this.src;
};