Benutzer:Schnark/js/personendaten.js

In der heutigen Welt hat Benutzer:Schnark/js/personendaten.js in verschiedenen Lebensbereichen große Bedeutung erlangt. Ob auf persönlicher, beruflicher oder sozialer Ebene, Benutzer:Schnark/js/personendaten.js spielt eine entscheidende Rolle bei der Entscheidungsfindung und der Art und Weise, wie wir täglichen Herausforderungen begegnen. Benutzer:Schnark/js/personendaten.js ist seit langem Gegenstand von Untersuchungen und Analysen durch Experten verschiedener Disziplinen, die versuchen, seine Auswirkungen auf die Gesellschaft besser zu verstehen. In diesem Artikel werden wir verschiedene Aspekte im Zusammenhang mit Benutzer:Schnark/js/personendaten.js untersuchen, von seiner Entwicklung im Laufe der Zeit bis zu seinem Einfluss auf das moderne Leben. Wir werden auch seine Bedeutung für die individuelle und kollektive Entwicklung sowie die möglichen Auswirkungen, die es für die Zukunft hat, analysieren.
//Dokumentation unter ] <nowiki>

/*global mediaWiki, ve, OO*/

(function ($, mw, libs) {
"use strict";

mw.messages.set({
	'schnark-pd-bug': 'Fehler melden (in neuem Tab)',
	'schnark-pd-help': 'Hilfe',
	'schnark-pd-help-title': 'Hilfeseite zu Personendaten (in neuem Tab)',
	'schnark-pd-check': 'Fehlerliste',
	'schnark-pd-check-title': 'Liste mit Fehlern in PD (in neuem Tab)',
	'schnark-pd-headline': 'Bearbeiten der Personendaten',
	'schnark-pd-button-edit': 'Bearbeiten',
	'schnark-pd-button-create': 'Personendaten erzeugen',
	'schnark-pd-button-ve': 'Personendaten bearbeiten',
	'schnark-pd-button-ve-title': 'Personendaten (evt. inkl. Normdaten) bearbeiten oder erstellen',
	'schnark-pd-ve-label': 'Personendaten-Vorlage',
	'schnark-pd-ve-edit': 'Personendaten bearbeiten'
});

var version = '6.15', config, hasOwn, teObject, pdParser;

config = {
	onlyApi: false, //nur API verwenden, keine Anzeige der Knöpfe
	dontShowFootlinks: false, //keine Links in der Fußzeile zeigen

	//Bearbeitungskommentare
	commentPDneu: '+ PD',
	commentPDfix: 'PD-fix',
	commentSortfix: 'Sortierschlüssel',
	commentCatfix: 'Kat.-fix',
	commentStaatenfix: 'Kat. Staatsangehörigkeit'
};

//Hilfsfunktionen
hasOwn = Object.prototype.hasOwnProperty;
function trim (s) { //entfernt überflüssige Leerzeichen
	return s.replace(/^+|+$/g, '').replace(/\s+/g, ' ');
}
function unlink (s) { //entlinkt einen Text
	return s.replace(/\|]*\|(]*)\]\]/g, '$1').replace(/\\]/g, '');
}
function extrahiere (regex, text) { //liefert erste Klammer des regulären Ausdrucks
	var match = regex.exec(text);
	return match === null ? '' : match;
}
function extrahiereG (regex, text) { //liefert alle ersten Klammern des regulären Ausdrucks
	var match, ret = ;
	/*jshint boss: true*/
	while (match = regex.exec(text)) {
		if (match) {
			ret.push(match);
		}
	}
	/*jshint boss: false*/
	return ret;
}
function posAdjektiv (adj, beschreibung) { //findet "deutsch-türkische Schauspielerin", "deutscher Fußballspieler" etc.
	var pos = beschreibung.indexOf(adj + '-');
	if (pos === -1) {
		pos = beschreibung.indexOf(adj + 'e');
	}
	if (pos > 0 && .indexOf(beschreibung.charAt(pos - 1)) === -1) {
		pos = -1;
	}
	return pos;
}
//]
function koelnerPhonetik (s) {
	s = replaceSpecial(s).toLowerCase();
	var phon = , i;
	function code (a, b, c) {
		switch (a) {
		case 'a': case 'e': case 'i': case 'j': case 'o': case 'u': case 'y': return 0;
		case 'b': return 1;
		case 'f': case 'v': case 'w': return 3;
		case 'g': case 'k': case 'q': return 4;
		case 'l': return 5;
		case 'm': case 'n': return 6;
		case 'r': return 7;
		case 's': case 'z': return 8;
		case 'p': return (b === 'h') ? 3 : 1;
		case 'd': case 't': return (b === 'c' || b === 's' || b === 'z') ? 8 : 2;
		case 'c': return (c === 's' || c === 'z') ? 8 :
			(b === 'a' || b === 'h' || b === 'k' || b === 'o' || b === 'q' || b === 'u' || b === 'x') ? 4 :
			(b === 'l' || b === 'r') && (c === '') ? 4 : 8;
		case 'x': return (c === 'c' || c === 'k' || c === 'q') ? 8 : 48;
		default: return '';
		}
	}
	for (i = 0; i < s.length; i++) {
		phon.push(code(s.charAt(i), s.charAt(i + 1), s.charAt(i - 1)));
	}
	phon = phon.join('').split('');
	for (i = 0; i < phon.length - 1; i++) {
		if (phon === phon) {
			phon = '';
		}
	}
	phon = phon.join('').split('');
	for (i = 1; i < phon.length; i++) {
		if (phon === '0') {
			phon = '';
		}
	}
	return phon.join('');
}

//Funktionen zur Formatierung

//liefert vollständigen Namen zu einem Teil
function vollerName (voll, teil) {
	if (
		(teil.indexOf('Mac') === 0 || teil.indexOf('Mc') === 0 || teil.indexOf('M’') === 0) &&
		(voll.indexOf('Mac') > -1 || voll.indexOf('Mc') > -1 || voll.indexOf('M’') > -1)
	) {
		return voll.replace(/M(?:ac|c|’).*/, teil);
	}
	if (teil.indexOf(' ') > -1 || voll.indexOf(' ') === -1 || voll.indexOf(teil) > -1) {
		return teil; //scheint bereits vollständig zu sein
	}
	var namen = voll.split(' '), phon = koelnerPhonetik(teil), phon2, i, index = -1, indexPart = -1;
	if (phon === '') {
		return teil;
	}
	for (i = 0; i < namen.length; i++) {
		phon2 = koelnerPhonetik(namen);
		if (phon === phon2) {
			index = i;
			break;
		} else if (phon2.indexOf(phon) === 0 || (phon2 && phon.indexOf(phon2) === 0)) {
			indexPart = i;
		}
	}
	if (index === -1) {
		index = indexPart;
	}
	if (index === -1) {
		index = namen.length - 1;
	}
	namen = teil; //phonetisch gleichen (oder letzten) Teil ersetzen
	return namen.join(' ');
}
//liefert Klammerzusatz für Alternativnamen
function anameKlammer (erklaerung) {
	if (erklaerung.indexOf('Pseud') > -1) {
		return ' (Pseudonym)';
	}
	if (erklaerung.indexOf('Spitzname') > -1) {
		return ' (Spitzname)';
	}
	if (erklaerung.indexOf('geb') > -1) {
		return ' (Geburtsname)';
	}
	if (erklaerung.indexOf('*') > -1) {
		return ' (Geburtsname)';
	}
	if (erklaerung.indexOf('eigentlich') > -1) {
		return ' (wirklicher Name)';
	}
	if (erklaerung.indexOf('bürgerlich') > -1) {
		return ' (wirklicher Name)';
	}
	return '';
}
//liefert Datum im Standardformat zurück
function datumFormat (d) {
	return d.replace(/]/g, '').replace(/\b0/g, '').replace(/\.\s*/g, '. ')
		.replace(/J(?:ahr)?h\./, 'Jahrhundert')
		.replace(/Jan\./, 'Januar').replace(/Jänner/, 'Januar')
		.replace(/Feb\./, 'Februar').replace(/Mär\./, 'März')
		.replace(/Apr\./, 'April').replace(/Jun\./, 'Juni')
		.replace(/Jul\./, 'Juli').replace(/Aug\./, 'August')
		.replace(/Sep\./, 'September').replace(/Okt\./, 'Oktober')
		.replace(/Nov\./, 'November').replace(/Dez\./, 'Dezember')
		.replace(/^(?:um|etwa|ca\.|cirka)\s*/, 'um ')
		.replace(/^(?:wahrscheinlich|vermutlich|wohl)\s*/, 'unsicher: ')
		.replace(/12\.\s*(\d\d\d)/, 'Dezember $1').replace(/11\.\s*(\d\d\d)/, 'November $1')
		.replace(/10\.\s*(\d\d\d)/, 'Oktober $1').replace(/9\.\s*(\d\d\d)/, 'September $1')
		.replace(/8\.\s*(\d\d\d)/, 'August $1').replace(/7\.\s*(\d\d\d)/, 'Juli $1')
		.replace(/6\.\s*(\d\d\d)/, 'Juni $1').replace(/5\.\s*(\d\d\d)/, 'Mai $1')
		.replace(/4\.\s*(\d\d\d)/, 'April $1').replace(/3\.\s*(\d\d\d)/, 'März $1')
		.replace(/2\.\s*(\d\d\d)/, 'Februar $1').replace(/1\.\s*(\d\d\d)/, 'Januar $1')
		//1. oder 2. Januar 2000 -> 1. Januar 2000 oder 2. Januar 2000
		.replace(/(\d\.) oder (\d?\d\.) (+ .*)$/, '$1 $3 oder $2 $3')
		//1. Januar oder 1. Februar 2000 -> 1. Januar 2000 oder 1. Februar 2000
		.replace(/(\d\. +) oder (\d?\d\. +) (.*)$/, '$1 $3 oder $2 $3');
}
//liefert zu einem Datum die passende Kategorie
function jahresKat (d) {
	var vchr = (/ v\. Chr\./).test(d) ? ' v. Chr.' : '',
		kat = extrahiere(/(\d+\. Jahrhundert (?:v. Chr. )?oder \d+\. Jahrhundert)/, d); //zwei Jahrhunderte
	if (kat !== '') {
		return 'im ' + kat.replace(/Jahrhundert (?:v. Chr. )?/, '') + vchr;
	}

	//nach Jahrhundert suchen
	kat = extrahiere(/(\d+\. Jahrhundert)/, d);
	if (kat !== '') { //gefunden?: 'im ' voranstellen
		return 'im ' + kat + vchr;
	}

	//um XX00 -> XX. oder (XX+1). Jahrhundert
	kat = extrahiere(/(?:um|unsicher:) (\d\d?)00/, d);
	if (kat !== '') {
		return 'im ' + String(parseInt(kat, 10)) + '. oder ' +
			String(parseInt(kat, 10) + 1) + '. Jahrhundert' + vchr;
	}

	//zwischen a und b -> a oder b (ergibt gleiche Kategorie)
	d = d.replace(' und ', ' oder ');

	//zwei Daten im gleichen Jahr
	kat = extrahiere(/\D(\d+) oder.* \1/, d);
	if (kat !== '') {
		return kat + vchr;
	}

	//zwei (nach obigem verschiedene) Jahre im gleichen Jahrhundert
	kat = extrahiere(/(?:^|\D)(\d\d?)\d\d oder.* \1\d\d/, d);
	if (kat !== '') {
		return 'im ' + String(parseInt(kat, 10) + 1) + '. Jahrhundert' + vchr;
	}

	//zwei Jahre (nach obigem in verschiedenen Jahrhunderten)
	kat = extrahiere(/(\d\d?)\d\d oder.* \d\d?\d\d/, d);
	if (kat !== '') {
		return 'im ' + String(parseInt(kat, 10) + 1) + '. oder ' +
			String(parseInt(kat, 10) + 2) + '. Jahrhundert' + vchr;
	}

	//ungenaues Jahr suchen
	kat = extrahiere(/(?:vor|nach|um|unsicher:) (\d\d?)\d\d/, d);
	if (kat !== '') { //gefunden?: in Jahrhundert umrechnen
		return 'im ' + String(parseInt(kat, 10) + 1) + '. Jahrhundert' + vchr;
	}

	kat = extrahiere(/(\d+)(?: v\. Chr\.)?\W*$/, d);
	if (kat !== '') {
		return kat + vchr;
	}

	return '';
}
//liefert Ort im Standardformat zurück
function ortFormat (ort) {
	ort = ort.replace(/^(der Nähe von|nahe)/, 'bei');
	ort = ort.replace(/;.*$/, ''); //nach Semikolon kommt nichts interessantes mehr
	if (/\(*$/.test(ort)) { //Klammer versehentlich abgeschnitten?
		ort += ')';
	}
	return ort.replace(/''+/g, '');
}
//ersetzt Sonderzeichen für Sortierung
function replaceSpecial (text) {
//Sonderzeichen:
//* Lateinisch-1 (nur Buchstaben)
//* Lateinisch erweitert-A
//* Lateinisch erweitert-B (ohne ƄƅƍƜƦƧƨƪƱƷƸƺƻƼƽƾǀǁǂǃǮǯȜȝȢȣɁɂɅ)
//* Lateinisch erweitert-C (ohne ⱷ)
//* Zusätzliches Lateinisch erweitert
//* Lateinisch erweitert-D (fehlt)
//* dazu -’'.…
	return text.replace(//g, 'A').replace(//g, 'a')
		.replace(//g, 'Ae').replace(//g, 'ae')
		.replace(//g, 'B').replace(//g, 'b')
		.replace(//g, 'C').replace(//g, 'c')
		.replace(//g, 'D').replace(//g, 'd')
		.replace(/ȸ/g, 'db')
		.replace(//g, 'Dz').replace(//g, 'dz')
		.replace(//g, 'E').replace(//g, 'e')
		.replace(//g, 'F').replace(//g, 'f')
		.replace(//g, 'G').replace(//g, 'g')
		.replace(//g, 'H').replace(//g, 'h')
		.replace(/Ƕ/g, 'Hv').replace(/ƕ/g, 'hv')
		.replace(//g, 'I').replace(//g, 'i')
		.replace(/IJ/g, 'Ij').replace(/ij/g, 'ij')
		.replace(//g, 'J').replace(//g, 'j')
		.replace(//g, 'K').replace(//g, 'k')
		.replace(//g, 'L').replace(//g, 'l')
		.replace(//g, 'Lj').replace(/lj/g, 'lg')
		.replace(//g, 'M').replace(//g, 'm')
		.replace(//g, 'N').replace(//g, 'n')
		.replace(//g, 'Nj').replace(/nj/g, 'nj')
		.replace(//g, 'O').replace(//g, 'o')
		.replace(/Œ/g, 'Oe').replace(/œ/g, 'oe')
		.replace(/Ƣ/g, 'Oi').replace(/ƣ/g, 'oi')
		.replace(//g, 'P').replace(//g, 'p')
		.replace(/Ɋ/g, 'Q').replace(/ɋ/g, 'q')
		.replace(/ȹ/g, 'qp')
		.replace(//g, 'R').replace(//g, 'r')
		.replace(//g, 'S').replace(//g, 's')
		.replace(/ß/g, 'ss')
		.replace(//g, 'T').replace(//g, 't')
		.replace(/Þ/g, 'Th').replace(/þ/g, 'th')
		.replace(//g, 'U').replace(//g, 'u')
		.replace(//g, 'V').replace(//g, 'v')
		.replace(//g, 'W').replace(//g, 'w')
		.replace(//g, 'X').replace(//g, 'x')
		.replace(//g, 'Y').replace(//g, 'y')
		.replace(//g, 'Z').replace(//g, 'z')
		.replace(//g, '');
}

function StaatenManager (pdParser, pd) {
	StaatenManager.init();
	this.pd = pd;
	if (!pd) {
		this.pd = {
			name: pdParser.getCurrentContent('NAME'),
			kurzbeschreibung: pdParser.getCurrentContent('KURZBESCHREIBUNG'),
			geburtsdatum: pdParser.getCurrentContent('GEBURTSDATUM'),
			sterbedatum: pdParser.getCurrentContent('STERBEDATUM')
		};
	}
}

//Staaten
/* Einträge bestehen aus:
 * adj:
	String - Adjektiv, das in Kurzbeschreibung auftauchen muss, oder
	Array - mehrere Adjektive, oder
	Function - Funktion, die Kurzbeschreibung prüft
 * kat:
	String - Kategorie, oder
	Object - Sammlung aus Kategorie -> Jahresbereich
 * sprache:
	String - Sprache (für Ansetzung des Namens), oder
	Function - Funktion, die Sprache liefert, oder
	undefined
 * Funktionen erhalten dabei die vollständigen Daten als Parameter
*/
StaatenManager.staatenListe = [
	//Kategorie:Person nach Staatsangehörigkeit
	{adj: 'afghanisch', kat: 'Afghane'},
	{adj: 'ägyptisch', kat: {'Ägypter': '1922|'}},
	{adj: 'albanisch', kat: 'Albaner'},
	{adj: 'algerisch', kat: 'Algerier'},
	{adj: 'andorranisch', kat: 'Andorraner'},
	{adj: 'angolanisch', kat: 'Angolaner', sprache: 'portugiesisch'},
	{adj: 'antiguanisch', kat: 'Antiguaner', sprache: 'englisch'},
	{adj: 'äquatorialguineisch', kat: 'Äquatorialguineer', sprache: 'spanisch'},
	{adj: 'argentinisch', kat: 'Argentinier', sprache: 'spanisch'},
	{adj: 'armenisch', kat: 'Armenier'},
	{adj: 'aserbaidschanisch', kat: 'Aserbaidschaner'},
	{adj: 'äthiopisch', kat: 'Äthiopier'},
	{adj: 'australisch', kat: 'Australier', sprache: 'englisch'},
	{adj: 'bahamaisch', kat: 'Bahamaer', sprache: 'englisch'},
	{adj: 'bahrainisch', kat: 'Bahrainer'},
	{adj: 'bangladeschisch', kat: 'Bangladescher'},
	{adj: 'barbadisch', kat: 'Barbadier', sprache: 'englisch'},
	{adj: 'belgisch', kat: 'Belgier', sprache: 'luxemburgisch'},
	{adj: 'belizisch', kat: 'Belizer', sprache: 'englisch'},
	{adj: 'beninisch', kat: 'Beniner', sprache: 'französisch'},
	{adj: 'bhutanisch', kat: 'Bhutaner'},
	{adj: 'bolivianisch', kat: 'Bolivianer', sprache: 'spanisch'},
	{adj: 'bosnisch-herzegowinisch', kat: {'Jugoslawe': '1918|1992', 'Bosnier': '1992|'}},
	{adj: 'botswanisch', kat: 'Botswaner'},
	{adj: 'brasilianisch', kat: 'Brasilianer', sprache: 'portugiesisch'},
	{adj: 'britisch', kat: 'Brite', sprache: 'englisch'},
	{adj: 'bruneiisch', kat: 'Bruneier'},
	{adj: 'bulgarisch', kat: 'Bulgare'},
	{adj: 'burkinisch', kat: 'Burkiner', sprache: 'französisch'},
	{adj: 'burundisch', kat: 'Burundier'},
	{adj: 'chilenisch', kat: 'Chilene', sprache: 'chilenisch'},
	{adj: function (pd) {
		var pos = posAdjektiv('chinesisch', pd.kurzbeschreibung);
		if (pd.kurzbeschreibung.indexOf('Hongkong') > -1) {
			pos = -1;
		}
		return pos;
	}, kat: {'Chinese': '1689|'}, sprache: 'chinesisch'},
	{adj: function (pd) {
		var pos = posAdjektiv('chinesisch', pd.kurzbeschreibung), pos2 = pd.kurzbeschreibung.indexOf('Hongkong');
		if (pos2 === -1) {
			pos = -1;
		} else if (pos === -1) {
			pos = pos2;
		}
		return pos;
	}, kat: 'Chinese (Hongkong)', sprache: 'chinesisch'},
	{adj: 'costa-ricanisch', kat: 'Costa-Ricaner', sprache: 'spanisch'},
	{adj: 'dänisch', kat: 'Däne', sprache: 'skandinavisch'},
	{adj: function (pd) {
		var pos = posAdjektiv('deutsch', pd.kurzbeschreibung);
		if (pd.kurzbeschreibung.indexOf('DDR') > -1) {
			pos = -1;
		}
		return pos;
	}, kat: {'Deutscher': '1913|'}, sprache: 'deutsch'},
	{adj: 'dominicanisch', kat: 'Dominicaner', sprache: 'englisch'},
	{adj: 'dominikanisch', kat: 'Dominikaner (Staatsangehöriger)', sprache: 'spanisch'},
	{adj: 'dschibutisch', kat: 'Dschibutier'},
	{adj: 'ecuadorianisch', kat: 'Ecuadorianer', sprache: 'spanisch'},
	{adj: 'eritreisch', kat: 'Eritreer'},
	{adj: 'estnisch', kat: 'Este'},
	{adj: 'fidschianisch', kat: 'Fidschianer'},
	{adj: 'finnisch', kat: 'Finne'},
	{adj: 'französisch', kat: 'Franzose', sprache: 'französisch'},
	{adj: 'gabunisch', kat: 'Gabuner', sprache: 'französisch'},
	{adj: 'gambisch', kat: 'Gambier'},
	{adj: 'georgisch', kat: 'Georgier'},
	{adj: 'ghanaisch', kat: 'Ghanaer', sprache: 'englisch'},
	{adj: 'grenadisch', kat: 'Grenader', sprache: 'englisch'},
	{adj: 'griechisch', kat: {'Grieche': '1829|'}},
	{adj: 'guatemaltekisch', kat: 'Guatemalteke', sprache: 'spanisch'},
	{adj: 'guinea-bissauisch', kat: 'Guinea-Bissauer', sprache: 'portugiesisch'},
	{adj: 'guineisch', kat: 'Guineer', sprache: 'französisch'},
	{adj: 'guyanisch', kat: 'Guyaner', sprache: 'englisch'},
	{adj: 'haitianisch', kat: 'Haitianer'},
	{adj: 'honduranisch', kat: 'Honduraner', sprache: 'spanisch'},
	{adj: 'indisch', kat: 'Inder'},
	{adj: 'indonesisch', kat: 'Indonesier'},
	{adj: 'irakisch', kat: 'Iraker'},
	{adj: 'iranisch', kat: 'Iraner'},
	{adj: 'irisch', kat: 'Ire'},
	{adj: 'isländisch', kat: 'Isländer', sprache: 'isländisch'},
	{adj: 'israelisch', kat: 'Israeli'},
	{adj: 'italienisch', kat: {'Historische Person (Italien)': '480|1860', 'Italiener': '1861|'}, sprache: 'italienisch'},
	{adj: 'ivorisch', kat: 'Ivorer', sprache: 'französisch'},
	{adj: 'jamaikanisch', kat: 'Jamaikaner', sprache: 'englisch'},
	{adj: 'japanisch', kat: 'Japaner', sprache: function (pd) {
		return (/192|19\d|1\d\d|\b\d\d\d\b|v\. Chr\./).test(pd.geburtsdatum) ?
			'japanisch vor Showa' : 'japanisch';
	}},
	{adj: 'jemenitisch', kat: 'Jemenit'},
	{adj: 'jordanisch', kat: 'Jordanier'},
	{adj: 'kambodschanisch', kat: 'Kambodschaner'},
	{adj: 'kamerunisch', kat: 'Kameruner', sprache: 'französisch'},
	{adj: 'kanadisch', kat: 'Kanadier', sprache: function (pd) {
		return (/^+$/).test(pd.name) ? 'englisch' : 'französisch';
	}},
	{adj: 'kap-verdisch', kat: 'Kap-Verdier', sprache: 'portugiesisch'},
	{adj: 'kasachisch', kat: 'Kasache'},
	{adj: 'katarisch', kat: 'Katarer'},
	{adj: 'kenianisch', kat: 'Kenianer'},
	{adj: 'kirgisisch', kat: 'Kirgise'},
	{adj: 'kiribatisch', kat: 'Kiribatier'},
	{adj: 'kolumbianisch', kat: 'Kolumbianer', sprache: 'spanisch'},
	{adj: 'komorisch', kat: 'Komorer'},
	{adj: function (pd) {
		var pos = posAdjektiv('kongolesisch', pd.kurzbeschreibung);
		if (pd.kurzbeschreibung.indexOf('Demokratische Republik') === -1) {
			pos = -1;
		}
		return pos;
	}, kat: 'Kongolese (Demokratische Republik Kongo)', sprache: 'französisch'},
	{adj: function (pd) {
		var pos = posAdjektiv('kongolesisch', pd.kurzbeschreibung);
		if (pd.kurzbeschreibung.indexOf('Demokratische Republik') !== -1) {
			pos = -1;
		}
		return pos;
	}, kat: 'Kongolese (Republik Kongo)', sprache: 'französisch'},
	{adj: function (pd) {
		var pos = posAdjektiv('macauisch', pd.kurzbeschreibung);
		if (pos === -1) {
			pos = pd.kurzbeschreibung.indexOf('Macau');
		}
		return pos;
	}, kat: 'Chinese (Macau)', sprache: 'chinesisch'},
	{adj: 'nordkoreanisch', kat: 'Nordkoreaner', sprache: 'koreanisch'},
	{adj: 'südkoreanisch', kat: 'Südkoreaner', sprache: 'koreanisch'},
	{adj: 'kroatisch', kat: {'Jugoslawe': '1918|1992', 'Kroate': '1992|'}},
	{adj: 'kubanisch', kat: 'Kubaner', sprache: 'spanisch'},
	{adj: 'kuwaitisch', kat: 'Kuwaiter'},
	{adj: 'laotisch', kat: 'Laote'},
	{adj: 'lesothisch', kat: 'Lesother'},
	{adj: 'lettisch', kat: 'Lette'},
	{adj: 'libanesisch', kat: 'Libanese'},
	{adj: 'liberianisch', kat: 'Liberianer', sprache: 'englisch'},
	{adj: 'libysch', kat: 'Libyer'},
	{adj: 'liechtensteinisch', kat: 'Liechtensteiner', sprache: 'deutsch'},
	{adj: 'litauisch', kat: 'Litauer'},
	{adj: 'lucianisch', kat: 'Lucianer', sprache: 'englisch'},
	{adj: 'luxemburgisch', kat: 'Luxemburger', sprache: 'luxemburgisch'},
	{adj: 'madagassisch', kat: 'Madagasse'},
	{adj: 'malawisch', kat: 'Malawier'},
	{adj: 'malaysisch', kat: 'Malaysier'},
	{adj: 'maledivisch', kat: 'Malediver'},
	{adj: 'malisch', kat: 'Malier', sprache: 'französisch'},
	{adj: 'maltesisch', kat: 'Malteser'},
	{adj: 'marokkanisch', kat: 'Marokkaner'},
	{adj: 'marshallisch', kat: 'Marshaller', sprache: 'englisch'},
	{adj: 'mauretanisch', kat: 'Mauretanier'},
	{adj: 'mauritisch', kat: 'Mauritier', sprache: 'englisch'},
	{adj: 'mazedonisch', kat: {'Jugoslawe': '1918|1991', 'Mazedonier': '1991|'}},
	{adj: 'mexikanisch', kat: 'Mexikaner', sprache: 'spanisch'},
	{adj: 'mikronesisch', kat: 'Mikronesier', sprache: 'englisch'},
	{adj: 'moldauisch', kat: 'Moldawier', sprache: 'rumänisch'},
	{adj: 'monegassisch', kat: 'Monegasse', sprache: 'französisch'},
	{adj: 'mongolisch', kat: 'Mongole'},
	{adj: 'montenegrinisch', kat: {'Jugoslawe': '1918|1992', 'Serbe': '1992|2006', 'Montenegriner': '1992|'}},
	{adj: 'mosambikanisch', kat: 'Mosambikaner', sprache: 'portugiesisch'},
	{adj: , kat: 'Myanmare'},
	{adj: 'namibisch', kat: 'Namibier', sprache: 'englisch'},
	{adj: 'nauruisch', kat: 'Nauruer'},
	{adj: 'nepalesisch', kat: 'Nepalese'},
	{adj: 'neuseeländisch', kat: 'Neuseeländer', sprache: 'englisch'},
	{adj: 'nicaraguanisch', kat: 'Nicaraguaner', sprache: 'spanisch'},
	{adj: 'niederländisch', kat: 'Niederländer', sprache: 'niederländisch'},
	{adj: 'nigerianisch', kat: 'Nigerianer', sprache: 'englisch'},
	{adj: 'nigrisch', kat: 'Nigrer', sprache: 'französisch'},
	{adj: 'norwegisch', kat: 'Norweger', sprache: 'skandinavisch'},
	{adj: 'omanisch', kat: 'Omaner'},
	{adj: 'österreichisch', kat: {'Österreicher': '1918|'}, sprache: 'deutsch'},
	{adj: 'osttimoresisch', kat: 'Osttimorese'},
	{adj: 'pakistanisch', kat: 'Pakistaner'},
	{adj: 'palauisch', kat: 'Palauer'},
	{adj: 'panamaisch', kat: 'Panamaer', sprache: 'spanisch'},
	{adj: 'papua-neuguineisch', kat: 'Papua-Neuguineer', sprache: 'englisch'},
	{adj: 'paraguayisch', kat: 'Paraguayer', sprache: 'spanisch'},
	{adj: 'peruanisch', kat: 'Peruaner'},
	{adj: 'philippinisch', kat: 'Philippiner'},
	{adj: 'polnisch', kat: 'Pole'},
	{adj: 'portugiesisch', kat: 'Portugiese', sprache: 'portugiesisch'},
	{adj: 'ruandisch', kat: 'Ruander', sprache: 'englisch'},
	{adj: 'rumänisch', kat: 'Rumäne', sprache: 'rumänisch'},
	{adj: 'russisch', kat: 'Russe', sprache: 'russisch'},
	{adj: 'salomonisch', kat: 'Salomoner', sprache: 'englisch'},
	{adj: 'salvadorianisch', kat: 'Salvadorianer', sprache: 'spanisch'},
	{adj: 'sambisch', kat: 'Sambier', sprache: 'englisch'},
	{adj: 'samoanisch', kat: 'Samoaner'},
	{adj: 'san-marinesisch', kat: 'San-Marinese', sprache: 'italienisch'},
	{adj: 'são-toméisch', kat: 'São-Toméer', sprache: 'portugiesisch'},
	{adj: 'saudi-arabisch', kat: 'Saudi-Araber'},
	{adj: 'schwedisch', kat: 'Schwede', sprache: 'skandinavisch'},
	{adj: function (pd) {
		var pos = posAdjektiv('schweizerisch', pd.kurzbeschreibung);
		if (pos === -1) {
			pos = pd.kurzbeschreibung.indexOf('Schweizer ');
		}
		return pos;
	}, kat: 'Schweizer', sprache: function (pd) {
		return pd.kurzbeschreibung.indexOf(' von ') > -1 ||
			pd.kurzbeschreibung.indexOf(' zu') > -1 ||
			pd.kurzbeschreibung.indexOf(' und ') > -1 ? 'deutsch' : 'französisch';
	}},
	{adj: 'senegalesisch', kat: 'Senegalese', sprache: 'französisch'},
	{adj: 'serbisch', kat: {'Jugoslawe': '1918|1992', 'Serbe': '1992|'}},
	{adj: 'seychellisch', kat: 'Seycheller'},
	{adj: 'sierra-leonisch', kat: 'Sierra-Leoner', sprache: 'englisch'},
	{adj: 'simbabwisch', kat: 'Simbabwer', sprache: 'englisch'},
	{adj: 'singapurisch', kat: 'Singapurer'},
	{adj: 'slowakisch', kat: {'Slowake': '', 'Tschechoslowake': '1918|1992'}, sprache: 'tschechisch'},
	{adj: 'slowenisch', kat: {'Jugoslawe': '1918|1992', 'Slowene': '1992|'}},
	{adj: 'somalisch', kat: 'Somalier'},
	{adj: 'spanisch', kat: 'Spanier', sprache: 'spanisch'},
	{adj: 'sri-lankisch', kat: 'Sri-Lanker'},
	{adj: function (pd) {
		return pd.kurzbeschreibung.indexOf('Kitts ');
	}, kat: 'Staatsangehöriger von St. Kitts und Nevis', sprache: 'englisch'},
	{adj: 'südafrikanisch', kat: 'Südafrikaner', sprache: 'niederländisch'},
	{adj: 'sudanesisch', kat: 'Sudanese'},
	{adj: 'südsudanesisch', kat: 'Südsudanese'},
	{adj: 'surinamisch', kat: 'Surinamer', sprache: 'niederländisch'},
	{adj: 'swasiländisch', kat: 'Swasi', sprache: 'englisch'},
	{adj: 'syrisch', kat: 'Syrer'},
	{adj: 'tadschikisch', kat: 'Tadschike'},
	{adj: 'tansanisch', kat: 'Tansanier'},
	{adj: 'thailändisch', kat: 'Thailänder', sprache: 'thailändisch'},
	{adj: 'togoisch', kat: 'Togoer', sprache: 'französisch'},
	{adj: 'tonganisch', kat: 'Tongaer'},
	{adj: function (pd) {
		return pd.kurzbeschreibung.indexOf('Trinidad und Tobago');
	}, kat: 'Staatsangehöriger von Trinidad und Tobago', sprache: 'englisch'},
	{adj: 'tschadisch', kat: 'Tschader', sprache: 'französisch'},
	{adj: 'tschechisch', kat: {'Tscheche': '', 'Tschechoslowake': '1918|1992'}, sprache: 'tschechisch'},
	{adj: 'tunesich', kat: 'Tunesier'},
	{adj: 'türkisch', kat: 'Türke'},
	{adj: 'turkmenisch', kat: 'Turkmene'},
	{adj: 'tuvaluisch', kat: 'Tuvaluer'},
	{adj: 'ugandisch', kat: 'Ugander', sprache: 'englisch'},
	{adj: 'ukrainisch', kat: 'Ukrainer', sprache: 'russisch'},
	{adj: 'ungarisch', kat: {'Ungar': '1918|'}},
	{adj: 'uruguayisch', kat: 'Uruguayer', sprache: 'spanisch'},
	{adj: 'amerikanisch', kat: 'US-Amerikaner', sprache: 'englisch'}, //einschließlich US-amerikanisch
	{adj: 'usbekisch', kat: 'Usbeke'},
	{adj: 'vanuatuisch', kat: 'Vanuatuer', sprache: 'englisch'},
	{adj: 'vatikanisch', kat: 'Staatsangehöriger des Vatikan'},
	{adj: 'venezolanisch', kat: 'Venezolaner', sprache: 'spanisch'},
	{adj: function (pd) {
		return pd.kurzbeschreibung.indexOf('Vereinigten Arabischen Emirate');
	}, kat: 'Staatsangehöriger der Vereinigten Arabischen Emirate'},
	{adj: 'vietnamesisch', kat: 'Vietnamese'},
	{adj: 'vincentisch', kat: 'Vincenter', sprache: 'englisch'},
	{adj: , kat: 'Weißrusse', sprache: 'russisch'},
	{adj: 'zentralafrikanisch', kat: 'Zentralafrikaner'},
	{adj: 'zyprisch', kat: 'Zyprer'},
	//Sonderfälle
	{adj: 'staatenlos', kat: 'Staatenloser'},
	{adj: function (pd) {
		return pd.kurzbeschreibung.indexOf('unbekannter Staatsangehörigkeit');
	}, kat: 'Staatsangehörigkeit unbekannt'},
	//Kategorie:Person nach historischer Staatsangehörigkeit
	{adj: 'badisch', kat: {'Badener': '1918|1934', 'Deutscher': '1913|'}, sprache: 'deutsch'},
	{adj: 'Danziger', kat: 'Danziger'},
	{adj: function (pd) {
		var pos = posAdjektiv('deutsch', pd.kurzbeschreibung);
		if (pd.kurzbeschreibung.indexOf('DDR') === -1) {
			pos = -1;
		}
		return pos;
	}, kat: {'DDR-Bürger': '', 'Deutscher': '1990|'}, sprache: 'deutsch'},
	{adj: 'jugoslawisch', kat: {'Jugoslawe': '1918|1992'}},
	{adj: 'preußisch', kat: {'Preuße': '1842|1934', 'Deutscher': '1913|'}, sprache: 'deutsch'},
	{adj: function (pd) {
		var pos = posAdjektiv('römisch', pd.kurzbeschreibung);
		if (pd.kurzbeschreibung.indexOf('katholisch') > -1) { //römisch-katholische Bischöfe sind keine Römer...
			pos = -1;
		}
		return pos;
	}, kat: 'Römer', sprache: 'römisch'},
	{adj: 'sowjetisch', kat: 'Sowjetbürger'},
	{adj: 'tschechoslowakisch', kat: 'Tschechoslowake', sprache: 'tschechisch'},
	{adj: 'württembergisch', kat: {'Württemberger': '1819|1934', 'Deutscher': '1913|'}, sprache: 'deutsch'},
	//nicht in Kategorie:Person nach Staatsangehörigkeit, aber in WP:NKS
	{adj: 'kosovarisch', kat: {'Kosovare': '', 'Serbe': '1992|'}},
	{adj: 'niueanisch', kat: {'Niueaner': '', 'Neuseeländer': ''}},
	{adj: 'saharauisch', kat: {'Sahraui': '', 'Marokkaner': ''}, sprache: 'spanisch'},
	{adj: 'taiwanisch', kat: {'Taiwaner': '1949|', 'Chinese': '|1949'}, sprache: 'chinesisch'},
	//ganz ohne eigene Kategorie, aber in WP:NKS
	{adj: function (pd) {
		return pd.kurzbeschreibung.indexOf('Cookinseln');
	}, kat: 'Neuseeländer'},
	//keine Staatsangehörigkeiten, aber ähnlich verwendet
	{adj: 'englisch', kat: {'Engländer': '', 'Brite': '1707|'}, sprache: 'englisch'},
	{adj: 'katalanisch', kat: 'Person (Katalonien)', sprache: 'spanisch'},
	{adj: 'nordirisch', kat: {'Nordire': '', 'Brite': '1801|'}, sprache: 'englisch'},
	{adj: 'palästinensisch', kat: 'Palästinenser'},
	{adj: 'schottisch', kat: {'Schotte': '', 'Brite': '1707|'}, sprache: 'englisch'},
	{adj: 'walisisch', kat: {'Waliser': '', 'Brite': '1707|'}, sprache: 'englisch'},
	//Mischformen
	{adj: 'austroamerikanisch', kat: {'Österreicher': '1918|', 'US-Amerikaner': ''}, sprache: 'deutsch'},
	{adj: 'deutschamerikanisch', kat: {'Deutscher': '1913|', 'US-Amerikaner': ''}, sprache: 'deutsch'}
];

StaatenManager.init = function () {
	if (StaatenManager.staatenRe) {
		return;
	}
	var staaten = , i, kat, k;
	function add (k) {
		k = mw.util.escapeRegExp(k.toLowerCase()).replace(/ /g, '+');
		if (staaten.indexOf(k) === -1) {
			staaten.push(k);
		}
	}
	for (i = 0; i < StaatenManager.staatenListe.length; i++) {
		kat = StaatenManager.staatenListe.kat;
		if (typeof kat === 'string') {
			add(kat);
		} else {
			for (k in kat) {
				if (hasOwn.call(kat, k)) {
					add(k);
				}
			}
		}
	}
	StaatenManager.staatenRe = staaten.join('|');
};
StaatenManager.getRe = function () {
	StaatenManager.init();
	return StaatenManager.staatenRe;
};

StaatenManager.prototype.getStaaten = function () {
	var i, j, adj, pos, staaten = ;
	for (i = 0; i < StaatenManager.staatenListe.length; i++) {
		adj = StaatenManager.staatenListe.adj;
		if (typeof adj === 'function') {
			pos = adj(this.pd);
		} else if (Array.isArray(adj)) {
			pos = -1;
			for (j = 0; j < adj.length; j++) {
				pos = posAdjektiv(adj, this.pd.kurzbeschreibung);
				if (pos > -1) {
					break;
				}
			}
		} else {
			pos = posAdjektiv(adj, this.pd.kurzbeschreibung);
		}
		if (pos > -1) {
			staaten.push($.extend({}, StaatenManager.staatenListe, {pos: pos}));
		}
	}
	staaten.sort(function (a, b) {
		return a.pos - b.pos;
	});
	return staaten;
};

StaatenManager.prototype.getStaatskats = function () {
	var i, kat, staaten = this.getStaaten(), kats = ;
	function makeYear (datum) {
		var vChr = 1, j;
		datum = jahresKat(datum);
		if (datum.indexOf('v. Chr.') > -1) {
			vChr = -1;
		}
		j = extrahiere(/im (\d+)\. oder/, datum);
		if (j !== '') {
			j *= 100;
		} else {
			j = extrahiere(/im (\d+)\./, datum);
			if (j !== '') {
				j *= 100;
				j -= 50;
			} else {
				j = Number(datum.replace(/\D+/g, ''));
			}
		}
		return vChr * j;
	}
	function checkRange (range, geb, gest) {
		if (range === '') {
			return true;
		}
		range = range.split('|');
		if (range === '') { //Teste: geb < range
			if (geb === '') { //zeitlos, also wohl älter
				return true;
			}
			geb = makeYear(geb);
			return geb <= Number(range);
		} else if (range === '') { //Teste: range < gest
			if (geb === '' && gest === '') { //zeitlos, also wohl älter
				return false;
			}
			if (gest === '') { //lebt noch
				return true;
			}
			gest = makeYear(gest);
			return Number(range) <= gest;
		} else { //Teste: (geb < range && range < gest) || (range < geb && geb < range)
			if (geb === '' && gest === '') { //zeitlos, also wohl älter
				return false;
			}
			if (geb === '') {
				geb = Number(range);
			} else {
				geb = makeYear(geb);
			}
			if (gest === '') {
				gest = Number(range);
			} else {
				gest = makeYear(gest);
			}
			return (geb <= Number(range) && Number(range) <= gest) ||
				(Number(range) <= geb && geb <= Number(range));
		}
	}
	function getKatsByRange (kats, geb, gest) {
		var k, ret = ;
		for (k in kats) {
			if (hasOwn.call(kats, k)) {
				if (checkRange(kats, geb, gest)) {
					ret.push(k);
				}
			}
		}
		return ret;
	}
	function add (k) {
		var i;
		if (Array.isArray(k)) {
			for (i = 0; i < k.length; i++) {
				add(k);
			}
		} else {
			if (kats.indexOf(k) === -1) {
				kats.push(k);
			}
		}
	}
	for (i = 0; i < staaten.length; i++) {
		kat = staaten.kat;
		if (typeof kat === 'string') {
			add(kat);
		} else {
			add(getKatsByRange(kat, this.pd.geburtsdatum, this.pd.sterbedatum));
		}
	}
	return kats.join('|');
};

StaatenManager.prototype.getSprache = function () {
	var i, sprache, staaten = this.getStaaten();
	for (i = 0; i < staaten.length; i++) {
		sprache = staaten.sprache;
		if (typeof sprache === 'function') {
			return sprache(this.pd);
		} else if (sprache) {
			return sprache;
		}
	}
};

//Namenskonverter
function Namenskonverter (pdParser, text) {
	this.pd = {
		name: pdParser.getSuggestions('NAME') || '',
		kurzbeschreibung: pdParser.getSuggestions('KURZBESCHREIBUNG') || '',
		geburtsdatum: pdParser.getSuggestions('GEBURTSDATUM') || '',
		sterbedatum: pdParser.getSuggestions('STERBEDATUM') || ''
	};
	this.text = text;
}
Namenskonverter.prototype.getSprache = function () {
	if (!this.spracheCache) {
		this.spracheCache = (new StaatenManager(false, this.pd)).getSprache();
	}
	return this.spracheCache;
};
Namenskonverter.prototype.is = function (test, n) {
	var f = Namenskonverter.isData;
	if (typeof f === 'string') {
		return f === this.getSprache();
	}
	return f(n, this.pd, this.text);
};
Namenskonverter.prototype.convert = function (type, n) {
	return Namenskonverter.convertData(n, this.pd, this.text, this);
};
//liefert ein Array zurück, es ist n === ar + ar + ar,
//ar enthält nur Präpositionen, Artikel u. Ä. (inkl. führender und folgender Leerzeichen)
Namenskonverter.namensteile = function (n) {
	var expr = ' (?:(?: ?e)? |u? (?:e )?|' +
			'e? |e?|e(?:l?i)? | |os? |' +
			'as || |en |f |u (?:er )?|' +
			'n (?:der )?|p e | )',
		pos = n.search(new RegExp(expr)), //erste Präposition/...
		vor;
	if (pos === -1) {
		return ;
	}
	vor = n.slice(0, pos);
	n = n.slice(pos);
	pos = (new RegExp('(.*' + expr + ')')).exec(n); //letzte Präposition/...
	pos = pos.length;
	return ;
};
//macht den ersten Buchstaben eines Namens groß, falls dieser beim Umstellen
//zu einem Kleinbuchstaben wurde, inkl. trim und Entfernung überflüssiger Satzzeichen
Namenskonverter.vorneGross = function (n) {
	n = trim(n).replace(/,,+/g, ',').replace(/,$/, '').replace(/;/g, '');
	if (n.indexOf(', ') === -1) { //kein Komma, also nichts zu verändern
		return n;
	}
	return n.slice(0, 1).toUpperCase() + n.slice(1);
};

Namenskonverter.isData = {
//virtual indent
fremdalphabet: function (n) {
	//Beginn des Unicode-Blocks Armenisch, danach Hebräisch, Arabisch
	//und ähnlich unleserliches Zeug, bei dem normalen Benutzern
	//nicht zuzutrauen ist, korrekt ein Komma einzufügen
	return n.charCodeAt(0) > 1328;
},
mittelalter: function (n, pd) { //offiziell 501 bis 1501, aber im 15. Jahrhundert zu viele Ausnahmen
	return pd.sterbedatum.search(/(?:\b|1)\d\d/) > -1 || //gestorben 500 bis 1399
		pd.sterbedatum.search(/(?:\b|1)\. Jahrhundert/) > -1; //gestorben 6. Jahrhundert bis 14. Jahrhundert
},
spaetmittelalter: function (n, pd) {
	//jenes 15. Jahrhundert (und wehe, ein Historiker beschwert sich über die Bezeichnung!)
	return pd.sterbedatum.search(/\b14\d\d/) > -1 || //gestorben 14xx
		pd.sterbedatum.search(/\b15\. Jahrhundert/) > -1; //gestorben 15. Jahrhundert
},
verwandt: function (n) {
	return (/ (?:Mac|Ó|Abu|Fitz|Ben) /).test(n);
},
generation: function (n) {
	return (/ (d(\.|er|ie) )?(Ältere|Jüngere|en(?:ior|\.)|r\.$|un(?:ior|\.)|r\.)/).test(n);
},
nummer: function (n) {
	return (/ +\./).test(n); //Test auf Mittelninitial unnötig
},
vollername: function (n, pd) {
	if (pd.name.indexOf(', ') === -1 || (/,.*,/).test(pd.name)) {
		return false;
	}
	var nameRE = '\\b' + mw.util.escapeRegExp(pd.name.replace(/^(.*), (.*)$/, '$2 $1')) + '\\b';
	return (new RegExp(nameRE)).test(n);
},
ducearletc: function (n) {
	return (/\b(?:Duc|Earl)\b/).test(n);
},
britischerAdel: function (n) {
	var re = new RegExp('^+, \\d+\\. (?:' + [
		'Duke', 'Duchess', 'Marquess', 'Marchioness', 'Earl', 'Countess', 'Viscount', 'Viscountess', 'Baron', 'Baroness'
	].join('|') + ')(?:( of)? +)?$');
	return (re).test(n);
},
roemisch: 'römisch',

//Sprachen
arabisch: function (n) {
	return (/(?:^| )(?:Abu|Umm|Ibn|Bint|)/).test(n);
},
chinesisch: 'chinesisch',
deutsch: 'deutsch',
luxemburgisch: 'luxemburgisch',
englisch: 'englisch',
franzoesisch: 'französisch',
islaendisch: 'isländisch',
italienisch: 'italienisch',
japanischVorShowa: 'japanisch vor Showa',
japanisch: 'japanisch',
koreanisch: 'koreanisch',
niederlaendisch: 'niederländisch',
osmanisch: function (n, pd) {
	return (pd.kurzbeschreibung.indexOf('osmanische') > -1 &&
		//warum auch immer so viele Bulgaren als osmanische Beamte Karriere gemacht haben
		pd.kurzbeschreibung.indexOf('bulgarische') === -1) ||
		(pd.kurzbeschreibung.indexOf('türkische') > -1 &&
		(/193|1\d\d|\b\d\d\d\b/).test(pd.sterbedatum)); //vor 1934 gestorben
},
portugiesisch: 'portugiesisch',
rumaenisch: 'rumänisch',
russisch: 'russisch',
skandinavisch: 'skandinavisch',
spanisch: 'spanisch',
chilenisch: 'chilenisch',
thailaendisch: 'thailändisch',
tschechisch: 'tschechisch',

test: function (n, pd) {
	return pd.kurzbeschreibung.indexOf('Testperson') > -1;
}
//virtual outdent
};
Namenskonverter.convertData = {
//virtual indent
generation: function (n, pd, text, that) {
	var zusatz;
	if (n.indexOf('Ältere') > -1) {
		zusatz = ' der Ältere';
	} else if (n.indexOf('Jüngere') > -1) {
		zusatz = ' der Jüngere';
	} else if (/en(?:ior|\.)|r\.$/.test(n)) {
		zusatz = ' senior';
	} else if (/un(?:ior|\.)|r\./.test(n)) {
		zusatz = ' junior';
	}
	return that.run(
		n.replace(/ (d(\.|er|ie) )?(Ältere|Jüngere|en(?:ior|\.)|r\.$|un(?:ior|\.)|r\.)/, '')
	) + zusatz;
},
nummer: function (n, pd, text, that) {
	//Zahlen per Unterstrich am Namen befestigen
	return that.run(n.replace(/ (+\.)/g, '_$1')).replace(/_(+\.)/g, ' $1');
},
britischerAdel: function (n) {
	var name = n.split(',');
	return Namenskonverter.convertData.normal(name.replace(/^Sir /, '')) + ',' + name;
},
vollername: function (n, pd) {
	var name = pd.name.replace(/^(.*), (.*)$/, '$2 $1'),
		pos = n.indexOf(name),
		vorne = n.slice(0, pos), hinten = n.slice(pos + name.length);
	return pd.name.replace(/, /, hinten + ', ' + vorne);
},

ohneAenderung: function (n) {
	return n;
},
normal: function (n) {
	n = trim(n);
	var pos = n.lastIndexOf(' ');
	return pos === -1 ? n : n.slice(pos + 1) + ', ' + n.slice(0, pos);
},
kommaEinfuegen: function (n) {
	return n.replace(/ /, ', ');
},

hinten: function (n) { //setzt Präpositionen/... hinter den Vornamen
	var teile = Namenskonverter.namensteile(n);
	return teile === '' ?
		Namenskonverter.convertData.normal(n) :
		teile.replace(/\s+$/, '') + ', ' + teile + teile;
},
vorne: function (n) { //setzt Präpositionen/... vor den Nachnamen
	var teile = Namenskonverter.namensteile(n);
	return teile === '' ?
		Namenskonverter.convertData.normal(n) :
		teile + teile.replace(/\s+$/, '') + ', ' + teile;
},
deutsch: function (n) { //setzt Präpositionen/... für deutsche Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile === '') {
		return Namenskonverter.convertData.normal(n);
	}
	teile = teile.replace(/\s+$/, '') + ', ';
	if (teile.indexOf(' und ') > -1) { //von und zu immer hinten
		return teile + teile + teile;
	}
	pos = teile.search(/ (?:om |u |os? |as || |en )/); //vom, etc. nach vorne
	return pos === -1 ?
		teile + teile + teile :
		teile.slice(pos) + teile + teile + teile.slice(0, pos);
},
skandinavisch: function (n) { //setzt Präpositionen/... für skandinavische Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile === '') {
		return Namenskonverter.convertData.normal(n);
	}
	teile = teile.replace(/\s+$/, '') + ', ';
	//romanisches nach vorne
	pos = teile.search(/ (?:e?|e(?:l?i)? | |os? |as || )/);
	return pos === -1 ?
		teile + teile + teile :
		teile.slice(pos) + teile + teile + teile.slice(0, pos);
},
franzoesisch: function (n) { //setzt Präpositionen/... für französische Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile === '') {
		return Namenskonverter.convertData.normal(n);
	}
	teile = teile.replace(/\s+$/, '') + ', ';
	pos = teile.search(/ (?:l|du|van)/i); //la/le, du, van, etc. nach vorne
	return pos === -1 ?
		teile + teile + teile :
		teile.slice(pos) + teile + teile + teile.slice(0, pos);
},
italienisch: function (n, pd) { //setzt Präpositionen/... für italienische Sprache
	if (/18\d\d|19\d\d|2\d\d\d|(?:(?:19|2\d)\. Jahrhundert)/.test(pd.geburtsdatum)) {
		return Namenskonverter.convertData.vorne(n);
	}
	var teile = Namenskonverter.namensteile(n);
	if (teile === '') {
		return Namenskonverter.convertData.normal(n);
	}
	if ((/ d/i).test(teile)) {
		return teile.replace(/\s+$/, '') + ', ' + teile + teile; //de etc. vor 19. Jh. hinten
	} else {
		return teile + teile.replace(/\s+$/, '') + ', ' + teile; //sonst vorne
	}
},
rumaenisch: function (n) { //setzt Präpositionen/... für rumänische Sprache
	var teile = Namenskonverter.namensteile(n);
	if (teile === '') {
		return Namenskonverter.convertData.normal(n);
	}
	if (/ de /.test(teile)) {
		return teile.replace(/\s+$/, '') + ', ' + teile + teile; //de hinten
	} else {
		return teile + teile.replace(/\s+$/, '') + ', ' + teile; //sonst vorne
	}
},
spanisch: function (n) { //setzt Präpositionen/... für spanische Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile === '') {
		pos = n.search(/  /); //mit e/y verbundene Nachnamen
		if (pos !== -1) {
			pos = n.lastIndexOf(' ', pos - 1);
		}
		if (pos === -1) { //zwei Nachnamen?
			pos = n.search(/ \S+ \S+\s*$/);
		}
		return pos === -1 ?
			Namenskonverter.convertData.normal(n) :
			n.slice(pos).replace(/\s+$/, '') + ', ' + n.slice(0, pos);
	}
	if ((/ la/i).test(teile)) {
		return teile + teile.replace(/\s+$/, '') + ', ' + teile; //las, la vorne
	} else {
		return teile.replace(/\s+$/, '') + ', ' + teile + teile; //sonst hinten
	}
},
chilenisch: function (n) {
	//setzt Präpositionen/... für Chilenen (wie hinten, aber mit spanisch als Fallback für zwei Nachnamen
	var teile = Namenskonverter.namensteile(n);
	return teile === '' ?
		Namenskonverter.convertData.spanisch(n) :
		teile.replace(/\s+$/, '') + ', ' + teile + teile;
},

arabisch: function (n) {
	return Namenskonverter.convertData.verwendung(n) //nach Verwendung
		.replace(/^()(.*)$/, '$2, a$1') //Artikel nach hinten
		.replace(/,(.*),/, ',$1'); //doppeltes Komma korrigieren
},
chinesisch: function (n) { //TODO: sehr obskur
	var traditional = true;
	if (/^\S+(?:y|ie) /.test(n)) { //Eddie, Jacky, etc. sind nicht traditionell
		traditional = false;
	} else if (/^\S{5,}/.test(n)) { //lange Namen deuten auf nicht traditionell
		traditional = false;
	}
	if (n.indexOf('yu') > -1) {
		traditional = true;
	}
	if (traditional) {
		return Namenskonverter.convertData.kommaEinfuegen(n);
	} else {
		return Namenskonverter.convertData.normal(n);
	}
},
roemisch: function (n) {
	var pos = n.search(/ \S+ \S+\s*$/); //Pronomen Gentilnamen Cognomen
	return pos === -1 ?
		Namenskonverter.convertData.normal(n) :
		n.slice(pos).replace(/\s+$/, '') + ', ' + n.slice(0, pos);
},

verwandt: function (n) {
	var vor = n.replace(/ (Mac|Ó|Abu|Fitz|Ben) .*$/, ''),
		nach = n.slice(vor.length);
	return nach + ', ' + vor;
},

verwendung: function (n, pd, text) { //versucht Namen so anzusetzen, wie er im Text am häufigsten vorkommt
//n an Leerzeichen und Bindestrichen aufspalten
	var worte = n.split(/+/), trenner = '\\b*',
		regex, regexe = , i, j,
		treffer, vorkommen = , hk = {}, max = 0, hf = '',
		name = '', rest = n;
//zu regulärem Ausdruck zusammensetzen
	for (i = 0; i < worte.length; i++) {
		if (!worte) {
			continue;
		}
		for (j = i; j < worte.length; j++) {
			if (j === i) {
				regex = mw.util.escapeRegExp(worte) + trenner;
			} else if (j === worte.length - 1) {
				regex += '(?:' + mw.util.escapeRegExp(worte) + ')?';
			} else {
				regex += '(?:' + mw.util.escapeRegExp(worte) + trenner + ')?';
			}
		}
		regexe.push('(?:' + regex + ')');
	}
	if (regexe.length === 0) {
		return n;
	}
	regex = '(' + regexe.join('|') + ')s?\\b';
	regex = new RegExp(regex, 'ig');
//alle Vorkommen suchen, normalisieren, ungültige entfernen
/*jshint boss: true*/
	while (treffer = regex.exec(text)) {
		treffer = treffer;
		treffer = trim(treffer.replace(//g, ' ')).toLowerCase();
		if (treffer.length >= 4) {
			vorkommen.push(treffer);
		}
	}
/*jshint boss: false*/
	if (vorkommen.length === 0) {
		return n;
	}
//häufigste Form ermitteln
	for (i = 0; i < vorkommen.length; i++) {
		if (hk] === undefined) {
			hk] = 1;
		} else {
			hk]++;
		}
	}
	for (i in hk) {
		if (hasOwn.call(hk, i) && hk > max) {
			max = hk;
			hf = i;
		}
	}
//häufigste Form als Nachnamen extrahieren
	hf = hf.split(' ');
	for (i = 0; i < hf.length; i++) {
		regex = '(' + mw.util.escapeRegExp(hf) + trenner + ')';
		regex = new RegExp(regex, 'i');
		treffer = regex.exec(n);
		if (treffer) {
			name += treffer;
			rest = rest.replace(regex, '');
		}
	}
//Rest als Vorname nehmen
	name = trim(name) + ', ' + trim(rest);
	if (name.replace(/,/, '') === n) {
		name = n;
	}
	return name;
}
//virtual outdent
};
Namenskonverter.engine = [
//virtual indent
,
,
,
, //hinter generation, arabisch
,
, //Regel steht nirgends, scheint aber weitläufig gelebte Praxis zu sein
, //auf Vorschlag von ]
,
,
//, //Test
, //wegen Abu nach arabisch, aber bei derzeitiger Implementierung dann hier sinnlos
,
,
,
,
,
,
,

,
, //inkl. Belgien, für alle Sprachen
,
,
,
,
, //außer Belgien, s. o.
,
,
,
,
,
 //eigentlich nur z, ze, Rest nach Ursprungssprache
//virtual outdent
];
Namenskonverter.prototype.run = function (n) {
	var engine = Namenskonverter.engine, i;
	for (i = 0; i < engine.length; i++) {
		if (this.is(engine, n)) {
			return Namenskonverter.vorneGross(this.convert(engine, n));
		}
	}
	return Namenskonverter.vorneGross(this.convert('normal', n));
};

function PersonendatenParser (title, wikitext, autoedit) {
	this.title = title;
	this.wikitext = wikitext;
	this.autoedit = !!autoedit;
	this.events = {};
	this.initFields('originalContent', '');
	this.initFields('currentContent', '');
	this.initFields('suggestions', );
	this.initFields('status', '');
	this.init();
}

PersonendatenParser.FIELDS = [
	'NAME', 'ALTERNATIVNAMEN', 'KURZBESCHREIBUNG', 'GEBURTSDATUM', 'GEBURTSORT', 'STERBEDATUM', 'STERBEORT',
	'SORTIERUNG', 'Staatsangehörigkeit', 'Geboren', 'Gestorben', 'Geschlecht'
];

PersonendatenParser.prototype.initFields = function (name, value) {
	var data = {}, i;
	for (i = 0; i < PersonendatenParser.FIELDS.length; i++) {
		data] = value;
	}
	this = data;
};

PersonendatenParser.prototype.on = function (event, handler) {
	this.events = this.events || {handlers: };
	this.events.handlers.push(handler);
};
PersonendatenParser.prototype.trigger = function (event) {
	var that = this;
	this.events = this.events || {handlers: };
	if (this.events.running) {
		return;
	}
	this.events.running = true;
	setTimeout(function () {
		var i;
		for (i = 0; i < that.events.handlers.length; i++) {
			that.events.handlers();
		}
		that.events.running = false;
	});
};

PersonendatenParser.prototype.getValue = function (name, field) {
	if (PersonendatenParser.FIELDS.indexOf(field) === -1) {
		throw new Error('Unknown "' + field + '"');
	}
	return this;
};
PersonendatenParser.prototype.setValue = function (name, field, value) {
	if (PersonendatenParser.FIELDS.indexOf(field) === -1) {
		throw new Error('Unknown "' + field + '"');
	}
	this = value;
};

PersonendatenParser.prototype.getOriginalContent = function (field) {
	return this.getValue('originalContent', field);
};
PersonendatenParser.prototype.getCurrentContent = function (field) {
	var val = this.getValue('currentContent', field);
	if (this.autoedit) {
		val = this.doAutoedit(field, val);
	}
	return val;
};
PersonendatenParser.prototype.getSuggestions = function (field) {
	return Array.prototype.slice.apply(this.getValue('suggestions', field));
};
PersonendatenParser.prototype.getStatus = function (field) {
	return this.getValue('status', field);
};

PersonendatenParser.prototype.setOriginalContent = function (field, value) {
	this.setValue('originalContent', field, value);
};
PersonendatenParser.prototype.setCurrentContent = function (field, value) {
	if (this.autoedit) {
		value = this.doAutoedit(field, value, true);
	}
	this.setValue('currentContent', field, value);
	return value;
};
PersonendatenParser.prototype.setSuggestions = function (field, value) {
	this.setValue('suggestions', field, value);
	this.trigger('suggestionsChanged');
};
PersonendatenParser.prototype.setStatus = function (field, value) {
	if (value === this.getStatus(field)) {
		return;
	}
	this.setValue('status', field, value);
	this.trigger('statusChanged');
};

PersonendatenParser.prototype.isAutofill = function (field) {
	return .indexOf(field) > -1;
};
PersonendatenParser.prototype.getAutofixer = function (field) {
	function trimUnlink (v) {
		return trim(unlink(v));
	}
	function special (v) {
		return replaceSpecial(trim(v));
	}
	function staaten (v) {
		return trim(v.replace(/_/g, ' ').replace(/\s*\|\s*/g, '|'));
	}
	function kategorie1 (v) {
		return trim(v.replace(/_/g, ' '));
	}
	function kategorie2 (v) {
		v = trim(v.replace(/_/g, ' '));
		return v.charAt(0).toUpperCase() + v.slice(1);
	}
	function noop (v) {
		return v;
	}

	switch (field) {
	case 'NAME':
	case 'ALTERNATIVNAMEN':
	case 'GEBURTSORT':
	case 'STERBEORT':
		return trim;
	case 'KURZBESCHREIBUNG':
	case 'GEBURTSDATUM':
	case 'STERBEDATUM':
		return trimUnlink;
	case 'SORTIERUNG':
		return special;
	case 'Staatsangehörigkeit':
		return staaten;
	case 'Geboren':
	case 'Gestorben':
		return kategorie1;
	case 'Geschlecht':
		return kategorie2;
	default:
		return noop;
	}
};
PersonendatenParser.prototype.doAutoedit = function (field, value, onlyFix) {
	var newValue = '', type;
	if (value === '' && !onlyFix) {
		type = 'autofill';
		if (this.isAutofill(field)) {
			newValue = this.getSuggestions(field) || '';
		}
	} else {
		type = 'autofix';
		newValue = this.getAutofixer(field)(value);
	}
	this.setStatus(field, newValue !== value ? type : '');
	return newValue;
};

PersonendatenParser.prototype.init = function () {
	this.parsePD();
	this.parseKat();
	this.createPD();
	this.copyToCurrentContent();
	this.createKat();
};
PersonendatenParser.prototype.hasPD = function () {
	return !!(
		this.getOriginalContent('NAME') ||
		this.getOriginalContent('ALTERNATIVNAMEN') ||
		this.getOriginalContent('KURZBESCHREIBUNG') ||
		this.getOriginalContent('GEBURTSDATUM') ||
		this.getOriginalContent('GEBURTSORT') ||
		this.getOriginalContent('STERBEDATUM') ||
		this.getOriginalContent('STERBEORT')
	);
};
PersonendatenParser.prototype.parsePD = function () {
	//auch falsche (z. B. nicht beendete) PD finden, \n am Ende erzwingen
	var pd = extrahiere(/(\{\{personendaten(?:.|\n)+)/i, this.wikitext) + '\n',
		name = extrahiere(/\|\s*NAME\s*= *(.*)\n/, pd),
		//falschen ALTERNATIVNAME und mehrzeilige auch übernehmen
		anamen = extrahiere(/\|\s*ALTERNATIVNAMEN?\s*= *(*)\n/, pd).replace(/\n/g, ' '),
		kurz = extrahiere(/\|\s*KURZBESCHREIBUNG\s*= *(.*)\n/, pd),
		gebdatum = extrahiere(/\|\s*GEBURTSDATUM\s*= *(.*)\n/, pd),
		gebort = extrahiere(/\|\s*GEBURTSORT\s*= *(.*)\n/, pd),
		sterbedatum = extrahiere(/\|\s*STERBEDATUM\s*= *(.*)\n/, pd),
		sterbeort = extrahiere(/\|\s*STERBEORT\s*= *(.*)\n/, pd);
	//Autokorrektur auslösen bei nicht normgerechten PD
	if (pd !== '\n' && pd.indexOf('}}') === -1) {
		name += ' ';
	}
	if (pd !== '\n' && pd.indexOf('ALTERNATIVNAMEN') === -1) {
		anamen += ' ';
	}
	sterbeort = sterbeort.replace(/\}\}\s*$/, ' ');
	this.setOriginalContent('NAME', name);
	this.setOriginalContent('ALTERNATIVNAMEN', anamen);
	this.setOriginalContent('KURZBESCHREIBUNG', kurz);
	this.setOriginalContent('GEBURTSDATUM', gebdatum);
	this.setOriginalContent('GEBURTSORT', gebort);
	this.setOriginalContent('STERBEDATUM', sterbedatum);
	this.setOriginalContent('STERBEORT', sterbeort);
};
PersonendatenParser.prototype.parseKat = function () {
	this.setOriginalContent('SORTIERUNG',
		extrahiere(/\{\{(?:SORTIERUNG|DEFAULTSORT):(*)\}\}/, this.wikitext)
	);
	this.setOriginalContent('Staatsangehörigkeit',
		extrahiereG(
			new RegExp('\\\\])', 'gi'),
			this.wikitext
		).join('|')
	);
	this.setOriginalContent('Geboren',
		extrahiere(/\(]*)(?:\||\]\])/i, this.wikitext)
	);
	this.setOriginalContent('Gestorben',
		extrahiere(/\(]*)(?:\||\]\])/i, this.wikitext)
	);
	this.setOriginalContent('Geschlecht',
		extrahiere(/\unbekannt)\s*(?:\||\]\])/i, this.wikitext)
	);
};
PersonendatenParser.prototype.createPD = function () {
	//extrahiert Einleitung aus Text
	function cleanText (text) {
		var pos;
		text = text.replace(/<!--(?:.|\n)*?-->/g, ''); //Kommentare entfernen
		text = text.replace(/\{\|(?:.|\n)*?\|\}/g, ''); //Tabellen (Nicht-Standard-Infoboxen entfernen)
		text = text.replace(/\{\{QS-+\}\}/, ''); //QS-Antrag entfernen etc.
		text = text.replace(/\{\{Löschantragstext+\}\}/, '');
		text = text.replace(/\{\{Redundanztext+\}\}/, '');
		text = text.replace(/\{\{Widerspruch+\}\}/, '');
		text = text.replace(/\{\{Allgemeinverständlichkeit+\}\}/, '');
		text = text.replace(/\{\{Belege(?: fehlen)?+\}\}/, '');
		text = text.replace(/\]*(\]*\]\]]*)*\]\]/g, ''); //Bilder entfernen
		text = text.replace(/<ref*\/\s*>/g, ''); //wiederholte Fußnoten entfernen
		text = text.replace(/<ref*>(?:.|\n)*?<\s*\/\s*ref\s*>/g, ''); //Fußnoten entfernen
		text = text.replace(/\{\{\s*Infobox\b(*\{\{(*\{\{*\}\})**\}\})**\}\}/, '');
			//Infoboxen entfernen TODO 4-fach verschachtelt
		text = text.replace(/\{\{\s*Dieser Artikel\|(*\{\{*\}\})**\}\}/, ''); //"Dieser Artikel" entfernen
		text = text.replace(/\\|]*)(\|]*)?\]\]/g, '$1'); //Daten entlinken

		text = text.replace(/\n=(?:.|\n)*/, ''); //alles nach erster Überschrift weg
		text = text.replace(/^\s+|\s+$/g, ''); //Whitespace entfernen, doppelte Whitespace müssen noch bleiben!
		pos = text.indexOf('\'\'\''); //Zeilen vor erstem Fettdruck ignorieren
		if (pos > -1) {
			pos = text.slice(0, pos).lastIndexOf('\n');
		}
		if (pos > -1) {
			text = text.slice(pos + 1);
		}
		text = text.replace(/\n *\n(?:.|\n)*/, ''); //nur erster Absatz

		text = text.replace(/|&nbsp;|&thinsp;/g, ' ');

		return trim(text);
	}
	//extrahiert alle Namen aus Text
	function getNames (title, text) {
	/*jshint boss: true*///für while (treffer = match)
		var textName = text, namenOhneKomma = ,
			nameOhneKomma = title.replace(/ \(.*$/, ''), //Titel ohne Klammer
			nameOhneZusatz = nameOhneKomma.replace(/ (von|der|aus) .*$/, ''), //Lais von Korinth etc.
			treffer, davor, klammerzusatz, teile, i, re, posOz, posOk, sprache,
			reAnameVorne = '(?:' + [
				'eigentlich\\s*', 'bürgerlich\\s*', 'Pseud(?:onym|\\.):?\\s*',
				'geb(?:\\.|ürtig|oren(?: als|e)?)\\s*',
				'\\*.{0,25}?(?:\\bi\\b*?)?\\bals\\s+'
			].join('|') + ')',
			reAnameHinten = '(?:\\s*(?:Pseud(?:onym|\\.)|Spitzname)])';

		if (nameOhneKomma !== nameOhneZusatz) {
			posOz = text.indexOf('\'\'\'' + nameOhneZusatz + '\'\'\'');
			posOk = text.indexOf('\'\'\'' + nameOhneKomma + '\'\'\'');
			if (posOz !== -1 && (posOk === -1 || posOk > posOz)) {
				nameOhneKomma = nameOhneZusatz;
			}
		}

		namenOhneKomma.push();

		//Ordenskürzel entfernen
		textName = textName.replace(/ \]+\|(?:*|OdeM|FdCC|fj)\]\],?/g, '');

		//Kursiv um (Sprach-)Vorlagen entfernen
		textName = textName.replace(/'''*(\{\{*\}\})'''*/g, '$1');

		re = /'''(.*)'''( \S* )'''(.*)'''/; //'''Vorname''' Ungebräuchlich '''Nachname'''
		treffer = re.exec(textName);
		if (treffer) {
			namenOhneKomma.push( + treffer + treffer, ' (vollständiger Name)']);
			textName = textName.replace(re, '');
		}

		//Vorname "Spitzname" Nachname
		textName = textName.replace(/('''+) (+) (+''')/, '$1 $3, \'\'\'$2 $3 (Spitzname)');

		//fett oder kursiv mit evt. Erklärung
		re = new RegExp('(' + reAnameVorne + '?)\'\'\'*(.*?)\'\'\'*(' + reAnameHinten + '?)', 'g');
		while (treffer = re.exec(textName)) {
			if (/^\]*\]\]$/.test(treffer)) { //Film-/Musik-/... Titel ignorieren
				continue;
			}
			davor = textName.slice(0, treffer.index).replace(/^.*\s+(\S+)\s*$/, '$1'); //Wort vor dem Treffer
			if (davor === 'Band' || davor === 'Bestseller' || davor === 'Buch' || davor === 'Film' ||
				davor === 'Partei' || (/oman$/).test(davor) || davor === 'Zeitschrift' || davor === 'Zeitung') {
				continue;
			}
			klammerzusatz = anameKlammer(treffer + treffer); //nach Erklärung suchen
			if (klammerzusatz === '' && !(/\b\./).test(treffer)) { //abgekürzte Namen sind nicht vollständig
				klammerzusatz = ' (vollständiger Name)';
				teile = nameOhneKomma.split(' ');
				for (i = 0; i < teile.length; i++) {//alle Namensbestandteile enthalten?
					if (treffer.indexOf(teile.replace(/\.$/, '')) === -1) {
						klammerzusatz = '';
					}
				}
			}
			namenOhneKomma.push(, klammerzusatz]);
		}
		textName = textName.replace(re, ''); //anschließend entfernen

		//Alternativnamen im Text
		re = new RegExp ('(' + reAnameVorne + ')(\\S*(?:\\s+\\S*)?)', 'g');
		while (treffer = re.exec(textName)) {
			namenOhneKomma.push(.replace(/*$/, ''), anameKlammer(treffer)]);
		}
		textName = textName.replace(re, ''); //anschließend entfernen

		re = new RegExp ('(\\S*(?:\\s+\\S*)?)(' + reAnameHinten + ')', 'g');
		while (treffer = re.exec(textName)) {
			namenOhneKomma.push(.replace(/*$/, ''), anameKlammer(treffer)]);
		}
		textName = textName.replace(re, ''); //anschließend entfernen

		//Name in Sprachvorlage
		re = /\{\{\s*(?:lang\|)?\s*(+)\s*\|(*)(?:\||\}\})/g; //{{RuS|Russischer Name}}
		while (treffer = re.exec(textName)) {
			sprache = treffer;
			switch (sprache.toLowerCase()) {
			case 'ka': case 'abs': sprache = 'abchasisch'; break;
			case 'am': case 'ams': sprache = 'amharisch'; break;
			case 'ar': case 'arf': case 'ars': sprache = 'arabisch'; break;
			case 'az': case 'azs': sprache = 'aserbaidschanisch'; break;
			case 'be': case 'bes': sprache = 'weißrussisch'; break;
			case 'bg': case 'bgs': sprache = 'bulgarisch'; break;
			case 'cs': case 'css': sprache = 'tschechisch'; break;
			case 'el': case 'elsalt': case 'elsmit': case 'elsneu': case 'grs': sprache = 'griechisch'; break;
			case 'elsalt2': sprache = 'altgriechisch'; break;
			case 'elsmit2': sprache = 'mittelgriechisch'; break;
			case 'elsneu2': sprache = 'neugriechisch'; break;
			case 'en': case 'ens': sprache = 'englisch'; break;
			case 'fa': case 'fas': sprache = 'persisch'; break;
			case 'he': case 'hes': sprache = 'hebräisch'; break;
			case 'ja': case 'jas': sprache = 'japanisch'; break;
			case 'ka': case 'kas': sprache = 'georgisch'; break;
			case 'pl': case 'pls': sprache = 'polnisch'; break;
			case 'ro': case 'ros': sprache = 'rumänisch'; break;
			case 'ru': case 'rus': sprache = 'russisch'; break;
			case 'uk': case 'uks': sprache = 'ukrainisch'; break;
			case 'zh': case 'zhs': case 'zh-hani': //TODO alle Parameter beachten
			case 'zh-hans': case 'zh-hant': sprache = 'chinesisch'; treffer = treffer.replace(/.*=\s*/, ''); break;

			case 'ipa': case 'audio': sprache = 'JULGREGDATUM'; break; //keine Sprachvorlage, wie JULGREGDATUM behandeln
			}
			if (sprache !== 'JULGREGDATUM' && treffer) {
				namenOhneKomma.push(, ' (' + sprache + ')']);
			}
		}

		return namenOhneKomma;
	}
	//extrahiert Informationen aus der "Bio-Klammer"
	function bioKlammer (text) {
		var gebDatum = '', gebOrt = '', sterbeDatum = '', sterbeOrt = '', pos, re, bioklammer, gebText, sterbeText,
			posIst, posWar, dauerzustand, vchr, jh, frueh, jahr;

		bioklammer = extrahiere(
			/(\(*(?:\(*\)*)*(?:|- | -|&dagger;|geb|gest)*(?:\(*\)*)*\))/,
		text); //Klammer mit Lebensdaten
		if (bioklammer === '') {
			bioklammer = extrahiere(/((?:\*|geb\.|geboren|von).*)(?:\bist|\bwar)/, text);
		}
		if (bioklammer === '') {
			bioklammer = extrahiere(/((?:\*|geb\.|geboren|von).*)/, text);
		}
		if (bioklammer !== '') {
			bioklammer = bioklammer.replace(/.*\*/, '*'); //alles vor * ignorieren
			pos = bioklammer.indexOf('†'); //nach Geburts- und Sterbedatum aufteilen
			if (pos === -1) {
				pos = bioklammer.indexOf('&dagger;');
			}
			if (pos === -1) {
				pos = bioklammer.indexOf('+');
			}
			if (pos === -1) {
				pos = bioklammer.indexOf('bis ');
			}
			if (pos === -1) {
				pos = bioklammer.indexOf('–');
			}
			if (pos === -1) {
				pos = bioklammer.indexOf('gest.');
			}
			if (pos === -1) {
				pos = bioklammer.indexOf('gestorben');
			}
			if (pos === -1) {
				pos = bioklammer.indexOf(' -');
			}
			if (pos === -1) {
				pos = bioklammer.indexOf('- ');
			}

			gebText = bioklammer;
			sterbeText = '';
			if (pos !== -1) {
				gebText = bioklammer.slice(0, pos);
				sterbeText = bioklammer.slice(pos);
			}
			re = /\{\{JULGREGDATUM\|\s*(\d\d?\s*\|\s*\d\d?\s*\|\s*\d+)/; //{{JULGREGDATUM|1|2|3456}}
			gebDatum = extrahiere(re, gebText).replace(/\|/g, '. ');
			sterbeDatum = extrahiere(re, sterbeText).replace(/\|/g, '. ');
			//Datum in allen möglichen Formaten
			//TODO: zwischen, oder
			re = new RegExp('((?:(?:' + [
				'um', 'etwa', 'wahrscheinlich', 'vermutlich', 'wohl', 'ca\\.', 'cirka', 'vor', 'nach', 'getauft', 'begraben'
			].join('|') + ')\\s*)?(?:' + [
				'\\d', 'Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'
			].join('|') + ').*(?:' + [
				'\\d', 'Jahrhundert', 'Jahrh\\.', 'Jh\\.', 'Chr\\.'
			].join('|') + '))');
			if (gebDatum === '') {
				gebDatum = extrahiere(re, gebText);
			}
			if (sterbeDatum === '') {
				sterbeDatum = extrahiere(re, sterbeText);
			}

			gebDatum = datumFormat(gebDatum); //ordentlich formatieren
			sterbeDatum = datumFormat(sterbeDatum);

			//TODO: an irgendeinen Standard anpassen
			gebOrt = extrahiere(/\bi\s+(.*?)\s*(?: als .*)?(?:*$)/, gebText); //Ort suchen
			if (gebOrt === '') {
				gebOrt = extrahiere(/\b((?:bei|nahe)\s+.*?)\s*(?: als .*)?(?:*$)/, gebText);
			}
			if (gebOrt === '') {
				gebOrt = extrahiere(/\b(auf\s+.*?)\s*(?: als .*)?(?:*$)/, gebText);
			}
			gebOrt = ortFormat(gebOrt);
			sterbeOrt = extrahiere(/\bi\s+(.*?)\s*(?:*$)/, sterbeText);
			if (sterbeOrt === '') {
				sterbeOrt = extrahiere(/\b((?:bei|nahe)\s+.*?)\s*(?:*$)/, sterbeText);
			}
			if (sterbeOrt === '') {
				sterbeOrt = extrahiere(/\b(auf\s+.*?)\s*(?:*$)/, sterbeText);
			}
			sterbeOrt = ortFormat(sterbeOrt);

			if (sterbeOrt !== '' && unlink(gebOrt).indexOf(unlink(sterbeOrt)) === 0) { //sterbeOrt ist Kurzform von gebOrt
				sterbeOrt = gebOrt;
			}
			if (/\beb(?:en)d/.test(sterbeText)) { //ebenda etc.
				sterbeOrt = gebOrt;
			}
		}

		if (gebDatum === '' && sterbeDatum === '') { //kein Datum gefunden, aber laut Beschreibung aktuell
			posIst = text.indexOf(' ist ');
			posWar = text.indexOf(' war ');
			dauerzustand = text.indexOf('Heilige') + 1 + text.indexOf('Märtyrer') + 1;
			if (
				((posIst > -1) && //Person "ist" noch jemand und
				((posWar === -1) || //"war" noch nie jemand oder
				(posIst < posWar))) && //"ist" erst und "war" und
				dauerzustand === 0 //ist nichts, was man auch nach dem Tod ist
			) {
				gebDatum = '20. Jahrhundert';
			}
		}
		if (gebDatum === '' && sterbeDatum !== '') { //geboren unbekannt, aber gestorben
			vchr = (sterbeDatum.indexOf('v. Chr.') > -1); //v. Chr.?
			frueh = false; //in ersten 20 Jahren gestorben?
			jh = extrahiere(/(\d\d?)\. Jahrhundert/, sterbeDatum);
			if (jh === '') {
				jahr = extrahiere(/(\d\d\d\d?)/, sterbeDatum);
				if (jahr !== '') {
					jh = Math.floor(jahr / 100) + 1; //Jahrhundert
					jahr %= 100; //Jahre im Jahrhundert
					if ((!vchr && jahr <= 20) || (vchr && jahr >= 80)) {
						frueh = true;
					}
				}
			}
			if (jh !== '') {
				jh = Number(jh);
				if (frueh) {
					gebDatum = (vchr ? (jh + 1) : (jh - 1)) + '. Jahrhundert' + (vchr ? ' v. Chr.' : '');
				} else {
					gebDatum = (vchr ? (jh + 1) : (jh - 1)) + '. Jahrhundert' + (vchr ? ' v. Chr.' : '') +
						' oder ' + jh + '. Jahrhundert' + (vchr ? ' v. Chr.' : '');
				}
			}
		}
		return ;
	}
	//extrahiert Kurzbeschreibung
	function getDesc (text) {
		var relRegexp = /(, (?:der|die|das) .*$)/,
			kurz = extrahiere(
				/\b(?:ist|war|gilt als) (?:eine?|die|der)?\s*(?:ehemaliger? )?(.*?)(?:\.|$)/,
				text).replace(/\.$/, ''); //bis zum ersten Punkt
		kurz = unlink(trim(kurz)).replace(
			/\bvo (?:\d\d?\. \S+ )?(\d\d\d\d?)\s*(?:bis|)\s*(?:(?:zum\s*)?\d\d?\. \S+ )?(\d\d\d\d?)\s*(.*)$/,
			'$3 ($1–$2)'); //von-bis in Klammer
		if (kurz.indexOf('r der') === 0) { //abgeschnittenes 'einer der ...'
			kurz = 'eine' + kurz;
		}
		if (!(/\d\d\d/).test(extrahiere(relRegexp, kurz))) { //im Relativsatz kein Jahr vorhanden
			kurz = kurz.replace(relRegexp, ''); //dann diesen entfernen
		}
		return kurz;
	}

	var text, namenOhneKomma, bio, i, uniq = , anamen = , name, namenskonverter;

	text = cleanText(this.wikitext);
	namenOhneKomma = getNames(this.title, text);
	bio = bioKlammer(text);

	//nameFormat baut auf anderen Daten auf
	for (i = 0; i < namenOhneKomma.length; i++) {
		name = trim(namenOhneKomma);
		if (uniq.indexOf(name) === -1) {
			uniq.push(name);
		} else {
			namenOhneKomma.splice(i--, 1);
		}
	}

	this.setSuggestions('KURZBESCHREIBUNG', );
	this.setSuggestions('GEBURTSDATUM', )]);
	this.setSuggestions('GEBURTSORT', )]);
	this.setSuggestions('STERBEDATUM', )]);
	this.setSuggestions('STERBEORT', )]);
	namenskonverter = new Namenskonverter(this, text);

	this.setSuggestions('NAME', )]);
	namenskonverter = new Namenskonverter(this, text); //da sich der Name jetzt verändert hat...

	for (i = 1; i < namenOhneKomma.length; i++) {
		name = namenOhneKomma === ' (Pseudonym)' ? //falls kein Pseudonym: ergänzen
			namenOhneKomma :
			vollerName(namenOhneKomma, namenOhneKomma);
		anamen.push(namenskonverter.run(name) + namenOhneKomma);
	}
	this.setSuggestions('ALTERNATIVNAMEN', );
};
PersonendatenParser.prototype.copyToCurrentContent = function () {
	var that = this, sourceF = this.hasPD() ? function (field) {
		return that.getOriginalContent(field);
	} : function (field) {
		return that.getSuggestions(field) || '';
	};

	this.setCurrentContent('NAME', sourceF('NAME'));
	this.setCurrentContent('ALTERNATIVNAMEN', sourceF('ALTERNATIVNAMEN'));
	this.setCurrentContent('KURZBESCHREIBUNG', sourceF('KURZBESCHREIBUNG'));
	this.setCurrentContent('GEBURTSDATUM', sourceF('GEBURTSDATUM'));
	this.setCurrentContent('GEBURTSORT', sourceF('GEBURTSORT'));
	this.setCurrentContent('STERBEDATUM', sourceF('STERBEDATUM'));
	this.setCurrentContent('STERBEORT', sourceF('STERBEORT'));
	this.setCurrentContent('SORTIERUNG', this.getOriginalContent('SORTIERUNG'));
	this.setCurrentContent('Staatsangehörigkeit', this.getOriginalContent('Staatsangehörigkeit'));
	this.setCurrentContent('Geboren', this.getOriginalContent('Geboren'));
	this.setCurrentContent('Gestorben', this.getOriginalContent('Gestorben'));
	this.setCurrentContent('Geschlecht', this.getOriginalContent('Geschlecht'));
};
PersonendatenParser.prototype.createKat = function () {
	var sortierung = this.getAutofixer('SORTIERUNG')(this.getCurrentContent('NAME')), //SORTIERUNG wie NAME
		staaten = (new StaatenManager(this)).getStaatskats(),
		geboren = jahresKat(this.getCurrentContent('GEBURTSDATUM')),
		gestorben = jahresKat(this.getCurrentContent('STERBEDATUM')),
		geschlecht = /^\S*\s*\S*(?:\Sisches?|libysches?|deutsches?|\Siges?|\Sin)\b/.test(
			trim(this.getCurrentContent('KURZBESCHREIBUNG'))
		) ? 'Frau' : 'Mann'; //erstes Wort mit weiblicher/sächlicher Endung?
	if (this.title.indexOf(sortierung) === 0) { //keine Sortierung nötig
		sortierung = '';
	}
	this.setSuggestions('SORTIERUNG', );
	this.setSuggestions('Staatsangehörigkeit', );
	this.setSuggestions('Geboren', );
	this.setSuggestions('Gestorben', );
	this.setSuggestions('Geschlecht', );
};

PersonendatenParser.prototype.updateSuggestions = function () {
	this.createKat();
};

PersonendatenParser.prototype.getWikitext = function (wikitext) {
	var i, re, chunks, kat, kats, pd, text = wikitext || this.wikitext;

	function splitForUpdate (text) {
		var commentPD, commentSortierung, re, match, kat, iw, kats = {};
		re = new RegExp('<!--\\s*Bitte\\s+nicht\\s+l(?:oe|ö)schen.?\\s+' +
			'Zur\\s+Erkl(?:ae|ä)rung\\s+siehe\\s+(?:\\\\])?.?\\s*-->\\s*');
		text = text.replace(re, '');
		re = /\n\s*(<!--*-->)\s*\{\{\s*ersonendaten\b/;
		commentPD = extrahiere(re, text);
		text = text.replace(re, '\n{{Personendaten');
		commentSortierung = extrahiere(/\{\{(?:SORTIERUNG|DEFAULTSORT):*\}\}\s*(<!--(?:.|\n)*?-->)/, text);
		re = /\\|]*?)\s*(?:\|(]*))?\]\](?:\s*(<!--(?:.|\n)*?-->))?\s*/gi;
		/*jshint boss: true*/
		while (match = re.exec(text)) {
			kat = match.replace(/+/g, ' ');
			kat = kat.slice(0, 1).toUpperCase() + kat.slice(1);
			if (kat.indexOf('Geboren ') === 0) {
				kat = 'geboren';
			} else if (kat.indexOf('Gestorben ') === 0) {
				kat = 'gestorben';
			} else if (.indexOf(kat) !== -1) {
				kat = 'geschlecht';
			}
			kats = {sort: match, comment: match};
		}
		/*jshint boss: false*/
		text = text.replace(/\{\{\s*ersonendaten\b(?:*\{\{*\}\})**\}\}/, '');
		text = text.replace(/\{\{(?:SORTIERUNG|DEFAULTSORT):*\}\}\s*(?:<!--(?:.|\n)*?-->)?/, '');
		text = text.replace(re, '');

		re = /\n((?:\{2,3}(?:-+)?|simple):]*\]\]|<!--(?:|-|--)*-->|\s)*)$/i;
		iw = extrahiere(re, text).replace(/^\s+|\s+$/g, '');
		text = text.replace(re, '');
		text = text.replace(/\s*$/, '');

		return {
			text: text,
			kats: kats,
			commentPD: commentPD,
			commentSortierung: commentSortierung,
			iw: iw
		};
	}

	function getComment (d) {
		if (!d || !d.comment) {
			return '';
		}
		return ' ' + d.comment;
	}

	re = new RegExp('^(?:' + StaatenManager.getRe() + ')$', 'i');
	pd = '{{Personendaten' +
		'\n|NAME=' + this.getCurrentContent('NAME') +
		'\n|ALTERNATIVNAMEN=' + this.getCurrentContent('ALTERNATIVNAMEN') +
		'\n|KURZBESCHREIBUNG=' + this.getCurrentContent('KURZBESCHREIBUNG') +
		'\n|GEBURTSDATUM=' + this.getCurrentContent('GEBURTSDATUM') +
		'\n|GEBURTSORT=' + this.getCurrentContent('GEBURTSORT') +
		'\n|STERBEDATUM=' + this.getCurrentContent('STERBEDATUM') +
		'\n|STERBEORT=' + this.getCurrentContent('STERBEORT') +
		'\n}}';
	chunks = splitForUpdate(text);
	text = chunks.text;
	text += '\n\n'; //Leerzeile vor SORTIERUNG/Kategorien
	if (chunks.commentPD) {
		pd = chunks.commentPD + '\n' + pd;
	}
	if (this.getCurrentContent('SORTIERUNG')) {
		text += '{{SORTIERUNG:' + this.getCurrentContent('SORTIERUNG') + '}}' +
			(chunks.commentSortierung ? ' ' + chunks.commentSortierung : '') + '\n';
	}
	for (kat in chunks.kats) {
		if (hasOwn.call(chunks.kats, kat)) {
			if (.indexOf(kat) === -1 && !re.test(kat)) {
				text += '[[Kategorie:' + kat +
					(chunks.kats.sort ? '|' + chunks.kats.sort : '') + ']]' +
					(chunks.kats.comment ? ' ' + chunks.kats.comment : '') + '\n';
			}
		}
	}
	kats = this.getCurrentContent('Staatsangehörigkeit').split('|');
	for (i = 0; i < kats.length; i++) {
		if (kats) {
			text += ' + ']]' + getComment(chunks.kats]) + '\n';
		}
	}
	if (this.getCurrentContent('Geboren')) {
		text += ']' +
			getComment(chunks.kats.geboren) + '\n';
	}
	if (this.getCurrentContent('Gestorben')) {
		text += ']' +
			getComment(chunks.kats.gestorben) + '\n';
	}
	if (this.getCurrentContent('Geschlecht')) {
		text += ']' +
			getComment(chunks.kats.geschlecht) + '\n';
	}
	text += '\n'; //Leerzeile vor Personendaten
	text += pd + '\n';
	if (chunks.iw) {
		text += '\n' + chunks.iw;
	}
	return text;
};
/*PersonendatenParser.prototype.getDataForVE = function () {
	function makeCatArray (staaten, geboren, gestorben, geschlecht) {
		var array = staaten.split('|');
		if (geboren) {
			array.push('Geboren ' + geboren);
		}
		if (gestorben) {
			array.push('Gestorben ' + gestorben);
		}
		if (geschlecht) {
			array.push(geschlecht);
		}
		return array;
	}
	return {
		pd: [
			this.getCurrentContent('NAME'),
			this.getCurrentContent('ALTERNATIVNAMEN'),
			this.getCurrentContent('KURZBESCHREIBUNG'),
			this.getCurrentContent('GEBURTSDATUM'),
			this.getCurrentContent('GEBURTSORT'),
			this.getCurrentContent('STERBEDATUM'),
			this.getCurrentContent('STERBEORT')
		],
		defaultsort: this.getCurrentContent('SORTIERUNG'),
		categories: {
			remove: makeCatArray(
				this.getOriginalContent('Staatsangehörigkeit'),
				this.getOriginalContent('Geboren'),
				this.getOriginalContent('Gestorben'),
				this.getOriginalContent('Geschlecht')
			),
			add: makeCatArray(
				this.getCurrentContent('Staatsangehörigkeit'),
				this.getCurrentContent('Geboren'),
				this.getCurrentContent('Gestorben'),
				this.getCurrentContent('Geschlecht')
			)
		}
	};
};*/
PersonendatenParser.prototype.getEditsummary = function () {
	var comments = ;
	if (!this.hasPD()) {
		comments.push(config.commentPDneu);
	} else if (
		this.getOriginalContent('NAME') !== this.getCurrentContent('NAME') ||
		this.getOriginalContent('ALTERNATIVNAMEN') !== this.getCurrentContent('ALTERNATIVNAMEN') ||
		this.getOriginalContent('KURZBESCHREIBUNG') !== this.getCurrentContent('KURZBESCHREIBUNG') ||
		this.getOriginalContent('GEBURTSDATUM') !== this.getCurrentContent('GEBURTSDATUM') ||
		this.getOriginalContent('GEBURTSORT') !== this.getCurrentContent('GEBURTSORT') ||
		this.getOriginalContent('STERBEDATUM') !== this.getCurrentContent('STERBEDATUM') ||
		this.getOriginalContent('STERBEORT') !== this.getCurrentContent('STERBEORT')
	) {
		comments.push(config.commentPDfix);
	}
	if (this.getOriginalContent('SORTIERUNG') !== this.getCurrentContent('SORTIERUNG')) {
		comments.push(config.commentSortfix);
	}
	if (
		this.getOriginalContent('Geboren') !== this.getCurrentContent('Geboren') ||
		this.getOriginalContent('Gestorben') !== this.getCurrentContent('Gestorben') ||
		this.getOriginalContent('Geschlecht') !== this.getCurrentContent('Geschlecht')
	) {
		comments.push(config.commentCatfix);
	}
	if (this.getOriginalContent('Staatsangehörigkeit') !== this.getCurrentContent('Staatsangehörigkeit')) {
		comments.push(config.commentStaatenfix);
	}
	return comments.join(', ');
};
PersonendatenParser.prototype.getMinorflag = function () {
	return this.hasPD();
};

function updateParser (pdParser, tE) {
	var data = tE.getVal();
	pdParser.setCurrentContent('NAME', data.name);
	pdParser.setCurrentContent('ALTERNATIVNAMEN', data.alternativnamen);
	pdParser.setCurrentContent('KURZBESCHREIBUNG', data.kurzbeschreibung);
	pdParser.setCurrentContent('GEBURTSDATUM', data.geburtsdatum);
	pdParser.setCurrentContent('GEBURTSORT', data.geburtsort);
	pdParser.setCurrentContent('STERBEDATUM', data.sterbedatum);
	pdParser.setCurrentContent('STERBEORT', data.sterbeort);
	pdParser.setCurrentContent('SORTIERUNG', data.sortierung);
	pdParser.setCurrentContent('Staatsangehörigkeit', data.staaten);
	pdParser.setCurrentContent('Geboren', data.geboren);
	pdParser.setCurrentContent('Gestorben', data.gestorben);
	pdParser.setCurrentContent('Geschlecht', data.geschlecht);
}

//Interface initialisieren
function addInterface (tE, pdParser) {
	var url;
	function redoKatWrapper () {
		updateParser(pdParser, tE);
		pdParser.updateSuggestions();
		tE.setSuggestions('sortierung', pdParser.getSuggestions('SORTIERUNG'));
		tE.setSuggestions('staaten', pdParser.getSuggestions('Staatsangehörigkeit'));
		tE.setSuggestions('geboren', pdParser.getSuggestions('Geboren'));
		tE.setSuggestions('gestorben', pdParser.getSuggestions('Gestorben'));
		tE.setSuggestions('geschlecht', pdParser.getSuggestions('Geschlecht'));
	}
	function makeTEInput (field, text) {
		return {
			text: text || field,
			val: pdParser.getCurrentContent(field),
			autofill: pdParser.isAutofill(field),
			autocorr: pdParser.getAutofixer(field)
		};
	}
	tE.addInput('name', makeTEInput('NAME'));
	tE.addInput('alternativnamen', makeTEInput('ALTERNATIVNAMEN'));
	tE.addInput('kurzbeschreibung', makeTEInput('KURZBESCHREIBUNG'));
	tE.addInput('geburtsdatum', makeTEInput('GEBURTSDATUM'));
	tE.addInput('geburtsort', makeTEInput('GEBURTSORT'));
	tE.addInput('sterbedatum', makeTEInput('STERBEDATUM'));
	tE.addInput('sterbeort', makeTEInput('STERBEORT'));

	tE.addInput('sortierung', makeTEInput('SORTIERUNG', '<code>{{SORTIERUNG}}</code>'));
	tE.addInput('staaten', makeTEInput('Staatsangehörigkeit'));
	tE.addInput('geboren', makeTEInput('Geboren', 'Kategorie:Geboren'));
	tE.addInput('gestorben', makeTEInput('Gestorben', 'Kategorie:Gestorben'));
	tE.addInput('geschlecht', makeTEInput('Geschlecht', 'Geschlecht: Kategorie:'));

	tE.get$('sortierung').addClass('noime');
	tE.get$('geboren').addClass('noime');
	tE.get$('gestorben').addClass('noime');
	tE.get$('geschlecht').addClass('noime');

	tE.setSuggestions('name', pdParser.getSuggestions('NAME'));
	tE.setSuggestions('alternativnamen', pdParser.getSuggestions('ALTERNATIVNAMEN'));
	tE.setSuggestions('kurzbeschreibung', pdParser.getSuggestions('KURZBESCHREIBUNG'));
	tE.setSuggestions('geburtsdatum', pdParser.getSuggestions('GEBURTSDATUM'));
	tE.setSuggestions('geburtsort', pdParser.getSuggestions('GEBURTSORT'));
	tE.setSuggestions('sterbedatum', pdParser.getSuggestions('STERBEDATUM'));
	tE.setSuggestions('sterbeort', pdParser.getSuggestions('STERBEORT'));

	tE.setSuggestions('sortierung', pdParser.getSuggestions('SORTIERUNG'));
	tE.setSuggestions('staaten', pdParser.getSuggestions('Staatsangehörigkeit'));
	tE.setSuggestions('geboren', pdParser.getSuggestions('Geboren'));
	tE.setSuggestions('gestorben', pdParser.getSuggestions('Gestorben'));
	tE.setSuggestions('geschlecht', pdParser.getSuggestions('Geschlecht'));

	tE.get$('name').trigger('focus').on('change', redoKatWrapper);
	tE.get$('kurzbeschreibung').on('change', redoKatWrapper);
	tE.get$('geburtsdatum').on('change', redoKatWrapper);
	tE.get$('sterbedatum').on('change', redoKatWrapper);

	if (config.dontShowFootlinks) {
		return;
	}

	url = mw.config.get('wgScript') + '?title=Benutzer_Diskussion:Schnark/js/personendaten.js' +
		'&action=edit&section=new&preloadtitle=%5B%5B' + encodeURIComponent(mw.config.get('wgTitle')) + '%5D%5D';
	tE.addFootitem(mw.html.element('a',
		{href: url, title: mw.msg('schnark-pd-bug'), target: '_blank', rel: 'noopener'}, tE.getVersion(true)));

	url = 'https://de.wikipedia.orghttps://wikifreehand.com/de/Hilfe:Personendaten';
	tE.addFootitem(mw.html.element('a',
		{href: url, title: mw.msg('schnark-pd-help-title'), target: '_blank', rel: 'noopener'}, mw.msg('schnark-pd-help')));

	url = 'https://tools.wmflabs.org/checkpersondata/cgi-bin/pd.cgi?view=detail&pageid=' + mw.config.get('wgArticleId');
	tE.addFootitem(mw.html.element('a',
		{href: url, title: mw.msg('schnark-pd-check-title'), target: '_blank', rel: 'noopener'}, mw.msg('schnark-pd-check')));
}

//Anbindung an templateEditor
function waitForTE (f, ve) {
	var load = true;
	function f2 (tE) {
		load = false;
		mw.hook('userjs.load-script.templateEditor').remove(f2);
		f(tE, ve);
	}
	mw.hook('userjs.load-script.templateEditor').add(f2);
	if (load) {
		//</nowiki>]<nowiki>
		mw.loader.load('https://de.wikipedia.org/w/index.php?title=Benutzer:Schnark/js/templateEditor.js' +
			'&action=raw&ctype=text/javascript');
	}
}

function register (tE, ve) {
	if (!teObject) {
		teObject = tE('Benutzer:Schnark/js/personendaten.js', 'pd', version, {
			onStart: onStart,
			onReady: onReady,
			onFinish: onFinish
		});
	}
	if (teObject) {
		if (ve) {
			$.when(mw.loader.using('mediawiki.util'), $.ready).then(addLinkForVE);
		} else {
			mw.hook('wikipage.content').add(makeButton);
		}
	}
}

function start () {
	$('#pdeditbutton').remove();
	teObject.start({headline: mw.msg('schnark-pd-headline')});
}

function onStart () {
	$('#pdeditbutton').remove();
	return 0;
}

function onReady () {
	pdParser = new PersonendatenParser(teObject.getTitle(), teObject.getText());
	addInterface(teObject, pdParser);
}

function onFinish () {
	var comment;
	updateParser(pdParser, teObject);
	comment = pdParser.getEditsummary();
	if (comment) {
		teObject.addComment(comment);
	}
	if (!pdParser.getMinorflag()) {
		teObject.setNotMinor();
	}
	teObject.setText(pdParser.getWikitext(teObject.getText()));
}

/* API:
	text: Wikitext
	title: Titel der Seite
	NAME, etc.: Funktionen, werden mit altem Wert und neuem Vorschlag aufgerufen, liefern neuen Wert zurück
	Rückgabe: neuer Wikitext
*/
function api (
	text, title,
	NAME, ALTERNATIVNAMEN, KURZBESCHREIBUNG, GEBURTSDATUM, GEBURTSORT, STERBEDATUM, STERBEORT,
	SORTIERUNG, Staaten, Geboren, Gestorben, Geschlecht
) {
	var pdParser = new PersonendatenParser(title, text, true);
	function getAndSet (field, f) {
		pdParser.setCurrentContent(field, f(pdParser.getCurrentContent(field), pdParser.getSuggestions(field) || ''));
	}
	getAndSet('NAME', NAME);
	getAndSet('ALTERNATIVNAMEN', ALTERNATIVNAMEN);
	getAndSet('KURZBESCHREIBUNG', KURZBESCHREIBUNG);
	getAndSet('GEBURTSDATUM', GEBURTSDATUM);
	getAndSet('GEBURTSORT', GEBURTSORT);
	getAndSet('STERBEDATUM', STERBEDATUM);
	getAndSet('STERBEORT', STERBEORT);
	pdParser.updateSuggestions();
	getAndSet('SORTIERUNG', SORTIERUNG);
	getAndSet('Staatsangehörigkeit', Staaten);
	getAndSet('Geboren', Geboren);
	getAndSet('Gestorben', Gestorben);
	getAndSet('Geschlecht', Geschlecht);
	return pdParser.getWikitext();
}

//initialisieren
function makeButton ($content) {
	if ($content.attr('id') !== 'mw-content-text') {
		return;
	}
	$('#pdeditbutton').remove();

	var $where, text, $pd, kats, i, kat;

	$pd = $content.find('#Vorlage_Personendaten');
	if ($pd.length === 1) {
		$where = $pd.show().find('th');
		text = mw.msg('schnark-pd-button-edit');
	} else { //TODO: bessere Erkennung
		kats = mw.config.get('wgCategories');
		for (i = 0; i < kats.length; i++) {
			kat = kats;
			if (
				kat === 'Mann' || kat === 'Frau' || kat === 'Geschlecht unbekannt' ||
				kat.indexOf('Geboren ') === 0 || kat.indexOf('Gestorben ') === 0
			) {
				start();
				break;
			}
		}
		if (kats.length === 0 || (kats.length === 1 && kats === 'Wikipedia:Qualitätssicherung')) {
		//keine Kategorien oder nur QS-Kat.
			$where = $('#firstHeading'); //TODO mw.hook('wikipage.title').add(...)
			text = mw.msg('schnark-pd-button-create');
		}
	}
	if ($where && $where.length) {
		$where.append(mw.html.element('input',
			{id: 'pdeditbutton', type: 'button', value: text, 'class': 'noprint'})
		).find('#pdeditbutton').on('click', start);
	}
}

function extendVE () {
	if (ve.ui.contextItemFactory.lookup('personendaten')) {
		return;
	}

	function PersonendatenTemplateContextItem () {
		PersonendatenTemplateContextItem.parent.apply(this, arguments);
	}

	OO.inheritClass(PersonendatenTemplateContextItem, ve.ui.MWTransclusionContextItem);

	PersonendatenTemplateContextItem.static.name = 'personendaten';
	PersonendatenTemplateContextItem.static.icon = 'userAvatar';
	PersonendatenTemplateContextItem.static.label = mw.msg('schnark-pd-ve-label');
	PersonendatenTemplateContextItem.static.isCompatibleWith = function (model) {
		var rawTitle, title;
		if (!PersonendatenTemplateContextItem.parent.static.isCompatibleWith.apply(this, arguments)) {
			return false;
		}
		rawTitle = ve.getProp(model.getAttribute('mw'), 'parts', 0, 'template', 'target', 'wt') || '';
		title = mw.Title.newFromText(rawTitle.trim(), 10);
		return !!title && title.getRelativeText(10) === 'Personendaten';
	};

	PersonendatenTemplateContextItem.prototype.renderBody = function () {
		var editButton = new OO.ui.ButtonWidget({
				label: mw.msg('schnark-pd-ve-edit')
			}).on('click', function () {
				start();
			});
		PersonendatenTemplateContextItem.parent.prototype.renderBody.apply(this, arguments);
		this.$body.append(editButton.$element);
	};

	ve.ui.contextItemFactory.register(PersonendatenTemplateContextItem);
}

function addLinkForVE () {
	$('#pdeditbutton-ve').remove();
	$(mw.util.addPortletLink(
		'p-tb', '#', mw.msg('schnark-pd-button-ve'), 'pdeditbutton-ve',
		mw.msg('schnark-pd-button-ve-title')
	)).on('click', function (e) {
		e.preventDefault();
		start();
	});
	mw.loader.using().then(extendVE);
}

function init (ve) {
	if (config.onlyApi) {
		return;
	}
	var title = mw.config.get('wgPageName');
	if (
		ve || (
			//Artikel existiert
			mw.config.get('wgArticleId') !== 0 &&
			//nur beim Betrachten
			mw.config.get('wgAction') === 'view' &&
			//nur im ANR
			mw.config.get('wgNamespaceNumber') === 0 &&
			//aktuelle Version
			window.location.search.indexOf('oldid') === -1 &&
			//kein personenähnlicher Artikel
			title !== 'Mann' && title !== 'Frau' && title.indexOf('Nekrolog_') !== 0
		)
	) {
		mw.loader.using('mediawiki.util').then(function () {
			waitForTE(register, ve);
		});
	}
}

mw.hook('userjs.load-script.personendaten').fire(config);
libs.personendaten = {
	version: version,
	start: start,
	api: api,
	PersonendatenParser: PersonendatenParser
};

init();
mw.hook('ve.activationComplete').add(function () {
	if (!ve.init.target.getSurface().isReadOnly()) {
		init(true);
	}
});
mw.hook('ve.deactivationComplete').add(function () {
	$('#pdeditbutton-ve').remove();
});

})(jQuery, mediaWiki, mediaWiki.libs);
//</nowiki>