MediaWiki:Numerakri.js

From Ekatra Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * Convert numbers between numeral systems.
 *
 * Dependencies: jquery.cookie, mediawiki.util, mediawiki.user
 * Source: https://www.mediawiki.org/wiki/MediaWiki:Gadget-Numerakri.js
 * Revision: 2019-01-11
 *
 * @copyright 2012 Daniel Friesen
 * @copyright 2012 Siddhartha Ghai
 * @copyright 2014 Bhawani Gautam
 * @copyright 2012-2018 Timo Tijhof
 * @license <https://opensource.org/licenses/MIT>
 */
/*jshint browser:true, unused:true, forin:false */
/*global $, mw */
(function () {
	'use strict';

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

	function isValidType(type) {
		return type === 'default' || availableMaps.indexOf(type) !== -1;
	}

	/** @return {Object.<RegExp>} */
	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
	 * @return {number} NodeFilter.FILTER_* constant
	 */
	function filterNode(node) {
		if (node.nodeType === Node.TEXT_NODE) {
			return NodeFilter.FILTER_ACCEPT;
		}
		var n = node.nodeName && node.nodeName.toLowerCase();
		if (n === 'input' || n === 'textarea' || $(node).hasClass('mw-numerakri-skip')) {
			// Skip this element and skip its children
			return NodeFilter.FILTER_REJECT;
		}
		// Skip this element, but check its children
		return NodeFilter.FILTER_SKIP;
	}

	/**
	 * @param {TextNode} node
	 */
	function handleTextNode(node) {
		var original = 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);
		}

		// The user may interact with the page. We pause so the browser can process
		// interaction. The text handler will continue after that.
		if (walker) {
			mw.requestIdleCallback(idleWalker);
		}
	}

	/**
	 * Save a browser cookie for 30 days, or remove it.
	 * @param {string|null} value
	 */
	function saveType(value) {
		mw.requestIdleCallback(function () {
			$.cookie('mw-numerakri-type', value, { expires: 30, path: '/' });
		});
	}

	/**
	 * @return {string}
	 */
	function getStoredType() {
		var value = $.cookie('mw-numerakri-type');
		if (value !== null && !isValidType(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') {
			// 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.
		if (!walker) {
			mw.requestIdleCallback(idleWalker);
		}
		walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ALL, filterNode, false);
	}

	/** @return {HTMLElement} */
	function createSelector() {
		var $select = $('<select>').addClass('mw-numerakri-skip').append(
			$('<option>').val('default').text(msg('option-default'))
		);
		availableMaps.forEach(function (type) {
			$select.append($('<option>').val(type).text(msg('option-' + type)));
		});
		$select.val(currentType);
		$select.on('change', function () {
			currentType = this.value;
			startPageConversion();
			saveType(currentType);
		});
		return $select[0];
	}

	function init() {
		var potlet, menu;

		// Decide which types to show
		availableMaps = ['gujarati', 'arabic'];

		// Decide selected type
		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 = $('<div>').addClass('mw-numerakri-menu').append(createSelector())[0];
		if(potlet) {
			potlet.appendChild(menu);
		}
	}

	$(function () {
		mw.requestIdleCallback(init);
	});
}());