mediawiki-extensions-Visual.../modules/ve-mw/ui/widgets/ve.ui.MWMediaSearchWidget.js
Moriel Schottlender 21ccb20d3a Only display image info if that exists
When a new search is performed in the MWMediaSearchWidget,
image thumbnail details are extracted from the API's imageinfo.
In some cases, this parameter does not exist. MWMediaResultWidget
relies on that parameter to get image details like 'src' and
width/height. Without it the search fails.

This fix makes sure that the thumbnail result is only displayed
if there are 'imageinfo' details at all. Otherwise, the image
result is ignored.

Bug: 61392
Change-Id: I5261059bb42d3a57441514eafaa0bffa3a3c3ba4
2014-02-14 18:12:26 -05:00

209 lines
5.3 KiB
JavaScript
Executable file

/*!
* VisualEditor UserInterface MWMediaSearchWidget class.
*
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/*global mw*/
/**
* Creates an ve.ui.MWMediaSearchWidget object.
*
* @class
* @extends OO.ui.SearchWidget
*
* @constructor
* @param {Object} [config] Configuration options
* @param {number} [size] Vertical size of thumbnails
*/
ve.ui.MWMediaSearchWidget = function VeUiMWMediaSearchWidget( config ) {
// Configuration intialization
config = ve.extendObject( {
'placeholder': ve.msg( 'visualeditor-media-input-placeholder' ),
'value': mw.config.get( 'wgTitle' )
}, config );
// Parent constructor
OO.ui.SearchWidget.call( this, config );
// Properties
this.sources = {};
this.size = config.size || 150;
this.queryTimeout = null;
this.titles = {};
this.queryMediaSourcesCallback = ve.bind( this.queryMediaSources, this );
// Events
this.$results.on( 'scroll', ve.bind( this.onResultsScroll, this ) );
// Initialization
this.$element.addClass( 've-ui-mwMediaSearchWidget' );
};
/* Inheritance */
OO.inheritClass( ve.ui.MWMediaSearchWidget, OO.ui.SearchWidget );
/* Methods */
/**
* Set the fileRepo sources for the media search
* @param {Object} sources The sources object
*/
ve.ui.MWMediaSearchWidget.prototype.setSources = function ( sources ) {
this.sources = sources;
};
/**
* Handle select widget select events.
*
* @param {string} value New value
*/
ve.ui.MWMediaSearchWidget.prototype.onQueryChange = function () {
var i, len;
// Parent method
OO.ui.SearchWidget.prototype.onQueryChange.call( this );
// Reset
this.titles = {};
for ( i = 0, len = this.sources.length; i < len; i++ ) {
delete this.sources[i].gsroffset;
}
// Queue
clearTimeout( this.queryTimeout );
this.queryTimeout = setTimeout( this.queryMediaSourcesCallback, 100 );
};
/**
* Handle results scroll events.
*
* @param {jQuery.Event} e Scroll event
*/
ve.ui.MWMediaSearchWidget.prototype.onResultsScroll = function () {
var position = this.$results.scrollTop() + this.$results.outerHeight(),
threshold = this.results.$element.outerHeight() - this.size;
if ( !this.query.isPending() && position > threshold ) {
this.queryMediaSources();
}
};
/**
* Query all sources for media.
*
* @method
*/
ve.ui.MWMediaSearchWidget.prototype.queryMediaSources = function () {
var i, len, source, url,
value = this.query.getValue();
if ( value === '' ) {
return;
}
for ( i = 0, len = this.sources.length; i < len; i++ ) {
source = this.sources[i];
// If we don't have either 'apiurl' or 'scriptDirUrl'
// the source is invalid, and we will skip it
if ( source.apiurl || source.scriptDirUrl !== undefined ) {
if ( source.request ) {
source.request.abort();
}
if ( !source.gsroffset ) {
source.gsroffset = 0;
}
if ( source.local ) {
url = mw.util.wikiScript( 'api' );
} else {
// If 'apiurl' is set, use that. Otherwise, build the url
// from scriptDirUrl and /api.php suffix
url = source.apiurl || ( source.scriptDirUrl + '/api.php' );
}
this.query.pushPending();
source.request = ve.init.mw.Target.static.apiRequest( {
'action': 'query',
'generator': 'search',
'gsrsearch': value,
'gsrnamespace': 6,
'gsrlimit': 15,
'gsroffset': source.gsroffset,
'prop': 'imageinfo',
'iiprop': 'dimensions|url',
'iiurlheight': this.size
}, {
'url': url,
// This request won't be cached since the JSON-P callback is unique. However make sure
// to allow jQuery to cache otherwise so it won't e.g. add "&_=(random)" which will
// trigger a MediaWiki API error for invalid parameter "_".
'cache': true,
// TODO: Only use JSON-P for cross-domain.
// jQuery has this logic built-in (if url is not same-origin ..)
// but isn't working for some reason.
'dataType': 'jsonp'
} )
.done( ve.bind( this.onMediaQueryDone, this, source ) )
.always( ve.bind( this.onMediaQueryAlways, this, source ) );
source.value = value;
}
}
};
/**
* Handle media query response events.
*
* @method
* @param {Object} source Media query source
*/
ve.ui.MWMediaSearchWidget.prototype.onMediaQueryAlways = function ( source ) {
source.request = null;
this.query.popPending();
};
/**
* Handle media query load events.
*
* @method
* @param {Object} source Media query source
* @param {Object} data Media query response
*/
ve.ui.MWMediaSearchWidget.prototype.onMediaQueryDone = function ( source, data ) {
if ( !data.query || !data.query.pages ) {
return;
}
var page, title,
items = [],
pages = data.query.pages,
value = this.query.getValue();
if ( value === '' || value !== source.value ) {
return;
}
if ( data['query-continue'] && data['query-continue'].search ) {
source.gsroffset = data['query-continue'].search.gsroffset;
}
for ( page in pages ) {
// Verify that imageinfo exists
// In case it does not, skip the image to avoid errors in
// ve.ui.MWMediaResultWidget
if ( pages[page].imageinfo && pages[page].imageinfo.length > 0 ) {
title = new mw.Title( pages[page].title ).getMainText();
if ( !( title in this.titles ) ) {
this.titles[title] = true;
items.push(
new ve.ui.MWMediaResultWidget(
pages[page],
{ '$': this.$, 'size': this.size }
)
);
}
}
}
this.results.addItems( items );
};