mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-25 14:56:20 +00:00
a8fcfb4a14
If the query is cleared, we should still clear all items and abort any loading that may be going on. We should still not try and load results for an empty query though. Change-Id: If23c6e0469247b1c1c47ab85b9a5a010e8a4fe26
215 lines
4.9 KiB
JavaScript
Executable file
215 lines
4.9 KiB
JavaScript
Executable file
/*!
|
|
* VisualEditor UserInterface MWMediaSelectWidget class.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/*global mw*/
|
|
|
|
/**
|
|
* Creates an ve.ui.MWMediaSelectWidget object.
|
|
*
|
|
* @class
|
|
* @extends ve.ui.Widget
|
|
*
|
|
* @constructor
|
|
* @param {Object} [config] Config options
|
|
* @param {number} [size] Vertical size of thumbnails
|
|
*/
|
|
ve.ui.MWMediaSelectWidget = function VeUiMWMediaSelectWidget( config ) {
|
|
// Configuration intialization
|
|
config = config || {};
|
|
|
|
// Parent constructor
|
|
ve.ui.Widget.call( this, config );
|
|
|
|
// Properties
|
|
this.input = new ve.ui.TextInputWidget( {
|
|
'$$': this.$$,
|
|
'placeholder': ve.msg( 'visualeditor-media-input-placeholder' ),
|
|
'value': mw.config.get( 'wgTitle' ),
|
|
'icon': 'search'
|
|
} );
|
|
this.select = new ve.ui.SelectWidget( { '$$': this.$$ } );
|
|
this.$query = this.$$( '<div>' );
|
|
this.$results = this.$$( '<div>' );
|
|
|
|
this.sources = ve.copyArray( ve.init.platform.getMediaSources() );
|
|
this.size = config.size || 150;
|
|
this.inputTimeout = null;
|
|
this.titles = {};
|
|
this.queryMediaSourcesCallback = ve.bind( this.queryMediaSources, this );
|
|
|
|
// Events
|
|
this.input.connect( this, { 'change': 'onInputChange' } );
|
|
this.select.connect( this, { 'select': 'onSelectSelect' } );
|
|
this.$results.on( 'scroll', ve.bind( this.onResultsScroll, this ) );
|
|
|
|
// Initialization
|
|
this.$query
|
|
.addClass( 've-ui-mwMediaSelectWidget-query' )
|
|
.append( this.input.$ );
|
|
this.$results
|
|
.addClass( 've-ui-mwMediaSelectWidget-results' )
|
|
.append( this.select.$ );
|
|
this.$
|
|
.addClass( 've-ui-mwMediaSelectWidget' )
|
|
.append( this.$results, this.$query );
|
|
this.queryMediaSources();
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
ve.inheritClass( ve.ui.MWMediaSelectWidget, ve.ui.Widget );
|
|
|
|
/* Events */
|
|
|
|
/**
|
|
* @event select
|
|
* @param {Object} item Media item info
|
|
*/
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Handle select widget select events.
|
|
*
|
|
* @param {string} value New value
|
|
*/
|
|
ve.ui.MWMediaSelectWidget.prototype.onInputChange = function () {
|
|
var i, len;
|
|
|
|
if ( this.loading ) {
|
|
this.request.abort();
|
|
}
|
|
|
|
// Reset
|
|
this.select.clearItems();
|
|
for ( i = 0, len = this.sources.length; i < len; i++ ) {
|
|
delete this.sources[i].gsroffset;
|
|
}
|
|
|
|
// Queue
|
|
clearTimeout( this.inputTimeout );
|
|
this.inputTimeout = setTimeout( this.queryMediaSourcesCallback, 100 );
|
|
};
|
|
|
|
/**
|
|
* Handle select widget select events.
|
|
*
|
|
* @param {ve.ui.MWMediaSelectItemWidget} item Selected item
|
|
* @emits select
|
|
*/
|
|
ve.ui.MWMediaSelectWidget.prototype.onSelectSelect = function ( item ) {
|
|
this.emit( 'select', item ? item.getData() : null );
|
|
};
|
|
|
|
/**
|
|
* Handle results scroll events.
|
|
*
|
|
* @param {jQuery.Event} e Scroll event
|
|
*/
|
|
ve.ui.MWMediaSelectWidget.prototype.onResultsScroll = function () {
|
|
var position = this.$results.scrollTop() + this.$results.outerHeight(),
|
|
threshold = this.select.$.outerHeight() - this.size;
|
|
if ( !this.input.isPending() && position > threshold ) {
|
|
this.queryMediaSources();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Query all sources for media.
|
|
*
|
|
* @method
|
|
*/
|
|
ve.ui.MWMediaSelectWidget.prototype.queryMediaSources = function () {
|
|
var i, len, source,
|
|
value = this.input.getValue();
|
|
|
|
if ( value === '' ) {
|
|
return;
|
|
}
|
|
|
|
for ( i = 0, len = this.sources.length; i < len; i++ ) {
|
|
source = this.sources[i];
|
|
if ( source.request ) {
|
|
source.request.abort();
|
|
}
|
|
if ( !source.gsroffset ) {
|
|
source.gsroffset = 0;
|
|
}
|
|
this.input.pushPending();
|
|
source.request = $.ajax( {
|
|
'url': source.url,
|
|
'data': {
|
|
'format': 'json',
|
|
'action': 'query',
|
|
'generator': 'search',
|
|
'gsrsearch': value,
|
|
'gsrnamespace': 6,
|
|
'gsrlimit': 15,
|
|
'gsroffset': source.gsroffset,
|
|
'prop': 'imageinfo',
|
|
'iiprop': 'dimensions|url',
|
|
'iiurlheight': this.size
|
|
},
|
|
'dataType': 'jsonp'
|
|
} )
|
|
.always( ve.bind( this.onMediaQueryAlways, this, source ) )
|
|
.done( ve.bind( this.onMediaQueryDone, this, source ) );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle media query response events.
|
|
*
|
|
* @method
|
|
* @param {Object} source Media query source
|
|
*/
|
|
ve.ui.MWMediaSelectWidget.prototype.onMediaQueryAlways = function ( source ) {
|
|
source.request = null;
|
|
this.input.popPending();
|
|
};
|
|
|
|
/**
|
|
* Handle media query load events.
|
|
*
|
|
* @method
|
|
* @param {Object} source Media query source
|
|
* @param {Object} data Media query response
|
|
*/
|
|
ve.ui.MWMediaSelectWidget.prototype.onMediaQueryDone = function ( source, data ) {
|
|
if ( !data.query || !data.query.pages ) {
|
|
return;
|
|
}
|
|
|
|
var page, title,
|
|
items = [],
|
|
pages = data.query.pages,
|
|
value = this.input.getValue();
|
|
|
|
if ( value === '' ) {
|
|
return;
|
|
}
|
|
|
|
if ( data['query-continue'] && data['query-continue'].search ) {
|
|
source.gsroffset = data['query-continue'].search.gsroffset;
|
|
}
|
|
|
|
for ( page in pages ) {
|
|
title = pages[page].title;
|
|
if ( !( title in this.titles ) ) {
|
|
this.titles[title] = true;
|
|
items.push(
|
|
new ve.ui.MWMediaSelectItemWidget(
|
|
pages[page],
|
|
{ '$$': this.$$, 'size': this.size }
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
this.select.addItems( items );
|
|
};
|