User:Erutuon/scripts/Marshallese.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.

$(() => {
	// C: non-glide consonant
	// G: glide
	// V: vowel
	// #: word boundary
	// None of these occur in any of the rows.
	const specialNotation = {
		'C': '[pmtnrlkŋ][ʲˠʷ]?',
		'G': '[jɰw]',
		'V': '[æɛei]',
		'#': '(?:^|\s|$)',
	};
	const hasSpecialNotation = str => {
		return new RegExp('[' + Object.keys(specialNotation).join('') + ']')
			.test(str);
	};
	
	// Add search box above table of Marshallese transcriptions
	// in [[Module:mh-pronunc]].
	// The box will accept the symbols C, G, V, #,
	// which match a non-glide consonant, glide, vowel, and word boundary.
	// If any of the symbols are included,
	// the box searches the phonemic IPA transcription.
	$('.mh-transcription-list').get().forEach(t => {
		const getRows = t => {
			return $(t).find('tr').filter((_, e) => {
				return $(e).find('th').length === 0;
			});
		};
		
		const name1 = 'mh-transcription-list-search';
		const $searchLabel = $('<label>')
			.attr('for', name1)
			.text('Filter table rows:');
		
		const $searchBox = $('<input>')
			.attr({ 'type': 'search', 'id': name1 })
			.on('input', function() {
				const $rows = getRows(t);
				const input = $(this).val();
				let filterer;
				if (hasSpecialNotation(input)) {
					const regex = new RegExp(
						input.replace(/./g, char => specialNotation[char]
							? specialNotation[char] + "\\.?"
							: char),
						'g'
					);
					filterer = ($row, templateInput, phonemic) => {
						return phonemic && regex.test(phonemic);
					};
				} else {
					const lowercaseInput = input.toLowerCase();
					filterer = ($row, templateInput) => {
						return $row.text().toLowerCase().includes(input)
						|| templateInput && templateInput.toLowerCase().includes(input);
					};
				}
				$rows.each((_, row) => {
					const $row = $(row);
					$row.css(
						'display',
						filterer($row, $row.data('templateInput'), $row.data('phonemic')) ? '' : 'none');
				});
			});
		
		const name2 = 'show-only-differences';
		const $checkBoxLabel = $('<label>')
			.attr('for', name2)
			.text('Show only rows with a difference');
		const $checkBox = $('<input>')
			.attr({ 'type': 'checkbox', 'id': name2 })
			.on('change', function() {
				const $rows = getRows(t);
				$rows.each((_, row) => {
					const $row = $(row);
					$row.css(
						'display',
						!this.checked || Boolean($row.data('hasDifference')) ? '' : 'none');
				});
			});
		$(t).before($('<p>').append($searchLabel, $searchBox), $('<p>').append($checkBoxLabel, $checkBox));
	});
	
	const forEachTextNode = (func) => {
		const key = Symbol();
		const apply = node => {
			if (!(node instanceof Node)) {
				throw new TypeError("Expected Node");
			}
			for (const child of node.childNodes) {
				if (child[key])
					continue;
				switch (child.nodeType) {
					case Node.ELEMENT_NODE: apply(child); break;
					case Node.TEXT_NODE: func(child);
				}
				child[key] = true;
			}
		};
		return apply;
	};
	
	const addZwnjBeforeCedilla = forEachTextNode(textNode => {
		textNode.nodeValue = textNode.nodeValue
			.normalize('NFD')
			.replace(/\u200C*\u0327/g, '\u200C\u0327')
			.normalize('NFC');
	});
	
	$(":lang(mh)").get().forEach(addZwnjBeforeCedilla);
});