MediaWiki:Gadget-Edittools.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.

A registered user can add custom menus by putting code like the following in Special:MyPage/common.js:

mw.loader.using(['mediawiki.cookie', 'jquery.textSelection']).done(function() {
    mw.hook('enwiktionary.edittools.addCategory').fire([
        {name: 'Polabian', html: '<span class="charinsert">A a Ă ă Å å Ą ą B b B́ b́ C c Ć ć Č č D d Ď ď Ʒ ʒ Ʒ́ ʒ́ E e Ė ė Ĕ ĕ F f G g Ǵ ǵ H h Χ χ Χ́ χ́ I i J j K k Ḱ ḱ L l Ľ ľ M m Ḿ ḿ N n Ń ń O o Ö ö Ǫ ǫ P p Ṕ ṕ R r Ŕ ŕ S s Ś ś Š š T t Ť ť U u Ü ü V v V́ v́ Z z Ź ź Ž ž ai̯ au̯ åi̯ åu̯ oi̯ üi̯</span>'},
        { name: 'Slovincian', insertBefore: 'Polabian', html: '...' }
    ]);
});

insertBefore provides the name of the menu to place the new menu before. Omit insertBefore to put the menu at the end of the dropdown list.


// <nowiki>
// implicit dependencies: mediawiki.cookie, jquery.textselection

(function charInsertIIFE () {
"use strict";

function CharInsertCookieLegacy(key) {
	this.key = key;
}

CharInsertCookieLegacy.prototype = {
	get: function () {
		return parseInt(mw.cookie.get(this.key), 10) || 0;
	},
	set: function (value) {
		if (typeof value === 'number')
			value = value.toString(10);
		
		if (!(typeof value === 'string' && !isNaN(parseInt(value, 10))))
			throw new TypeError("Expected string or number");
		
		return mw.cookie.set(this.key, value);
	},
};

function CharInsertCookie(key) {
	this.key = key;
}

CharInsertCookie.prototype = {
	get: function () {
		return mw.cookie.get(this.key);
	},
	set: function (value) {
		if (typeof value !== 'string')
			throw new TypeError("Expected string");
		
		return mw.cookie.set(this.key, value);
	},
};

var charInsertCookieLegacy = new CharInsertCookieLegacy('edittoolscharsubset');
var charInsertCookie = new CharInsertCookie('edittoolscharsubsetname');

/* ===applyCharinserts=== */
/* handle <span class="charinsert"> like <charinsert> */

function applyCharinserts() {
	var textbox = $('#wpTextbox1');
	
	textbox.encapsulate = function (left, right) {
		return this.textSelection(
			'encapsulateSelection', {
				pre: left.replace(/^ */, '')
					.replace(/\u0640(.)/g, '$1'), // remove ARABIC TATWEEL before Arabic diacritic
				peri: '',
				post: right
			}
		);
	};
	
	// Must set insertArgs in the element for which this is an event handler.
	function clickFunction () {
		textbox.encapsulate.apply(textbox, this.insertArgs);
		return false;
	}
	
	function getInsertArg(string, start, end) {
		string = string.substring(start, end);
		return string !== ''
			? string.replace(/\x22/g, '&quot;')
			: string;
	}
	
	function makeInsertArgs(string) {
		var index = string.indexOf('+');
		if (index === -1)
			index = string.length;
		var left = getInsertArg(string, 0, index);
		var right = getInsertArg(string, index + 1);
		return [ left, right ];
	}
	
	function makeCharInserter(string) {
		var charInserter = document.createElement('a');
		charInserter.onclick = clickFunction;
		charInserter.href = '#';
		charInserter.classList.add("charinserter");
		
		var insertArgs = makeInsertArgs(string);
		charInserter.insertArgs = insertArgs.map(function(arg) {
			return arg
				// No-break space (&nbsp;) must become an ASCII space in the inserted
				// text, but it must be displayed as a no-break space so that combining
				// characters are clickable.
				.replace(/\xA0/g, ' ')
				// Dotted circle can be used as a seat for diacritics
				// but shouldn't be inserted into the text box.
				.replace(/^\u25CC(.)/, '$1');
			
		});
		var visibleText = insertArgs.join('');
		charInserter.appendChild(document.createTextNode(visibleText));
		
		return charInserter;
	}

	function charInsertify(parent) {
		if (parent.charInsertified)
			return;
		
		parent.charInsertified = true;
		
		// Go through all child nodes of parent.
		for (var childNode = parent.firstChild;
				childNode !== null;
				childNode = childNode.nextSibling) {
			if (childNode.nodeType === 1) { // Element node
				charInsertify(childNode);
			} else if (childNode.nodeType === 3) { // Text node
				// There is a newline when the wikitext has a line break
				// followed by a character entity reference such as &#x2002;.
				// Remove the newline because we don't want to see multiple spaces.
				childNode.nodeValue = childNode.nodeValue
					.replace(/^\n+/, '');
				var strings = childNode.nodeValue
					// Split text content on ASCII whitespace characters
					// and en space (U+2002) character references.
					.split(/[ \f\n\r\t\v\u2002]+/g)
					.filter(function(string) { return string !== ''; });
				
				var addedNew = false;

				for (var i = 0; i < strings.length; ++i) {
					if (i > 0)
						parent.insertBefore(document.createTextNode(' '), childNode);
					
					parent.insertBefore(makeCharInserter(strings[i]), childNode);
					
					addedNew = true;
				}
				
				if (addedNew)
					parent.removeChild(childNode);
			}
		}
	}
	
	var charInsertSpans = document.querySelectorAll('#editpage-specialchars .charinsert');
	Array.prototype.forEach.call(charInsertSpans, charInsertify);
}

function makeCharSubsetMenuText(slug) {
	return decodeURIComponent(slug
				.replace(/\.([0-9A-F][0-9A-F])/g, '%$1')
				.replace(/_/g, '%20'));
}

/* ===addCharSubsetMenu=== */
/* add menu for selecting subsets of secial characters */
function addCharSubsetMenu() {
	var edittools = $('#editpage-specialchars');
	if (edittools.length === 0) return;

	var menu = $('<select>')
				.attr("id", 'charSubsetControl').css("display", "inline")
				.on("change", function() {
					chooseCharSubset($(this).val());
				});

	var sections = edittools.find('p');
	if (sections.length === 0) return;
	
	sections.each(function() {
		var slug = ($(this).attr("id") || '').replace(/^edittools-/, '');
		$('<option>').text(makeCharSubsetMenuText(slug)).val(slug).appendTo(menu);
	});

	/* default subset from cookie */
	var sectionIndex = charInsertCookieLegacy.get();
	var sectionName = charInsertCookie.get();

	if (!sectionName) {
		sectionName = menu.children()[sectionIndex].value;
	}

	/* update dropdown control to value of cookie */
	menu.val(sectionName);

	/* display the subset indicated by the cookie */
	chooseCharSubset(sectionName);

	edittools.prepend(menu);
}

/* ===chooseCharSubsetMenu=== */

/* select subsection of special characters */
function chooseCharSubset(sectionSlug) {
	var sections = $('#editpage-specialchars').find('p');
	var idToShow = 'edittools-' + sectionSlug;
	for (var i = 0; i < sections.length; i++) {
		var style = sections[i].style;
		style.display = sections[i].id == idToShow ? 'inline' : 'none';
	}
	
	charInsertCookie.set(sectionSlug);
}

$(function() {
	var action = mw.config.get('wgAction');
	if (window.testNewEditJs || !(action === 'edit' || action === 'submit' 
			|| $('#editpage-specialchars').length > 0))
		return;
	
	if (!window.doNotUseDefaultEditTools) // [[User:Conrad.Irwin/edittools.js]]
		addCharSubsetMenu();
	
	applyCharinserts();
	// Provide three arguments to add one category;
	// provide an array with [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
	// to add more than one.
	mw.hook('enwiktionary.edittools.addCategory').add(function (categoryName, categoryInsertBefore, categoryHtml) {
		var categories = (categoryInsertBefore !== undefined)
			? [{name: categoryName, insertBefore: categoryInsertBefore, html: categoryHtml}]
			: categoryName;
		
		var edittools = $('#editpage-specialchars');
		if (edittools.length === 0) return;
		var menu = edittools.find('#charSubsetControl');
		if (menu.length === 0) return;
		
		for (var i = 0; i < categories.length; i++) {
			var category = categories[i];
			var name = category.name,
				insertBefore = category.insertBefore,
				html = category.html;
			name = name.replace(/\s/g, '_');
			var selector = insertBefore ? '#charSubsetControl option[value="' + insertBefore.replace('"', '\\"') + '"]' : null;
			var option = $('<option>').text(makeCharSubsetMenuText(name)).val(name);
			
			$('<p>').attr('id', 'edittools-' + name).addClass('speciallang').css('display', 'none').html(html).appendTo(edittools);
			if (selector && menu.find(selector)) {
				option.insertBefore(selector);
			} else {
				option.appendTo(menu);
			}
		}
		
		applyCharinserts();
		var cookieVal = charInsertCookie.get();
		menu.val(cookieVal);
		chooseCharSubset(cookieVal);
	});
});

})();

// </nowiki>