User:SD0001/parseTemplate.js

// /** * 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 + 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 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 ' failed');			}); }	});

if(! resolveRedirects) { return result; }

};

window.parseTemplate = parseTemplate; //