User:Siddhartha Ghai/TWG.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
( function ( window, document, $, TWG, undefined ) { // Wrap with anonymous function
/**
 * **************** twAddPortlet() ****************
 *
 * Adds a portlet menu to one of the navigation areas on the page.
 * This is necessarily quite a hack since skins, navigation areas, and
 * portlet menu types all work slightly different.
 *
 * Available navigation areas depend on the skin used.
 * Monobook:
 *  "column-one", outer div class "portlet", inner div class "pBody". Existing portlets: "p-cactions", "p-personal", "p-logo", "p-navigation", "p-search", "p-interaction", "p-tb", "p-coll-print_export"
 *  Special layout of p-cactions and p-personal through specialized styles.
 * Vector:
 *  "mw-panel", outer div class "portal", inner div class "body". Existing portlets/elements: "p-logo", "p-navigation", "p-interaction", "p-tb", "p-coll-print_export"
 *  "left-navigation", outer div class "vectorTabs" or "vectorMenu", inner div class "" or "menu". Existing portlets: "p-namespaces", "p-variants" (menu)
 *  "right-navigation", outer div class "vectorTabs" or "vectorMenu", inner div class "" or "menu". Existing portlets: "p-views", "p-cactions" (menu), "p-search"
 *  Special layout of p-personal portlet (part of "head") through specialized styles.
 * Modern:
 *  "mw_contentwrapper" (top nav), outer div class "portlet", inner div class "pBody". Existing portlets or elements: "p-cactions", "mw_content"
 *  "mw_portlets" (sidebar), outer div class "portlet", inner div class "pBody". Existing portlets: "p-navigation", "p-search", "p-interaction", "p-tb", "p-coll-print_export"
 *
 * @param String navigation -- id of the target navigation area (skin dependant, on vector either of "left-navigation", "right-navigation", or "mw-panel")
 * @param String id -- id of the portlet menu to create, preferably start with "p-".
 * @param String text -- name of the portlet menu to create. Visibility depends on the class used.
 * @param String type -- type of portlet. Currently only used for the vector non-sidebar portlets, pass "menu" to make this portlet a drop down menu.
 * @param Node nextnodeid -- the id of the node before which the new item should be added, should be another item in the same list, or undefined to place it at the end.
 *
 * @return Node -- the DOM node of the new item (a DIV element) or null
 */

function twgAddPortlet( navigation, id, text, type, nextnodeid )
{
	//sanity checks, and get required DOM nodes
	var root = document.getElementById( navigation );
	if ( !root ) {
		return null;
	}

	var item = document.getElementById( id );
	if (item) {
		if (item.parentNode && item.parentNode === root) {
			return item;
		}
		return null;
	}

	var nextnode;
	if (nextnodeid) {
		nextnode = document.getElementById(nextnodeid);
	}

	//verify/normalize input
	type = (skin === "vector" && type === "menu" && (navigation === "left-navigation" || navigation === "right-navigation")) ? "menu" : "";
	var outerDivClass;
	var innerDivClass;
	switch (skin)
	{
		case "vector":
			if (navigation !== "portal" && navigation !== "left-navigation" && navigation !== "right-navigation") {
				navigation = "mw-panel";
			}
			outerDivClass = (navigation === "mw-panel") ? "portal" : (type === "menu" ? "vectorMenu extraMenu" : "vectorTabs extraMenu");
			innerDivClass = (navigation === "mw-panel") ? 'body' : (type === 'menu' ? 'menu':'');
			break;
		case "modern":
			if (navigation !== "mw_portlets" && navigation !== "mw_contentwrapper") {
				navigation = "mw_portlets";
			}
			outerDivClass = "portlet";
			innerDivClass = "pBody";
			break;
		default:
			navigation = "column-one";
			outerDivClass = "portlet";
			innerDivClass = "pBody";
			break;
	}

	//Build the DOM elements.
	var outerDiv = document.createElement( 'div' );
	outerDiv.className = outerDivClass+" emptyPortlet";
	outerDiv.id = id;
	if (type === "menu") {
		// fix drop-down arrow image in Vector skin
		outerDiv.style.backgroundImage = 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAQCAMAAAAlM38UAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA9QTFRFsbGxmpqa3d3deXl58/n79CzHcQAAAAV0Uk5T/////wD7tg5TAAAAMklEQVR42mJgwQoYBkqYiZEZAhiZUFRDxWGicEPA4nBRhNlAcYQokpVMDEwD6kuAAAMAyGMFQVv5ldcAAAAASUVORK5CYII=")';
		outerDiv.style.backgroundPosition = 'right 60%';
	}
	if ( nextnode && nextnode.parentNode === root ) {
		root.insertBefore( outerDiv, nextnode );
	} else {
		root.appendChild( outerDiv );
	}

	var h5 = document.createElement( 'h3' );
	if (type === 'menu') {
		var span = document.createElement( 'span' );
		span.appendChild( document.createTextNode( text ) );
		h5.appendChild( span );

		var a = document.createElement( 'a' );
		a.href = "#";
		span = document.createElement( 'span' );
		span.appendChild( document.createTextNode( text ) );
		a.appendChild( span );
		h5.appendChild( a );
	} else {
		h5.appendChild( document.createTextNode( text ) );
	}
	outerDiv.appendChild( h5 );

	var innerDiv = document.createElement( 'div' ); //not strictly necessary with type vectorTabs, or other skins.
	innerDiv.className = innerDivClass;
	outerDiv.appendChild(innerDiv);

	var ul = document.createElement( 'ul' );
	innerDiv.appendChild( ul );

	return outerDiv;
}

/**
 * **************** twAddPortletLink() ****************
 * Builds a portlet menu if it doesn't exist yet, and add the portlet link.
 * @param task: Either a URL for the portlet link or a function to execute.
 */
function twgAddPortletLink( task, text, id, tooltip )
{
	var link = mw.util.addPortletLink( 'p-TWG', typeof task === "string" ? task : "#", text, id, tooltip );
	if (jQuery.isFunction(task)) jQuery(link).click(function(ev){ task(); ev.preventDefault(); });
	return link;
}

/*Here begins the common tagger code. This can be importscript()ed from anywhere and should work as long as the module messages are already defined before it.*/
TWG.tagger = {};
/*namespace is a number containing the namespace number to be tested.
Function returns true or false depending on whether or not its the current namespace.
Function's output will be used to decide whether to add the mod to the menu or not.*/
/*TWG.tagger.nsselect = function namespaceselector(namespace) {
	var flag = 0;
	$.each(namespace, function (k,v) {
		if((mw.config.get('wgNamespaceNumber')) === v) {
		flag++;
		return false;
		}
	});
	return false;
};
*/

/*creates the tab
*/
TWG.tagger.tab = function createtab(i) {
	if(TWG.messages[i].namespace.indexOf(mw.config.get('wgNamespaceNumber'))!==-1) {
		twgAddPortletLink( function(){ TWG.tagger.createform(TWG.messages[i]); }, TWG.messages[i].name, 'p-'+TWG.messages[i].name, TWG.messages[i].altname );
	}
};

/*Loads the commonsets*/
TWG.tagger.loadcommonsets = function loadcommonsets(settype, mod, form) {
	if (mod.commonsets !== undefined && mod.commonsets !== null && mod.commonsets !== []) {
		$.each(mod.commonsets, function (k,v) {
			if (v.type === settype) {
				$.each(TWG.messages.commonsets[v.name], function(key, val) {
					form.append(val);
				});
			}
		});
	}
};

/* Creates the form and renders it.
*/
//Fix this to call another function to edit another page with fixed input params.
TWG.tagger.createform = function createform(mod) {
	TWG.thismod = mod;
	var Window = new Morebits.simpleWindow(mod.size.width, mod.size.height);
	Window.setScriptName( TWG.messages.scriptname );
	Window.addFooterLink( mod.footer.text, mod.footer.link );
	Window.setTitle( mod.title );

	var i, form = (mod.submit) ? new Morebits.quickForm( TWG.tagger.evaluate ) : new Morebits.quickForm( TWG.tagger.evaluate, 'change' );

	TWG.tagger.loadcommonsets('pre', mod, form);
	
	$.each(mod.templates, function (k,v) {
		form.append(v);
	});

	TWG.tagger.loadcommonsets('post', mod, form);
	
	if (mod.submit) {
	form.append( { type:'submit' } );
	}

	var result = form.render();
	Window.setContent( result );
	Window.display();
};

/*Does the actual work
Callback accepts the parameter input as an object with various properties representing various templates. The name property of the various properties is the templatename used, the other property names are the parameter names and their values are parameter values.
E.g:
{type:'append/prepend/replace',wrap:'',summary:'',text:'',tags:{{name:merge, 1:whateverpagename},{name:cleanup, reason:whateverreason}}}
*/
TWG.tagger.callback = function callback(pageobj, page) {
	var mod = TWG.thismod;
	var statelem, params, text;
	if (typeof pageobj.type === 'string') {
		statelem = page.getStatusElement();
		params = pageobj;
	}
	else {
		statelem = pageobj.getStatusElement();
		text = pageobj.getPageText();
		params = pageobj.getCallbackParameters();
	}
	
	var code = '';
	$.each(params.tags, function (k,v) {
		code+= '{{' + v.name;
		$.each(v, function (key, val) {
			if (key !== 'name') {
				code+= '|' + key + '=' + val;
			}
		});
		code+= '}}\n';
	});
	if (params.wrap !== null && params.wrap !== undefined) {
		code = '<' + params.wrap + '>\n' + code + '</' + params.wrap + '>';
	}
	if (params.text !== undefined) {
		if (params.text[0] === 'PRE' || params.text[0] === 'POST') {
			code = params.text[0] === 'PRE' ? params.text[1] + '\n' + code : code + '\n' + params.text[1];
		}
	}
	switch (params.type) {
		case 'prepend':
			page.setPrependText(code);
			page.setEditSummary(params.summary);
			page.prepend();
			break;
		case 'append':
			page.setAppendText(code);
			page.setEditSummary(params.summary);
			page.append();
			break;
		case 'replace':
			text = code;
			pageobj.setPageText(text);
			pageobj.setEditSummary(params.summary);
			pageobj.save();
			break;
		default:
			statelem.error(TWG.messages.typeerror);
			return;
	}
};

/*Function to evaluate params for altpages, and give an output object to be passed to callback.*/
TWG.tagger.alteval = function alteval (params, i, mod) {
	var output = {};
	output.tags = {};
	var thispage = mw.config.get('wgPageName');
	$.each(params.tags, function(j, val) {
		if (typeof mod.page.alt[i][val.name] === 'object') {
			output.tags[j] = {};
			output.tags[j].name = mod.page.alt[i][val.name].NAME;
			$.each(mod.page.alt[i][val.name], function(p, q) {
				if (p!== 'NAME') {
					if (q === 'THISPAGE') {
						output.tags[j][p] = thispage;
					}
					else {
						$.each(params.tags[j], function(x,y) {
							if (q === x) {
								output.tags[j][p] = y;
							}
						});
					}
					if (output.tags[j][p] === null || output.tags[j][p] === undefined) {
						output.tags[j][p] = q;
					}
				}
			});
		}
	});
	return output;
};

/*Recursive function to analyze all templates and their parameters and return an object acceptable to callback.*/
TWG.tagger.evaluateinput = function evaluate(i, e, parent) {
	var flag, m1, m2, outsub, param, outreg, subreg;
	var form = e.target;
	var input = form[i];
	var output = {};
	var l1 = (input.name.match(/IGNORE/g) === null) ? 0 : input.name.match(/IGNORE/g).length;
	var l2 = (parent.match(/IGNORE/g) === null) ? 0 : parent.match(/IGNORE/g).length;
	if (input.name.match(/PLAINTEXT/) !== null) {
		output = [];
		output[0] = input.name.match(/PRE/) !== null ? 'PRE' : 'POST';
		output[1] = input.value;
		return output;
	}
	if (l1 === l2) {
		if (parent.replace(/(\.|IGNORE)/g,'') === '') {
			if (input.name.match(/COMMONPARAM/) !== null) {
				if (input.value !== null && input.value !== undefined && input.value !== '') {
					param = input.name.replace(/COMMONPARAM/,'');
					output[param] = input.value;
				}
			}
			else {
				output.name = input.value;
			}
		}
		else if (input.value !== null && input.value !== undefined && input.value !== '') {
			param = input.name;
			outreg = new RegExp(parent, 'g');
			param = param.replace(outreg,'');
			param = param.replace(/\./g,'');
			output[param] = input.value;
		}
	}
	subreg = new RegExp(input.name);
	$(form).find('input, textarea, select').each(function(k, v) {
		if (v !== null && v.type !== undefined) {
			if ((v.type === 'checkbox' && v.checked) || (v.type === 'radio' && v.checked) || v.type.match(/select/) !== null || v.type === 'text' || v.type === 'textarea') {
				if (v.name.match(subreg) !== null) {
					m1 = (v.name.match(/\./g) === null) ? [] : v.name.match(/\./g);
					m2 = (input.name.match(/\./g) === null) ? [] : input.name.match(/\./g);
					if (m1.length - m2.length === 1) {
						outsub = TWG.tagger.evaluateinput(k, e, input.name);
					}
				}
				else if (v.name.match(/COMMONPARAM/) !== null) {
						outsub = TWG.tagger.evaluateinput(k, e, '');
				}
				$.extend(true, output, outsub);
			}
		}
	});
	return output;
};

/*Evaluates the input form subgroup elements of a particular element and returns an object of them.*/
//Fix this to evaluate checkbox and radio subgroups.
/*
TWG.tagger.subgroupevaluate = function subgroupevaluate(i, e) {
	var form = e.target;
	var flag, flagfixed;
	var ir = new RegExp(form[i].name,'g');
	var output = {};
	for (flag in form) {
		if (form[flag] !== null) {
			if ((form[flag].type === 'checkbox' && form[flag].checked) || form[flag].type === 'radio' || form[flag].type === 'select' || form[flag].type === 'text') {
				if (form[flag].name.match(/subgroup/gi) !== null && form[flag].name.match(ir) !== null) {
					flagfixed = form[flag].name.replace(/(subgroup|\.)/gi,'');
					flagfixed = flagfixed.replace(ir,'');
					output[flagfixed] = $.extend(true, {}, form[flag]);
				}
			}
		}
	}
	return output;
};
*/

/*Calculates the params per the form, load()s the page and calls callback
*/
TWG.tagger.evaluate = function evaluateform(e) {
	var mod = TWG.thismod;
	var form = e.target;
	var page, pagename, subs, thispagename, thispage, altpage, altpagename, creatortalk, creatorname, redirect, namespaces, altparams;
	var params = {};

	thispagename = mw.config.get('wgPageName');
	thispage = new Morebits.wiki.page(thispagename);
	thispage.lookupCreator();
	namespaces = mw.config.get('wgFormattedNamespaces');

	//set all params here
	params.type = mod.page.main.type;
	params.wrap = mod.page.main.wrap;
	params.summary = mod.page.main.summary;
	params.tags = {};
	//evaluate inputs
	$(form).find('input, textarea, select').each(function(k, v) {
		if (v !== null && v.type !== undefined) {
			if ((v.type === 'checkbox' && v.checked) || (v.type === 'radio' && v.checked) || v.type.match(/select/) !== null || v.type === 'text' || v.type === 'textarea') {
				if (v.name.match(/(\.|COMMONPARAM)/) === null) {
					subs = TWG.tagger.evaluateinput(k, e, ''); //this evaluates all inputs and returns an object appropriate for callback.
					if (subs[0] === 'PRE' || subs[0] === 'POST') {
						params.text = $.extend(true, [], subs);
					}
					else {
						params.tags[k] = $.extend({}, subs);
					}
				}
			}
		}
	});
	
	creatorname = thispage.getCreator();
	creatortalk = namespaces['3'] + ':' + creatorname;
	pagename = mod.page.main.name.replace(/THISPAGE/g, thispagename);
	pagename.replace(/CREATORTALK/g, creatortalk);
	
	Morebits.simpleWindow.setButtonsEnabled( false );
	Morebits.status.init( e.target );
	
	redirect = mod.redirect.replace(/THISPAGE/g, thispage);
	redirect.replace(/CREATORTALK/g, creatortalk);

	Morebits.wiki.actionCompleted.redirect = redirect;
	Morebits.wiki.actionCompleted.notice = mod.completed;

	page = new Morebits.wiki.page(pagename, mod.start);
	switch (mod.page.main.type) {
		case 'prepend':
		case 'append':
			TWG.tagger.callback(params, page);
			break;
		default:
			page.setCallbackParameters(params);
			page.load(TWG.tagger.callback);
			break;
	}
	$.each(mod.page.alt, function(k, v) {
		altpagename = v.name.replace(/THISPAGE/g, thispage);
		altpagename.replace(/CREATORTALK/g, creatortalk);
		altpage = new Morebits.wiki.page(altpagename);
		altparams = TWG.tagger.alteval(params, k, mod);		
		altparams.type = v.type;
		altparams.wrap = v.wrap;
		altparams.summary = v.summary;
		switch (v.type) {
			case 'prepend':
			case 'append':
				TWG.tagger.callback(altparams, altpage);
				break;
			default:
				altpage.setCallbackParameters(altparams);
				altpage.load(TWG.tagger.callback);
				break;
		}
	});
};

/*General initialization code. Initializes all modules one by one. */
$(document).ready(function initialize() {
	twgAddPortlet('right-navigation', 'p-TWG', 'TWG', 'menu', 'p-search');
	$.each(TWG.mods, function (k,v) {
		TWG.tagger.tab(v);
	});
});
} ( window, document, jQuery, window.TWG )); // End wrap with anonymous function