User:PleaseStand/userinfo-dev.js: Difference between revisions

Content deleted Content added
dev copy
 
Undid revision 1225074654 by PleaseStand (talk) - "named" group was removed in git:mediawiki/core/+/93e8350d295203ac
 
(42 intermediate revisions by the same user not shown)
Line 1:
// based on http://en.wikipedia.org/wiki/User:Fran_Rogers/dimorphism.js
// quick and dirty MW 1.19 fix
// and on http://en.wikipedia.org/wiki/User:Splarka/sysopdectector.js
 
mw.loader.using( "mediawiki.util", function ( mw, $ ) {
 
var h = mw.html,
// based on http://en.wikipedia.org/wiki/User:Fran Rogers/dimorphism.js
api,
// and on http://en.wikipedia.org/wiki/User:Splarka/sysopdectector.js
ns = mw.config.get( 'wgNamespaceNumber' ),
title = mw.config.get( 'wgTitle' );
 
// Bail out unless on a user or user talk page, and not a subpage.
if ( ns !== 2 && ns !== 3 || title.indexOf( '/' ) >= 0 ) {
return;
}
 
// TODO: add additional languages
mw.messages.set( {
'ps-userinfo-gendersymbol-male': '♂',
'ps-userinfo-gendersymbol-female': '♀',
'ps-userinfo-gendersymbol-unknown': '',
'ps-userinfo-itemseparator': '$1\u00a0| $2',
'ps-userinfo-groupseparator': '$1, $2',
 
'ps-userinfo-seconds': '$1 {{PLURAL:$1|second|seconds}}',
'ps-userinfo-minutes': '$1 {{PLURAL:$1|minute|minutes}}',
'ps-userinfo-hours': '$1 {{PLURAL:$1|hour|hours}}',
'ps-userinfo-days': '$1 {{PLURAL:$1|day|days}}',
'ps-userinfo-weeks': '$1 {{PLURAL:$1|week|weeks}}',
'ps-userinfo-months': '$1 {{PLURAL:$1|month|months}}',
'ps-userinfo-years': '$1 {{PLURAL:$1|year|years}}',
'ps-userinfo-years-months': '$1 {{PLURAL:$1|year|years}} $2 {{PLURAL:$2|month|months}}',
 
'ps-userinfo-blocked': 'BLOCKED',
'ps-userinfo-editcount': '$1 {{PLURAL:$1|edit|edits}}',
'ps-userinfo-invalid': 'invalid username',
'ps-userinfo-ipv4': 'IPv4 address',
'ps-userinfo-ipv6': 'IPv6 address',
'ps-userinfo-lastedited': 'last edited [$2 $1 ago]',
'ps-userinfo-missing': 'username not registered',
'ps-userinfo-registration': '$1 old',
'ps-userinfo-user': '{{GENDER:$1|registered user}}'
} );
 
/**
* Get the user's information by performing an AJAX request and processing the response.
* @param {string} userName The user to retrieve information for
* @return {jQuery.Deferred}
*/
function getInfo( userName ) {
return api.get( {
action: 'query',
list: 'users|usercontribs',
maxage: 300,
uclimit: 1,
ucprop: 'timestamp',
ucuser: userName,
usprop: 'blockinfo|editcount|gender|registration|groups',
ususers: userName
} ).then( function ( data ) {
var query = data.query, user = query.users[0], contribs = query.usercontribs;
 
return {
name: userName,
isInvalid: 'invalid' in user,
isIPv4: mw.util.isIPv4Address( userName ),
isIPv6: mw.util.isIPv6Address( userName ),
isMissing: 'missing' in user,
groups: user.groups || [],
editCount: 'editcount' in user ? user.editcount : null,
registration: user.registration ? new Date( user.registration ) : null,
isBlocked: 'blockexpiry' in user,
gender: user.gender || 'unknown',
lastEdited: ( contribs && contribs[0] ) ? new Date( contribs[0].timestamp ) : null
};
} );
}
 
/**
* Display the user's information on screen.
* @param info Information about the user
*/
function displayInfo( info ) {
var userPrefix = mw.config.get( 'wgFormattedNamespaces' )[2] + ':',
items = [],
groups = [],
text = '';
 
if ( info.isBlocked ) {
/*global window, wgNamespaceNumber, wgTitle, wgFormattedNamespaces
items.push( h.element( 'a', { href:
wgArticlePath, wgScriptPath, addOnloadHook, importScriptURI, sajax_init_object*/
mw.util.wikiScript( 'index' ) + '?' + $.param( {
title: 'Special:Log',
page: userPrefix + info.name
} )
}, new h.Raw( h.element( 'strong', {}, mw.msg( 'ps-userinfo-blocked' ) ) ) ) );
}
 
if ( info.isMissing ) {
// for non-Firefox browsers
items.push( mw.message( 'ps-userinfo-missing' ).escaped() );
if(!Array.prototype.map) {
} else if ( info.isIPv4 ) {
Array.prototype.map = function(fun /*, thisp*/) {
items.push( mw.message( 'ps-userinfo-ipv4' ).escaped() );
var len = this.length >>> 0;
} else if (typeof fun !=info.isIPv6 "function") {
items.push( mw.message( 'ps-userinfo-ipv6' ).escaped() );
throw new TypeError();
} else if ( info.isInvalid ) {
}
items.push( mw.message( 'ps-userinfo-invalid' ).escaped() );
var res = new Array(len);
} else {
var thisp = arguments[1];
$.each( info.groups, function ( i, v ) {
for (var i = 0; i < len; i++) {
if ( $.inArray( v, ['*', 'user', 'autoconfirmed'] ) < 0 ) {
if (i in this) {
groups.push( mw.message( 'group-' + v + '-member', info.gender ).escaped() );
res[i] = fun.call(thisp, this[i], i, this);
}
}
} );
}
if ( groups.length ) {
return res;
items.push( groups.join( mw.message( 'ps-userinfo-groupseparator', '', '' ).escaped() ) );
};
} else {
}
items.push( mw.message( 'ps-userinfo-user', info.gender ).escaped() );
}
 
if ( info.registration ) {
items.push( mw.message(
'ps-userinfo-registration',
buildDurationMessage( info.registration, new Date() ).text(),
info.gender
).escaped() );
}
 
if ( info.editCount !== null ) {
function UserinfoJsParseDate(utcDate) {
items.push( h.element( 'a', { href:
// The ISO 8601 date format used by MediaWiki
'https://xtools.wmcloud.org/ec/' +
var s = /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/.exec(utcDate);
encodeURIComponent( ___location.hostname ) + '/' +
if(s === null) {
encodeURIComponent( info.name )
return null;
}, mw.message( 'ps-userinfo-editcount', info.editCount ).text() ) );
}
}
var d = new Date();
}
d.setUTCFullYear(s[1], s[2] - 1, s[3]);
d.setUTCHours(s[4], s[5], s[6]);
return d;
}
 
if ( info.lastEdited ) {
function UserinfoJsValidateIp(ip) {
items.push( mw.message(
var ipSplit = ip.split(".");
'ps-userinfo-lastedited',
if(ipSplit.length != 4) {
buildDurationMessage( info.lastEdited, new Date() ).escaped(),
return false;
$( '<a>' ).prop( 'href', mw.util.getUrl( 'Special:Contributions/' + info.name ) ),
}
mw.html.escape( info.name )
if(ipSplit.map(function(s){return parseInt(s,10);}).join(".") != ip) {
).parse() );
return false;
}
}
return true;
}
 
// Add the gender symbol after the user's name
function UserinfoJsFormatQty(qty, singular, plural) {
$( '<span>' )
return String(qty).replace(/\d{1,3}(?=(\d{3})+(?!\d))/g, "$&,") + "\u00a0" + (qty == 1 ? singular : plural);
.prop( 'id', 'ps-gender-' + info.gender )
}
.css( {
paddingLeft: '0.25em',
fontFamily: '"Lucida Grande", "Lucida Sans Unicode", sans-serif',
fontSize: '75%'
} )
.text( mw.msg( 'ps-userinfo-gendersymbol-' + info.gender ) )
.appendTo( '#firstHeading' );
 
// Replace the subtitle with the rest of the info
function UserinfoJsFormatDateRel(old) {
$( '#siteSub' ).html( items.join( mw.message( 'ps-userinfo-itemseparator', '', '' ).escaped() ) );
// The code below requires the computer's clock to be set correctly.
}
var age = new Date().getTime() - old.getTime();
var ageNumber, ageRemainder, ageWords;
if(age < 60000) {
// less than one minute old
ageNumber = Math.floor(age / 1000);
ageWords = UserinfoJsFormatQty(ageNumber, "second", "seconds");
} else if(age < 3600000) {
// less than one hour old
ageNumber = Math.floor(age / 60000);
ageWords = UserinfoJsFormatQty(ageNumber, "minute", "minutes");
} else if(age < 86400000) {
// less than one day old
ageNumber = Math.floor(age / 3600000);
ageWords = UserinfoJsFormatQty(ageNumber, "hour", "hours");
ageRemainder = Math.floor((age - ageNumber * 3600000) / 60000);
} else if(age < 604800000) {
// less than one week old
ageNumber = Math.floor(age / 86400000);
ageWords = UserinfoJsFormatQty(ageNumber, "day", "days");
} else if(age < 2592000000) {
// less than one month old
ageNumber = Math.floor(age / 604800000);
ageWords = UserinfoJsFormatQty(ageNumber, "week", "weeks");
} else if(age < 31536000000) {
// less than one year old
ageNumber = Math.floor(age / 2592000000);
ageWords = UserinfoJsFormatQty(ageNumber, "month", "months");
} else {
// one year or older
ageNumber = Math.floor(age / 31536000000);
ageWords = UserinfoJsFormatQty(ageNumber, "year", "years");
ageRemainder =
Math.floor((age - ageNumber * 31536000000) / 2592000000);
if(ageRemainder) {
ageWords += " " +
UserinfoJsFormatQty(ageRemainder, "month", "months");
}
}
return ageWords;
}
 
/**
// If on a user or user talk page, and not a subpage...
* Load messages corresponding to MediaWiki user groups into mw.messages.
if((wgNamespaceNumber == 2 || wgNamespaceNumber == 3) && !(/\//.test(wgTitle))) {
* @return {jQuery.Deferred}
// add a hook to...
*/
addOnloadHook(function(){
function loadGroupMemberMessages() {
// Request the user's information from the API.
return api.get( {
// Note that this is allowed to be up to 5 minutes old.
action: 'query',
amfilter: '-member',
// But if the user is an IP, do not look up. --PS 2010-05-16
amlang: mw.config.get( 'wgUserLanguage' ),
// Commented out to allow "last edit" lookups --PS 2010-06-27
amprefix: 'group-',
/* if(UserinfoJsValidateIp(wgTitle)) {
maxage: 86400,
// Code copied from bottom of callback function --PS 2010-05-16
meta: 'allmessages'
var fh = window.document.getElementById("firstHeading");
} ).then( function ( data ) {
var stDiv = window.document.createElement("div");
$.each( data.query.allmessages, function ( i, message ) {
stDiv.innerHTML = "An anonymous user";
mw.messages.set( message.name, message['*'] );
fh.parentNode.insertBefore(stDiv, fh.nextSibling);
} );
return;
} );
} */
}
var et = encodeURIComponent(wgTitle);
var x = sajax_init_object();
x.open("GET", wgScriptPath + "/api.php?format=json&maxage=300&action=query&list=users|usercontribs&usprop=blockinfo|editcount|gender|registration|groups&uclimit=1&ucprop=timestamp&ususers=" + et + "&ucuser=" + et , true);
x.onreadystatechange = function() {
if(x.readyState != 4 || x.status != 200) {
return;
}
// When response arrives extract the information we need.
var query = eval("(" + x.responseText + ")");
if(!query.query) { return; } // Suggested by Gary King to avoid JS errors --PS 2010-08-25
query = query.query;
var user, invalid, missing, groups, editcount, registration, blocked, gender, lastEdited;
try {
user = query.users[0];
invalid = typeof user.invalid != "undefined";
missing = typeof user.missing != "undefined";
groups = (typeof user.groups == "object") ? user.groups : [];
editcount = (typeof user.editcount == "number") ? user.editcount : null;
registration = (typeof user.registration == "string") ?
UserinfoJsParseDate(user.registration) : null;
blocked = typeof user.blockedby != "undefined";
gender = (typeof user.gender == "string") ? user.gender : null;
lastEdited = (typeof query.usercontribs[0] == "object") &&
(typeof query.usercontribs[0].timestamp == "string") ?
UserinfoJsParseDate(query.usercontribs[0].timestamp) : null;
} catch(e) {
return; // Not much to do if the server is returning an error (e.g. if the username is malformed).
}
 
/**
// Format the information for on-screen display
* Convert a duration to a message object.
* @param {Date} start The duration's start
var statusText = "";
* @param {Date} end The duration's end
var ipUser = false;
* @return {mediaWiki.Message}
*/
function buildDurationMessage( start, end ) {
var age = ( end - start ) / 1000; // start with seconds
if ( age < 60 ) {
return mw.message( 'ps-userinfo-seconds', Math.floor( age ) );
}
 
age /= 60; // convert to minutes
// User status
if ( age < 60 if(blocked) {
return mw.message( 'ps-userinfo-minutes', Math.floor( age ) );
statusText += "<a href=\"" + wgScriptPath +
}
"/index.php?title=Special:Log&amp;page=" +
encodeURIComponent(wgFormattedNamespaces[2] + ":" + user.name) +
"&amp;type=block\">blocked</a> ";
}
if (missing) {
statusText += "username not registered";
} else if (invalid) {
statusText += (ipUser = UserinfoJsValidateIp(user.name)) ? "anonymous user" : "invalid username";
} else {
// User is registered and may be in a privileged group. Below we have a list of user groups.
// Only need the ones different from the software's name (or ones to exclude), though.
var friendlyGroupNames = {
// Exclude implicit user group information provided by MW 1.17 --PS 2010-02-17
'*': false,
'user': false,
'autoconfirmed': false,
sysop: "administrator",
accountcreator: "account creator",
"import": "importer",
transwiki: "transwiki importer",
"ipblock-exempt": "IP block exemption",
oversight: "oversighter",
confirmed: "confirmed user",
abusefilter: "edit filter manager",
autoreviewer: "autopatrolled user" // Group has been renamed --PS 2010-07-06
};
var friendlyGroups = [];
for(var i = 0; i < groups.length; ++i) {
var s = groups[i];
if(friendlyGroupNames.hasOwnProperty(s)) {
if(friendlyGroupNames[s]) {
friendlyGroups.push(friendlyGroupNames[s]);
}
} else {
friendlyGroups.push(s);
}
}
switch(friendlyGroups.length) {
case 0:
// User not in a privileged group
// Changed to "registered user" by request of [[User:Svanslyck]]
// --PS 2010-05-16
// statusText += "user";
if(blocked) {
statusText += "user";
} else {
statusText += "registered user";
}
break;
case 1:
statusText += friendlyGroups[0];
break;
case 2:
statusText += friendlyGroups[0] + " and " + friendlyGroups[1];
break;
default:
statusText += friendlyGroups.slice(0, -1).join(", ") +
", and " + friendlyGroups[friendlyGroups.length - 1];
break;
}
}
// Registration date
if(registration) {
statusText += ", " + UserinfoJsFormatDateRel(registration) + " old";
}
// Edit count
if(editcount !== null) {
statusText += ", with " +
"<a href=\"http://toolserver.org/~tparis/count/index.php?name=" +
encodeURIComponent(user.name) +
"&amp;lang=en&amp;wiki=wikipedia\">" +
UserinfoJsFormatQty(editcount, "edit", "edits") + "</a>";
}
// Prefix status text with correct article
if("AEIOaeio".indexOf(statusText.charAt(0)) >= 0) {
statusText = "An " + statusText;
} else {
statusText = "A " + statusText;
}
 
age /= 60; // convert to hours
// Add full stop to status text
if ( age < 24 ) {
statusText += ".";
return mw.message( 'ps-userinfo-hours', Math.floor( age ) );
}
 
age /= 24; // convert to days
// Last edited --PS 2010-06-27
if ( age < 7 ) {
// Added link to contributions page --PS 2010-07-03
return mw.message( 'ps-userinfo-days', Math.floor( age ) );
if(lastEdited) {
}
statusText += " Last edited <a href=\"" + wgArticlePath.replace("$1", "Special:Contributions/" + encodeURIComponent(user.name)) + "\">" + UserinfoJsFormatDateRel(lastEdited) + " ago</a>.";
}
 
age /= 7; // convert to weeks
// Show the correct gender symbol
if ( age < 4.3481 ) {
var fh = window.document.getElementById("firstHeading") ||
return mw.message( 'ps-userinfo-weeks', Math.floor( age ) );
window.document.getElementById("section-0");
}
// Add classes for blocked, registered, and anonymous users
var newClasses = [];
if(blocked) {
newClasses.push("ps-blocked");
}
if(ipUser) {
newClasses.push("ps-anonymous");
} else if(invalid) {
newClasses.push("ps-invalid");
} else {
newClasses.push("ps-registered");
}
fh.className += (fh.className.length ? " " : "") + groups.map(function(s) {
return "ps-group-" + s;
}).concat(newClasses).join(" ");
var genderSpan = window.document.createElement("span");
genderSpan.id = "ps-gender-" + (gender || "unknown");
genderSpan.style.paddingLeft = "0.25em";
genderSpan.style.fontFamily = '"Lucida Grande", "Lucida Sans Unicode", "sans-serif"';
genderSpan.style.fontSize = "75%";
var genderSymbol;
switch(gender) {
case "male": genderSymbol = "\u2642"; break;
case "female": genderSymbol = "\u2640"; break;
default: genderSymbol = ""; break;
}
genderSpan.appendChild(window.document.createTextNode(genderSymbol));
fh.appendChild(genderSpan);
 
age /= 4.3481; // convert to months
// Now show the other information. Non-standard? Yes, but it gets the job done.
if ( age < 12 ) {
// Add a period after the tagline when doing so. --PS 2010-07-03
return mw.message( 'ps-userinfo-months', Math.floor( age ) );
}
 
// convert to years and months
var ss = window.document.getElementById("siteSub");
var years = Math.floor( age / 12 ), months = Math.floor( age % 12 );
if(!ss) {
if ( months ) {
ss = window.document.createElement("div");
return mw.message( 'ps-userinfo-years-months', years, months );
ss.id = "siteSub";
}
ss.innerHTML = "From Wikipedia, the free encyclopedia";
return mw.message( 'ps-userinfo-years', years );
var bc = window.document.getElementById("bodyContent");
}
bc.insertBefore(ss, bc.firstChild);
}
ss.innerHTML = '<span id="ps-userinfo">' + statusText + '</span> ' + ss.innerHTML + '.';
};
x.send(null);
});
}
 
mw.loader.using( [
});
'mediawiki.api',
'mediawiki.jqueryMsg',
'mediawiki.language',
'mediawiki.util'
], function () {
api = new mw.Api();
loadGroupMemberMessages().done( function () {
getInfo( title ).done( displayInfo );
} );
} );
} )( mediaWiki, jQuery );