Benutzer:Schnark/js/personendaten.js

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

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
//Dokumentation unter [[Benutzer:Schnark/js/personendaten]] <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(/^[\s\u200E\u200F]+|[\s\u200E\u200F]+$/g, '').replace(/\s+/g, ' ');
}
function unlink (s) { //entlinkt einen Text
	return s.replace(/\[\[[^\]|]*\|([^\]]*)\]\]/g, '$1').replace(/\[\[/g, '').replace(/\]\]/g, '');
}
function extrahiere (regex, text) { //liefert erste Klammer des regulären Ausdrucks
	var match = regex.exec(text);
	return match === null ? '' : match[1];
}
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[1]) {
			ret.push(match[1]);
		}
	}
	/*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;
}
//[[Kölner Phonetik]]
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[i] === phon[i + 1]) {
			phon[i] = '';
		}
	}
	phon = phon.join('').split('');
	for (i = 1; i < phon.length; i++) {
		if (phon[i] === '0') {
			phon[i] = '';
		}
	}
	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[i]);
		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[index] = 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\.) ([a-zA-Zä]+ .*)$/, '$1 $3 oder $2 $3')
		//1. Januar oder 1. Februar 2000 -> 1. Januar 2000 oder 1. Februar 2000
		.replace(/(\d\. [a-zA-Zä]+) oder (\d?\d\. [a-zA-Zä]+) (.*)$/, '$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(/[DŽDžDZDz]/g, 'Dz').replace(/[dždz]/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(/[LJLj]/g, 'Lj').replace(/lj/g, 'lg')
		.replace(/[ḾṀṂ]/g, 'M').replace(/[ḿṁṃ]/g, 'm')
		.replace(/[ÑŃŅŇŊƝǸȠṄṆṈṊ]/g, 'N').replace(/[ñńņňʼnŋƞǹȵṅṇṉṋ]/g, 'n')
		.replace(/[NJNj]/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[0-6]|19[01]\d|1[0-8]\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 (/^[\-a-zA-Z ’'.0-9]+$/).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: ['myanmarisch', 'birmanisch', 'burmesisch'], 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: ['weißrussisch', 'belarussisch'], 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[i].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[i].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[j], this.pd.kurzbeschreibung);
				if (pos > -1) {
					break;
				}
			}
		} else {
			pos = posAdjektiv(adj, this.pd.kurzbeschreibung);
		}
		if (pos > -1) {
			staaten.push($.extend({}, StaatenManager.staatenListe[i], {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[0] === '') { //Teste: geb < range[1]
			if (geb === '') { //zeitlos, also wohl älter
				return true;
			}
			geb = makeYear(geb);
			return geb <= Number(range[1]);
		} else if (range[1] === '') { //Teste: range[0] < gest
			if (geb === '' && gest === '') { //zeitlos, also wohl älter
				return false;
			}
			if (gest === '') { //lebt noch
				return true;
			}
			gest = makeYear(gest);
			return Number(range[0]) <= gest;
		} else { //Teste: (geb < range[0] && range[0] < gest) || (range[0] < geb && geb < range[1])
			if (geb === '' && gest === '') { //zeitlos, also wohl älter
				return false;
			}
			if (geb === '') {
				geb = Number(range[0]);
			} else {
				geb = makeYear(geb);
			}
			if (gest === '') {
				gest = Number(range[0]);
			} else {
				gest = makeYear(gest);
			}
			return (geb <= Number(range[0]) && Number(range[0]) <= gest) ||
				(Number(range[0]) <= geb && geb <= Number(range[1]));
		}
	}
	function getKatsByRange (kats, geb, gest) {
		var k, ret = [];
		for (k in kats) {
			if (hasOwn.call(kats, k)) {
				if (checkRange(kats[k], 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[i]);
			}
		} else {
			if (kats.indexOf(k) === -1) {
				kats.push(k);
			}
		}
	}
	for (i = 0; i < staaten.length; i++) {
		kat = staaten[i].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[i].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')[0] || '',
		kurzbeschreibung: pdParser.getSuggestions('KURZBESCHREIBUNG')[0] || '',
		geburtsdatum: pdParser.getSuggestions('GEBURTSDATUM')[0] || '',
		sterbedatum: pdParser.getSuggestions('STERBEDATUM')[0] || ''
	};
	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[test];
	if (typeof f === 'string') {
		return f === this.getSprache();
	}
	return f(n, this.pd, this.text);
};
Namenskonverter.prototype.convert = function (type, n) {
	return Namenskonverter.convertData[type](n, this.pd, this.text, this);
};
//liefert ein Array zurück, es ist n === ar[0] + ar[1] + ar[2],
//ar[1] enthält nur Präpositionen, Artikel u. Ä. (inkl. führender und folgender Leerzeichen)
Namenskonverter.namensteile = function (n) {
	var expr = ' (?:[vV][ao][nm](?: ?[dD]e[nr])? |[zZ]u[mr]? (?:[dD]e[nr] )?|' +
			'[zZ]e? |[dD]e?[’\']|[dD]e(?:[g ]l?i)? |[dD][aiu] |[dD]os? |' +
			'[lL]as |[lL][’\']|[lL][ae] |[tT]en |[aA]f |[aA]u[sf] (?:[dD]er )?|' +
			'[iI]n (?:der )?|[oO]p [dD]e |[aA] )',
		pos = n.search(new RegExp(expr)), //erste Präposition/...
		vor;
	if (pos === -1) {
		return [n, '', ''];
	}
	vor = n.slice(0, pos);
	n = n.slice(pos);
	pos = (new RegExp('(.*' + expr + ')')).exec(n); //letzte Präposition/...
	pos = pos[1].length;
	return [vor, n.slice(0, pos), n.slice(pos)];
};
//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[5-9]|1[0-3])\d\d/) > -1 || //gestorben 500 bis 1399
		pd.sterbedatum.search(/(?:\b[6-9]|1[0-4])\. 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|[sS]en(?:ior|\.)|[sS]r\.$|[jJ]un(?:ior|\.)|[jJ]r\.)/).test(n);
},
nummer: function (n) {
	return (/ [IVXL]+\./).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|[aA][lnrstz])[\- ]/).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[0-4]|1[0-8]\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 (/[sS]en(?:ior|\.)|[sS]r\.$/.test(n)) {
		zusatz = ' senior';
	} else if (/[jJ]un(?:ior|\.)|[jJ]r\./.test(n)) {
		zusatz = ' junior';
	}
	return that.run(
		n.replace(/ (d(\.|er|ie) )?(Ältere|Jüngere|[sS]en(?:ior|\.)|[sS]r\.$|[jJ]un(?:ior|\.)|[jJ]r\.)/, '')
	) + zusatz;
},
nummer: function (n, pd, text, that) {
	//Zahlen per Unterstrich am Namen befestigen
	return that.run(n.replace(/ ([IVXL]+\.)/g, '_$1')).replace(/_([IVXL]+\.)/g, ' $1');
},
britischerAdel: function (n) {
	var name = n.split(',');
	return Namenskonverter.convertData.normal(name[0].replace(/^Sir /, '')) + ',' + name[1];
},
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[1] === '' ?
		Namenskonverter.convertData.normal(n) :
		teile[2].replace(/\s+$/, '') + ', ' + teile[0] + teile[1];
},
vorne: function (n) { //setzt Präpositionen/... vor den Nachnamen
	var teile = Namenskonverter.namensteile(n);
	return teile[1] === '' ?
		Namenskonverter.convertData.normal(n) :
		teile[1] + teile[2].replace(/\s+$/, '') + ', ' + teile[0];
},
deutsch: function (n) { //setzt Präpositionen/... für deutsche Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile[1] === '') {
		return Namenskonverter.convertData.normal(n);
	}
	teile[2] = teile[2].replace(/\s+$/, '') + ', ';
	if (teile[1].indexOf(' und ') > -1) { //von und zu immer hinten
		return teile[2] + teile[0] + teile[1];
	}
	pos = teile[1].search(/ (?:[vV]om |[zZ]u[mr] |[dD]os? |[lL]as |[lL][’']|[lL][ae] |[tT]en )/); //vom, etc. nach vorne
	return pos === -1 ?
		teile[2] + teile[0] + teile[1] :
		teile[1].slice(pos) + teile[2] + teile[0] + teile[1].slice(0, pos);
},
skandinavisch: function (n) { //setzt Präpositionen/... für skandinavische Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile[1] === '') {
		return Namenskonverter.convertData.normal(n);
	}
	teile[2] = teile[2].replace(/\s+$/, '') + ', ';
	//romanisches nach vorne
	pos = teile[1].search(/ (?:[dD]e?[’']|[dD]e(?:[g ]l?i)? |[dD][aiu] |[dD]os? |[lL]as |[lL][’']|[lL][ae] )/);
	return pos === -1 ?
		teile[2] + teile[0] + teile[1] :
		teile[1].slice(pos) + teile[2] + teile[0] + teile[1].slice(0, pos);
},
franzoesisch: function (n) { //setzt Präpositionen/... für französische Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile[1] === '') {
		return Namenskonverter.convertData.normal(n);
	}
	teile[2] = teile[2].replace(/\s+$/, '') + ', ';
	pos = teile[1].search(/ (?:l[ae]|du|van)/i); //la/le, du, van, etc. nach vorne
	return pos === -1 ?
		teile[2] + teile[0] + teile[1] :
		teile[1].slice(pos) + teile[2] + teile[0] + teile[1].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[1] === '') {
		return Namenskonverter.convertData.normal(n);
	}
	if ((/ d/i).test(teile[1])) {
		return teile[2].replace(/\s+$/, '') + ', ' + teile[0] + teile[1]; //de etc. vor 19. Jh. hinten
	} else {
		return teile[1] + teile[2].replace(/\s+$/, '') + ', ' + teile[0]; //sonst vorne
	}
},
rumaenisch: function (n) { //setzt Präpositionen/... für rumänische Sprache
	var teile = Namenskonverter.namensteile(n);
	if (teile[1] === '') {
		return Namenskonverter.convertData.normal(n);
	}
	if (/ de /.test(teile[1])) {
		return teile[2].replace(/\s+$/, '') + ', ' + teile[0] + teile[1]; //de hinten
	} else {
		return teile[1] + teile[2].replace(/\s+$/, '') + ', ' + teile[0]; //sonst vorne
	}
},
spanisch: function (n) { //setzt Präpositionen/... für spanische Sprache
	var teile = Namenskonverter.namensteile(n), pos;
	if (teile[1] === '') {
		pos = n.search(/ [eiy] /); //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[1])) {
		return teile[1] + teile[2].replace(/\s+$/, '') + ', ' + teile[0]; //las, la vorne
	} else {
		return teile[2].replace(/\s+$/, '') + ', ' + teile[0] + teile[1]; //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[1] === '' ?
		Namenskonverter.convertData.spanisch(n) :
		teile[2].replace(/\s+$/, '') + ', ' + teile[0] + teile[1];
},

arabisch: function (n) {
	return Namenskonverter.convertData.verwendung(n) //nach Verwendung
		.replace(/^[aA]([lnrstz][\- ])(.*)$/, '$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 (/^[^XY]\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[i]) {
			continue;
		}
		for (j = i; j < worte.length; j++) {
			if (j === i) {
				regex = mw.util.escapeRegExp(worte[j]) + trenner;
			} else if (j === worte.length - 1) {
				regex += '(?:' + mw.util.escapeRegExp(worte[j]) + ')?';
			} else {
				regex += '(?:' + mw.util.escapeRegExp(worte[j]) + 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[1];
		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[vorkommen[i]] === undefined) {
			hk[vorkommen[i]] = 1;
		} else {
			hk[vorkommen[i]]++;
		}
	}
	for (i in hk) {
		if (hasOwn.call(hk, i) && hk[i] > max) {
			max = hk[i];
			hf = i;
		}
	}
//häufigste Form als Nachnamen extrahieren
	hf = hf.split(' ');
	for (i = 0; i < hf.length; i++) {
		regex = '(' + mw.util.escapeRegExp(hf[i]) + trenner + ')';
		regex = new RegExp(regex, 'i');
		treffer = regex.exec(n);
		if (treffer) {
			name += treffer[1];
			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
['generation', 'generation'],
['nummer', 'nummer'],
['arabisch', 'arabisch'],
['vollername', 'vollername'], //hinter generation, arabisch
['fremdalphabet', 'ohneAenderung'],
['britischerAdel', 'britischerAdel'], //Regel steht nirgends, scheint aber weitläufig gelebte Praxis zu sein
['ducearletc', 'normal'], //auf Vorschlag von [[Benutzerin:Silewe]]
['roemisch', 'roemisch'],
['mittelalter', 'ohneAenderung'],
//['spaetmittelalter', 'verwendung'], //Test
['verwandt', 'verwandt'], //wegen Abu nach arabisch, aber bei derzeitiger Implementierung dann hier sinnlos
['islaendisch', 'ohneAenderung'],
['osmanisch', 'ohneAenderung'],
['koreanisch', 'kommaEinfuegen'],
['japanischVorShowa', 'kommaEinfuegen'],
['japanisch', 'normal'],
['thailaendisch', 'ohneAenderung'],
['chinesisch', 'chinesisch'],

['deutsch', 'deutsch'],
['luxemburgisch', 'vorne'], //inkl. Belgien, für alle Sprachen
['englisch', 'vorne'],
['skandinavisch', 'skandinavisch'],
['franzoesisch', 'franzoesisch'],
['italienisch', 'italienisch'],
['niederlaendisch', 'hinten'], //außer Belgien, s. o.
['portugiesisch', 'hinten'],
['rumaenisch', 'rumaenisch'],
['russisch', 'vorne'],
['spanisch', 'spanisch'],
['chilenisch', 'chilenisch'],
['tschechisch', 'hinten'] //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[i][0], n)) {
			return Namenskonverter.vorneGross(this.convert(engine[i][1], 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[PersonendatenParser.FIELDS[i]] = value;
	}
	this[name] = data;
};

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

PersonendatenParser.prototype.getValue = function (name, field) {
	if (PersonendatenParser.FIELDS.indexOf(field) === -1) {
		throw new Error('Unknown "' + field + '"');
	}
	return this[name][field];
};
PersonendatenParser.prototype.setValue = function (name, field, value) {
	if (PersonendatenParser.FIELDS.indexOf(field) === -1) {
		throw new Error('Unknown "' + field + '"');
	}
	this[name][field] = 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 ['NAME', 'KURZBESCHREIBUNG', 'SORTIERUNG', 'Geboren', 'Gestorben', 'Geschlecht'].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)[0] || '';
		}
	} 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('\\[\\[(?:kategorie|category):\\s*(' + StaatenManager.getRe() + ')\\s*(?:\\||\\]\\])', 'gi'),
			this.wikitext
		).join('|')
	);
	this.setOriginalContent('Geboren',
		extrahiere(/\[\[(?:kategorie|category):\s*geboren[ _]([^|\]]*)(?:\||\]\])/i, this.wikitext)
	);
	this.setOriginalContent('Gestorben',
		extrahiere(/\[\[(?:kategorie|category):\s*gestorben[ _]([^|\]]*)(?:\||\]\])/i, this.wikitext)
	);
	this.setOriginalContent('Geschlecht',
		extrahiere(/\[\[(?:kategorie|category):\s*(mann|frau|geschlecht[ _]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(/\[\[(Datei|File|Bild|Image):[^\[\]]*(\[\[[^\[\]]*\]\][^\[\]]*)*\]\]/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(/\[\[\s*(\d[^\]\|]*)(\|[^\]]*)?\]\]/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(/[\u0000-\u001F\u2000-\u200F\u2028-\u202F]|&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[nm]\\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([nameOhneKomma, '']);

		//Ordenskürzel entfernen
		textName = textName.replace(/ \[\[[^|\]]+\|(?:[A-Z][A-Z][A-Za-z]*|OdeM|FdCC|fj)\]\],?/g, '');

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

		re = /'''(.*)'''( [A-ZÄÖÜ]\S* )'''(.*)'''/; //'''Vorname''' Ungebräuchlich '''Nachname'''
		treffer = re.exec(textName);
		if (treffer) {
			namenOhneKomma.push([treffer[1] + treffer[2] + treffer[3], ' (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[2])) { //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' || (/[rR]oman$/).test(davor) || davor === 'Zeitschrift' || davor === 'Zeitung') {
				continue;
			}
			klammerzusatz = anameKlammer(treffer[1] + treffer[3]); //nach Erklärung suchen
			if (klammerzusatz === '' && !(/\b[A-ZÄÖÜ]\./).test(treffer[2])) { //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[2].indexOf(teile[i].replace(/\.$/, '')) === -1) {
						klammerzusatz = '';
					}
				}
			}
			namenOhneKomma.push([treffer[2], klammerzusatz]);
		}
		textName = textName.replace(re, ''); //anschließend entfernen

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

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

		//Name in Sprachvorlage
		re = /\{\{\s*(?:lang\|)?\s*([\-A-Za-z0-9]+)\s*\|([^\|\}]*)(?:\||\}\})/g; //{{RuS|Russischer Name}}
		while (treffer = re.exec(textName)) {
			sprache = treffer[1];
			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[2] = treffer[2].replace(/.*=\s*/, ''); break;

			case 'ipa': case 'audio': sprache = 'JULGREGDATUM'; break; //keine Sprachvorlage, wie JULGREGDATUM behandeln
			}
			if (sprache !== 'JULGREGDATUM' && treffer[2]) {
				namenOhneKomma.push([treffer[2], ' (' + 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[n|m]\s+(.*?)\s*(?: als .*)?(?:[\s.,;)]*$)/, gebText); //Ort suchen
			if (gebOrt === '') {
				gebOrt = extrahiere(/\b((?:bei|nahe)\s+.*?)\s*(?: als .*)?(?:[\s.,;)]*$)/, gebText);
			}
			if (gebOrt === '') {
				gebOrt = extrahiere(/\b(auf\s+.*?)\s*(?: als .*)?(?:[\s.,;)]*$)/, gebText);
			}
			gebOrt = ortFormat(gebOrt);
			sterbeOrt = extrahiere(/\bi[n|m]\s+(.*?)\s*(?:[\s.,;)]*$)/, sterbeText);
			if (sterbeOrt === '') {
				sterbeOrt = extrahiere(/\b((?:bei|nahe)\s+.*?)\s*(?:[\s.,;)]*$)/, sterbeText);
			}
			if (sterbeOrt === '') {
				sterbeOrt = extrahiere(/\b(auf\s+.*?)\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 [gebDatum, gebOrt, sterbeDatum, sterbeOrt];
	}
	//extrahiert Kurzbeschreibung
	function getDesc (text) {
		var relRegexp = /(, (?:der|die|das) .*$)/,
			kurz = extrahiere(
				/\b(?:ist|war|gilt als) (?:eine?|die|der)?\s*(?:ehemaliger? )?(.*?[^0-9A-Z])(?:\.|$)/,
				text).replace(/\.$/, ''); //bis zum ersten Punkt
		kurz = unlink(trim(kurz)).replace(
			/\bvo[nm] (?:\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[i][0]);
		if (uniq.indexOf(name) === -1) {
			uniq.push(name);
		} else {
			namenOhneKomma.splice(i--, 1);
		}
	}

	this.setSuggestions('KURZBESCHREIBUNG', [trim(getDesc(text))]);
	this.setSuggestions('GEBURTSDATUM', [trim(bio[0])]);
	this.setSuggestions('GEBURTSORT', [trim(bio[1])]);
	this.setSuggestions('STERBEDATUM', [trim(bio[2])]);
	this.setSuggestions('STERBEORT', [trim(bio[3])]);
	namenskonverter = new Namenskonverter(this, text);

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

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

	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', [sortierung]);
	this.setSuggestions('Staatsangehörigkeit', [staaten]);
	this.setSuggestions('Geboren', [geboren]);
	this.setSuggestions('Gestorben', [gestorben]);
	this.setSuggestions('Geschlecht', [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+(?:\\[\\[)?Wikipedia:Personendaten(?:\\]\\])?.?\\s*-->\\s*');
		text = text.replace(re, '');
		re = /\n\s*(<!--[^>]*-->)\s*\{\{\s*[Pp]ersonendaten\b/;
		commentPD = extrahiere(re, text);
		text = text.replace(re, '\n{{Personendaten');
		commentSortierung = extrahiere(/\{\{(?:SORTIERUNG|DEFAULTSORT):[^}]*\}\}\s*(<!--(?:.|\n)*?-->)/, text);
		re = /\[\[(?:kategorie|category):\s*([^\]\|]*?)\s*(?:\|([^\]]*))?\]\](?:\s*(<!--(?:.|\n)*?-->))?\s*/gi;
		/*jshint boss: true*/
		while (match = re.exec(text)) {
			kat = match[1].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 (['Mann', 'Frau', 'Geschlecht unbekannt'].indexOf(kat) !== -1) {
				kat = 'geschlecht';
			}
			kats[kat] = {sort: match[2], comment: match[3]};
		}
		/*jshint boss: false*/
		text = text.replace(/\{\{\s*[Pp]ersonendaten\b(?:[^{}]*\{\{[^{}]*\}\})*[^{}]*\}\}/, '');
		text = text.replace(/\{\{(?:SORTIERUNG|DEFAULTSORT):[^}]*\}\}\s*(?:<!--(?:.|\n)*?-->)?/, '');
		text = text.replace(re, '');

		re = /\n((?:\[\[(?:[a-z]{2,3}(?:-[\-a-z]+)?|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 (['geboren', 'gestorben', 'geschlecht'].indexOf(kat) === -1 && !re.test(kat)) {
				text += '[[Kategorie:' + kat +
					(chunks.kats[kat].sort ? '|' + chunks.kats[kat].sort : '') + ']]' +
					(chunks.kats[kat].comment ? ' ' + chunks.kats[kat].comment : '') + '\n';
			}
		}
	}
	kats = this.getCurrentContent('Staatsangehörigkeit').split('|');
	for (i = 0; i < kats.length; i++) {
		if (kats[i]) {
			text += '[[Kategorie:' + kats[i] + ']]' + getComment(chunks.kats[kats[i]]) + '\n';
		}
	}
	if (this.getCurrentContent('Geboren')) {
		text += '[[Kategorie:Geboren ' + this.getCurrentContent('Geboren') + ']]' +
			getComment(chunks.kats.geboren) + '\n';
	}
	if (this.getCurrentContent('Gestorben')) {
		text += '[[Kategorie:Gestorben ' + this.getCurrentContent('Gestorben') + ']]' +
			getComment(chunks.kats.gestorben) + '\n';
	}
	if (this.getCurrentContent('Geschlecht')) {
		text += '[[Kategorie:' + this.getCurrentContent('Geschlecht') + ']]' +
			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.org/wiki/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>[[Benutzer:Schnark/js/templateEditor.js]]<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)[0] || ''));
	}
	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[i];
			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[0] === '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(['ext.visualEditor.mwtransclusion', 'oojs-ui.styles.icons-user']).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>