Benutzer:Björn Hagemann/userstatus.js
aus Wikipedia, der freien Enzyklopädie
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
- Opera: Strg+F5
/**
@Description:
* Eng: This script shows on user-pages some information about the user
* Deu: Dieses Script zeigt auf Userseiten einige Informationen über den User an
@Author: Steef389 - 2010-2013 by [[:de:User:Steef389/js/user_status.js]];
@Author: Perhelion - adapted 2016, modified & simplified & fallback language
@Revision: 20:17, 2 October 2018 (UTC)
See also [[:de:Benutzer:Schnark/js/letzteredit]], [[:de:Benutzer:Schnark/js/extratabs]]
* @Required modules: mediawiki.util, mediawiki.api, mediawiki.language, mediawiki.cookie, jquery.spinner
* @TODO: fakeLoader, group since when?, edits/day, Error msg on maxlag instead of disapear
* better IP support
<nowiki>
**/
/* eslint indent:["error","tab",{"outerIIFEBody":0}], one-var:0, vars-on-top:0, camelcase:0, no-underscore-dangle:0 */
/* global jQuery, mediaWiki, indexedDB*/
(function ($, mw) {
'use strict';
// var us = {};
var project = window.project || mw.config.get('wgDBname');
var msg, i18n = {
en: {
noReason: 'reason removed',
blockCmt: 'no block comment',
never: 'never',
not: ' not ',
block: 'blocked',
and: '$1 and$2',
// load : "loading…",
count: 'Count',
noedit: 'None (only deleted contributions?)',
// noav : "not available",
nodb: '‹not in database›',
noGrp: 'no extended group',
dates: ['years', 'months', 'days', 'hours', 'minutes', 'seconds'],
date: ['year', 'month', 'day', 'hour', 'minute', 'second'],
nosec: 'less than one ',
curtimeDiff: ' Locale time difference to server time in s: ',
thxGn: 'given',
thxRd: 'received',
nova: 'new',
ago: '$1 ago',
blocklog: 'Block log',
// Title-lines
contrib: 'Edits: ',
usub: 'Subpages⬇',
thxGvng: 'Thanksgivings: ',
reviews: 'Active reviews: ',
regdate: 'Registration: ',
laedit: 'Last edited: ',
lala: 'Last log activity',
fiedit: 'First edit: ',
blocks: 'Block-status: ',
loGrp: 'Local user-groups: ',
glGrp: 'Global user-groups: ',
blockEnd: 'Block-end: ',
blocker: 'Blocker: ',
blockReason: 'Block-reason: '
},
de: {
noReason: 'Begründung entfernt',
blockCmt: 'kein Sperrkommentar',
never: 'nie',
not: ' nicht ',
block: 'gesperrt',
and: '$1 und$2',
// load : "wird geladen…",
count: 'Anzahl',
noedit: 'Keiner (nur gelöschte Beiträge?)',
// noav : "nicht verfügbar",
nodb: '‹nicht in Datenbank›',
noGrp: 'keine erweiterte Gruppe',
date: ['Jahr', 'Monat', 'Tag', 'Stunde', 'Minute', 'Sekunde'],
dates: ['Jahren', 'Monaten', 'Tagen', 'Stunden', 'Minuten', 'Sekunden'],
nosec: 'vor weniger als einer ',
curtimeDiff: ' Lokale Zeit-Differenz zur Server-Zeit in s: ',
thxGn: 'gegeben',
thxRd: 'erhalten',
nova: 'neu',
ago: 'vor $1',
blocklog: 'Benutzersperr-Logbuch',
// Zeilentitel
thxGvng: 'Danksagungen: ',
contrib: 'Edits: ', // "Bearbeitungen: ",
usub: 'Unterseiten⬇',
reviews: 'Aktive Sichtungen: ',
regdate: 'Registrierungsdatum: ',
laedit: 'Letzter Edit: ',
lala: 'Letzte Log-Aktivität',
fiedit: 'Erster Edit: ',
blocks: 'Sperrstatus: ',
loGrp: 'Lokale Benutzergruppen: ',
glGrp: 'Globale Benutzergruppen: ',
blockEnd: 'Sperr-Ende: ',
blocker: 'Sperrender: ',
blockReason: 'Sperrbegründung: '
}
};
var data;
/* = { // JSON cache
timestamp: "",
timediff: "",
editcount: "",
groups: "",
gender: "",
lastedit: "",
firstedit: "",
reviews: "",
glGrp: "" {array}
};*/
var us = mw.libs.userstatus = {
name: 'Userstatus',
version: 1.79,
// Config
lastEditSeconds: false, // Show seconds of last edit
styleMissingData: 'color:#999;font-size:90%;', // Styles for missing entries
styleLoading: 'font-style:italic;',
styleBlocked: 'color:#c00',
styleNotBlocked: 'color:#080',
viewPatrolNumber: false, // Primary for DeWP, supports a direct bot activation.
lang: mw.config.get('wgUserLanguage'),
user: mw.config.get('wgTitle'),
cookie: [],
thanks: 0,
patrols: 0,
actions: [],
dbVersion: 0,
getLocalNames: function (groups, specialpage) {
if (groups) {
var arr = [];
specialpage = specialpage || 'ListGroupRights';
for (var i = 0; i < groups.length; ++i) {
var g = groups[i];
var n = us.groupNames[g];
// Link it
if (n) arr.push('<a href="/wiki/Special:' + specialpage + '#' + g + '">' + n + '</a>');
// arr.push('<a href="#" onclick="mw.libs.userstatus.goGroupPage(event)" title="' +
// arr.push(n);
else arr.push(g);
}
// log("GetLocalNames ", arr, groups);
groups = arr.join(', ').replace(/(.*),([^,]*)$/, msg.and); // String
}
return groups;
},
/* goGroupPage: function (e) {
if (e.target) {
var $e = $(e.target);
$e.injectSpinner("grp");
var title = "grouppage-" + $e.attr('title');
$e.attr('target', '_blank');
$e.off('click').attr('onclick', '');
e.preventDefault();
us.ajaxRequest("&smaxage=86400&meta=allmessages&amenableparser=1&amincludelocal=1&amfrom="+ title +"&amto="+ title +"&refix=grouppage-&amlang=" + mw.config.get("wgContentLanguage"),
function (aw) {
$.removeSpinner("grp");
if (!aw || !aw.query) return;
aw = aw.query.allmessages;
if (aw.length)
window.open("/wiki/" + aw[0]["*"]);
}
);
}
return false;
},*/
writeGroups: function (groups) {
groups = us.getLocalNames(groups);
if (!groups) {
groups = $('<span>', {
style: us.styleMissingData + us.styleLoading,
text: msg.noGrp
});
}
us.loading_groups.replaceWith(groups);
},
getGroupNames: function (aw) {
if (!aw || !aw.query) return;
aw = aw.query.allmessages;
var al = aw.length;
var groups = {};
while (al--) {
var an = aw[al];
var name = an.name;
if (/^group-/.test(name)) {
name = RegExp.rightContext || name.substring(6);
groups[name] = an['*'];
}
}
us.groupNames = groups;
// mw.log("groupNames: ", us.groupNames);
us.init();
},
ajaxRequest: function (params, onSuccess, trial) {
// mw.log("ajaxRequest", params, onSuccess, trial);
var url = us.api;
if (!(params instanceof Object)) { // Workaround for mw.Api bug
url += params + '&maxage=2419200&smaxage=2419200';
params = {};
} else {
params.maxlag = 3;
params.maxage = 2419200;
params.smaxage = 2419200;
// params.timeout = 2400;
if (trial) params.maxlag *= trial;
// params.timeout *= trial;
}
var api = $.getJSON(url, params, onSuccess)
/* $.ajax( {
dataType: 'json',
url: url,
data: params,
cache: true,
success: onSuccess
} )*/
/* var api = new mw.Api() // Buggy ? Too much timeout errors !
.get(params, { timeout: 2400 * (trial || 1) })*/
.fail(function (jqXHR, status) {
var note = 'Timeout fail, maybe try again!';
var error = [us.name + ': AJAX-Request failed ', note, params, jqXHR, status];
if (trial) {
if (!status.textStatus || (status.textStatus !== 'timeout' && jqXHR !== 'maxlag')) note = $('<span>Fehler bitte <a href="' + mw.util.getUrl('User_talk:Perhelion') + '">Perhelion</a> melden.<br>Konkrete Error: ' + jqXHR + '</span>');
mw.notify(note, {
title: us.name + ':',
type: 'error'
});
note = '';
if (!(params instanceof Object) && /list=usercontribs/.test(params)) { // Fallback only
us.actions.push({
params: params,
func: onSuccess
});
us.us_last_edit_loading.replaceWith($('<a>', {
// "class" : "mw-userlink",
title: 'Contributions ' + us.user,
href: '/w/index.php?title=Special:Contributions/' + us.user,
text: '?',
click: us.doRequest
}));
// $.removeSpinner("contribs");
}
} else if (status.textStatus === 'timeout' || jqXHR === 'maxlag') { // Try once again
api.abort();
mw.log.warn(error);
return us.ajaxRequest(params, onSuccess, 2);
}
mw.log.warn(error);
});
},
getThanks: function (e) {
var params = '&list=logevents&continue=-%7C%7C&leprop=&letype=thanks&ledir=newer&lelimit=max';
/* {
list : "logevents",
leprop : "",
letype : "thanks",
ledir : "newer",
lelimit : 500
};*/
if (e instanceof Object) {
if (e.target) { // reset
if (us.e) {
// TODO should be synchron requests allowed?
return mw.notify('Synchron requests are not yet supported.', {
title: us.name + ':',
type: 'error'
});
}
e = e.target;
us.e = $(e);
us.e.attr('title', us.e.text());
us.e.text('');
us.e.injectSpinner('thx');
us.thanks = 0;
} else if (e.lecontinue) {
params += '&lecontinue=' + e.lecontinue;
// params.lecontinue = e.lecontinue;
}
if (us.e.attr('title') === msg.thxGn) params += '&leuser=' + us.user;
// params.leuser = us.user;
else // if (us.e === msg.thxrc)
params += '&letitle=User:' + us.user;
// params.letitle = "User:" + us.user;
}
us.actions.push({
params: params,
func: us.writeThanks
});
us.doRequest();
},
writeThanks: function (uq) {
if (!uq.query || !uq.query.logevents) return mw.log.warn(uq);
var e = us.e;
var user = 'page=';
var aw = uq.query.logevents;
us.thanks += aw.length;
mw.log(us.thanks);
if (uq.continue) return us.getThanks(uq.continue);
if (e[0].title === msg.thxGn) user += '&user=';
user += us.user;
$.removeSpinner('thx');
e.text(us.thanks);
e.off('click');
e[0].target = '_blank';
e[0].href = '/w/index.php?title=Special%3ALog&type=thanks&' + user + '&year=&month=-1&tagfilter=&hide_thanks_log=0';
delete us.e;
},
getUploads: function (e) {
// "&list=usercontribs&uclimit=max&ucuser=Perhelion&ucnamespace=6&ucprop=ids&ucshow=new
var params = /* {
list: "logevents",
leprop: "ids",
letype: "upload",
ledir: "newer",
leuser: us.user,
lelimit: 500
}*/
'&list=logevents&letype=upload&ledir=newer&lelimit=max&leuser=' + us.user;
function _start(u) {
var text = u.text();
u.prop('title', text);
u.text('');
u.injectSpinner('upload' + text);
u.data('upl', 0);
u.data('del', 0);
}
if (e instanceof Object) {
if (e.target) { // Reset
if (us.e) {
// No synchron requests allowed
return mw.notify('Synchron requests are not yet supported.', {
title: us.name + ':',
type: 'error'
});
}
e = e.target;
us.e = $(e);
if (us.e.text() !== msg.nova) {
us.e2 = us.e.nextAll('a').eq(0);
if (us.e2.text() === msg.nova) _start(us.e2); else delete us.e2;
}
_start(us.e);
} else if (e.lecontinue) { params += '&lecontinue=' + e.lecontinue; }
params += '&leprop=ids';
if (us.e.prop('title') === msg.nova) params += '&leaction=upload/upload';
// params.leaction = "upload/upload";
else if (us.e2) params += '|type';
}
us.actions.push({
params: params,
func: us.writeUploads
});
us.doRequest();
},
writeUploads: function (uq) {
if (!uq.query || !uq.query.logevents || !us.e) return mw.log(uq, us.e);
function _insert(e) {
$.removeSpinner('upload' + e.prop('title'));
e.text('');
e.off('click');
e.append(e.data('del'),
$('<span>', {
style: us.styleMissingData + us.styleLoading,
text: ' (+' + (e.data('upl') - e.data('del')) + ' del.)'
}));
e[0].target = '_blank';
e[0].href = '/w/index.php?title=Special:Log&type=upload&user=' + us.user + '&subtype=' +
((e[0].title === msg.nova) ? 'upload' : '');
// "//commons.wikimedia.org/wiki/Special:ListFiles/" + us.user;
}
var e = us.e;
var e2 = us.e2;
var aw = uq.query.logevents;
var alen = aw.length,
i = 0;
var del = e.data('del');
if (e2) {
var upl = e2.data('upl');
var del2 = e2.data('del');
for (i; i < alen; ++i) {
var a = aw[i];
var c = a.pageid ? 1 : 0;
if (a.action === 'upload') {
upl++;
del2 += c;
}
del += c;
}
e2.data('upl', upl);
e2.data('del', del2);
} else {
for (i; i < alen; ++i) if (aw[i].pageid) del++;
}
e.data('upl', e.data('upl') + alen);
e.data('del', del);
if (uq.continue) return us.getUploads(uq.continue);
_insert(e);
if (e2) {
_insert(e2);
delete us.e2;
}
delete us.e;
},
/**
* @Main write function
*
* @param {json} uq user query, used also without api
* @return {object} writing the results
*/
writeCommonInfo: function (uq) {
// mw.log( uq );
var aw = uq.query;
if (!aw || !aw.users[0] || Object.prototype.hasOwnProperty.call(aw, 'missing') || Object.prototype.hasOwnProperty.call(aw.users[0], 'invalid')) return us.statusBox.remove();
aw = aw.users[0];
var edits = aw.editcount,
groups = data.groups || aw.groups,
blocked = data.blockreason || aw.blockexpiry,
gender = data.gender || aw.gender,
uploads = $('<a>', {
// href: '#',
title: msg.count,
text: msg.nova,
style: us.styleLoading,
click: us.getUploads
});
if (aw.registration) data.registration = aw.registration;
us.now = us.getDateFromTimestamp(uq.curtimestamp || us.now || mw.now());
us.user = us.user.replace(/ /g, '_'); // encodeURIComponent?
us.first = 1;
if (edits) { // Write last edit
if (data.editcount && data.lastedit && data.editcount === edits) {
uq.query.usercontribs = [{
timestamp: data.lastedit
}
];
us.writeLastEdit(uq);
} else {
// console.log(["writeLastEdit data", data.lastedit, data.registration || data.firstedit]);
data.editcount = edits;
us.loading_last_edit.css('display', 'block');
us.actions.push({
params:
/* {
smaxage: 9000,
maxage: 9000,
requestid: "contribs",
list : "usercontribs",
ucprop: "timestamp",
uclimit : 1,
uccontinue: new Date(us.now).toISOString().replace(/[^\d]/g, '').slice(0,14) + "|2", // workaround for timeout bug!?
ucstart: us.now.toISOString(),
ucend: data.lastedit || (data.registration || data.firstedit)? new Date(data.registration || data.firstedit).toISOString(): undefined, // faster?
ucuser : us.user,
// ucdir: "older"
}*/
'&list=usercontribs&ucuser=' + us.user + '&uclimit=1&uccontinue=' + (new Date(us.now).toISOString().replace(/[^\d]/g, '').slice(0, 14) + '|2'),
func: us.writeLastEdit
});
}
$('#t-contributions').remove(); // we setted a new one
} else { us.writeLastEdit({}); }
if (groups) {
edits = $('<a>', {
title: 'Supercount User Analysis',
href: '//tools.wmflabs.org/supercount/index.php?user=' + us.user + '&project=' + location.hostname,
text: edits
});
if ($.inArray('bot', groups) === -1) {
us.review = $.inArray('editor', groups) !== -1;
// FIXME: Due bug [[phab:T136493]] patrols are not working correct
if (us.review /* || $.inArray("patroller", groups) !== -1 || $.inArray("sysop", groups) !== -1 */) {
if (typeof data.reviews === 'number') {
// De Powerusers: Benutzer:HRoestBot/Nachsichten
us.writePatrolCount(data.reviews);
} else if (typeof data.reviews === 'undefined' && us.viewPatrolNumber && project === 'dewiki') {
us.actions.push({
params: {
prop: 'revisions',
rvlimit: 1,
rvprop: 'content',
titles: 'User:' + us.user + '/Sichterbeiträge'
},
func: us.writeBotPatrolCount
});
}
} else if (us.viewPatrolNumber) { us.us_patrolcount_loading.parent().remove(); }
edits = [
edits, ' • ',
$('<a>', { // XTools
href: '//xtools.wmflabs.org/ec' + mw.config.get('wgServer').substr(1) + '/' + us.user,
title: 'Edit Counter – analysis of user contributions',
text: 'XTools',
style: us.styleLoading,
target: '_blank'
}), ' • ',
$('<a>', { // MyGallery
href: '//commons.wikimedia.org/w/index.php?title=Commons:MyGallery/' +
us.user + '&withJS=MediaWiki:JSONListUploads.js',
title: 'Commons tool: JSONListUploads.js',
text: (project === 'commonswiki' ? '' : 'c:') + 'MyGallery',
style: us.styleLoading,
target: '_blank'
}), ' • ',
(project !== 'commonswiki' ? $('<a>', {
href: '/wiki/Special:ListFiles/' + us.user,
text: 'Upload',
title: 'Special:Listfiles',
style: us.styleLoading
}) : 'Upload'),
(us.lang === 'de' ? '-' : ' ') + msg.count + ': ',
uploads.clone(1).text('total'), ' / ', uploads
];
if (!$('#t-subpages').length) { // Subpages
edits.push($('<a>', {
href: mw.util.getUrl('Special:Prefixindex/User:' + us.user + '/'),
text: ' • ' + msg.usub,
style: us.styleMissingData + us.styleLoading
}));
}
} else if (us.us_patrolcount_loading) { us.us_patrolcount_loading.parent().remove(); }
us.loading_editcount.replaceWith(edits);
if (!aw.implicitgroups && !data.groups) {
aw.implicitgroups = ['*', 'user', 'autoconfirmed'];
if (groups.length < 4) aw.implicitgroups.pop(); // bit hacky
}
data.groups = $.grep(groups, function (n) { // Exclude not needed items
return $.inArray(n, aw.implicitgroups) === -1;
});
us.writeGroups(data.groups);
$('#t-userrights').remove(); // we setted a new one
}
if (!data.registration) {
us.actions.push({
params: '&list=logevents&leprop=timestamp|type&letype=newusers&lelimit=1&ledir=newer&leend=2005-12-30T00:00:00Z&leuser=' + us.user,
func: us.writeRegistration
});
if (data.firstedit) {
uq.query.usercontribs = [{
timestamp: data.firstedit
}
];
us.writeFirstEdit(uq);
} else if (edits) { // get first edit before around 22:16, 7 September 2005
us.actions.push({
params: {
list: 'usercontribs',
ucuser: us.user,
uclimit: 1,
ucend: '2005-09-08T00:00:00Z', // faster?
ucdir: 'newer', // List oldest first!! Note: ucstart has to be before ucend.
ucprop: 'timestamp'
},
func: us.writeFirstEdit
});
}
} else { us.writeRegistration(data.registration); }
us.loading_blocked.replaceWith($('<a>', {
style: ((blocked) ? us.styleBlocked : us.styleNotBlocked),
id: 'us_block_status_span',
href: '/w/index.php?title=Special:Log/block&page=User:' + us.user,
title: msg.blocklog,
text: ((blocked) ? ' ' : msg.not) + msg.block
}));
if (blocked) {
data.blockreason = aw.blockreason || ' ';
us.ul.append([
$('<li>', {
id: 'us_block_time'
}).append([
$('<b>').text('• ' + msg.blockEnd),
$.createSpinner('us_block_time_loading')
]),
$('<li>', {
id: 'us_block_reason'
}).append([
$('<b>').text('• ' + msg.blockReason),
$.createSpinner('us_block_reason_loading')
]),
$('<li>', {
id: 'us_blocker'
}).append([
$('<b>').text('• ' + msg.blocker),
$.createSpinner('us_blocker_loading')
])
]);
// if (!data.locked)
us.actions.push({
params: {
list: 'logevents',
letitle: 'User:' + us.user,
letype: 'block',
lelimit: 1
},
func: us.writeBlockDetail
});
}
if (data.glGrp && !data.locked) { // TODO 1.31.0-wmf get also local group info
uq.query.globaluserinfo = {
groups: (data.glGrp.length) ? data.glGrp : null
};
us.writeGlobalGroup(uq);
} else {
us.actions.push({
params: /* {
meta : "globaluserinfo",
guiuser : us.user,
guiprop : "groups"
}*/
'&meta=globaluserinfo&guiprop=groups&guiuser=' + us.user,
func: us.writeGlobalGroup
});
}
if (gender) {
data.gender = gender;
var genderSn = '';
switch (gender) {
case 'male':
genderSn = ' \u2642';
break;
case 'female':
genderSn = ' \u2640';
}
genderSn = $('<span>', {
id: 'ps-gender-' + gender,
style: 'font-size:80%',
text: genderSn
});
$('#firstHeading').append(genderSn);
}
if (us.actions.length) us.doRequest();
},
createBox: function () { /* Box erstellen */
var statusBox = $('<div>', {
style: 'border-bottom:1px solid #aaa;text-shadow:1px 1px 1px #eff;', // padding:1px
id: 'us_box'
});
if (mw.config.get('skin') === 'vector') // Fix size in vector
statusBox.css('font-size', '0.8em');
// var spanFrag = $("<span>", {style: us.styleLoading, text: msg.load});
var spanFrag = $.createSpinner();
us.loading_editcount = spanFrag.clone();
us.loading_registration = spanFrag.clone();
us.loading_groups = spanFrag.clone();
us.loading_blocked = spanFrag.clone();
us.us_global_group_loading = spanFrag.clone();
us.us_last_edit_loading = $.createSpinner('contribs');
us.loading_last_edit = $('<li>', {
// id : "us_last_edit",
// style : "display: none" //if (edits)
});
us.us_global_group = $('<li>', {
// id: "us_global_group",
style: 'display: none'
});
var thx = $('<a>', {
// href: '#',
title: msg.count,
text: msg.thxRd,
style: us.styleLoading,
click: us.getThanks
});
var $userrights = $('#t-userrights a');
var $contributions = $('#t-contributions a');
if ($userrights.length) { $userrights.attr('title', $userrights.text()); } else {
$userrights = $('<a>', {
href: '/wiki/Special:UserRights/' + us.user
});
}
if ($contributions.length) { $contributions.attr('title', $contributions.text()); } else {
$contributions = $('<a>', {
href: '/wiki/Special:Contributions/' + us.user
});
}
var ul = $('<ul>', {
style: 'list-style: none'
}).append($('<li>', {
id: 'us_editcount'
}).append([
$('<b>').append($contributions.text(msg.contrib)),
us.loading_editcount]));
// Sichtungen/Patrols
us.us_log_count = $('<span>');
if (us.viewPatrolNumber) {
us.us_patrolcount_loading = $('<a>', {
style: us.styleLoading,
// href: '#',
click: us.getPatrolCount,
text: msg.count
});
us.us_log_count = $('<span>').append([
$('<b>').text(msg.reviews), us.us_patrolcount_loading, ' • '
]);
}
ul.append(us.us_log_count)
.append($('<li>', {
id: 'us_reg_date'
}).append([
$('<b>').text(msg.regdate), us.loading_registration,
$('<a>', {
target: '_blank',
style: 'float:right;' + us.styleMissingData,
href: 'https://tools.wmflabs.org/meta/userpages/' + us.user,
title: 'Find this users pages on all Wikimedia wikis.',
text: '• User pages'
})
]))
// Lokale Gruppen
.append($('<li>' /* , { id : "us_local_group" }*/).append([
$('<b>').append($userrights.text(msg.loGrp)),
us.loading_groups,
$('<a>', {
target: '_blank',
style: 'float:right;' + us.styleMissingData,
href: 'https://tools.wmflabs.org/meta/stalktoy/' + us.user,
title: 'View global details about this user across all Wikimedia wikis.',
text: '• Stalk toy'
})
]))
// 'Global Groups'
.append(us.us_global_group.append([
$('<b>').append(
$('<a>', {
href: '/w/index.php?title=Special:GlobalUsers&limit=1&username=' + us.user,
text: msg.glGrp
})),
us.us_global_group_loading,
$('<a>', {
target: '_blank',
style: 'float:right;' + us.styleMissingData,
href: 'https://tools.wmflabs.org/meta/globalgroups/',
title: 'A review of extra permissions assigned to global groups on Wikimedia Foundation wikis.',
text: '• GlobalGroups'
})
]))
// Letzter Edit
.append(us.loading_last_edit.append([
$('<b>').text(msg.laedit), us.us_last_edit_loading, ' ',
$('<a>', {
target: '_blank',
style: us.styleMissingData,
href: '/w/index.php?title=Special:Log/' + us.user + '&hide_thanks_log=0&hide_patrol_log=0&hide_tag_log=0',
title: msg.lala,
text: '• LLA'
}),
$('<a>', {
target: '_blank',
style: 'float:right;' + us.styleMissingData,
href: 'https://tools.wmflabs.org/meta/crossactivity/' + us.user,
title: 'Measures user\'s latest edit, bureaucrat, or sysop activity on all wikis.',
text: '• CrossActivity'
})
]));
statusBox.append(ul);
// Block & thx
us.ul = $('<ul>', {
style: 'list-style:none'
}).append($('<li>', {
id: 'us_block_status'
}).append([
$('<b>').text(msg.blocks),
us.loading_blocked,
' • ',
$('<b>').text(msg.thxGvng),
thx, ' / ',
thx.clone(1).text(msg.thxGn),
// Purge link removeDataStore
$('<a>', {
// target: '_blank',
style: 'float:right;' + us.styleMissingData,
// href: '#',
title: 'Delete data store for this user',
text: 'purge',
click: us.removeDataStore
})
])
);
statusBox.append(us.ul);
$('#firstHeading').after(statusBox);
us.statusBox = statusBox;
},
removeDataStore: function (e) {
var key = project + us.user,
// name = us.name + us.user,
db = window.indexedDB;
e.preventDefault();
if (!db || !us.dbVersion) return mw.cookie.set(key, null);
var request = db.open(us.name, us.dbVersion + 1); // sure the key exists
request.onerror = function (e) {
mw.log.warn('Error removing data: ${e}', key, e.oldVersion, e.target.result.version);
};
request.onupgradeneeded = function (/* e*/) {
db = this.result;
if (!db.objectStoreNames.contains(key)) {
mw.log.warn('No stored object found: ', e.oldVersion, db.version, key);
return;
}
// mw.log("deleteObjectStore", key, db.version, db.objectStoreNames.contains(key));
db.deleteObjectStore(key);
};
request.onsuccess = function (/* e*/) {
db = this.result;
if (!db.objectStoreNames.contains(key)) {
us.dbVersion = db.version;
// mw.log(`Successfully removed ${key}: ${e.target.result}`, db.version);
db.close();
us.init(); // Restart
// us.createBox();us.run();// needs new box
// location.reload();
return;
}
// console.warn(`Successfully open but fail on remove: ${e.target.result}`, key, e.oldVersion, db.version);
};
},
setCookie: function () {
var domain = (mw.config.get('wgNoticeProject') === 'wikipedia') ? 'wikipedia.org' : '';
var name = us.name + us.user;
/**
* @param {string} key (for "glGrp": deprecated)
* @param {json} data
* @return {void}
*/
var _saveCookie = function (key, data) {
mw.cookie.set(name, JSON.stringify(data), {
prefix: key,
expires: 600000, // Save 1 week
domain: domain
});
};
if (!us.actions[0] && JSON && data.editcount) {
if (us.cookie.length) window.clearTimeout(us.cookie.shift());
// Check only once
var saveData = window.indexedDB ?
/**
* @param {string} name (for "glGrp": deprecated)
* @param {json} JSdata
* @param {integer} version
*/
function (name, JSdata, version) { // _saveIDB
var store,
key = (name || project) + us.user, // ??
db = window.indexedDB,
request = {};
data = { // TODO: Maybe extend
name: key,
data: JSdata
};
request = db.open(us.name, (version || undefined));
// indexedDB.deleteDatabase("Userstatus")
request.onupgradeneeded = function () {
db = this.result;
mw.log(db.version + ' onupgradeDB ' + db.objectStoreNames.contains(key));
if (!db.objectStoreNames.contains(key)) { // New
store = db.createObjectStore(key, {
keyPath: 'name'
});
store.createIndex('data', 'data', {
unique: false
});
// Use transaction oncomplete to make sure the objectStore creation is finished before adding data into it.
store.transaction.oncomplete = function () {
// Store values in the newly created objectStore.
mw.log(db.version + ' onupgradeDB.transaction.complete store ' + db.objectStoreNames.contains(key));
store = db.transaction(key, 'readwrite').objectStore(key);
var req = store.get(key);
req.onsuccess = function () {
mw.log('Success writeDb.transaction close', this.result);
// db.close();
};
};
} else { mw.log.warn('saveDB FAIL'); }
};
request.onerror = function () {
_saveCookie(name, JSdata);
mw.log.warn(us.name, 'saved data as cookie. Failed openDB:', this.error.message);
};
request.onsuccess = function () {
db = this.result;
us.dbVersion = db.version;
db.onversionchange = function () {
us.dbVersion = db.version;
mw.log('closedDB');
db.close();
};
mw.log(name, db.versio + ' onsuccess ' + db.objectStoreNames.contains(key));
if (db.objectStoreNames.contains(key)) { // Overwrite
store = db.transaction(key, 'readwrite').objectStore(key);
var req = store.put(data);
// mw.log("openDb get users:", db.objectStoreNames);
req.onsuccess = function () {
if (this.result) // only one
mw.log('replaceDB store DONE:', this.result, req);
else // Try new version?
saveData(name, JSdata, us.dbVersion + 1);
};
req.onerror = function () {
mw.log.warn(us.name, key, 'replaceDB store FAIL:', this.error);
};
} else {
saveData(name, JSdata, us.dbVersion + 1);
}
};
} :
_saveCookie;
us.cookie.push(setTimeout(function (us) { // prevent double exec
if (us.cookie.length === 1) {
data.timestamp = new Date().valueOf();
if (!data.registration) delete data.registration;
if (data.gender && data.gender === 'unknown') delete data.gender;
// window.clearTimeout(us.cookie.shift()); // us.cookie[0]
saveData('', data);
mw.log('Do save ' + us.name + ': ' + JSON.stringify(data));
} else if (us.cookie.length) {
window.clearTimeout(us.cookie.shift()); // delete previous
us.setCookie(); // try again
}
}, 500, us));
}
},
/**
* API in race condition
* @param {array} us.actions
* @var {json: param, callback} action
*/
doRequest: function () {
var action = us.actions.shift();
if (action) us.ajaxRequest(action.params, action.func);
if (us.actions.length) {
window.setTimeout(function (thisObj) {
thisObj.doRequest();
}, 100, us);
}
},
getDateDiff: function (now, date) {
var dStr = [];
var d = {
years: 0,
months: 0,
days: 0,
hours: 0,
minutes: 0,
seconds: 0
};
var diffDays = 0;
if (now > date) {
diffDays = Math.floor((now - date) / 86400000);
if (diffDays > 27) {
// Years and Months
var year = now.getFullYear();
d.years = date.getFullYear();
date.setFullYear(year);
d.years = year - d.years;
d.months = date.getMonth();
var months = now.getMonth();
if (date > now) { // the date month >= now month
d.years--;
d.months = 12 - d.months + months;
date.setFullYear(year - 1);
} else { d.months = months - d.months; }
var days = date.getDate();
d.days = now.getDate();
if (d.days < days) {
d.days += (new Date(new Date(year, months) - 1000).getDate()) - days;
d.months--;
months--;
} else { d.days -= days; }
date = new Date(year, months, days, date.getHours(), date.getMinutes(), date.getSeconds());
}
if (now < date) date = new Date(date - 1000);
// Diff days and lower
var s = Math.abs(now - date) / 1000;
d.seconds = Math.floor(s % 60);
s = s / 60;
d.minutes = Math.floor(s % 60);
s = s / 60;
d.hours = Math.floor(s % 24);
d.days = Math.floor(s / 24);
for (var i = 0, dlen = msg.date.length; i < dlen; ++i) {
var t = d[i18n.en.dates[i]];
if (t) dStr.push(t + ' ' + ((t > 1) ? msg.dates[i] : msg.date[i]));
}
}
return (dStr.length) ?
dStr.join(', ').replace(/(.*),([^,]*)/, msg.and).replace(/(.*)/, msg.ago) +
((d.months + d.years) ? ' = ' + diffDays + ' ' + msg.dates[2] : '') :
msg.nosec;
},
getDateFromTimestamp: function (t) {
if (!t) return false;
t = new Date(t);
return isNaN(t.valueOf()) ? false : t;
},
writeRegistration: function (aw) {
// mw.log(aw);
if (aw) {
if (aw instanceof Object) {
if (!aw || !aw.query) return;
aw = aw.query.logevents;
if (aw && aw.length && aw[0].action === 'newusers') aw = aw[0].timestamp;
}
if (!(aw instanceof Object)) {
us.loading_registration.replaceWith(us.formatDate(aw) + ' ',
$('<span>', {
style: us.styleMissingData,
text: '(' + us.getDateDiff(us.now, us.getDateFromTimestamp(aw)) + ')'
}));
data.registration = aw;
return us.setCookie();
}
}
us.loading_registration.replaceWith($('<span>', {
style: us.styleMissingData + us.styleLoading,
text: msg.nodb
}));
},
writeFirstEdit: function (aw) {
var date = aw.query.usercontribs;
if (!date.length) return;
date = data.firstedit = date[0].timestamp;
$('#us_reg_date').append(
$('<li>').append([
$('<b>', {
text: '• ' + msg.fiedit
}), us.formatDate(date) + ' ',
$('<span>', {
style: us.styleMissingData,
text: '(' + us.getDateDiff(us.now, us.getDateFromTimestamp(date)) + ')'
})
]));
},
writeLastEdit: function (aw) {
var uc = aw.query;
if (!uc || !uc.usercontribs || !uc.usercontribs.length) {
return us.us_last_edit_loading.replaceWith($('<span>', {
style: us.styleMissingData,
text: msg.noedit
}));
}
uc = uc.usercontribs;
var date = data.lastedit = uc[0].timestamp;
// console.log(date, uc);
us.us_last_edit_loading.replaceWith(us.getDateDiff(us.now, us.getDateFromTimestamp(date)), ' ',
$('<span>', {
style: us.styleMissingData,
text: '(' + us.formatDate(date) + ')'
}));
data.timediff = mw.now() - us.now;
$('#us_editcount').append(
$('<span>', {
style: 'float:right;' + us.styleMissingData,
text: msg.curtimeDiff /* + " – " + us.formatDate(now)*/
}).append(
$('<b>', {
text: Math.round(data.timediff / 1000)
})));
us.setCookie();
},
writeGlobalGroup: function (aw) {
aw = aw.query.globaluserinfo;
var groups = data.glGrp = aw.groups;
if (groups && groups.length) {
us.us_global_group_loading.replaceWith(us.getLocalNames(groups, 'GlobalGroupPermissions'));
us.us_global_group.css('display', 'block');
}
// Global lock?
if (aw.locked !== undefined) {
data.locked = 1;
/* us.actions.push( {
params: {
"list": "logevents",
"leprop": "user|timestamp|comment",
"letype": "globalauth",
"letitle": 'User:' + us.user + '@global',
"lelimit": "1"
}, func: us.writeGlobalBlock
} );
us.doRequest(); */
mw.loader.using('mediawiki.ForeignApi').done(function () {
var Api = (project === 'metawiki') ? mw.Api : mw.ForeignApi;
Api = new Api('https://meta.wikimedia.org/w/api.php');
Api.get({
action: 'query',
list: 'logevents',
leprop: 'user|timestamp|comment',
letype: 'globalauth',
letitle: 'User:' + us.user + '@global',
lelimit: '1'
}).done(us.writeGlobalBlock);
});
}
us.setCookie();
},
// /w/api.php?action=query&format=json&list=logevents&leprop=user%7Ctimestamp%7Ccomment&letype=globalauth&letitle=User%3APerhelion%40global&lelimit=1
writeGlobalBlock: function (aw) {
if (!aw.query) return mw.log('API FAIL:', aw);
aw = aw.query.logevents[0];
$('#us_block_status_span').replaceWith($('<a>', {
style: us.styleBlocked,
id: 'us_block_status_span',
href: '//meta.wikimedia.org/w/index.php?title=Special:Log/&type=globalauth&user=&page=User%3A' + us.user + '%40global',
title: msg.blocklog,
text: 'locked (global)'
})
.append($('<span>', {
style: us.styleMissingData,
text: ' – ' + us.formatDate(aw.timestamp)
}))
);
$('#us_block_status').parent().append([
$('<li>', {
// id: 'us_block_reason'
}).append([
$('<b>').text('• ' + msg.blockReason),
us.parseComment(aw.comment)
]),
$('<li>', {
// id: 'us_blocker'
}).append([
$('<b>').text('• ' + msg.blocker),
$('<a>', {
'class': 'mw-userlink',
'title': 'User:' + aw.user,
'href': mw.util.getUrl('User:' + aw.user),
'text': aw.user
})
])
]);
},
// /w/api.php?action=query&format=json&list=logevents&letype=block&letitle=User%3AErwin_Lindemann
writeBlockDetail: function (aw) {
var duration = 'infinite',
expiry = '';
if (aw.query) {
aw = aw.query.logevents[0];
if (aw && aw.params) {
duration = aw.params.duration;
if (/in(de)?finite/.test(duration)) expiry = msg.never; else expiry = us.formatDate(aw.params.expiry);
} else if (!aw || !aw.commenthidden) {
aw = { commenthidden: 1 };
}
// mw.log("writeBlockDetail:", aw, expiry);
}
duration = $('#us_block_status_span').text(msg.block + ' (' + duration + ')');
if (aw.timestamp)
duration.append($('<span>', {
style: us.styleMissingData,
text: ' – ' + us.formatDate(aw.timestamp)
}));
if (expiry) $('#mw-spinner-us_block_time_loading').replaceWith(expiry);
else $('#us_block_time').remove();
$('#mw-spinner-us_block_reason_loading').replaceWith(us.parseComment(aw.comment, (aw.commenthidden)));
if (aw.user)
$('#mw-spinner-us_blocker_loading').replaceWith($('<a>', {
'class': 'mw-userlink',
'title': 'User:' + aw.user,
'href': mw.util.getUrl('User:' + aw.user),
'text': aw.user
}));
else $('#mw-spinner-us_blocker_loading').replaceWith(msg.nodb);
},
writeBotPatrolCount: function (aw) {
aw = aw.query;
var patrols = '';
if (aw) {
for (var key in aw.pages) {
if (key !== '-1') {
patrols += parseInt(aw.pages[key].revisions[0]['*'], 10);
break;
}
}
}
if (patrols) return us.writePatrolCount(patrols);
},
writePatrolCount: function (aw) {
// log(aw);
if (aw instanceof Object) {
var ql = aw.query.logevents;
if (!ql) return;
us.patrols += ql.length;
if (aw.continue) return us.getPatrolCount(aw.continue);
$.removeSpinner('pat');
aw = us.patrols;
}
us.us_patrolcount_loading.replaceWith($('<a>', {
title: 'Review-Log',
href: '/w/index.php?title=Special:Log&type=' + (us.review ? 'review&subtype=accept' : 'patrol&subtype=patrol') + '&user=' + us.user,
text: aw
}));
data.reviews = aw;
us.setCookie();
},
getPatrolCount: function (e) {
// Reviews only on some Wikis like de
// mw.log(us.review,e)
var params = /* {
list : "logevents",
ledir : "newer",
leprop : "",
// letype : "patrol",
leaction : "patrol/patrol",
leuser : ,
lelimit : 500
}*/
'&list=logevents&leprop=&ledir=newer&lelimit=max&leuser=' +
us.user + '&leaction=' +
(us.review ? 'review/approve' : 'patrol/patrol');
// params.leaction = "review/approve";
// params.letype = "review";
if (e instanceof Object) {
if (e.target) { // reset
$(e.target).injectSpinner('pat');
us.patrols = 0;
} else if (e.lecontinue) {
// params.lecontinue = e.lecontinue;
params += '&lecontinue=' + e.lecontinue;
}
}
us.actions.push({
params: params,
func: us.writePatrolCount
});
us.doRequest();
},
parseComment: function (text, hidden) {
var comment = $('<span>', { 'class': 'comment' });
if (typeof (text) === 'undefined') {
if (hidden) {
return comment.append(msg.noReason).css('color', '#999');
} else {
mw.notify($('<span>Fehler bitte <a href="' + mw.util.getUrl('User_talk:Perhelion') + '">Perhelion</a> melden:<br><i>Undefined comment at page ' + us.user + '</i></span>'), {
title: us.name + ':',
type: 'error'
});
return comment.append('undefined').css('color', 'red');
}
} else if (!text) { return comment.append(msg.blockCmt).css('color', '#999'); }
var intLink = /(.*?)\[\[((.*?)\|)?(.*?)\]\](.*)/;
var suche = text;
var erg;
while ((erg = intLink.exec(suche)) !== null) {
erg[3] = (erg[2]) ?
$('<a>', {
href: mw.util.getUrl(erg[3]),
title: erg[3]
}) :
$('<a>', {
href: mw.util.getUrl(erg[4]),
title: erg[4]
});
comment.append([erg[1], erg[3].text(erg[4])]);
suche = erg[5];
}
return comment.append(suche);
},
formatDate: function (datum) {
if (!(datum instanceof Object)) datum = new Date(datum);
try {
datum = datum.toLocaleDateString(us.lang, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
} catch (e) {
if (e.name === 'RangeError') datum = datum.toLocaleString();
}
return datum;
},
getStoredData: function () {
var store,
key = project + us.user,
db,
request;
try {
request = indexedDB.open(us.name);
request.onsuccess = function () {
db = this.result;
db.onversionchange = function () {
mw.log('closedDb');
db.close();
};
mw.log(db.version + ' successDb ' + db.objectStoreNames.contains(key), key);
if (db.objectStoreNames.contains(key)) {
store = db.transaction(key, 'readonly').objectStore(key);
store.transaction.oncomplete = function () {
// mw.log("readDb.transaction.oncomplete runDataStore", data);
us.runDataStore();
db.close();
};
var req = store.get(key);
// mw.log("openDb get user:", store, req);
req.onsuccess = function () {
if (this.result) {
mw.log('openDb get user DONE:', this.result);
data = this.result.data;
} else { us.getCookie(this.error); }
};
req.onerror = function () {
us.getCookie(this.error);
};
} else {
us.getCookie('new user');
}
};
request.onerror = function () {
us.getCookie(this.error);
};
} catch (e) {
// console.warn(e);
us.getCookie('unknownError');
}
},
getCookie: function (err) {
mw.log(us.name + ' FAIL open indexedDB store, try get cookie: ' + (err || ''));
data = mw.cookie.get(us.name + us.user);
us.runDataStore();
},
initI18N: function (i18n) {
var i, chain = mw.language.getFallbackLanguageChain();
for (i = chain.length - 1; i >= 0; i--)
if (chain[i] in i18n) msg = i18n[chain[i]];
},
init: function () {
if (this.statusBox || (this.statusBox = $('#us_box'))[0]) this.statusBox.remove();
this.usprop = 'blockinfo|editcount|gender|groups|implicitgroups|registration';
this.self = mw.config.get('wgUserName') === this.user;
this.initI18N(i18n);
this.createBox();
if (!this.first) { // as purge button
if (!this.lastEditSeconds) msg.date.pop();
msg.nosec += msg.date.slice(-1)[0];
if (mw.cookie && JSON) {
if (window.indexedDB) {
// us.getStoredData("GlGrp");
this.getStoredData();
} else {
// us.getCookie("GlGrp");
this.getCookie();
}
} else this.run();
} else this.run();
// mw.hook( 'resourceloader.loadEnd' ).add( function () { us.run(); } );
},
runDataStore: function () {
if (data) { // Max 255 Bytes for Cookie
// mw.log( 'data', data, decodeURI( data ), 'glGrp: ', data.glGrp );
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) {
mw.log.warn(e, data);
data = {};
}
}
var q = {
query: {}
};
q.query.users = [data];
// data.glGrp = data.glGrp || this.glGrpStore;
if (data.timestamp) this.now = mw.now() - data.timediff; else
// if (glGrp) q.query.globaluserinfo = { groups : glGrp };
// mw.log("data parsed", data, "Time: ", data.timestamp, data.timediff, "glGrp: ", data.glGrp, q);
if (Math.abs((mw.now() - data.timestamp - data.timediff) / 1000) < 90) // only if 1.5 min
// q.curtimestamp = us.now; // Speedup: Maybe only if lastedit is minimum one day old?
// mw.log(us.name + " RUN fast mode");
return this.writeCommonInfo(q);
if (data.editcount) this.usprop = 'editcount'; // Minimize API request
}
data = data || {};
// data.glGrp = this.glGrpStore;
mw.log('run' + this.name, data, data.glGrp);
this.run();
// mw.hook( 'resourceloader.loadEnd' ).add( us.run );
},
run: function () {
if (this.self) { // Omit API-request
data.glGrp = mw.config.get('wgGlobalGroups');
return this.writeCommonInfo({
query: {
users: [{
editcount: mw.config.get('wgUserEditCount'),
registration: mw.config.get('wgUserRegistration'),
groups: mw.config.get('wgUserGroups'),
blockreason: '' // we self should know :P
}]
}
});
}
$(function () {
// Start API-request (in race condition)
us.ajaxRequest({
curtimestamp: us.now ? 0 : 1,
list: 'users',
ususers: us.user,
usprop: us.usprop
}, us.writeCommonInfo);
});
}
};
if ([2, 3].indexOf(mw.config.get('wgNamespaceNumber')) !== -1 && us.user.indexOf('/') === -1 &&
(!mw.config.get('wgArticleId') || mw.config.get('wgAction') === 'view')) {
$(document).trigger('loadWikiScript', us);
$.when(mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.language', 'mediawiki.cookie', 'jquery.spinner']),
$.ready).then(function () {
us.api = '//' + location.hostname + mw.util.wikiScript('api') + '?action=query&format=json';
us.ajaxRequest('&maxage=2419200&smaxage=2419200&meta=allmessages&amenableparser=1&amincludelocal=1&refix=group-', us.getGroupNames);
if (!mw.libs.viewerInfo) // Number of observers
mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:Perhelion/viewerInfo.js&action=raw&ctype=text/javascript');
});
}
// mw.log = function(){console.log(arguments);};
}(jQuery, mediaWiki));
// </nowiki> EOF