Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
  • Konqueror and Chrome: click Reload or press F5;
  • Opera: clear the cache in Tools → Preferences;
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.

/*
 * Automatically create form-of entries based on meta-data within entries.
 * See [[User:Conrad.Irwin/creation.js/documentation]] for information.
 */


/*
 * Language-specific formatting rules for form-of entries.
 * 
 * These come in two different types. The first type is the "internal" type.
 * These are entries whose content is generated directly from within the script.
 * The second type is "external", where the content is generated by template-izing one
 * of the subpages of the script, such as User:Conrad.Irwin/creation.js/basicNoun.
 * 
 * The internal type is always attempted first. If no internal rule is available for the
 * current to-be-created entry, the external type is used as a fallback.
 */


/*
 * "Internal" entry generation rules.
 * 
 * These are all called via the get_preload_text function, which returns a fully-formed entry with a language header.
 */
function get_preload_text(params)
{
	try
	{
		if (!get_preload_text[params.lang])
			return false;
		
		var entry = {
			lang_header: '=={'+'{subst:#invoke:language utilities|lookup_language|' + params.lang + '|names}}==',
			pronunc: '',
			pos_header: '===' + params.pos.charAt(0).toUpperCase() + params.pos.substr(1) + '===',
			head: '{{head|' + params.lang + '}}',
			def: '',
			declension: '',
			conjugation: '' };
		
		get_preload_text[params.lang](params, entry);
		
		entry_text =
			entry.lang_header +
			(entry.pronunc ? '\n\n===Pronunciation===\n' + entry.pronunc : '') +
			'\n\n' + entry.pos_header +
			'\n' + entry.head +
			'\n\n' + '# ' + entry.def +
			(entry.declension ? '\n\n====Declension====\n' + entry.declension : '') +
			(entry.conjugation ? '\n\n====Conjugation====\n' + entry.conjugation : '');
		
		return entry_text;
	}
	catch (e)
	{
		if (e instanceof PreloadTextError)
			return false;
		else
			throw e;
	}
}

// Exception class
function PreloadTextError()
{
	this.name = 'PreloadTextError';
	this.message = 'Could not generate a form-of entry for this language and form.';
}

PreloadTextError.prototype = new Error();
PreloadTextError.prototype.constructor = PreloadTextError;


/*
 * Generation rules for each language.
 * 
 * Each function has two parameters.
 * params holds the parameters given in the template.
 * entry holds the various parts of the entry to be created.
 * 
 * The function's task is to provide new values for some of the parts of the new entry.
 * In most cases, entry.def (the definition line) will be overridden, but the headword (entry.head)
 * or even the part-of-speech or language headers can be overridden if necessary.
 * 
 * If the function is not able to handle the current form for whatever reason, throw an exception:
 * throw new PreloadTextError();
 */

// Azeri
get_preload_text.az =
	function (params, entry)
	{
		entry.def = '{'+'{inflection of|' + params.origin + '||';

		if (form == 'definite-plural')
			entry.def += 'definite|p';
		else if (form == 'definite-accusative')
			entry.def += 'definite|acc|s';
		else if (form == 'plural-definite-accusative')
			entry.def += 'definite|acc|p';
		else if (form == 'dative')
			entry.def += 'dat|s';
		else if (form == 'plural-dative')
			entry.def += 'dat|p';
		else if (form == 'locative')
			entry.def += 'loc|s';
		else if (form == 'plural-locative')
			entry.def += 'loc|p';
		else if (form == 'ablative')
			entry.def += 'abl|s';
		else if (form == 'plural-ablative')
			entry.def += 'abl|p';
		else if (form == 'genitive')
			entry.def += 'definite|gen|s';
		else if (form == 'plural-genitive')
			entry.def += 'definite|gen|p';
		
		entry.def += '|lang=' + params.lang + '}}';
	}

// Catalan
get_preload_text.ca =
	function (params, entry)
	{
		var template = {
			'plural':'plural of',
			'masculine-plural': 'masculine plural of',
			'feminine':'feminine of',
			'feminine-plural': 'plural of'};
		
		if (!template[params.form])
			throw new PreloadTextError();
		
		entry.head = '{'+'{head|' + params.lang + '|' + params.pos + ' form}}';
		entry.def = '{{' + template[params.form] + (params.form == 'plural' ? '|nocat=1|' : '|') + params.origin + '|lang=' + params.lang + '}}';
	};

// English
get_preload_text.en =
	function (params, entry)
	{
		switch (params.form)
		{
			case 'comparative':
				entry.def = '{'+'{en-comparative of|' + params.origin + (params.pos != 'adjective' ? '|POS=' + params.pos : '') + '}}';
				break;
			case 'superlative':
				entry.def = '{'+'{en-superlative of|' + params.origin + (params.pos != 'adjective' ? '|POS=' + params.pos : '') + '}}';
				break;
			case 'third-person-singular':
				entry.def = '{'+'{en-third-person singular of|' + params.origin + '}}';
				break;
			case 'simple-past':
				entry.def = '{'+'{en-simple past of|' + params.origin + '}}';
				break;
			case 'simple-past-and-participle':
				entry.def = '{'+'{en-past of|' + params.origin + '}}';
				break;
			default:
				entry.def = '{'+'{' + params.form.replace(/-/g, ' ') + ' of|' + params.origin + '|lang=' + params.lang + '}}';
		}
	};

// Esperanto
get_preload_text.eo =
	function (params, entry)
	{
		var stem = params.origin.substr(0, params.origin.length - 1);
		var ending = params.origin.substr(params.origin.length - 1);
		
		if (ending != 'o' && ending != 'a')
			throw new PreloadTextError();
		
		var is_proper = params.pos == 'proper noun';
		
		switch(params.form)
		{
			case 'uncountable-accusative':
				entry.def = stem + '|' + ending + 'n|unc=yes';
				break;
			case 'plural':
				entry.def = stem + '|' + ending + 'j' + (is_proper ? '-proper' : '');
				break;
			case 'accusative':
				entry.def = stem + '|' + ending + 'n' + (is_proper ? '-properpl' : '');
				break;
			case 'accusative-plural':
				entry.def = stem + '|' + ending + 'jn' + (is_proper ? '-properpl' : '');
				break;
			default:
				throw new PreloadTextError();
		}
		
		entry.def = '{'+'{eo-form of|' + entry.def + '}}'
	};

// Spanish
get_preload_text.es =
	function (params, entry)
	{
		var template = {
			'plural':'plural of',
			'masculine-plural': 'masculine plural of',
			'feminine':'feminine of',
			'feminine-plural': 'plural of'};
		
		if (!template[params.form])
			throw new PreloadTextError();
		
		entry.head = '{'+'{head|' + params.lang + '|' + params.pos + ' form}}';
		entry.def = '{{' + template[params.form] + (params.form == 'plural' ? '|nocat=1|' : '|') + params.origin + '|lang=' + params.lang + '}}';
	};

// Persian
get_preload_text.fa =
	function (params, entry)
	{
		entry.head = '{'+'{fa-word' + (params.transliteration ? ('|tr=' + params.transliteration) : '') + '}}'
		
		switch (params.form)
		{
			case 'comparative':
				entry.def = '{'+'{fa-adj-form|c|' + params.origin + '}}';
				break;
			case 'superlative':
				entry.def = '{'+'{fa-adj-form|s|' + params.origin + '}}';
				break;
			default:
				throw new PreloadTextError();
		}
	};

// French
get_preload_text.fr =
	function (params, entry)
	{
		var template = {
			'plural':'plural of',
			'masculine-plural': 'masculine plural of',
			'feminine':'feminine of',
			'feminine-plural': 'plural of'};
		
		if (!template[params.form])
			throw new PreloadTextError();
		
		entry.head = '{'+'{head|' + params.lang + '|g=?-p|' + (params.pos == 'noun' ? 'plural' : params.pos + ' form') +'	}}';
		entry.def = '{{' + template[params.form] + (params.form == 'plural' ? '|nocat=1|' : '|') + params.origin + '|lang=' + params.lang + '}}';
	};

// Dutch
get_preload_text.nl =
	function (params, entry)
	{
		switch (params.form)
		{
			case 'plural':
				entry.head = '{'+'{head|nl|noun plural form}}';
				entry.def = '{{nl-noun form of|pl|' + params.origin + '}}';
				break;
			case 'diminutive':
				entry.head = '{'+'{nl-noun-dim}}';
				entry.def = '{{nl-noun form of|dim|' + params.origin + '}}';
				break;
			default:
				throw new PreloadTextError();
		}
	};

// Polish
get_preload_text.pl =
	function (params, entry)
	{
		var formof = params.form.replace(/-/g, ' ');
		formof = formof.replace('third person', 'third-person');
		formof = formof.replace('simple past and participle', 'past');
		entry.def = '{'+'{' + formof + ' of|' + params.origin + '|lang=pl' + (params.pos == 'adverb' ? '|POS=adverb' : '') + '}}';
	};

get_preload_text.sl =
	function (params, entry)
	{
		switch (params.form)
		{
			case 'comparative':
			case 'superlative':
				entry.pronunc = '{{rfp|lang=sl}}';
				entry.head = '{'+'{sl-' + (params.pos == 'adverb' ? 'adv' : 'adj') + '-' + (params.form == 'comparative' ? 'comp' : 'sup') + '|' + params.target + '}}';
				
				// If there is a comma in the head, assume it has multiple accented variants.
				// Add a link with an alternative display form instead.
				entry.def =
					'{{' + params.form + ' of' +
					(params.pos == 'adverb' ? '|POS=adverb' : '') +
					(params.origin.indexOf(',') != -1 ? '|' + params.origin_pagename : '') +
					'|' + params.origin +
					'|lang=sl}}';
				
				// If it's an adjective, add a declension table
				if (params.pos == 'adjective')
				{
					var stem = params.target.substr(0, params.target.length - 1);
					var ending = params.target.substr(params.target.length - 1);
					
					// Adjective comparatives and superlatives must always end in -i
					if (ending != 'i')
						throw new PreloadTextError();
					entry.declension = '{{sl-decl-adj|' + stem + '|def=1}}';
				}
				break;
			default:
				throw new PreloadTextError();
		}
	}

// Swedish
get_preload_text.sv =
	function (params, entry)
	{
		if (! (params.form.match(/^superlative/) || params.form.match(/^comparative/) || params.form.match(/^positive/)) )
			throw new PreloadTextError();
		
		var template = false;
		
		switch (params.form + '/' + params.gender)
		{
			case 'positive/n':
				template = 'sv-adj-form-abs-indef-n'; break;
			case 'positive/m':
				template = 'sv-adj-form-abs-def-m'; break;
			case 'positive-definite/':
				template = 'sv-adj-form-abs-def'; break;
			case 'positive-plural/':
				template = 'sv-adj-form-abs-pl'; break;
			case 'comparative/':
				if (params.pos == 'adjective')
					template = 'sv-adj-form-comp';
				else
					template = 'sv-adv-form-comp';
				break;
			case 'superlative-attributive/m':
				template = 'sv-adj-form-sup-attr-m'; break;
			case 'superlative-attributive-definite/':
				template = 'sv-adj-form-sup-attr'; break;
			case 'superlative-attributive-plural/':
				template = 'sv-adj-form-sup-attr-pl'; break;
			case 'superlative-predicative/':
				template = 'sv-adj-form-sup-pred'; break;
			case 'superlative/':
				template = 'sv-adv-form-sup'; break;
			default:
				throw new PreloadTextError();
		}
		
		entry.def = '{'+'{' + template + '|' + params.origin + '}}';
	}

// Tajik
get_preload_text.tg =
	function (params, entry)
	{
		entry.head = '{'+'{tg-word' + (params.transliteration ? ('|tr=' + params.transliteration) : '') + '}}'
		switch(params.form)
		{
			case 'comparative':
				entry.def = '{'+'{tg-adj-form|c|' + params.origin + '}}';
				break;
			case 'superlative':
				entry.def = '{'+'{tg-adj-form|s|' + params.origin + '}}';
				break;
			default:
				throw new PreloadTextError();
		}
	};

// Turkish
get_preload_text.tr =
	function (params, entry)
	{
		entry.def = '{'+'{inflection of|' + params.origin + '||';

		if (form == 'definite-plural')
			entry.def += 'definite|p';
		else if (form == 'definite-accusative')
			entry.def += 'definite|acc|s';
		else if (form == 'plural-definite-accusative')
			entry.def += 'definite|acc|p';
		else if (form == 'dative')
			entry.def += 'dat|s';
		else if (form == 'plural-dative')
			entry.def += 'dat|p';
		else if (form == 'locative')
			entry.def += 'loc|s';
		else if (form == 'plural-locative')
			entry.def += 'loc|p';
		else if (form == 'ablative')
			entry.def += 'abl|s';
		else if (form == 'plural-ablative')
			entry.def += 'abl|p';
		else if (form == 'genitive')
			entry.def += 'definite|gen|s';
		else if (form == 'plural-genitive')
			entry.def += 'definite|gen|p';
		
		entry.def += '|lang=' + params.lang + '}}';
	}

// Yiddish
get_preload_text.yi =
	function (params, entry)
	{
		switch (params.form)
		{
			case 'plural':
				entry.head = '{'+'{head|' + params.lang + (params.transliteration ? '|tr=' + params.transliteration : '') + '}}';
				entry.def = '{'+'{plural of|' + params.origin + '|lang=' + params.lang + '}}';
				break;
			case 'past-participle':
				entry.head = '{'+'{head|' + params.lang + '|past participle' + (params.transliteration ? '|tr=' + params.transliteration : '') + '}}';
				entry.def = '{'+'{past participle of|' + params.origin + '|lang=' + params.lang + '}}';
				break;
			default:
				throw new PreloadTextError();
		}
	};


/*
 * "External" generation rule definitions.
 * 
 * These are used when there is no "internal" rule for a given situation.
 */

/*
 * Which template should be used for "external" rule definitions?
 * 
 * Check if we know of a template that will do the job.
 * Use an explicit list to make it harder to subvert.
 */
function get_preload_template (form, lang, link)
{
	var prefix = 'User:Conrad.Irwin/creation.js/';

	if (lang == 'da' && form.match(/genitive/))
	{
		return prefix + 'inflNoun';
	}
	else if (lang == 'he')
	{
		switch (form)
		{
			case 'plural':
			case 'construct':
				return prefix + 'inflNoun';
			default:
				return false;
		}
	}
	else  // Defaults
	{
		switch (form)
		{
			case 'plural' :
			case 'diminutive' :
			case 'genitive' :
			case 'diminutive-plural' :
			case 'genitive-and-plural':
				return prefix + 'basicNoun';
			case 'plural-definite':
			case 'plural-indefinite':
			case 'singular-definite':
			case 'vocative':
			case 'singular-vocative':
			case 'plural-vocative':
				return prefix + 'inflNoun';
			case 'third-person-singular':
			case 'present-participle':
			case 'simple-past':
			case 'past-participle':
				if(lang=='da') return prefix+'inflForm';
			case 'simple-past-and-participle':
				return prefix + 'basicVerb';
			case 'present':
			case 'past':
			case 'infinitive':
			case 'imperative':
				return prefix + 'inflForm';
			case 'positive':
				return prefix + 'positiveAdjective';
			case 'comparative':
			case 'superlative':
			case 'inflected-form':
			case 'exaggerated':
				if ((lang == 'hu' || lang == 'cs' || lang == 'sl') && get_part_of_speech(link) == 'adverb')
					return prefix + 'basicAdverb';
				return prefix + 'basicAdjective';
			default:
				return false;
		}
	}
}

/* 
 * Rules to full in each of the values in the template subpage.
 * 
 * The rules consist of a series of "set" functions. These set the parameters that
 * are then used in one of the templated subpages of the script to make an entry.
 * See the documentation page for a full list of subpages.
 * 
 * Each function returns a "variable", which acts like a wrapper for a defined parameter.
 * For example, returning variable('lang', 'en') will cause any instances of '{{{lang}}}'
 * in the subpage template to be replaced with 'en'.
 */

// The page's language.
function set_lang (lang)
{
	return variable ('lang', lang);
}

// The optional language parameter to give to a template (either '' for english, or '|lang=xx')
function set_template_lang (lang, form)
{
	// Did I say the last one was horrible?...
	if (lang == 'gd' && form == 'genitive-and-plural')
	{
		return variable('template-lang', '|lang=gd}}%0A%23 {'+'{plural of|' + wgTitle + '|lang=gd');
	}
	else
	{
		return variable ('template-lang', '|lang=' + lang);
	}
}

//The gender template with a leading space (or an empty string if no gender)
function set_gender_template (gender, lang)
{
	if (lang == 'he')
		return variable ('gender-template', ' {'+'{romanization of Hebrew}}')

	if (lang == 'bg')
		return variable ('gender-template', ' ['+'[Category:Bulgarian terms lacking transliteration]]')
	
	gender = gender.replace('pl','p').replace(/([mfnc])p/, '$1-p');

	if (gender.length == 0)
	{
		return variable ('gender-template', '');
	}
	else
	{
		return variable ('gender-template', '{'+'{'+ gender + '}}');
	}
}

//The optional |g= argument to { {head}}
function set_template_gender (gender, lang, form)
{
	if (form == 'diminutive-plural')
		return variable('template-gender','|plural=1');
	
	if (form == 'plural' && gender.indexOf('p') < 0)
	   gender += 'p';

	gender = gender.replace('pl','p').replace(/([mfnc])p/, '$1-p');

	if (gender.length == 0)
		return variable('template-gender','');
	else
		return variable('template-gender', '|g=' + gender);
}

//Form of templates for genders. These are the ones that exist, if we need others,
//either create them or use { {form-of}} manually.
function gender_form (gender)
{
	if (gender == 'mpl')
	{
		return 'masculine plural';
	}
	else if (gender == 'f')
	{
		return 'feminine';
	}
	else if (gender == 'fpl')
	{
		return 'feminine plural';
	}
	else if (gender == 'n')
	{
		return 'neuter';
	}
	else if (gender == 'mfpl')
	{
		return 'plural';
	}
	else
	{
		throw new Error('Not simple gender?!');
	}

}

// The page name that the entry is a form of.
// Returns in PIPED format. Which, while kind of bad, works as either embedded in [[%s]] or { {template|%s}}
// and most templates take an optional display parameter as the first after the link.
function set_origin (given, lang)
{
	//Remove links from given parameters, and unencode underscores to spaces
	if (given)
	{
		given = given.replace(/\[\[([^\|\]])*\|?([^\]]+)\]\]/g,'$2').replace(/_/g,' ');
		return variable ('origin', wgTitle + '|' + given);
	}

	return variable ('origin', wgTitle);
}

// The page we are about to create with links on individual words.
function set_pagename_linked_and_template_head (link, lang, form)
{
	var pagename = (link.innerText || link.textContent);

	var op = pagename;
	pagename = pagename.replace (/([ -])/g,']]$1[[')

	if (op != pagename)
	{
		pagename = '[['+pagename+']]';
		return variable('pagename-linked', pagename) + variable('template-head','|head=' + pagename)
	}

   return variable('pagename-linked', pagename) + variable('template-head','');
}

// Script code
function set_template_sc (lang)
{
	var sc;
	
	switch (lang)
	{
		case 'he':
			sc = 'Hebr';
			break;
		default:
			return variable('template-sc','');
	}
	return variable('template-sc','|sc='+sc)
}

//The form-of template we are to use
function set_form_template (form, lang, gender, link)
{
	var formof = form.replace(/-/g,' ');

	if (lang == 'eo')
	{
		return variable('form-template', 'eo-form of');
	}
	else if (formof == 'construct')
	{
		return variable('form-template', 'form of|' + formof.replace('-',' ') + ' form');
	}
	else if (lang == 'da')
	{
		return variable('form-template', 'form of|' +formof.replace('-',' '));
	}
	
	if (formof == 'positive')
	{
		formof = gender_form (gender)
		
		if (formof == 'plural')
		{
			return variable('form-template','form of|plural');
		}
	}
	else
	{
		formof = formof.replace('third person', 'third-person');
		formof = formof.replace('simple past and participle', 'past');
	}

	if (formof == 'diminutive plural')
		return variable('form-template', 'diminutive of|plural=1');

	if (lang == 'gd' && formof == 'genitive and plural')
	{
		formof = 'genitive'
	}
	
	formof = formof + ' of';
	
	return variable('form-template', formof);
}

function set_part_of_speech (link,lang,form)
{
	if (lang=='da')
		return variable('part-of-speech', 'verb');
	else
		return variable('part-of-speech', get_part_of_speech(link));
}

/*
 * Create a "variable".
 * 
 * This is a template-like replacement of text wrapped in {{{ }}} with something else.
 */
function variable (name, replacement)
{
	return get_escape ('s~{'+'{'+'{' + clean_regexp (name) + '}}}~' + clean_variable (replacement) + '~g;')
}

//AutoEdit irritatingly doesn't undo %XX encodings, so we can't send them.
//FIXME: Not a lot we can do about &s maybe patch AutoEdit.
function get_escape (get)
{
	return get.replace (/&/g,'%26'); //This shouldn't do anything to variables.
}

function clean_regexp (re)
{
	return re.replace (/([\\\*\+\[\]\{\}\(\)\.~])/g,'\\$1');
}

function clean_variable (va)
{
	return va.replace (/([\\~])/g,'\\$1').replace(/&/g,'{'+'{subst:⅋}}').replace(/#/,'{'+'{subst:♯}}'); //Yucky HACK
}

/*
 * The starting point of the whole script.
 * 
 * This adds a hook to the page load event so that the script runs
 * and processes the accelerated links once the page is done loading.
 */
$( function ()
	{
		// Don't do anything unless the current page is in the main namespace.
		if (wgNamespaceNumber && wgPageName != 'Wiktionary:Sandbox')
			return;
		
		// Find all the links that are marked as accelerated.
		// Then go over each red link and see if we can "enhance" it into a green link.
		poss = find_form_of_spans ();
		
		for (var i = 0;i<poss.length; i++)
		{
			var link = find_red_link (poss[i]);
			
			if (link)
			{
				// We can enhance this link, let's do it.
				try
				{
					process_link (poss[i].className.replace(/(^| +)form-of( +|$)/,'').split(' '), link);
					link.style.color = '#22CC00';
				}
				catch(e)
				{
					// We didn't manage to process the link; something must be wrong.
					// Show dotted underline under the link instead to indicate this.
					link.style.borderBottom = '2px dashed #22CC00';
					console.log(e.message);
				}
			}
		}

	} );

/*
 * Recursively find anything tagged with the "form-of" class.
 */
function find_form_of_spans ()
{
	if (typeof(document.getElementsByClassName) == 'function')
	{
		return document.getElementsByClassName('form-of');
	}
	else
	{
		var spans = document.getElementsByTagName ('span');
		var form_ofs = new Array ();

		for (var i=0; i<spans.length; i++)
		{
			if (spans[i].className.match(/(^| +)form-of( +|$)/))
			{
				form_ofs.push (spans[i]);
			}
		}

		return form_ofs;
	}
}

/*
 * Recursively find first red link in "form-of" spans.
 * FIXME: would be better to return an array as multiple params often occur
 */
function find_red_link (span)
{
	var poss = span.firstChild;

	while (poss)
	{
		if(poss.nodeType == 1)
		{
			if (poss.nodeName.toUpperCase () == 'A' && poss.className.indexOf('new') >= 0)
				return poss;
			else if (recurse = find_red_link(poss))
				return recurse;
		}
		
		poss = poss.nextSibling;
	}

	return null;
}

/*
 * Convert a raw red link into a snazzy green one.
 */
function process_link (details, link)
{
	// First, gather all the information that was given in the span's class.
	var params = get_params(details, link)
	
	// Now build a new "green link" URL to replace the original red link with
	var workerHref = '';
	
	// First, try to create an entry "internally".
	if (preload = get_preload_text(params))
	{
		workerHref = '&preloadtext=' + encodeURIComponent(preload);
	}
	// If that doesn't work, then try generating an entry "externally" by using one of
	// the script's subpages as a template.
	else if (preload = get_preload_template(params.form, params.lang, link))
	{
		workerHref =
			'&preload=' + encodeURIComponent(preload) +
			'&autoedit=' +
			((params.lang == 'da') ? set_part_of_speech (link, params.lang, params.form) : '') +
			set_lang (params.lang) + set_template_lang (params.lang, params.form) +
			set_gender_template (params.gender, params.lang) + set_template_gender(params.gender, params.lang, params.form) +
			set_origin (params.origin == params.origin_pagename ? false : params.origin, params.lang2 ? params.lang2 : params.lang) + set_pagename_linked_and_template_head (link, params.lang, params.form) +
			set_form_template (params.form, params.lang, params.gender, link) +
			set_template_sc (params.lang)
	}
	else
	{
		throw new Error('Unable to process link: "' + details + '"');
	}
	
	// Did we manage to generate a form-of entry?
	// Then replace the link's URL.
	link.href +=
		'&editintro=User:Conrad.Irwin/creation.js/intro' + workerHref
		+ '&preloadsummary=' + encodeURIComponent('Creating ' + params.form + ' form of [[' + params.origin_pagename + ']] ([[WT:ACCEL|Accelerated]])' )
		+ '&preloadminor=true';
}

// Get the parameters from the span's class
function get_params(details, link)
{
	// Default values
	var params = {
		lang: 'en',
		lang2: null,
		gender: '',
		form: '',
		origin: wgTitle,
		origin_pagename: wgTitle,
		target: link.innerText || link.textContent,
		target_pagename: null,
		pos: get_part_of_speech(link) }
	
	// Go over each value and add it
	for (var i = 0; i < details.length; i++)
	{
		if (details[i].match(/(^| +)([^ ]+)-form-of( +|$)/))
		{
			params.form = RegExp.$2;
		}
		else if (details[i].match(/(^| +)lang-([^ ]+)( +|$)/))
		{
			params.lang = RegExp.$2;
		}
		else if (details[i].match(/(^| +)lang2-([^ ]+)( +|$)/))
		{
			params.lang2 = RegExp.$2;
		}
		else if (details[i].match(/(^| +)gender-(([mfn]+|c)(pl)?)( +|$)/))
		{
			params.gender = RegExp.$2;
		}
		else if (details[i].match(/(^| +)origin-(.+)( +|$)/))
		{
			params.origin = unAnchorEncode(RegExp.$2);
		}
		else if (details[i].match(/(^| +)transliteration-(.+)( +|$)/))
		{
			params.transliteration = unAnchorEncode(RegExp.$2);
		}
	}
	
	return params;
}

function unAnchorEncode(x)
{
	return decodeURI(x.replace(/\./g, '%').replace(/_/g, '%20'));
}

//The part of speech, normally determined by other means.
function get_part_of_speech (link)
{
	var node = link;
	while(node)
	{
		while (node.previousSibling)
		{
			node = node.previousSibling;
			
			if (node.nodeType == 1 && node.nodeName.match(/^[hH][3456]$/))
			{
				return node.firstChild.innerHTML.replace(/^[1-9.]* /,'').toLowerCase();
			}
		}
		
		node = node.parentNode;
	}
	
	throw new Error('This entry seems to be formatted incorrectly. Does it have a language and part-of-speech header?');
}