Benutzer:KimKong/common.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
var pagename = mw.config.get( 'wgPageName' );
if ( pagename.indexOf("File:Wilhelm_Walther") != -1 ) {
        var jpgUrl = jQuery('.mw-thumbnail-link:last-child').attr('href');
        jQuery('#file').find('a').attr('href', jpgUrl);
}
mw.loader.load('//de.wikipedia.org/w/index.php?title=Benutzer:APPER/WikiHistory.js&action=raw&ctype=text/javascript');

/**
@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: 16:56, 15 December 2017 (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>
**/
/*global $, mediaWiki*/
(function (mw) {
"use strict";
// var us = {};
var project = window.project || mw.config.get("wgDBname");
var msg = {
	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›",
	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",
	"new": "new",
	ago: "$1 ago",
	blocklog: "Block log",
	// Title-lines
	contrib: "Edit count: ",
	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: "
};
var data;
/* = { // JSON for cache
registration: "",
blockreason: ""
timestamp: "",
timediff: "",
editcount: "",
groups: "",
gender: "",
lastedit: "",
firstedit: "",
reviews: "",
glGrp: "" {array}
};*/

var us = mw.libs.userstatus = {
	name: "Userstatus",
	version: 1.71,
	// 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.
	times: msg.dates,
	noGrp: "no extended group",
	lang: mw.config.get("wgUserLanguage"),
	cookie: [],
	thanks: 0,
	patrols: 0,
	actions: [],

	getLocalNames: function (groups) {
		if (groups) {
			var arr = [];
			for (var i = 0; i < groups.length; ++i) {
				var g = groups[i];
				var n = us.groupNames[g];
				if (n)
					/* // Link it
					arr.push('<a href="#" onclick="mw.libs.userstatus.goGroupPage(event)" title="' +
					g + '">' + n + '</a>');*/
					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: us.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;
		// console.log("groupNames: ", us.groupNames);
		us.init();
	},

	ajaxRequest: function (params, on_success, trial) {
		//log("ajaxRequest", params, on_success, 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;
			}
		}
		// $.getJSON(url, params, on_success)
		var api = $.ajax({
				dataType: "json",
				url: url,
				data: params,
				cache: true,
				success: on_success
			})
			/*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: on_success
						});
						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, on_success, 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(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.new) {
					us.e2 = us.e.nextAll("a").eq(0);
					if (us.e2.text() === msg.new)
						_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.new)
				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.new) ? "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;
	},

	/**
	 *  @brief Main write function
	 *  
	 *  @param [json] uq user query, used also without api
	 *  @return writing the results
	 */
	writeCommonInfo: function (uq) {
		mw.log(uq);
		var aw = uq.query;
		if ( !aw || !aw.users[0] || aw.hasOwnProperty('missing') || aw.users[0].hasOwnProperty('invalid') )
			return us.status_box.remove();
		aw = aw.users[0];
		var edits = aw.editcount;
		if (aw.registration)
			data.registration = aw.registration;
		var groups = data.groups || aw.groups;
		var blocked = data.blockreason || aw.blockexpiry;
		var gender = data.gender || aw.gender;
		var uploads = $("<a>", {
				href: "#",
				title: msg.count,
				text: msg.new,
				style: us.styleLoading,
				click: us.getUploads
			});
		us.now = us.getDateFromTimestamp(uq.curtimestamp || us.now || mw.now());

		if (edits) { // Write last edit
			if (data.editcount && data.lastedit && data.editcount === edits) {
				uq.query.usercontribs = [{
						timestamp: data.lastedit
					}
				];
				us.writeLastEdit(uq);
			} else {
				// mw.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 + "&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")
						us.writePatrolCount(data.reviews);
					// De Powerusers: Benutzer:HRoestBot/Nachsichten
					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, " • ", // MyGallery
					$("<a>", {
						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:") + "ListUploads",
						style: us.styleLoading,
						target: "_blank"
					}), ": ",
					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);

			data.groups = $.grep(groups, function (n, i) { // Exclude not needed items
					return $.inArray(n, ["*", 'user', "autoconfirmed"]) === -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")
						])
				]);

			us.actions.push({
				params: {
					list: "logevents",
					letitle: "User:" + us.user,
					letype: "block",
					lelimit: 1
				},
				func: us.writeBlockDetail
			});
		}

		if (data.glGrp) { //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 status_box = $("<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
			status_box.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 & Thx
		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, " • "
					]);
		}
		us.us_log_count = $("<li>")
			.append(us.us_log_count,
				$("<b>", {
					text: msg.thxGvng
				}),
				thx, " / ",
				thx.clone(1).text(msg.thxGn));
		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'
					})
				]));

		status_box.append(ul);

		us.ul = $('<ul>', {
				style: "list-style:none"
			})
			// Sperren
			.append($("<li>", {
					id: "us_block_status"
				}).append([
						$("<b>").text(msg.blocks), us.loading_blocked
					]));

		status_box.append(us.ul);
		$("#firstHeading").after(status_box);
		us.status_box = status_box;
	},

	setCookie: function () {
		var domain = (mw.config.get("wgNoticeProject") === "wikipedia") ? "wikipedia.org" : "";
		var name = us.name + us.user;

		if (!us.actions[0] && JSON && data.editcount) {
			if (us.cookie.length) {
				window.clearTimeout(us.cookie.shift());
			}
			/**
			 * @param {string} name
			 * @param {string} key  (for "glGrp": deprecated)
			 * @param {json} data
			 */
			var _saveCookie = function (key, data) {
				mw.cookie.set(name, JSON.stringify(data), {
					prefix: key,
					expires: 600000, // Save 1 week
					domain: domain
				});
			};

			// 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,
				request = {};
				data = { // TODO: Maybe extend
					'name': key,
					'data': JSdata
				};
				request = version ?
					indexedDB.open(us.name, version) :
					indexedDB.open(us.name);
				// indexedDB.deleteDatabase("Userstatus")

				request.onupgradeneeded = function (e) {
					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 (e) {
							// 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 (e) {
								console.log("Success writeDb.transaction close", this.result, this);
								// db.close();
							};
						};
					} else
						mw.log.warn("saveDb FAIL");
				};

				request.onerror = function (e) {
					_saveCookie(name, JSdata);
					mw.log.warn(us.name, "saved data as cookie. Fail openDb:", this.error.message);
				};

				request.onsuccess = function (e) {
					db = this.result;
					db.onversionchange = function (e) {
						mw.log("closedDb");
						db.close();
						// location.reload();
					};
					version = db.version;
					mw.log(version + ' 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 (e) {
							if (this.result) { // only one
								mw.log("replaceDb store DONE:", this.result, req);
							} else {
								saveData(name, JSdata, version + 1);
							}
						};
						req.onerror = function (e) {
							mw.log.warn(us.name, key, "replaceDb store FAIL:", this.error);
						};

					} else {
						saveData(name, JSdata, version + 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;
						saveData('', data);
						mw.log("Do save " + us.name + ": " + JSON.stringify(data));
						window.clearTimeout(us.cookie[0]);
					} else if (us.cookie.length) {
						window.clearTimeout(us.cookie.shift());
						us.setCookie();
					}
				}, 400, 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 d_str = [];
		var d = {
			years: 0,
			months: 0,
			days: 0,
			hours: 0,
			minutes: 0,
			seconds: 0
		};
		if (now > date) {
			// 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);
			// 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[us.times[i]];
				if (t)
					d_str.push(t + " " + ((t > 1) ? msg.dates[i] : msg.date[i]));
			}
		}
		return (d_str.length) ?
		d_str.join(", ").replace(/(.*)\,([^,]*)/, msg.and).replace(/(.*)/, msg.ago) :
		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;
		//log(aw);
		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;
		us.us_last_edit_loading.replaceWith(us.getDateDiff(us.now, us.getDateFromTimestamp(date)), " ",
			$("<span>", {
				style: us.styleMissingData,
				text: "(" + us.formatDate(date) + ")"
			}));
		data.timediff = new Date() - 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, status) {
		aw = aw.query.globaluserinfo;
		var groups = data.glGrp = aw.groups;
		if (groups && groups.length) {
			us.us_global_group_loading.replaceWith(us.getLocalNames(groups));
			us.us_global_group.css("display", "block");
		}
		// Global lock
		if (aw.locked) {
			data.locked = 1;
			$("#us_block_status_span").append(" [locked]");
		}
		us.setCookie();
	},

	// /w/api.php?action=query&format=json&list=logevents&letype=block&letitle=User%3AErwin_Lindemann
	writeBlockDetail: function (aw, status) {
		var duration,
			expiry;
		aw = aw.query.logevents[0];
		if (aw.params) {
			duration = aw.params.duration;
			expiry = "";
			if (/in(de)?finite/.test(duration))
				expiry = msg.never;
			else
				expiry = us.formatDate(aw.params.expiry);
		}
		// mw.log("writeBlockDetail:", aw, expiry);
		$("#us_block_status_span").text(msg.block + " (" + duration + ")")
		.append($("<span>", {
				style: us.styleMissingData,
				text: " – " + us.formatDate(aw.timestamp)
			}));
		$("#mw-spinner-us_block_time_loading").replaceWith(expiry);
		$("#mw-spinner-us_block_reason_loading").replaceWith(us.parseComment(aw.comment, (aw.commenthidden)));
		$("#mw-spinner-us_blocker_loading").replaceWith($("<a>", {
				"class": "mw-userlink",
				title: "User:" + aw.user,
				href: mw.util.getUrl("User:" + aw.user),
				text: aw.user
			}));
	},

	writeBotPatrolCount: function (aw, status) {
		aw = aw.query;
		var patrols = "";
		if (aw) {
			for (var key in aw.pages) {
				if ("-1" !== key) {
					patrols += parseInt(aw.pages[key].revisions[0]["*"], 10);
					break;
				}
			}
		}
		if (patrols)
			return us.writePatrolCount(patrols);
	},

	writePatrolCount: function (aw, status) {
		//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"
				// style : "font-style: normal"
			});

		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 (null !== (erg = intLink.exec(suche))) {
			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 (e) {
			db = this.result;
			db.onversionchange = function (e) {
				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 (e) {
					// mw.log("readDb.transaction.oncomplete runDataStore", data);
					us.runDataStore();
					db.close();
				};
				var req = store.get(key);
				// console.log("openDb get user:", store, req);
				req.onsuccess = function (e) {
					if (this.result) {
						mw.log("openDb get user DONE:", this.result);
						data = this.result.data;
					} else
						us.getCookie(this.error);
				};
				req.onerror = function (e) {
					us.getCookie(this.error);
				};
			} else {
				us.getCookie('new user');
			}
		};
		request.onerror = function (e) {
			us.getCookie(this.error);
		};
		} catch (e) {
			us.getCookie("unknownError");
		}
	},

	getCookie: function (err) {
		mw.log.warn(us.name + ' FAIL open indexedDB store, try cookie: ' + (err || ''));
		data = mw.cookie.get(us.name + us.user);
		us.runDataStore();
	},

	init: function () {
		this.user = mw.config.get("wgTitle");
		this.usprop = 'blockinfo|groups|editcount|gender|registration';

		if ($.inArray(mw.config.get("wgNamespaceNumber"), [2, 3]) === -1 ||
			/\//.test(this.user) ||
			(/index\.php$/.test(location.pathname) && !/redlink=1/.test(location.search)))
			return;
		this.self = (mw.config.get("wgUserName") === this.user);

		if ($.inArray('de', mw.language.getFallbackLanguageChain()) !== -1) {
			this.noGrp = "keine erweiterte Gruppe";
			msg = {
				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›",
				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",
				"new": "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: "
			};
		}
		if (!us.lastEditSeconds)
			msg.date.pop();
		msg.nosec += msg.date.slice(-1)[0];

		us.createBox();

		if (mw.cookie && JSON) {
			if (window.indexedDB) {
				// this.getStoredData("GlGrp");
				this.getStoredData();
			} else {
				// this.getCookie("GlGrp");
				this.getCookie();
			}
		} else
			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)
				us.now = new Date() - 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((new Date() - 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 us.writeCommonInfo(q);
				}
			if (data.editcount)
				this.usprop = 'editcount'; // Minimize API request
		}
		data = data || {};
		// data.glGrp = this.glGrpStore;
		// mw.log("RUN", data, data.glGrp)
		mw.hook('resourceloader.loadEnd').add(function () { us.run(); });
		},

	run: function () {
		if (us.self) { // Omit API-request
			data.glGrp = mw.config.get("wgGlobalGroups");
			return us.writeCommonInfo({
				query: {
					users: [{
							editcount: mw.config.get("wgUserEditCount"),
							registration: mw.config.get("wgUserRegistration"),
							groups: mw.config.get("wgUserGroups"),
							blockreason: "" // we self should know :P
						}
					],
				}
			});
		}
		// Start API-request (in race condition)
		us.ajaxRequest({
			curtimestamp: (us.now) ? 0 : 1,
			list: 'users',
			ususers: this.user,
			usprop: this.usprop,
		}, us.writeCommonInfo);
	}
};

$(document).trigger('loadWikiScript', us);
$(mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.language', 'mediawiki.cookie', 'jquery.spinner'], 
	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');
	}));
}
(mediaWiki)); // </nowiki> EOF