Benutzer:Schnark/js/Template.js

Im heutigen Artikel tauchen wir in die faszinierende Welt von Benutzer:Schnark/js/Template.js ein. Egal, ob Sie Informationen zu diesem Thema suchen, seine heutige Bedeutung verstehen möchten oder einfach etwas Neues entdecken möchten, hier sind Sie richtig. In den nächsten Zeilen werden wir verschiedene Aspekte im Zusammenhang mit Benutzer:Schnark/js/Template.js untersuchen, von seiner Geschichte bis zu seinen praktischen Anwendungen im Alltag. Bereiten Sie sich darauf vor, auf eine Wissens- und Entdeckungsreise einzutauchen, die Ihnen eine neue Perspektive auf Benutzer:Schnark/js/Template.js verschafft. Nicht verpassen!
//Dokumentation unter ] <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 = '';
			}
			return first + rest.replace(/+/g, '+');
		}),
		inner = (capturing ? '(' : '(?:') +
			'\\s*(?:<!--(?:+|-|--)*-->\\s*)*\\|(?:+|\\{|\\}|\\{\\{*\\}\\})*)';
	function makeCaseInvariant (c) {
		var upper = c.toUpperCase();
		if (c === '_') {
			return '+';
		} else if (c === upper) {
			return c;
		} else {
			return '';
		}
	}
	for (ns in allns) {
		if (allns === 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) + '*?){' + count + '}';
	}
	return new RegExp('^(*?' + pre + ')' + getInnerRE(template, true) + '(*)$');
}

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;
		if (occurences === undefined) {
			occurences = 1;
		} else {
			occurences++;
		}
	}
	for (i = 0; i < array.length; i++) {
		item = array;
		if (occurences > maxcount) {
			maxcount = occurences;
			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.replace(/~~~~open/g, '{{').replace(/~~~~close/g, '}}');
		this.post = result.replace(/~~~~open/g, '{{').replace(/~~~~close/g, '}}');

		params = result.replace(/~~~~open/g, '{{').replace(/~~~~close/g, '}}')
			.replace(/<!--\s*(?:\|+=\s*)*-->\s*/g, '')
			.replace(/\n(\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 = params.replace(/~~~~pipe/g, '|');
			if (i === 0) {
				continue;
			}
			if (i !== params.length - 1) {
				params = '|' + params;
			}
			lastNL = params.lastIndexOf('\n');
			if (lastNL === -1) {
				lastNL = params.search(/\s+$/);
			}
			if (lastNL > -1) {
				afterNL = params.slice(lastNL);
				if (/^\s*(?:<!--*-->\s*)*$/.test(afterNL)) {
					params = afterNL + params;
					params = params.slice(0, lastNL);
				}
			}
		}

		this.opening = params;
		this.closing = params.pop();

		this.params = ;
		this.paramVals = {};
		for (i = 1; i < params.length; i++) {
			result = /^(\s*\|\s*)(*)(\s*=\s*)(*)$/.exec(params);
			if (result) {
				pipe = result;
				name = result;
				equal = result;
				val = result;
			} else if (allowUnnamed) {
				result = /^(\s*\|\s*)(*)$/.exec(params);
				pipe = result;
				name = String(unnamed++);
				equal = '=';
				val = result;
			} 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 = {
				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;
				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;
			if (name.indexOf('~~~~comment') === 0) {
				continue;
			}
			paramVal = this.paramVals;
			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 === 1 ? '\n' : '') + stringX(' ', this.indention) +
			'|' + stringX(' ', this.indention);
	},
	getEqual: function (p, val) {
		var count1, count2;
		if (this.indention >= 0) {
			count1 = this.indention;
		} else {
			count1 = -this.indention - p.length;
		}
		if (count1 < 0) {
			count1 = 0;
		}
		count2 = this.indention;
		if (val === '' && this.indention === 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 = {
			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.val;
		this.paramVals.val = val;
		//2 Mal \= um JSHint glücklich zu machen
		if (oldVal === '' && val !== '' && (/\=$/).test(this.paramVals.equal)) {
			this.paramVals.equal += stringX(' ', this.indention);
		} else if (oldVal !== '' && val === '' && this.indention === 0) {
			this.paramVals.equal = this.paramVals.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 = to;
		paramVal = this.paramVals;
		delete this.paramVals;
		paramVal.equal = this.getEqual(paramVal.pipe + to, paramVal.val);
		this.paramVals = paramVal;
		return true;
	},
	remove: function (name) {
		var index = this.params.indexOf(name);
		if (index === -1) {
			setLastError('missing parameter "' + name + '"');
			return false;
		}
		delete this.paramVals;
		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 = a;
		}
		if (b !== undefined) {
			this.indention = b;
		}
		if (c !== undefined) {
			this.indention = c;
		}
		if (d !== undefined) {
			this.indention = d;
		}
		if (e !== undefined) {
			this.indention = e;
		}
		if (f !== undefined) {
			this.indention = f;
		}
	},
	normalize: function () {
		var i, name, pipe, equal;
		for (i = 0; i < this.params.length; i++) {
			name = this.params;
			pipe = this.getPipe();
			equal = this.getEqual(pipe + name, this.getVal(name));
			this.paramVals.pipe = pipe;
			this.paramVals.equal = equal;
		}
		this.closing = (this.indention === 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;
			this.paramVals.val = trimEnd(this.paramVals.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.indexOf('~~~~comment') === 0) {
				nextIndex = sortArray.indexOf(this.params);
				if (nextIndex > -1) {
					sortArray.splice(nextIndex, 0, this.params);
				}
			}
		}

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

		for (i = 0; i < this.params.length; i++) {
			name = this.params;
			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;
			if (name.indexOf('~~~~comment') === 0) {
				continue;
			}
			test = map;
			if (test === undefined) {
				setLastError('unknown parameter "' + name + '"');
				return false;
			}
			group = test.allGroup;
			if (group) {
				allGroups = true;
			}
			group = test.oneGroup;
			if (group) {
				if (oneGroups) {
					setLastError('duplicate parameters in group "' + group + '"');
					return false;
				}
				oneGroups = true;
			}
			if (test.re) {
				if (!test.re.test(this.paramVals.val)) {
					setLastError('illegal value "' + this.paramVals.val + '" for parameter "' + name + '"');
					return false;
				}
			}
		}
		for (param in map) {
			if (map.hasOwnProperty(param)) {
				test = map;
				optional = test.optional;
				group = test.oneGroup;
				if (!optional && group) {
					if (oneGroups) {
						continue;
					} else {
						setLastError('missing parameter from group "' + group + '"');
						return false;
					}
				}
				group = test.allGroup;
				if (group && allGroups) {
					optional = false;
				}
				if (!optional && this.params.indexOf(param) === -1) {
					setLastError('missing parameter "' + param + '"');
					return false;
				}
			}
		}
		return true;
	},

	getVal: function (name) {
		var paramVal = this.paramVals;
		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>