MediaWiki:Gadget-Numerakri.js

/** * Convert Arabic numerals to Devanagari and vice-versa. * * Revision: 2018-12-29 * Dependencies: jquery.cookie, mediawiki.util, mediawiki.user, mediawiki.RegExp * Source at https://www.mediawiki.org/wiki/MediaWiki:Gadget-Numerakri.js * * @copyright 2012 Daniel Friesen * @copyright 2012 Siddhartha Ghai * @copyright 2012-2018 Timo Tijhof * @license MIT  */ /*jshint browser:true, boss:true, unused:true, forin:false */ /*global $, mw */ (function {	'use strict';

var msgs = { 'option-default': { en: 'Default', bho: 'डिफ़ॉल्ट', gu: 'ડિફોલ્ટ', hi: 'डिफ़ॉल्ट', ne: 'डिफल्ट', pa: 'ਅਸਲ ਦਿੱਖ' },			'option-arabic': { en: '123' },			'option-devanagari': { en: 'Devanagari', bho: '१२३', hi: '१२३', ne: '१२३' },			'option-gujarati': { en: 'gujarati', gu: '૧૨૩' },			'option-gurmukhi': { en: 'Gurmukhi', pa: '੧੨੩' },			'label-url': { en: '//www.mediawiki.org/wiki/MediaWiki_talk:Gadget-Numerakri.js', bho: '//bh.wikipedia.org/wiki/विकिपीडिया:अंक_बदलाव ', gu: '//gu.wikipedia.org/wiki/વિકિસૂક્તિઃઅંક_પરિવર્તક', hi: '//hi.wikipedia.org/wiki/विकिपीडिया:अंक_परिवर्तक', ne: '//ne.wikipedia.org/wiki/विकिपीडिया:अंक_परिवर्तक', pa: '//pa.wikipedia.org/wiki/ਵਿਕੀਪੀਡੀਆ:ਅੰਕ ਬਦਲੋ' },			'label-text': { en: 'Convert numerals', bho: 'अंक बदलाव', gu: 'અંક પરિવર્તક', hi: 'अंक परिवर्तन', ne: 'अंक परिवर्तन', pa: 'ਅੰਕ ਬਦਲੋ' },			'label-tooltip': { en: 'Convert between Arabic and Devanagari numerals', bho: 'नागरी आ अरबी अंकों में अदला-बदली करीं', gu: 'ગુજરાતી અને અંગ્રેજી અંકોમાં પરિવર્તન કરો', hi: 'नागरी और अरबी अंकों में परिवर्तन करें', ne: 'देवनागरी र अरबी अंकहरूमा परिवर्तन गर्नुहोस्', pa: 'ਅੰਕਾਂ ਨੂ ਗੁਰਮੁਖੀ ਤੇ ਅਰਬੀ ਵਿਚ ਬਦਲੇਂ' }		},		maps = { // 0 to 9 arabic: [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ], devanagari: [ '०', '१', '२', '३', '४', '५', '६', '७', '८', '९' ], gujarati: [ '૦', '૧', '૨', '૩', '૪', '૫', '૬', '૭', '૮', '૯' ], gurmukhi: [ '੦', '੧', '੨', '੩', '੪', '੫', '੬', '੭', '੮', '੯' ] },

/**		 * The currently active type. *		 * Must be a key of `maps`, or 'default'. *		 * The default is 'default', which leaves the page unchanged. *		 * @property {string} */		currentType = 'default',

/**		 * Matcher for characters that can be mapped. *		 * For example, `matchers.devanagari[0]` matches Devanagari numeral for 0 * can be mapped with e.g. `maps.arabic[0]`. *		 * @var {RegExp} */		matchers = {},

walker, availableMaps, styleTag;

/** @return {boolean} */ function isValidType(type) { return type === 'default' || !!maps[type]; }

/** @return {Object.} */ function getMatchers(targetType) { var rChars; if (!matchers[targetType]) { rChars = { 0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [] }; $.each(maps, function (type, map) {				if (type !== targetType) {					for (var i = 0; i <= 9; i++) {						rChars[i].push(map[i]);					}				}			}); $.each(rChars, function (num, chars) {				rChars[num] = new RegExp('(' + chars.map(mw.RegExp.escape).join('|') + ')', 'g');			}); matchers[targetType] = rChars; }		return matchers[targetType]; }

function msg(key) { return msgs[key][mw.config.get('wgUserLanguage')] || msgs[key].en; }

/**	 * @param {HTMLElement|TextNode} node * @param {string} className * @return {boolean} */	function hasClass(node, className) { // First check if the node is HTMLElement, // and then check if it has the class return !!node.className && (" " + node.className + " ").indexOf(" " + className + " ") !== -1; }

/**	 * @param {HTMLElement|TextNode} node * @return {number} NodeFilter.FILTER_* constant */	function filterNode(node) { var n = node.nodeName && node.nodeName.toLowerCase; if (n === 'input' || n === 'textarea' || hasClass(node, 'mw-numerakri-skip')) { // Skip this element and skip its children as well return NodeFilter.FILTER_REJECT; }		if (node.nodeType === Node.TEXT_NODE) { return NodeFilter.FILTER_ACCEPT; }		// Skip this element, but check its children. return NodeFilter.FILTER_SKIP; }

/**	 * @param {TextNode} node */	function handleTextNode(node) { var original = String(node.nodeValue), changed = original, matchers = getMatchers(currentType), i = 0; for (i <= 9; i++) { changed = changed.replace(matchers[i], maps[currentType][i]); }		if (original !== changed) { node.nodeValue = changed; }	}

// https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw-method-requestIdleCallback function idleWalker(deadline) { var el; if (!walker) { return; }		while (deadline.timeRemaining > 0) { el = walker.nextNode; if (!el) { // Reached the end walker = null; return; }			handleTextNode(el); }

// deadline.timeRemaining reached 0. // The user may be interacting with the page and we need to pause so		// that the browser can process it (otherwise the user will perceive		// the browser as being frozen). The text handler will automatically // continue after that. if (walker) { mw.requestIdleCallback(idleWalker); }	}

/**	 * Save the selected type in the browser cookies and remember it * for 30 days. *	 * @param {string|null} value */	function saveType(value) { // Do this save action in the background (for performance) mw.requestIdleCallback(function {			$.cookie('mw-numerakri-type', value, { expires: 30, path: '/' });		}); }

/**	 * @return {string|null} */	function getStoredType { var value = $.cookie('mw-numerakri-type'); if (value !== null && !isValidType(value)) { mw.log.warn('Numerakri found invalid type in cookie: ' + value); // Remove bad cookie saveType(null);

value = null; }

return value || 'default'; }

function startPageConversion { if (styleTag) { // Undo style for a previously selected type $(styleTag).remove; styleTag = null; }

if (currentType === 'default') { // Type 'default' means "don't change the page". return; }

if (currentType === 'arabic') { styleTag = mw.loader.addStyleTag('.mw-parser-output ol, ol.references, li.references { list-style-type: decimal; }'); } else if (currentType === 'devanagari') { styleTag = mw.loader.addStyleTag('.mw-parser-output ol, ol.references, li.references { list-style-type: devanagari; }'); }

// If a walker is already active, replace it. // If no walker is active yet, start it via requestIdleCallback. if (!walker) { mw.requestIdleCallback(idleWalker); }

walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ALL, filterNode, false); }

/** @return {HTMLElement} */ function createSelector { var $select = $(' ').addClass('mw-numerakri-skip').append(			$(' ').val('default').text(msg('option-default'))		); for (var type in maps) { $select.append($(' ').val(type).text(msg('option-' + type))); }

$select.val(currentType);

$select.on('change', function {			currentType = this.value;			saveType(currentType);			startPageConversion;		});

return $select[0]; }

function init { var potlet, menu;

// Decide which maps are available on this wiki if (mw.config.get('wgContentLanguage') === 'gu') { // gu.wikipedia.org availableMaps = ['gujarati', 'arabic']; } else if (mw.config.get('wgContentLanguage') === 'pa') { // pa.wikipedia.org availableMaps = ['gurmukhi', 'arabic']; } else { // hi.wikipedia.org // ne.wikipedia.org availableMaps = ['devanagari', 'arabic']; }

// Read initial value from cookie storage currentType = getStoredType;

startPageConversion;

potlet = mw.util.addPortletLink(			'p-personal',			msg('label-url'),			msg('label-text'),			'pt-numconvert',			msg('label-tooltip'),			null,			mw.user.isAnon ? '#pt-createaccount' : '#pt-userpage'		); menu = document.createElement('div'); menu.className = 'mw-numerakri-menu'; menu.appendChild(createSelector); potlet.appendChild(menu); }

$.when(mw.loader.using('mediawiki.user'), $.ready).then(function {		mw.requestIdleCallback(init);	}); });