// <nowiki>
/**
* Returns an array of objects representing the usages of a given set of
* templates in the given wikitext. The object key-value pairs are the template
* |parameter=value pairs.
*
* Piped links, nested templates, nowiki tags and even HTML comments in parameter
* values are adequately accounted for.
*
* If resolveRedirects is set as true, any transclusions of the redirects of the
* templates would also be found. But since the API call needed to achieve this
* makes the function asynchronous, you need to provide a callback function to
* execute upon completion. The function takes the result array as the argument.
*
* Usage: Can be executed from the browser console or within another script.
*
* @param {string} wikitext Wikitext in which to search for the templates
*
* @param {(string[]|string)} templates Name of the template page, or array of
* template page names. Need not necessarily be pages in template namespace,
* any page can be used. If no namespace is provided, it is assumed as Template:.
*
* @param {boolean} [resolveRedirects=false] Also check for transclusions of
* redirects of the specified `templates`?
*
* @param {function} callback Callback function to execute after processing, if
* resolveRedirects is set true. The result array is passed as a parameter to it.
*
* @returns {Object[]} Returns only if `resolveRedirects` is unset
*
* @throws if (i) API call is unsuccessful (resolveRedirects mode only)
* (ii) The end of template is not found in the wikitext
* (iii) resolveRedirects is set true but no callback is provided
*
* ISSUES:
* 1. It is possible that a template usage found be entirely within a comment or
* nowiki tags.
* 2. Very rare situations where, within a parameter value, there are nowiki tags
* inside a comment, or vice-versa, will cause problems.
*
* Found any other bug? Report at [[User talk:SD0001]] or via email.
*
*/
var parseTemplate = function (wikitext, templates, resolveRedirects, callback) {
var strReplaceAt = function(string, index, char) {
var a = string.split("");
a[index] = char;
return a.join("");
};
var pageNameRegex = function(name) {
return '[' + name[0].toUpperCase() + name[0].toLowerCase() + ']' + mw.RegExp.escape(name.slice(1)).replace(/ |_/g,'[ _]');
};
var result = [];
if (typeof templates === 'string') {
templates = [ templates ];
}
var processTemplate = function(t) {
var re_string;
if (t.indexOf('Template:') === 0 || t.indexOf('template:') === 0) {
re_string = '(?:[tT]emplate:)?' + pageNameRegex(t.slice(9));
} else {
// namespace name including colon
var namespace = t.slice(0, t.indexOf(':') + 1);
// page name after colon
var pageName = t.slice(t.indexOf(':') + 1);
re_string = pageNameRegex(namespace) + pageNameRegex(pageName);
}
var t_re = new RegExp( '(\\{\\{\\s*' + re_string + '\\s*)(\\||\\}\\})', 'g');
var match = t_re.exec(wikitext);
while (match) {
var startIdx = match.index + match[1].length + 1;
// number of unclosed braces
var numUnclosed = 2;
// are we inside a comment or between nowiki tags?
var inCommentOrNowiki = false;
var i;
for ( i = startIdx; i < wikitext.length; i++ ) {
if (! inCommentOrNowiki) {
if (wikitext[i] === '{') {
if(wikitext[i+1] === '{') {
numUnclosed += 2;
i++;
}
} else if (wikitext[i] === '}') {
if(wikitext[i+1] === '}') {
numUnclosed -= 2;
i++;
if(numUnclosed === 0) {
break;
}
}
} else if (wikitext[i] === '|') {
if (numUnclosed > 2) {
// swap out pipes in internal templates with \1 character
wikitext = strReplaceAt(wikitext, i,'\1');
}
} else if (/^(<!--|<nowiki ?>)/.test(wikitext.slice(i, i + 9))) {
inCommentOrNowiki = true;
i += 3;
}
} else { // we are in a comment or nowiki
if (wikitext[i] === '|') {
// swap out pipes with \1 character
wikitext = strReplaceAt(wikitext, i,'\1');
} else if (/^(-->|<\/nowiki ?>)/.test(wikitext.slice(i, i + 10))) {
inCommentOrNowiki = false;
i += 2;
}
}
}
if (numUnclosed !== 0) {
console.error('[parseTemplate] Failed to find closing }} of ' + t);
}
// text is the template text excluding the the starting {{ and ending }}
var text = match[1].slice(2) + wikitext.slice(startIdx - 1, i - 1);
// swap out pipe in links with \1 control character
text = text.replace(/(\[\[[^\]]*?)\|(.*?\]\])/g, '$1\1$2');
var chunks = text.split('|');
var res = {};
res[0] = chunks[0].trim();
var unnamedIdx = 1;
for (i=1; i < chunks.length; i++) {
if (chunks[i].indexOf('=') === -1) {
res[unnamedIdx++] = chunks[i].replace(/\1/g,'|').trim();
} else {
var key = chunks[i].slice(0, chunks[i].indexOf('=')).trim();
var val = chunks[i].slice(chunks[i].indexOf('=') + 1).replace(/\1/g,'|').trim();
// changed back '\1' in value to pipes
res[key] = val;
}
}
result.push(res);
match = t_re.exec(wikitext);
}
};
if(resolveRedirects && callback === undefined) {
console.error('[parseTemplate] No callback function provided to execute after resolving of redirects');
}
templates.forEach(function (template, idx) {
if (template.indexOf(':') === -1) {
template = "Template:" + template;
}
processTemplate(template);
if(resolveRedirects) {
$.get('/w/api.php', {
"action": "query",
"format": "xml",
"prop": "linkshere",
"titles": template,
"lhshow": "redirect",
"lhlimit": "max"
}).done(function (response) {
$(response).find('lh').each(function(idx,tag) {
var template_redir = $(tag).attr('title');
if($(tag).attr('ns') === '0') {
template_redir = ':' + template_redir;
}
processTemplate(template_redir);
});
if (idx === templates.length - 1) {
callback(result);
}
}).fail(function (response) {
console.error('[parseTemplate] API call for getting redirects to {{'
+ template + '}} failed');
});
}
});
if(! resolveRedirects) {
return result;
}
};
window.parseTemplate = parseTemplate;
// </nowiki>