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/Template]] <nowiki>
/*global mediaWiki*/
(function ($, mw, libs) {
"use strict";

var lastError = '';
function setLastError (error) {
	lastError = error;
}
function getLastError () {
	return lastError;
}

function getInnerRE (template, capturing) {
	var ns, allns = mw.config.get('wgNamespaceIds'),
		templateREs = [],
		templateNameRE = template.replace(/^(.)(.*)$/, function (all, first, rest) {
			var firstLower = first.toLowerCase(), firstUpper = first.toUpperCase();
			if (firstLower !== firstUpper) {
				first = '[' + firstLower + firstUpper + ']';
			}
			return first + rest.replace(/[\s_]+/g, '[\\s_]+');
		}),
		inner = (capturing ? '(' : '(?:') +
			'\\s*(?:<!--(?:[^-]+|-[^-]|--[^>])*-->\\s*)*\\|(?:[^{}]+|\\{[^{]|\\}[^}]|\\{\\{[^{}]*\\}\\})*)';
	function makeCaseInvariant (c) {
		var upper = c.toUpperCase();
		if (c === '_') {
			return '[\\s_]+';
		} else if (c === upper) {
			return c;
		} else {
			return '[' + c + upper + ']';
		}
	}
	for (ns in allns) {
		if (allns[ns] === 10) {
			templateREs.push(ns.replace(/./g, makeCaseInvariant));
		}
	}
	return '\\{\\{\\s*(?:(?:' + templateREs.join('|') + ')\\s*:\\s*)?' + templateNameRE + inner + '\\}\\}';
}
function getRE (template, count) {
	var pre = '';
	if (count > 0) {
		pre = '(?:' + getInnerRE(template, false) + '[\\s\\S]*?){' + count + '}';
	}
	return new RegExp('^([\\s\\S]*?' + pre + ')' + getInnerRE(template, true) + '([\\s\\S]*)$');
}

function stringX (char, count) {
	var ret = '', i;
	for (i = 0; i < count; i++) {
		ret += char;
	}
	return ret;
}
function mostFrequent (array) {
	var occurences = {}, i, item, maxcount = 0, maxitem = 0;
	for (i = 0; i < array.length; i++) {
		item = array[i];
		if (occurences[item] === undefined) {
			occurences[item] = 1;
		} else {
			occurences[item]++;
		}
	}
	for (i = 0; i < array.length; i++) {
		item = array[i];
		if (occurences[item] > maxcount) {
			maxcount = occurences[item];
			maxitem = item;
		}
	}
	return maxitem;
}

function Template (template, text, count, ignoreDuplicate, allowUnnamed) {
	this.template = template;
	if (this.parse(text, count, ignoreDuplicate, allowUnnamed)) {
		setLastError('');
	}
}

Template.prototype = {
	parse: function (text, count, ignoreDuplicate, allowUnnamed) {
		var re = getRE(this.template, count),
			result, params,
			comments = 0, //Zähler für Kommentare
			unnamed = 1, //Zähler für unbenannte Parameter
			i, index, lastNL, afterNL, pipe, name, equal, val;
		result = re.exec(text.replace(/<!--.*?-->|<nowiki>.*?<\/nowiki>/g, function (all) {
			return all.replace(/\{\{/g, '~~~~open').replace(/\}\}/g, '~~~~close');
		}));
		if (!result) {
			setLastError('template not found');
			return false;
		}
		this.pre = result[1].replace(/~~~~open/g, '{{').replace(/~~~~close/g, '}}');
		this.post = result[3].replace(/~~~~open/g, '{{').replace(/~~~~close/g, '}}');

		params = result[2].replace(/~~~~open/g, '{{').replace(/~~~~close/g, '}}')
			.replace(/<!--\s*(?:\|[^=>]+=\s*)*-->\s*/g, '')
			.replace(/\n(\s*<!--[\s\S]*?-->)/g, function (all, $1) {
				return '\n|~~~~comment' + String(comments++) + '=' + $1;
			})
			.replace(/<!--.*?-->|<nowiki>.*?<\/nowiki>|\[\[.*?\]\]|\{\{[^{}]+\}\}/g, function (c) {
				return c.replace(/\|/g, '~~~~pipe');
			})
			.split('|');
		params.push('');
		for (i = 0; i < params.length; i++) {
			params[i] = params[i].replace(/~~~~pipe/g, '|');
			if (i === 0) {
				continue;
			}
			if (i !== params.length - 1) {
				params[i] = '|' + params[i];
			}
			lastNL = params[i - 1].lastIndexOf('\n');
			if (lastNL === -1) {
				lastNL = params[i - 1].search(/\s+$/);
			}
			if (lastNL > -1) {
				afterNL = params[i - 1].slice(lastNL);
				if (/^\s*(?:<!--[^\-]*-->\s*)*$/.test(afterNL)) {
					params[i] = afterNL + params[i];
					params[i - 1] = params[i - 1].slice(0, lastNL);
				}
			}
		}

		this.opening = params[0];
		this.closing = params.pop();

		this.params = [];
		this.paramVals = {};
		for (i = 1; i < params.length; i++) {
			result = /^(\s*\|\s*)([^=]*[^=\s])(\s*=\s*)([\s\S]*)$/.exec(params[i]);
			if (result) {
				pipe = result[1];
				name = result[2];
				equal = result[3];
				val = result[4];
			} else if (allowUnnamed) {
				result = /^(\s*\|\s*)([\s\S]*)$/.exec(params[i]);
				pipe = result[1];
				name = String(unnamed++);
				equal = '=';
				val = result[2];
			} else {
				setLastError('unnamed parameter');
				return false;
			}
			index = this.params.indexOf(name);
			if (index > -1) {
				if (ignoreDuplicate) {
					this.params.splice(index, 1);
				} else {
					setLastError('duplicate parameter "' + name + '"');
					return false;
				}
			}
			this.params.push(name);
			this.paramVals[name] = {
				pipe: pipe,
				equal: equal,
				val: val
			};
		}
		this.guessIndention();
		return true;
	},
	toString: function () {
		var unnamed = 1;
		return this.pre + '{{' + this.template + this.opening +
			this.params.map(function (name) {
				var paramVal = this.paramVals[name];
				if (name.indexOf('~~~~comment') === 0) {
					return '\n' + paramVal.val;
				}
				if (name.search(/\D/) === -1 && Number(name) === unnamed && paramVal.val.indexOf('=') === -1) {
					unnamed++;
					return paramVal.pipe + paramVal.val;
				}
				return paramVal.pipe + name + paramVal.equal + paramVal.val;
			}, this).join('') + this.closing + '}}' + this.post;
	},

	guessIndention: function () {
		var nl = [], beforePipe = [], afterPipe = [], beforeEqual = [], afterEqual = [], trailing = [],
			i, name, paramVal, hasNL, pipePos, pipeLength, equalPos, afterEqualCount;
		for (i = 0; i < this.params.length; i++) {
			name = this.params[i];
			if (name.indexOf('~~~~comment') === 0) {
				continue;
			}
			paramVal = this.paramVals[name];
			hasNL = paramVal.pipe.charAt(0) === '\n' ? 1 : 0;
			pipePos = paramVal.pipe.indexOf('|');
			pipeLength = paramVal.pipe.length;
			nl.push(hasNL);
			beforePipe.push(pipePos - hasNL);
			afterPipe.push(pipeLength - pipePos - 1);
			equalPos = paramVal.equal.indexOf('=');
			beforeEqual.push(equalPos);
			beforeEqual.push(-(pipeLength + name.length + equalPos));
			afterEqualCount = paramVal.equal.length - equalPos - 1;
			if (this.getVal(name) === '') {
				if (afterEqualCount === 0) {
					trailing.push(0);
				} else {
					afterEqual.push(afterEqualCount);
					trailing.push(1);
				}
			} else {
				afterEqual.push(afterEqualCount);
			}
		}
		this.indention = [
			mostFrequent(nl),
			mostFrequent(beforePipe),
			mostFrequent(afterPipe),
			mostFrequent(beforeEqual),
			mostFrequent(afterEqual),
			mostFrequent(trailing)
		];
	},
	getPipe: function () {
		return (this.indention[0] === 1 ? '\n' : '') + stringX(' ', this.indention[1]) +
			'|' + stringX(' ', this.indention[2]);
	},
	getEqual: function (p, val) {
		var count1, count2;
		if (this.indention[3] >= 0) {
			count1 = this.indention[3];
		} else {
			count1 = -this.indention[3] - p.length;
		}
		if (count1 < 0) {
			count1 = 0;
		}
		count2 = this.indention[4];
		if (val === '' && this.indention[5] === 0) {
			count2 = 0;
		}
		return stringX(' ', count1) + '=' + stringX(' ', count2);
	},

	insert: function (name, val, after, before) {
		if (this.params.indexOf(name) > -1) {
			setLastError('unexpected parameter "' + name + '"');
			return false;
		}
		var index = after ? this.params.indexOf(after) : -1, pipe, equal;
		if (index === -1) {
			index = before ? 0 : this.params.length;
		} else {
			if (!before) {
				index++;
			}
		}
		this.params.splice(index, 0, name);
		pipe = this.getPipe();
		equal = this.getEqual(pipe + name, val);
		this.paramVals[name] = {
			pipe: pipe,
			equal: equal,
			val: val
		};
		return true;
	},
	change: function (name, val) {
		if (this.params.indexOf(name) === -1) {
			setLastError('missing parameter "' + name + '"');
			return false;
		}
		var oldVal = this.paramVals[name].val;
		this.paramVals[name].val = val;
		//2 Mal \= um JSHint glücklich zu machen
		if (oldVal === '' && val !== '' && (/\=$/).test(this.paramVals[name].equal)) {
			this.paramVals[name].equal += stringX(' ', this.indention[4]);
		} else if (oldVal !== '' && val === '' && this.indention[5] === 0) {
			this.paramVals[name].equal = this.paramVals[name].equal.replace(/\=\s+$/, '=');
		}
		return true;
	},
	rename: function (from, to) {
		var index = this.params.indexOf(from), paramVal;
		if (index === -1) {
			setLastError('missing parameter "' + from + '"');
			return false;
		}
		if (this.params.indexOf(to) > -1) {
			setLastError('unexpected parameter "' + to + '"');
			return false;
		}
		this.params[index] = to;
		paramVal = this.paramVals[from];
		delete this.paramVals[from];
		paramVal.equal = this.getEqual(paramVal.pipe + to, paramVal.val);
		this.paramVals[to] = paramVal;
		return true;
	},
	remove: function (name) {
		var index = this.params.indexOf(name);
		if (index === -1) {
			setLastError('missing parameter "' + name + '"');
			return false;
		}
		delete this.paramVals[name];
		this.params.splice(index, 1);
		return true;
	},
	move: function (name, after) {
		var index = this.params.indexOf(name);
		if (index === -1) {
			setLastError('missing parameter "' + name + '"');
			return false;
		}
		this.params.splice(index, 1);
		index = this.params.indexOf(after);
		if (index === -1) {
			index = this.params.length;
		} else {
			index++;
		}
		this.params.splice(index, 0, name);
		return true;
	},

	getIndention: function () {
		return this.indention;
	},
	setIndention: function (a, b, c, d, e, f) {
		if (a !== undefined) {
			this.indention[0] = a;
		}
		if (b !== undefined) {
			this.indention[1] = b;
		}
		if (c !== undefined) {
			this.indention[2] = c;
		}
		if (d !== undefined) {
			this.indention[3] = d;
		}
		if (e !== undefined) {
			this.indention[4] = e;
		}
		if (f !== undefined) {
			this.indention[5] = f;
		}
	},
	normalize: function () {
		var i, name, pipe, equal;
		for (i = 0; i < this.params.length; i++) {
			name = this.params[i];
			pipe = this.getPipe();
			equal = this.getEqual(pipe + name, this.getVal(name));
			this.paramVals[name].pipe = pipe;
			this.paramVals[name].equal = equal;
		}
		this.closing = (this.indention[0] === 1) ? '\n' : '';
	},
	trim: function () {
		var i, name;
		function trimEnd (s) {
			return s.replace(/ +(\n|$)/g, '$1');
		}
		this.opening = trimEnd(this.opening);
		for (i = 0; i < this.params.length; i++) {
			name = this.params[i];
			this.paramVals[name].val = trimEnd(this.paramVals[name].val);
		}
	},

	sort: function (array, acceptUnknown) {
		var newArray = [], sortArray = [].slice.call(array), i, nextIndex, name;

		for (i = 0; i < this.params.length; i++) {
			if (this.params[i].indexOf('~~~~comment') === 0) {
				nextIndex = sortArray.indexOf(this.params[i + 1]);
				if (nextIndex > -1) {
					sortArray.splice(nextIndex, 0, this.params[i]);
				}
			}
		}

		for (i = 0; i < sortArray.length; i++) {
			if (this.params.indexOf(sortArray[i]) > -1) {
				newArray.push(sortArray[i]);
			}
		}

		for (i = 0; i < this.params.length; i++) {
			name = this.params[i];
			if (newArray.indexOf(name) === -1) {
				if (acceptUnknown || name.indexOf('~~~~comment') === 0) {
					newArray.push(name);
				} else {
					setLastError('unknown parameter "' + name + '"');
					return false;
				}
			}
		}
		this.params = newArray;
		return true;
	},
/*
re: regulärer Ausdruck für Wert
optional: true für optionale Werte
allGroup: falls ein Wert dieser Gruppe, so sind alle verpflichtend
oneGroup: genau ein (bzw. höchstens einer bei optional) Wert aus dieser Gruppe
*/
	validate: function (map) {
		var allGroups = {}, oneGroups = {}, test, group, i, name, param, optional;
		for (i = 0; i < this.params.length; i++) {
			name = this.params[i];
			if (name.indexOf('~~~~comment') === 0) {
				continue;
			}
			test = map[name];
			if (test === undefined) {
				setLastError('unknown parameter "' + name + '"');
				return false;
			}
			group = test.allGroup;
			if (group) {
				allGroups[group] = true;
			}
			group = test.oneGroup;
			if (group) {
				if (oneGroups[group]) {
					setLastError('duplicate parameters in group "' + group + '"');
					return false;
				}
				oneGroups[group] = true;
			}
			if (test.re) {
				if (!test.re.test(this.paramVals[name].val)) {
					setLastError('illegal value "' + this.paramVals[name].val + '" for parameter "' + name + '"');
					return false;
				}
			}
		}
		for (param in map) {
			if (map.hasOwnProperty(param)) {
				test = map[param];
				optional = test.optional;
				group = test.oneGroup;
				if (!optional && group) {
					if (oneGroups[group]) {
						continue;
					} else {
						setLastError('missing parameter from group "' + group + '"');
						return false;
					}
				}
				group = test.allGroup;
				if (group && allGroups[group]) {
					optional = false;
				}
				if (!optional && this.params.indexOf(param) === -1) {
					setLastError('missing parameter "' + param + '"');
					return false;
				}
			}
		}
		return true;
	},

	getVal: function (name) {
		var paramVal = this.paramVals[name];
		return paramVal ? paramVal.val : undefined;
	}
};

function TemplateWrapper (template, text, count, ignoreDuplicate, allowUnnamed) {
	var t = new Template(template, text, count, ignoreDuplicate, allowUnnamed);
	if (getLastError() === '') {
		return t;
	}
	if (this instanceof TemplateWrapper) {
		throw new Error(getLastError());
	} else {
		return null;
	}
}
libs.Template = TemplateWrapper;
libs.templateGetLastError = getLastError;

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