//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>