User:So9q/new-entry-creator.js

From Wiktionary, the free dictionary
Jump to navigation Jump to search

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.

This script is meant to replace the official NEC.

It adds a tab on top next to watch when installed to your common.js, it is best partnered with User:So9q/CreateTranslation.js and they can be installed with:

importScript( 'User:So9q/CreateTranslation.js' ); // Backlink: [[User:So9q/CreateTranslation.js]]
importScript( 'User:So9q/new-entry-creator.js' ); // Backlink: [[User:So9q/new-entry-creator.js]]

Customization

Default language

It can be customized by setting this variable like this in your common.js:

window.NEC_default_code == 'en'

Code structure

The code is structured in a dozen helper functions.

  • First we get initialize the variables from GET if any.
  • Then we check whether it is a stem update of inflections or if it is a first run.
  • Then we call the processor with the relevant sub array from the necinfl object and display the result
  • When "generate" is pressed we collect the values and generate the wikicode.

/*
// Rewrite of https://en.wiktionary.org/w/index.php?title=User:Yair_rand/newentrywiz.js&oldid=54099266
// by So9q 19/9-2019
*/
/* jshint sub:true, shadow:true, undef:true, unused:true, strict:true, forin:true, latedef:true */
/* global $, mw */
// <nowiki>

"use strict";

function customize() {
	if (typeof window.NEC_default_lang == 'undefined') {
	// Default is not defined, set to defaults
	window.NEC_default_lang = "da";
	}
}
function capitalize(s) {
		if (typeof s !== 'string') return '';
		return s.charAt(0).toUpperCase() + s.slice(1);
}

// Initialize variables
// Support prefilling via GET
if (mw.util.getParamValue('ct') == 'true') {
	var pos = mw.util.getParamValue('pos');
	var pos_cap = capitalize(pos);
	var def = '[[' + mw.util.getParamValue('def') + ']]';
	var lang = mw.util.getParamValue('lang');
}
else {
	// enable customization
	customize();
	var lang = window.NEC_default_lang;
	var pos, def = '';
}
function wikilinkText() {
    var sel;
    if (window.getSelection) {
        sel = window.getSelection();
        var activeElement = document.activeElement;
        if (activeElement.nodeName == "TEXTAREA" ||
           (activeElement.nodeName == "INPUT" && activeElement.type.toLowerCase() == "text")) {
               var val = activeElement.value, start = activeElement.selectionStart, end = activeElement.selectionEnd;
               activeElement.value = val.slice(0, start) + '[[' + sel + ']]' + val.slice(end);
        }
	}
}

function label() {
    var sel;
    if (window.getSelection) {
        sel = window.getSelection();
        var activeElement = document.activeElement;
        if (activeElement.nodeName == "TEXTAREA" ||
           (activeElement.nodeName == "INPUT" && activeElement.type.toLowerCase() == "text")) {
               var val = activeElement.value;
               activeElement.value = '{{lb|' + lang + '|}} ' + val;
        }
	}
}
function button(value,lang,append) {
    var sel;
    if (window.getSelection) {
        sel = window.getSelection();
        var activeElement = document.activeElement;
        if (activeElement.nodeName == "TEXTAREA" ||
           (activeElement.nodeName == "INPUT" && activeElement.type.toLowerCase() == "text")) {
            var val = activeElement.value;
            if (append === (null || undefined))
        		var append = '|'
        	if (append === false)
        		var append = ''
        	if (lang === false)
        		activeElement.value = val + '{{' + value + append + '}} ';
			else
            	activeElement.value = val + '{{' + value +'|' + lang + append + '}} ';
        }
	}
}

function setup_defs() {
	    var max_fields = 10;
    var wrapper = $(".container1");
    var add_button = $(".add_form_field");

    var x = 1;
    $(add_button).click(function(e) {
        e.preventDefault();
        if (x < max_fields) {
            x++;
            //add input box
            $(wrapper).append($('<div>').append(
            	$('<input>').addClass('def').attr({
					type: 'text',
					//name: def,
					size: 40
				}),
				'<a href="#" class="delete">Delete</a>'));
        } else {
            alert('You Reached the limits');
        }
    });

    $(wrapper).on("click", ".delete", function(e) {
        e.preventDefault();
        $(this).parent('div').remove();
        x--;
    });
}
function setup_headings() {
	$('#headings').on('click', '.delete_heading', function(e){
				console.info($(this).parent('div'));
				e.preventDefault();
				$(this).parent('div').remove();
		});
}
function clean(option) {
	// Clean spaces and parens
	return option.replace(/\s/g, '').replace(/\./g, '')
		.replace(/\(/g, '').replace(/\)/g, '');
}
function generate() {
	console.info('generate clicked');
	var lang = $('#lang').val();
	// Language heading
	var markup = "=={{subst:#invoke:languages/templates|getByCode|" + lang + "|getCanonicalName}}==\n\n";
	// Etymology
	if ($('#ety').val() !== '') {
		markup += "===Etymology===\n";
		markup += $('#ety').val() + "\n\n";
	}
	else {
		markup += "===Etymology===\n";
		markup += "{{rfe|" + lang + "}}\n\n";
	}
	// PoS
	var pos = $('[name="pos"]:checked').val();
	if (pos !== '') {
		markup += "===" + pos + "===\n";
	}
	else {
		// this should never happen because we select noun by default
		mw.notify('Please select a Part of Speech');
		return;
	}
	function get_value(option) {
		// Get the value from either the radio buttons value= field or 
		// the input=text coming after the radio button
		// Clean option
		var option = clean(option);
		var val = $('[name="'+option+'infl"]:checked').val();
		console.info('val: '+val)
		// Support languages where only prepend and append is set
		if (val === undefined)
			return '';
		// Get value from radio button
		else if (val !== 'other')
			return val;
		// Get value from input box after radio=other
		else {
			return $('.span_other_'+option).find('input[type=text]').val();
		}
	}
	// PoS subheading
	// Common vars
	var pre = $('[name=prepend]').val();
	var app = $('[name=append]').val();
	if ((pre && app) !== undefined) {
		// PoS
		if (pos == 'Noun') {
			var stem = get_value("Stem");
			var sd = get_value("Singular definite");
			var pl = get_value("Plural");
			markup += pre  + stem + sd + pl + app + "\n\n";
		}
		else if (pos == 'Verb') {
			var im = get_value("Imperative form without particle ending");
			// Halt and warn if imperative is not set
			if (im === '|' || im === 'undefined' || im === undefined){
				mw.notify('Imperative is empty, please fix');
				return;
			}
			else {
				var im_pe = get_value("Imperative (particle ending)");
				if (im_pe === '|particle=')
					im_pe = '';
				var inf = get_value("Infinitive");
				var pr = get_value("Present");
				var pa = get_value("Past");
				var pp = get_value("Past participle");
				// Handle double consonants
				var dc = $('[name="dc"]:checked').length;
				// We have 4 cases:
				// 1. with double consonant and no particle ending defined
				if (dc == '1' && im_pe === undefined)
					markup += pre + im + inf + pr + pa + pp + '|dc=1' + app + "\n\n";
				// 2. with double consonant and particle ending defined
				else if (dc == '1' && im_pe !== undefined)
					markup += pre + im + im_pe + inf + pr + pa + pp + '|dc=1' + app + "\n\n";
				// 3. with no double consonant and particle ending defined
				else if (dc !== '1' && im_pe !== undefined)
					markup += pre + im + im_pe + inf + pr + pa + pp + app + "\n\n";
				// 4. with no double consonant and no particle ending defined
				else
					markup += pre + im + inf + pr + pa + pp + app + "\n\n";
			}
		}
		else if (pos == 'Adjective') {
			var ne = get_value("Neuter");
			var pd = get_value("Plural or definite");
			var co = get_value("Comparative");
			var sp = get_value("Superlative pred");
			var sa = get_value("Superlative attr");
			markup += pre  + ne + pd + co + sp + sa + app + "\n\n";
		}
		else {
			markup += pre + app + "\n\n";
		}
	}
	else {
		mw.notify('No template or inflection data found. Defaulting to {{head}}');
		markup += "{{head" + "|" + lang + "|" + pos.toLowerCase() + "}}\n\n";
	}
	// Definitions
	$('.def').each(function(){
		if ($(this).val() === ''){
			mw.notify('A definition was empty, defaulted to {{rfdef}}');
			markup += "# {{rfdef|" + lang + "}}\n";
		}
		else
			markup += "# " + $(this).val() + "\n";
	});
	// Add newline after def
	markup += "\n";
	// Add headings
	$('.hl').each(function(){
		//var markup = markup
		if ($(this).next().val() === ''){
			mw.notify('A field is empty, please fix');
			return;
		}
		else {
			markup += "===" + $(this).text() + "===\n";
			markup += $(this).next().val() + "\n\n";
		}
	});
	$('#wpTextbox1').val(markup);
	// Edit summary
	var neceditsummary = "([[User:So9q/new-entry-creator.js|So9qs rewritten NEC]]) +" + lang + ":" + pos + ": "; 
	$('.def').each(function(){
			neceditsummary += $(this).val() + ", ";
	});
	// Set summary
	$("#wpSummary").val(neceditsummary);
}
// Inflection data ported and extended from newentrywiz.js
var title = mw.config.get( 'wgTitle' );
var necinfl = {
	// This is the reference object
	da: {
		noun: {
			prepend: '{{da-noun',
			append: '}}',
			// The subarrays in items all have this format: 
			// prepend/special word, append, wikicode
 			// The special words supported are: (inherited), 
 			// (no singular definite), (no plural), (not used), 
 			// (not comparable), input
			items: [['Stem', '', [
						['(inherited)', '', ''],
						['input', 'Other: ', '|stem=']
					]],
					['Singular definite', '', [
						// format: prepend/special, append, wikicode
						// specials: (inherited), (no singular definite), 
						// (no plural), (not used), (not comparable), input
						['', 'en', '|en'],
						['', 'et', '|et'],
						['', 't', '|t'],
						['', 'n', '|n'],
						['(no singular definite)', '', '|-'],
						['input', 'Other: ', '|']
					]], 
					['Plural', '', [
						['', 'e', '|e'],
						['', 'er', '|er'],
						['', 'r', '|r'],
						['', 's', '|s'],
						['', '', '|'], // e.g. syndefald
						['(no plural)', '', '|-'],
						['input', 'Other: ', '|']
					]]],
		},
		verb: {			
			prepend: '{{da-verb',
			append: '}}',
			double_consonant: '1', // 1=true 0=false
			items: [
				// This now supports splitting verbs with particle endings
				// Example: tage kål på
				['Imperative form without particle ending', '', [
					['input', ' ', '|'] // tage
				]], 
				['Imperative (particle ending)', '', [
					['input', 'Optional: ', '|particle='] // kål på
				]], 
				['Infinitive', '', [
					['(inherited)', '', '|'],
					['input', 'Other: ', '|']
				]], 
				['Present', '', [
					['', 'r', '|er'],
					['', 'er', '|er'],
					['(not used)', '', '|-'],
					['input', 'Other: ', '|']
				]], 
				['Past', '', [
					['', 'ede', '|ede'],
					['(not used)', '', '|-'],
					['input', 'Other: ', '|']
				]], 
				['Past participle', '', [
					['har ', 'et', '|har|et'],
					['har ', 't', '|har|t'],
					['(not used)', '', '|-'],
					['input', 'Other: ', '|har|']
				]], ],
		},
		adjective: {
			prepend: '{{da-adj',
			append: '}}',
			items: [['Neuter', '', [
				['', 't', '|t'],
				['input', 'Other: ', '|']
			]], 
			['Plural or definite', '', [
				['', 'e', '|e'],
				['', 'er', '|er'],
				['(not used)', '|-'],
				['input', 'Other: ', '|']
			]], 
			['Comparative', '', [
				['', 'ere', '|ere'],
				['mere ', '', '|mere'],
				['(not used)', '|-'],
				['input', 'Other: ', '|']
			]], 
			['Superlative pred.', '', [
				['', 'est', '|est'],
				['', 'st', '|st'],
				['mest ', '', '|mest'],
				['(not used)', '', '|-'],
				['input', 'Other: ', '|']
			]], 
			['Superlative attr.', '', [
				['', 'este', '|este'],
				['', 'ste', '|ste'],
				['mest ', 'e', '|mest ' + title + 'e'],
				['(not used)', '', '|-'],
				['input', 'Other: ', '|']
			]], ],
		},
		adverb: {prepend: '{{da-adv', append: '}}'},
		propernoun: {prepend: '{{da-proper noun', append: '}}'}
	},
	
	eo: {
		noun: {prepend: '{{eo-noun', append: '}}'},
		verb: {prepend: '{{eo-verb', append: '}}'},
		adjective: {prepend: '{{eo-adj', append: '}}'},
		adverb: {prepend: '{{eo-adv', append: '}}'},
		propernoun: {prepend: '{{eo-proper noun', append: '}}'}
	},

	es: {
		noun: {prepend: '{{es-noun', append: '}}'},
		adjective: {prepend: '{{es-adj', append: '}}'},
		adverb: {prepend: '{{es-adv', append: '}}'},
		propernoun: {prepend: '{{es-proper noun', append: '}}'}
	},

	fr: {
		noun: {
			prepend: '{{fr-noun', 
			append: '}}', 
			items: [['Gender', '', [
				['Unknown', '', ''],
				['masculine', '', '|m'],
				['feminine', '', '|f']
			]],
			['Plural', '', [
				['', 's', ''],
				['(uncountable)', '', '|pl=-'],
				['input', ' ', '|pl=']]]
			]
		},
		adjective: {prepend: '{{fr-adj', append: '}}'},
		verb: {prepend: '{{fr-verb', append: '}}'},
		adverb: {prepend: '{{fr-adv', append: '}}'}
	},
};

// This function sets up the inflection subsections based on the dynamic 
// variables stem and imperative when update_only is true.
// TODO implement imperative and generalize more
function setup_infl(update_only) {
	function processor(object, option, stem, imperative) {
		/*	This is the workhorse of the inflection setup.
			There are 4 scenarios:
			1) option is Stem and stem is null
				=> generate stem radio and input
			2) option is not stem and stem is null
				=> generate radio and input for the rest
			3) (update_only=true) option is Stem and stem is set
				Nothing is done in this case
			4) (update_only=true) option is not stem and stem is set
				=> update the inflections
		*/
		// Clean option
		var option = clean(option);
		var specials = ['(no singular definite)', '(no plural)', 
				'(not used)', '(not comparable)', '(uncountable)'];
		var infls = object[2].map(function(value, index){
				//console.info('subarray value: '+ value[0]);
				var pre = value[0];
				var app = value[1];
				var wiki = value[2];
				//console.info('pre: '+pre + ' app: '+app+' wiki: '+wiki)
				var title;
				if (stem){
					// Use clean stem as title
					title = stem.slice(6);
					console.info('stem is set: ' + stem);
				}
				else if (imperative) {
					// Use clean imperative as title
					title = imperative.slice(1);
					console.info('imperative is set: ' + imperative);					
				}
				else 
					title = mw.config.get( 'wgTitle' );
				// [0] = displayed infl
				// [1] = wikicode
				// Check the first options when 
				if (index === 0 && specials.indexOf(pre) === -1 &&
					option !== 'Stem' && pre !== 'input')
					return '<label class="nec_infl_removable">' +
							'<input type="radio" name="' + option + 'infl" value="' + wiki + '"' + 'checked="checked"' + '>' +
							pre + title + app +	'</label>';
				else if (pre === '(inherited)' && stem === null)
					// show only pre and check
					// stem must be null to avoid duplicating the stem input box on update
					return '<label>' +
							'<input type="radio" name="' + option + 'infl" value="' + wiki + '"' + 'checked="checked"' + '>' +
							 pre + 	'</label>';
				// Specials - show only pre
				else if (specials.indexOf(pre) !== -1)
					return '<label class="nec_infl_removable">' +
							'<input type="radio" name="' + option + 'infl" value="' + wiki + '"' + '>' +
							 pre + 	'</label>';
				else if (pre === 'input' && option === ('Stem' || 'Imperative form without particle ending') && stem === null)
					// This is the input we later gather the custom stem from
					// stem must be null to avoid duplicating the stem input box on update
					return $('<span>').addClass('span_other_'+ option).append(
						$('<input>').attr({
							type: 'radio',
							name: option + 'infl',
							value: 'other'
						}),
						$('<label>').text(app).append(
						$('<input>').addClass('input_text').attr({
							type: 'text',
							name: clean(option),
							value: wiki
						})));
				else if (pre == 'input' && option != 'Stem')
					// Other inputs not related to stem that we remove and update later
					return $('<span>').addClass('span_other_'+ option)
					.addClass('nec_infl_removable').append(
						$('<input>').attr({
							type: 'radio',
							name: option + 'infl',
							value: 'other'
						}),
						$('<label>').text(app).append(
						$('<input>').addClass('input_text').attr({
							type: 'text',
							name: 'other',
							value: wiki
						})));
				else if (option != 'Stem')
					// Options not related to stem or input
					return '<label class="nec_infl_removable">' +
							'<input type="radio" name="' + option + 'infl" value="' + wiki + '">' +
							pre + title + app + '</label>';
				});
		return infls;
	}
	// Common vars
	var lang = $('#lang').val();
	var pos = $('[name="pos"]:checked').val();
	var pos_lower = pos.toLowerCase();
	var title = mw.config.get( 'wgTitle' );
	
	// Update the inflection based on the stem the user typed in
	// TODO enable for imperative in verbs also.
	if (update_only === true && pos_lower in necinfl[lang]) {
		console.info('update_only infl');
		// This is never true if inflection data does not exist
		// Stem is already set, get it
		var stem_input = $('.input_text[name=stem]').val();
		var imperative_input = $('.input_text[name=imperative]').val();
		var stem;
		var imperative;
		if (stem_input !== '|stem=') {
			stem = stem_input;
			console.info('stem: ' + stem);
		}
		else if (imperative_input !== '|') {
			imperative = imperative_input;
			console.info('imperative: ' + imperative);
		}
		// Generate the inflections
		var infl_items_update = necinfl[lang][pos_lower]["items"].map(function(value){
			// [0] contains the the infl option
			// [1] empty
			// [2] subarray with options
			//console.info(value)
			var option = value[0];
			if (option == ('Stem' || 'Imperative')) {
				// These should not be updated
				return '';
			}
			else {
				// These should also be removed
				var p = $('<p>').addClass('nec_infl_removable_p').text(option+':');
				// Process subarray other than Stem
				var infls = processor(value, option, stem, imperative);
				//console.info(infls)
				return $(p).append(infls);
			}
		});
		// Obsolete ones now that a custom stem has been entered
		// parent is the var p in the loop
		$('.nec_infl_removable').parent().remove();
		$('.nec_infl_removable_p').remove();
		// Append custom stem-based inflections
		//console.info(infl_items_update)
		$('.infl').append(infl_items_update);
		setup_click_keyup();
	}
	// Update is not true, generate all inflection options
	else {
		//console.info('default infl');
		// Inflection data
		// Common var
		var p_title = $('<p>').text('Inflections');
		// Process inflection data if it exist
		if(necinfl.hasOwnProperty(lang)) {
			console.info('has lang data');
			if(pos && title && pos_lower in necinfl[lang]) {
				console.info('default infl ' + lang + ' ' + pos_lower);
				var pre = necinfl[lang][pos_lower]["prepend"];
				var app = necinfl[lang][pos_lower]["append"];
				var dc = necinfl[lang][pos_lower]["double_consonant"];
				// Include prepend and append as hidden
				var hidden = $('<span>').append(
						$('<input>').attr({
							type: 'hidden',
							name: 'prepend',
							value: pre
						}),
						$('<input>').attr({
							type: 'hidden',
							name: 'append',
							value: app
						}));
				// Handle double consonants
				if (dc == '1')
					var dc_p = $('<p>').text('Enable double consonant handling').append(
						$('<input>').attr({
							type: 'checkbox',
							name: 'dc',
					}));
				else
					var dc_p = '';
				// Check if items are defined
				var infl_items;
				if ("items" in necinfl[lang][pos_lower]){
					// Generate the inflections for the first time
					infl_items = necinfl[lang][pos_lower]["items"].map(function(value){
						// [0] contains the the infl option
						// [1] empty
						// [2] subarray with options
						var option = value[0];
						var p = $('<p>').text(option+':');
						// Process subarray
						var infls = processor(value, option, null);
						return $(p).append(infls);
					});
				} else {
					// Nothing other than prepend and append in the data
					infl_items = $('<small>').addClass('nec_infl_removable_p')
						.text('Only template found, no inflection data.').css('color', 'red');
				}
			}
			// Remove "No inflection data found" when switching PoS
			$('.infl').children().remove();
			$('.infl').append(p_title, hidden, dc_p, infl_items);
			setup_click_keyup();
		} // if data found
		else {
			console.info('no infl data');
			// No inflection data found at all
			// remove old inflections if any
			$('.infl').children().remove();
			$('.infl').append(p_title.append(
				$('</br>'),
				$('<small>').addClass('nec_infl_removable').text('No inflection data found').css('color', 'red')));
		}
	}
}

function setup_click_keyup() {
	console.info('running setup_click_keyup()')
	// Setup focus on other input boxes
	$('.input_text').focus(function() {
		$(this).parent().parent().find('[type=radio]').prop('checked', true);
	});
	// Reload infl if stem is changed
	$('.input_text[name=Stem]').keyup(function() {
		setTimeout(function(){ 
	        setup_infl(true);
		}, 1500);
	});
	// Reload infl if code is changed
	$('#lang').keyup(function() {
		setTimeout(function(){ 
	        setup_infl();
		}, 1500);
	});
	// Reload infl if imperative form is changed
	var imp_cleaned = clean('Imperative form without particle ending')
	$('.input_text[name='+imp_cleaned+']').keyup(function() {
		setTimeout(function(){ 
	        setup_infl(true);
		}, 1500);
	});
}

function form() {
	var necheadings = ['Usage notes', 'Inflection', 'Conjugation', 'Declension', 'Quotations', 'Synonyms', 'Antonyms', 'Hypernyms', 'Hyponyms', 'Meronyms', 'Holonyms', 'Derived terms', 'Related terms', 'Coordinate terms', 'Descendants', 'Translations', 'See also', 'References'];
	var necposlist = ['Noun', 'Verb', 'Adjective', 'Adverb', 'Pronoun', 'Conjunction', 'Interjection', 'Preposition', 'Proper noun', 'Article', 'Prepositional phrase', 'Contraction', 'Prefix', 'Suffix', 'Symbol', 'Letter', 'Idiom', 'Phrase'];
	var p_lang = $('<p>').text('Language code: ').append(
		$('<input>').attr({
			type: 'text',
			value: lang,
			id: 'lang',
			size: 10
	}))[0];
	// Etymology
	var ety_b_items = ['cog', 'com', 'af'];
	var ety_buttons = ety_b_items.map(function(value){
		return $('<input>').addClass('unselectable', 'eb').attr({
			type: 'button',
			name: value,
			value: 'Insert ' + value,
			onmousedown: 'button("' + value + '","'+ lang +'")'
		})[0];
	});
	//'<input class="unselectable" type="button" name="label" value="Insert label" onmousedown="label(); return false">';
	var p_ety = $('<p>').text('Etymology: ').append(
		$('<input>').attr({
			type: 'text',
			value: '',
			id: 'ety',
			size: 40
	}))[0];
	// Generate new array by mapping over necposlist
	var pos_items= necposlist.map(function(value) {
		// Check the PoS from GET
		if (pos_cap === value) {
			return '<label class="nec-' + value + '">' +
					'<input type="radio" name="pos" value="' + value + '"' + 'checked="checked"' + '>' +
					value +
					'</label>';
		}
		// Check noun by default if no PoS set
		else if (typeof pos_cap === 'undefined' && value == 'Noun') {
			console.info('defaulting to noun');
			return '<label class="nec-' + value + '">' +
					'<input type="radio" name="pos" value="' + value + '"' + 'checked="checked"' + '>' +
					value +
					'</label>';
		}
		else {
			return '<label class="nec-' + value + '">' +
					'<input type="radio" name="pos" value="' + value + '">' +
					value +
					'</label>';
		}
	});
	var pos_joined = '<p class="nec-options">' + pos_items.join(' ') + '</p>';
	var p_pos = $('<p>').text('Part of speech: ').append(pos_joined)[0];
	// Inflection
	var div_infl = $('<div>').addClass('infl');
	var link_button = '<input class="unselectable" type="button" name="PasteOver" value="[[ + ]]" onmousedown="wikilinkText(); return false">';
	var label_button = '<input class="unselectable" type="button" name="label" value="Insert label" onmousedown="label(); return false">';
	var rfdef_button = $('<input>').addClass('unselectable', 'eb').attr({
			type: 'button',
			name: 'rfdef',
			value: 'Insert {{rfdef|lang}}',
			title: 'Note: This is the default if a definition is left empty.',
			onmousedown: 'button("rfdef",true,false)' // false= no trailing '|'
		})[0];
	var def_buttons = $('<div>').addClass('def_buttons').append(
		link_button,label_button,rfdef_button);
	var div_def = $('<div>').addClass('container1').append(
		$('<button>').addClass('add_form_field').text('Add another definition'),
		$('<div>').append(
			$('<p>').text('Definition: '),
			$('<input>').addClass('def').attr({
				type: 'text',
				value: def,
				size: 40
			})));
	// Reference buttons
	var ref_button_da = $('<input>').addClass('unselectable', 'eb').attr({
			type: 'button',
			name: 'ddo',
			value: 'Insert {{R:DDO}}',
			onmousedown: 'button("R:DDO",false,false)' // no lang, no trailing '|'
		})[0];
	// TODO add customization
	var ref_buttons = $('<div>').addClass('ref_buttons').append(
		ref_button_da);
	// Generate the headings dynamically
	// First generate the links
	var heading_links= necheadings.map(function(value) {
		var h4 = $('<p>').text(value).addClass('hl').attr('id', 'p_' +value);
		var input = $('<input>')
			.css({
				width: '50%',
				'min-height': '5em'
			})
			.attr({
				type: 'textarea',
				value: '',
				id: 'input_' + value,
				/*rows: 5,
				cols: 20*/
			});
		var a = $('<a>')
			.attr({
				'title': 'Remove',
				'href': '#'})
			.text('(−)')
			.addClass('delete_heading');
		return $('<a title="Add ' + value + ' heading">' + value + ' </a>').click(function(){
			$('#headings').append($('<div>').append(h4, input, a));
		});
	});
	// Show the links
	var headings_links = $('<div>').attr({id:'headings_links'}).append(heading_links);
	// This is the div that the headings get appended to
	var headings = $('<div>').attr({id:'headings'});
	var generate_button = $('<input>').attr({
			type: 'submit',
			value: 'Generate',
	}).click(function(){generate()});
	var form = $('<div>').append(
		p_lang, ety_buttons, p_ety, p_pos, div_infl, def_buttons, div_def, headings_links, ref_buttons, headings, generate_button);
	// workaround bug related to the editform
	$('#editform').prepend($('#so9q-nec'), $('#editform').children().first());
	// Add the content
	$('#mw-content-text > div.mw-editintro').append(form);
	// Setup dynamic defs
	setup_defs();
	setup_headings();
	setup_infl();
	// Setup click on PoS
	$('[name="pos"]').click(setup_infl);
}

$(document).ready(function necload() {
	$.when( mw.loader.using('mediawiki.util'), $.ready ).then( function() {
		if (mw.config.get('wgPageName') === 'Special:Search') {
			mw.util.addPortletLink( mw.config.get('skin') === 'vector' ? 'p-views' : 'p-cactions', '/w/index.php?title=' + mw.util.getParamValue('search') + '&action=edit&editintro=User:So9q/usenec', 'nec', 'id', 'use the New Entry Creator to create a new entry', 'n', 'nextnode');
		}
		if (typeof variable === 'undefined' || mw.util.getParamValue('action') === 'undefined' || mw.util.getParamValue('action') === 'edit' && mw.util.getParamValue('redlink') === '1') {
			mw.util.addPortletLink( mw.config.get('skin') === 'vector' ? 'p-views' : 'p-cactions', '/w/index.php?title=' + mw.config.get('wgPageName') + '&action=edit&editintro=User:So9q/usenec', 'nec', 'id', 'use the New Entry Creator to create a new entry', 'm', 'nextnode');
		}
		if (document.getElementById('so9q-nec')) {
			form();
		}
	} );
});
// </nowiki>