2013-06-06 23:14:11 +00:00
|
|
|
/*!
|
2013-06-25 00:41:01 +00:00
|
|
|
* VisualEditor UserInterface MWMediaSearchWidget class.
|
2013-06-06 23:14:11 +00:00
|
|
|
*
|
2014-01-05 12:05:05 +00:00
|
|
|
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
|
2013-06-06 23:14:11 +00:00
|
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*global mw*/
|
|
|
|
|
|
|
|
/**
|
2013-06-25 00:41:01 +00:00
|
|
|
* Creates an ve.ui.MWMediaSearchWidget object.
|
2013-06-06 23:14:11 +00:00
|
|
|
*
|
|
|
|
* @class
|
2013-10-09 20:09:59 +00:00
|
|
|
* @extends OO.ui.SearchWidget
|
2013-06-06 23:14:11 +00:00
|
|
|
*
|
|
|
|
* @constructor
|
2013-09-25 10:21:09 +00:00
|
|
|
* @param {Object} [config] Configuration options
|
2013-06-06 23:14:11 +00:00
|
|
|
* @param {number} [size] Vertical size of thumbnails
|
|
|
|
*/
|
2013-06-25 00:41:01 +00:00
|
|
|
ve.ui.MWMediaSearchWidget = function VeUiMWMediaSearchWidget( config ) {
|
2013-06-06 23:14:11 +00:00
|
|
|
// Configuration intialization
|
2013-09-20 07:09:06 +00:00
|
|
|
config = ve.extendObject( {
|
2014-07-02 19:23:25 +00:00
|
|
|
'placeholder': ve.msg( 'visualeditor-media-input-placeholder' )
|
2013-09-20 07:09:06 +00:00
|
|
|
}, config );
|
2013-06-06 23:14:11 +00:00
|
|
|
|
|
|
|
// Parent constructor
|
2013-10-09 20:09:59 +00:00
|
|
|
OO.ui.SearchWidget.call( this, config );
|
2013-06-06 23:14:11 +00:00
|
|
|
|
|
|
|
// Properties
|
2013-12-16 12:09:54 +00:00
|
|
|
this.sources = {};
|
2013-06-06 23:14:11 +00:00
|
|
|
this.size = config.size || 150;
|
2013-06-25 00:41:01 +00:00
|
|
|
this.queryTimeout = null;
|
2013-06-06 23:14:11 +00:00
|
|
|
this.titles = {};
|
|
|
|
this.queryMediaSourcesCallback = ve.bind( this.queryMediaSources, this );
|
|
|
|
|
2014-05-13 06:27:18 +00:00
|
|
|
this.sourceCounter = 0;
|
|
|
|
|
|
|
|
this.$noItemsMessage = this.$( '<div>' )
|
|
|
|
.addClass( 've-ui-mwMediaSearchWidget-noresults' )
|
|
|
|
.text( ve.msg( 'visualeditor-dialog-media-noresults' ) )
|
|
|
|
.appendTo( this.$query );
|
|
|
|
|
2013-06-06 23:14:11 +00:00
|
|
|
// Events
|
|
|
|
this.$results.on( 'scroll', ve.bind( this.onResultsScroll, this ) );
|
|
|
|
|
|
|
|
// Initialization
|
2013-11-01 19:45:59 +00:00
|
|
|
this.$element.addClass( 've-ui-mwMediaSearchWidget' );
|
2013-06-06 23:14:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Inheritance */
|
|
|
|
|
2013-10-09 20:09:59 +00:00
|
|
|
OO.inheritClass( ve.ui.MWMediaSearchWidget, OO.ui.SearchWidget );
|
2013-06-06 23:14:11 +00:00
|
|
|
|
|
|
|
/* Methods */
|
|
|
|
|
2013-12-16 12:09:54 +00:00
|
|
|
/**
|
|
|
|
* Set the fileRepo sources for the media search
|
|
|
|
* @param {Object} sources The sources object
|
|
|
|
*/
|
|
|
|
ve.ui.MWMediaSearchWidget.prototype.setSources = function ( sources ) {
|
|
|
|
this.sources = sources;
|
|
|
|
};
|
|
|
|
|
2013-06-06 23:14:11 +00:00
|
|
|
/**
|
|
|
|
* Handle select widget select events.
|
|
|
|
*
|
|
|
|
* @param {string} value New value
|
|
|
|
*/
|
2013-06-25 00:41:01 +00:00
|
|
|
ve.ui.MWMediaSearchWidget.prototype.onQueryChange = function () {
|
2013-06-06 23:14:11 +00:00
|
|
|
var i, len;
|
|
|
|
|
2013-06-25 00:41:01 +00:00
|
|
|
// Parent method
|
2013-10-09 20:09:59 +00:00
|
|
|
OO.ui.SearchWidget.prototype.onQueryChange.call( this );
|
2013-06-25 00:41:01 +00:00
|
|
|
|
2013-06-06 23:14:11 +00:00
|
|
|
// Reset
|
2013-06-19 01:02:23 +00:00
|
|
|
this.titles = {};
|
2013-06-06 23:14:11 +00:00
|
|
|
for ( i = 0, len = this.sources.length; i < len; i++ ) {
|
|
|
|
delete this.sources[i].gsroffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Queue
|
2013-06-25 00:41:01 +00:00
|
|
|
clearTimeout( this.queryTimeout );
|
|
|
|
this.queryTimeout = setTimeout( this.queryMediaSourcesCallback, 100 );
|
2013-06-06 23:14:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle results scroll events.
|
|
|
|
*
|
|
|
|
* @param {jQuery.Event} e Scroll event
|
|
|
|
*/
|
2013-06-25 00:41:01 +00:00
|
|
|
ve.ui.MWMediaSearchWidget.prototype.onResultsScroll = function () {
|
2013-06-06 23:14:11 +00:00
|
|
|
var position = this.$results.scrollTop() + this.$results.outerHeight(),
|
2013-11-01 19:45:59 +00:00
|
|
|
threshold = this.results.$element.outerHeight() - this.size;
|
2013-06-25 00:41:01 +00:00
|
|
|
if ( !this.query.isPending() && position > threshold ) {
|
2013-06-06 23:14:11 +00:00
|
|
|
this.queryMediaSources();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Query all sources for media.
|
|
|
|
*
|
|
|
|
* @method
|
|
|
|
*/
|
2013-06-25 00:41:01 +00:00
|
|
|
ve.ui.MWMediaSearchWidget.prototype.queryMediaSources = function () {
|
2013-12-16 12:09:54 +00:00
|
|
|
var i, len, source, url,
|
2013-06-25 00:41:01 +00:00
|
|
|
value = this.query.getValue();
|
2013-06-18 21:25:56 +00:00
|
|
|
|
|
|
|
if ( value === '' ) {
|
|
|
|
return;
|
|
|
|
}
|
2013-06-06 23:14:11 +00:00
|
|
|
|
2014-05-13 06:27:18 +00:00
|
|
|
// Reset counter
|
|
|
|
this.sourceCounter = 0;
|
|
|
|
this.$noItemsMessage.hide();
|
|
|
|
|
2013-06-06 23:14:11 +00:00
|
|
|
for ( i = 0, len = this.sources.length; i < len; i++ ) {
|
|
|
|
source = this.sources[i];
|
2013-12-16 12:09:54 +00:00
|
|
|
// If we don't have either 'apiurl' or 'scriptDirUrl'
|
|
|
|
// the source is invalid, and we will skip it
|
2014-02-10 18:42:05 +00:00
|
|
|
if ( source.apiurl || source.scriptDirUrl !== undefined ) {
|
2014-06-18 01:25:39 +00:00
|
|
|
if ( source.request ) {
|
2013-12-16 12:09:54 +00:00
|
|
|
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();
|
2014-05-23 09:57:58 +00:00
|
|
|
source.request = ve.init.target.constructor.static.apiRequest( {
|
2014-01-05 04:44:13 +00:00
|
|
|
'action': 'query',
|
|
|
|
'generator': 'search',
|
|
|
|
'gsrsearch': value,
|
|
|
|
'gsrnamespace': 6,
|
2014-03-28 23:55:32 +00:00
|
|
|
'gsrlimit': 20,
|
2014-01-05 04:44:13 +00:00
|
|
|
'gsroffset': source.gsroffset,
|
|
|
|
'prop': 'imageinfo',
|
2014-06-03 17:47:18 +00:00
|
|
|
'iiprop': 'dimensions|url|mediatype',
|
2014-01-05 04:44:13 +00:00
|
|
|
'iiurlheight': this.size
|
|
|
|
}, {
|
2013-12-16 12:09:54 +00:00
|
|
|
'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;
|
2013-06-06 23:14:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle media query response events.
|
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @param {Object} source Media query source
|
|
|
|
*/
|
2013-06-25 00:41:01 +00:00
|
|
|
ve.ui.MWMediaSearchWidget.prototype.onMediaQueryAlways = function ( source ) {
|
2013-06-06 23:14:11 +00:00
|
|
|
source.request = null;
|
2013-06-25 00:41:01 +00:00
|
|
|
this.query.popPending();
|
2014-05-13 06:27:18 +00:00
|
|
|
|
|
|
|
// Count this source as done
|
|
|
|
this.sourceCounter++;
|
|
|
|
|
|
|
|
// Check if all sources are done
|
|
|
|
// TODO use $.when() instead (bug 65321)
|
|
|
|
if ( this.sourceCounter >= this.sources.length ) {
|
|
|
|
if ( this.results.getItems().length === 0 ) {
|
|
|
|
this.$noItemsMessage.show();
|
|
|
|
}
|
|
|
|
}
|
2014-05-16 22:35:41 +00:00
|
|
|
|
|
|
|
// Even if the whole list of sources didn't finish yet
|
|
|
|
// if there are results, make the message go away
|
|
|
|
if ( this.results.getItems().length > 0 ) {
|
|
|
|
this.$noItemsMessage.hide();
|
|
|
|
}
|
2013-06-06 23:14:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle media query load events.
|
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @param {Object} source Media query source
|
|
|
|
* @param {Object} data Media query response
|
|
|
|
*/
|
2013-06-25 00:41:01 +00:00
|
|
|
ve.ui.MWMediaSearchWidget.prototype.onMediaQueryDone = function ( source, data ) {
|
2013-06-06 23:14:11 +00:00
|
|
|
if ( !data.query || !data.query.pages ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-12-04 20:06:19 +00:00
|
|
|
var page, title,
|
2013-06-06 23:14:11 +00:00
|
|
|
items = [],
|
2013-06-18 21:25:56 +00:00
|
|
|
pages = data.query.pages,
|
2013-06-25 00:41:01 +00:00
|
|
|
value = this.query.getValue();
|
2013-06-18 21:25:56 +00:00
|
|
|
|
2013-06-19 01:02:23 +00:00
|
|
|
if ( value === '' || value !== source.value ) {
|
2013-06-18 21:25:56 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-06-06 23:14:11 +00:00
|
|
|
|
|
|
|
if ( data['query-continue'] && data['query-continue'].search ) {
|
|
|
|
source.gsroffset = data['query-continue'].search.gsroffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( page in pages ) {
|
2014-02-14 22:58:30 +00:00
|
|
|
// 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 }
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2013-06-06 23:14:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-25 00:41:01 +00:00
|
|
|
this.results.addItems( items );
|
2013-06-06 23:14:11 +00:00
|
|
|
};
|