mediawiki-extensions-Visual.../modules/ve-mw/dm/models/ve.dm.MWMediaResourceProvider.js
James D. Forrester ada58df361 build: Bump file copyright notices for 2016
Change-Id: I3c618c196e504a80ca297a4132a17f1977a24fb7
2016-01-03 14:57:25 -08:00

335 lines
8.1 KiB
JavaScript

/*!
* VisualEditor DataModel MWMediaResourceProvider class.
*
* @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* MediaWiki media resource provider.
*
* @class
* @extends ve.dm.APIResultsProvider
*
* @constructor
* @param {string} apiurl The API url
* @param {Object} [config] Configuration options
* @cfg {string} [scriptDirUrl] The url of the API script
*/
ve.dm.MWMediaResourceProvider = function VeDmMWMediaResourceProvider( apiurl, config ) {
config = config || {};
// Parent constructor
ve.dm.MWMediaResourceProvider.super.call( this, apiurl, config );
// Fetching configuration
this.scriptDirUrl = config.scriptDirUrl;
this.isLocal = config.local !== undefined;
if ( this.isLocal ) {
this.setAjaxSettings( {
url: mw.util.wikiScript( 'api' ),
// If the url is local use json
dataType: 'json'
} );
} else {
this.setAjaxSettings( {
// If 'apiurl' is set, use that. Otherwise, build the url
// from scriptDirUrl and /api.php suffix
url: this.getAPIurl() || ( this.scriptDirUrl + '/api.php' ),
// If the url is not the same origin use jsonp
dataType: 'jsonp',
// JSON-P requests are not cached by default and get a &_=random trail.
// While setting cache=true will still bypass cache in most case due to the
// callback parameter, at least drop the &_=random trail which triggers
// an API warning (invalid parameter).
cache: true
} );
}
this.siteInfoPromise = null;
this.thumbSizes = [];
this.imageSizes = [];
};
/* Inheritance */
OO.inheritClass( ve.dm.MWMediaResourceProvider, ve.dm.APIResultsProvider );
/* Methods */
/**
* @inheritdoc
*/
ve.dm.MWMediaResourceProvider.prototype.getStaticParams = function () {
return $.extend(
{},
// Parent method
ve.dm.MWMediaResourceProvider.super.prototype.getStaticParams.call( this ),
{
action: 'query',
iiprop: 'dimensions|url|mediatype|extmetadata|timestamp|user',
iiextmetadatalanguage: this.getLang(),
prop: 'imageinfo'
}
);
};
/**
* Initialize the source and get the site info.
*
* Connect to the api url and retrieve the siteinfo parameters
* that are required for fetching results.
*
* @return {jQuery.Promise} Promise that resolves when the class
* properties are set.
*/
ve.dm.MWMediaResourceProvider.prototype.loadSiteInfo = function () {
var provider = this;
if ( !this.siteInfoPromise ) {
this.siteInfoPromise = new mw.Api().get( {
action: 'query',
meta: 'siteinfo'
} )
.then( function ( data ) {
provider.setImageSizes( data.query.general.imagelimits || [] );
provider.setThumbSizes( data.query.general.thumblimits || [] );
provider.setUserParams( {
// Standard width per resource
iiurlwidth: provider.getStandardWidth()
} );
} );
}
return this.siteInfoPromise;
};
/**
* Override parent method and get results from the source
*
* @param {number} [howMany] The number of items to pull from the API
* @return {jQuery.Promise} Promise that is resolved into an array
* of available results, or is rejected if no results are available.
*/
ve.dm.MWMediaResourceProvider.prototype.getResults = function ( howMany ) {
var xhr,
aborted = false,
provider = this;
return this.loadSiteInfo()
.then( function () {
if ( aborted ) {
return $.Deferred().reject();
}
xhr = provider.fetchAPIresults( howMany );
return xhr;
} )
.then(
function ( results ) {
if ( !results || results.length === 0 ) {
provider.toggleDepleted( true );
return [];
}
return results;
},
// Process failed, return an empty promise
function () {
provider.toggleDepleted( true );
return $.Deferred().resolve( [] );
}
)
.promise( { abort: function () {
aborted = true;
if ( xhr ) {
xhr.abort();
}
} } );
};
/**
* Get continuation API data
*
* @param {number} howMany The number of results to retrieve
* @return {Object} API request data
*/
ve.dm.MWMediaResourceProvider.prototype.getContinueData = function () {
return {};
};
/**
* Set continuation data for the next page
*
* @param {Object} continueData Continuation data
*/
ve.dm.MWMediaResourceProvider.prototype.setContinue = function () {
};
/**
* Sort the results
*
* @param {Object[]} results API results
* @return {Object[]} Sorted results
*/
ve.dm.MWMediaResourceProvider.prototype.sort = function ( results ) {
return results;
};
/**
* Call the API for search results.
*
* @param {number} howMany The number of results to retrieve
* @return {jQuery.Promise} Promise that resolves with an array of objects that contain
* the fetched data.
*/
ve.dm.MWMediaResourceProvider.prototype.fetchAPIresults = function ( howMany ) {
var xhr,
ajaxOptions = {},
provider = this;
if ( !this.isValid() ) {
return $.Deferred().reject().promise( { abort: $.noop } );
}
ajaxOptions = this.getAjaxSettings();
xhr = new mw.Api().get( $.extend( {}, this.getStaticParams(), this.getUserParams(), this.getContinueData( howMany ) ), ajaxOptions );
return xhr
.then( function ( data ) {
var page, newObj, raw,
results = [];
if ( data.error ) {
provider.toggleDepleted( true );
return [];
}
if ( data.continue ) {
// Update the offset for next time
provider.setContinue( data.continue );
} else {
// This is the last available set of results. Mark as depleted!
provider.toggleDepleted( true );
}
// If the source returned no results, it will not have a
// query property
if ( data.query ) {
raw = data.query.pages;
if ( raw ) {
// Strip away the page ids
for ( page in raw ) {
if ( !raw[ page ].imageinfo ) {
// The search may give us pages that belong to the File:
// namespace but have no files in them, either because
// they were deleted or imported wrongly, or just started
// as pages. In that case, the response will not include
// imageinfo. Skip those files.
continue;
}
newObj = raw[ page ].imageinfo[ 0 ];
newObj.title = raw[ page ].title;
newObj.index = raw[ page ].index;
results.push( newObj );
}
}
}
return provider.sort( results );
} )
.promise( { abort: xhr.abort } );
};
/**
* Set name
*
* @param {string} name
*/
ve.dm.MWMediaResourceProvider.prototype.setName = function ( name ) {
this.name = name;
};
/**
* Get name
*
* @return {string} name
*/
ve.dm.MWMediaResourceProvider.prototype.getName = function () {
return this.name;
};
/**
* Get standard width, based on the provider source's thumb sizes.
*
* @return {number|undefined} fetchWidth
*/
ve.dm.MWMediaResourceProvider.prototype.getStandardWidth = function () {
return ( this.thumbSizes && this.thumbSizes[ this.thumbSizes.length - 1 ] ) ||
( this.imageSizes && this.imageSizes[ 0 ] ) ||
// Fall back on a number
300;
};
/**
* Get prop
*
* @return {string} prop
*/
ve.dm.MWMediaResourceProvider.prototype.getFetchProp = function () {
return this.fetchProp;
};
/**
* Set prop
*
* @param {string} prop
*/
ve.dm.MWMediaResourceProvider.prototype.setFetchProp = function ( prop ) {
this.fetchProp = prop;
};
/**
* Set thumb sizes
*
* @param {number[]} sizes Available thumbnail sizes
*/
ve.dm.MWMediaResourceProvider.prototype.setThumbSizes = function ( sizes ) {
this.thumbSizes = sizes;
};
/**
* Set image sizes
*
* @param {number[]} sizes Available image sizes
*/
ve.dm.MWMediaResourceProvider.prototype.setImageSizes = function ( sizes ) {
this.imageSizes = sizes;
};
/**
* Get thumb sizes
*
* @return {number[]} sizes Available thumbnail sizes
*/
ve.dm.MWMediaResourceProvider.prototype.getThumbSizes = function () {
return this.thumbSizes;
};
/**
* Get image sizes
*
* @return {number[]} sizes Available image sizes
*/
ve.dm.MWMediaResourceProvider.prototype.getImageSizes = function () {
return this.imageSizes;
};
/**
* Check if this source is valid.
*
* @return {boolean} Source is valid
*/
ve.dm.MWMediaResourceProvider.prototype.isValid = function () {
return this.isLocal ||
// If we don't have either 'apiurl' or 'scriptDirUrl'
// the source is invalid, and we will skip it
this.apiurl !== undefined ||
this.scriptDirUrl !== undefined;
};