User:Perhelion/userstatus.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-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 +"&amprefix=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&amprefix=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