2020-11-18 09:11:12 +00:00
|
|
|
/**
|
2020-11-18 10:51:22 +00:00
|
|
|
* @class Api
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} apiUrl
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function Api( apiUrl ) {
|
|
|
|
this.url = apiUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
$.extend( Api.prototype, {
|
|
|
|
url: '',
|
|
|
|
|
2016-07-25 13:35:41 +00:00
|
|
|
/**
|
2020-11-18 09:11:12 +00:00
|
|
|
* Fetches change tags
|
|
|
|
*
|
|
|
|
* @return {jQuery.jqXHR}
|
2016-07-25 13:35:41 +00:00
|
|
|
*/
|
2020-11-18 09:11:12 +00:00
|
|
|
fetchAvailableChangeTags: function () {
|
|
|
|
return $.ajax( {
|
|
|
|
url: this.url,
|
|
|
|
data: {
|
|
|
|
action: 'query',
|
|
|
|
list: 'tags',
|
|
|
|
tgprop: 'displayname',
|
|
|
|
tglimit: 500,
|
|
|
|
format: 'json'
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
},
|
2016-07-25 13:35:41 +00:00
|
|
|
|
2020-01-24 16:21:23 +00:00
|
|
|
/**
|
2020-11-18 09:11:12 +00:00
|
|
|
* Fetches a batch of revision data, including a gender setting for users who edited the revision
|
|
|
|
*
|
|
|
|
* @param {string} pageName
|
2022-03-28 14:02:25 +00:00
|
|
|
* @param {Object} options
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} [options.dir='older'] Sort direction
|
|
|
|
* @param {number} [options.limit=500] Result limit
|
2022-03-28 14:02:25 +00:00
|
|
|
* @param {number} [options.startId]
|
|
|
|
* @param {number} [options.endId]
|
|
|
|
* @param {Object.<string,string>} [options.knownUserGenders]
|
2020-11-18 09:11:12 +00:00
|
|
|
* @return {jQuery.promise}
|
2020-01-24 16:21:23 +00:00
|
|
|
*/
|
2020-11-18 09:11:12 +00:00
|
|
|
fetchRevisionData: function ( pageName, options ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
const deferred = $.Deferred(),
|
2020-11-18 09:11:12 +00:00
|
|
|
self = this;
|
2023-06-22 09:41:37 +00:00
|
|
|
let userXhr;
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
options = options || {};
|
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
const xhr = this.fetchRevisions( pageName, options )
|
2020-11-18 09:11:12 +00:00
|
|
|
.done( function ( data ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
let revs = data.query.pages[ 0 ].revisions;
|
|
|
|
const revContinue = data.continue,
|
2020-11-18 09:11:12 +00:00
|
|
|
genderData = options.knownUserGenders || {},
|
2022-03-28 14:02:25 +00:00
|
|
|
changeTags = options.changeTags;
|
2020-11-18 09:11:12 +00:00
|
|
|
|
|
|
|
if ( !revs ) {
|
|
|
|
return deferred.reject;
|
2018-08-22 08:00:30 +00:00
|
|
|
}
|
2016-06-27 14:00:13 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
if ( changeTags && changeTags.length > 0 ) {
|
|
|
|
revs = self.getRevisionsWithNewTags( revs, changeTags );
|
|
|
|
}
|
2018-08-22 08:00:30 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
// No need to query any gender data if masculine, feminine, and neutral are all
|
|
|
|
// the same anyway
|
2023-06-22 09:41:37 +00:00
|
|
|
const unknown = mw.msg( 'revisionslider-label-username', 'unknown' );
|
2020-11-18 09:11:12 +00:00
|
|
|
if ( mw.msg( 'revisionslider-label-username', 'male' ) === unknown &&
|
|
|
|
mw.msg( 'revisionslider-label-username', 'female' ) === unknown
|
|
|
|
) {
|
|
|
|
return deferred.resolve( { revisions: revs, continue: revContinue } );
|
|
|
|
}
|
2018-06-26 13:15:54 +00:00
|
|
|
|
2023-06-22 09:41:37 +00:00
|
|
|
const userNames = self.getUniqueUserNamesWithUnknownGender( revs, genderData );
|
2016-06-27 14:00:13 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
userXhr = self.fetchUserGenderData( userNames )
|
|
|
|
.done( function ( data2 ) {
|
|
|
|
if ( typeof data2 === 'object' &&
|
|
|
|
data2.query &&
|
|
|
|
data2.query.users &&
|
|
|
|
data2.query.users.length > 0
|
|
|
|
) {
|
|
|
|
$.extend( genderData, self.getUserGenderData( data2.query.users, genderData ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
revs.forEach( function ( rev ) {
|
2022-03-28 14:02:25 +00:00
|
|
|
if ( rev.user in genderData ) {
|
2020-11-18 09:11:12 +00:00
|
|
|
rev.userGender = genderData[ rev.user ];
|
2016-06-27 14:00:13 +00:00
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
} );
|
2016-06-27 14:00:13 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
deferred.resolve( { revisions: revs, continue: revContinue } );
|
|
|
|
} )
|
|
|
|
.fail( deferred.reject );
|
|
|
|
} )
|
|
|
|
.fail( deferred.reject );
|
|
|
|
|
|
|
|
return deferred.promise( {
|
|
|
|
abort: function () {
|
|
|
|
xhr.abort();
|
|
|
|
if ( userXhr ) {
|
|
|
|
userXhr.abort();
|
2016-08-15 18:15:30 +00:00
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches up to 500 revisions at a time
|
|
|
|
*
|
|
|
|
* @param {string} pageName
|
2022-03-28 14:02:25 +00:00
|
|
|
* @param {Object} [options]
|
2020-11-18 09:11:12 +00:00
|
|
|
* @param {string} [options.dir='older'] Sort direction
|
|
|
|
* @param {number} [options.limit=500] Result limit
|
2022-03-28 14:02:25 +00:00
|
|
|
* @param {number} [options.startId]
|
|
|
|
* @param {number} [options.endId]
|
2020-11-18 09:11:12 +00:00
|
|
|
* @return {jQuery.jqXHR}
|
|
|
|
*/
|
|
|
|
fetchRevisions: function ( pageName, options ) {
|
|
|
|
options = options || {};
|
2023-06-22 09:41:37 +00:00
|
|
|
const data = {
|
2020-11-18 09:11:12 +00:00
|
|
|
action: 'query',
|
|
|
|
prop: 'revisions',
|
|
|
|
format: 'json',
|
2023-08-25 12:42:32 +00:00
|
|
|
rvprop: 'ids|timestamp|user|parsedcomment|size|flags|tags',
|
2020-11-18 09:11:12 +00:00
|
|
|
titles: pageName,
|
|
|
|
formatversion: 2,
|
|
|
|
continue: '',
|
|
|
|
rvlimit: 500,
|
2022-03-28 14:02:25 +00:00
|
|
|
rvdir: options.dir || 'older'
|
2020-11-18 09:11:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if ( 'startId' in options ) {
|
|
|
|
data.rvstartid = options.startId;
|
|
|
|
}
|
|
|
|
if ( 'endId' in options ) {
|
|
|
|
data.rvendid = options.endId;
|
|
|
|
}
|
|
|
|
if ( 'limit' in options && options.limit <= 500 ) {
|
|
|
|
data.rvlimit = options.limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $.ajax( {
|
|
|
|
url: this.url,
|
|
|
|
data: data
|
|
|
|
} );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches gender data for maximum 50 user names
|
|
|
|
*
|
|
|
|
* @param {string[]} users
|
|
|
|
* @return {jQuery.jqXHR}
|
|
|
|
*/
|
|
|
|
fetchUserGenderData: function ( users ) {
|
|
|
|
if ( users.length === 0 ) {
|
|
|
|
return $.Deferred().resolve();
|
|
|
|
}
|
|
|
|
return $.ajax( {
|
|
|
|
url: this.url,
|
|
|
|
data: {
|
|
|
|
formatversion: 2,
|
2016-08-15 18:15:30 +00:00
|
|
|
action: 'query',
|
2020-11-18 09:11:12 +00:00
|
|
|
list: 'users',
|
2016-08-15 18:15:30 +00:00
|
|
|
format: 'json',
|
2020-11-18 09:11:12 +00:00
|
|
|
usprop: 'gender',
|
|
|
|
ususers: users.slice( 0, 50 ).join( '|' )
|
2016-06-27 14:00:13 +00:00
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
} );
|
|
|
|
},
|
2016-06-27 14:00:13 +00:00
|
|
|
|
2020-11-18 09:11:12 +00:00
|
|
|
/**
|
|
|
|
* @param {Object[]} revs
|
2022-03-28 14:02:25 +00:00
|
|
|
* @param {Object.<string,string>} knownUserGenders
|
2020-11-18 09:11:12 +00:00
|
|
|
* @return {string[]}
|
|
|
|
*/
|
|
|
|
getUniqueUserNamesWithUnknownGender: function ( revs, knownUserGenders ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
const allUsers = revs.map( function ( rev ) {
|
2022-03-28 14:02:25 +00:00
|
|
|
return !( 'anon' in rev ) && rev.user;
|
2020-11-18 09:11:12 +00:00
|
|
|
} );
|
2022-03-28 14:02:25 +00:00
|
|
|
return allUsers.filter( function ( name, index ) {
|
|
|
|
// Anonymous users don't have a name
|
|
|
|
return name && !( name in knownUserGenders ) &&
|
|
|
|
// This filters duplicates by rejecting all but the first one
|
|
|
|
allUsers.indexOf( name ) === index;
|
2020-11-18 09:11:12 +00:00
|
|
|
} );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2022-03-28 14:02:25 +00:00
|
|
|
* @param {Object[]} users
|
|
|
|
* @return {Object.<string,string>}
|
2020-11-18 09:11:12 +00:00
|
|
|
*/
|
2022-03-28 14:02:25 +00:00
|
|
|
getUserGenderData: function ( users ) {
|
2023-06-22 09:41:37 +00:00
|
|
|
const genderData = {};
|
2022-03-28 14:02:25 +00:00
|
|
|
users.forEach( function ( user ) {
|
|
|
|
if ( user.gender && user.gender !== 'unknown' ) {
|
|
|
|
genderData[ user.name ] = user.gender;
|
|
|
|
}
|
2020-11-18 09:11:12 +00:00
|
|
|
} );
|
|
|
|
return genderData;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {Object[]} revs
|
|
|
|
* @param {Object[]} changeTags
|
|
|
|
* @return {Object[]}
|
|
|
|
*/
|
|
|
|
getRevisionsWithNewTags: function ( revs, changeTags ) {
|
|
|
|
revs.forEach( function ( rev ) {
|
2022-03-28 14:02:25 +00:00
|
|
|
rev.tags = rev.tags.map( function ( tag ) {
|
|
|
|
changeTags.some( function ( changeTag ) {
|
|
|
|
if ( tag === changeTag.name ) {
|
|
|
|
tag = changeTag.displayname;
|
|
|
|
return true;
|
2020-11-18 09:11:12 +00:00
|
|
|
}
|
2022-03-28 14:02:25 +00:00
|
|
|
return false;
|
2020-11-18 09:11:12 +00:00
|
|
|
} );
|
2022-03-28 14:02:25 +00:00
|
|
|
return tag;
|
|
|
|
} ).filter( function ( tag ) {
|
|
|
|
// Remove hidden tags (tags with no displayname)
|
|
|
|
return tag;
|
|
|
|
} );
|
2020-11-18 09:11:12 +00:00
|
|
|
} );
|
|
|
|
return revs;
|
|
|
|
}
|
|
|
|
} );
|
2016-07-25 13:35:41 +00:00
|
|
|
|
2020-03-29 20:36:10 +00:00
|
|
|
module.exports = Api;
|