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

// Chinese dialectal maps, part of gadget zhDialMap
// designed by [[User:Suzukaze-c]], originally posted at User:Suzukaze-c/zhDialMap.js
// discussion: [[Module talk:User:Suzukaze-c/zh-dial-map]]

// ideas:
// * group locations in the popup by lect

'use strict';

mw.loader.load('oojs-ui-core');

var classes = {
	dotHover: 'zh-dial-map__dot--hover',
	dotSuperHover: 'zh-dial-map__dot--superHover',
	legendRowHover: 'zh-dial-map__legend-row--hover',
};

var groupZH = {
	'Mandarin': '官話',
	'Cantonese': '粵語',
	'Hakka': '客家話',
	'Huizhou': '徽語',
	'Gan': '贛語',
	'Jin': '晉語',
	'Jiuxing Yumin': '九姓漁民方言',
	'Northern Min': '閩北語',
	'Eastern Min': '閩東語',
	'Southern Min': '閩南語',
	'Puxian Min': '莆仙閩語',
	'Zhongshan Min': '中山閩語',
	'Central Min': '閩中語',
	'Shaojiang Min': '邵將閩語',
	'Southern Pinghua': '桂南平話',
	'Northern Pinghua': '桂北平話',
	'Shehua': '畬話',
	'Waxiang': '瓦鄉話',
	'Wu': '吳語',
	'Xiang': '湘語',
};

function zh(data) {
	var element;
	if (data.type == 'lang') {
		element = $('<span>');
	} else if (data.type == 'link') {
		element = $('<a>').attr('href', '/wiki/' + data.word + '#Chinese');
	}
	element.addClass('Hani').attr('lang', 'zh').text(data.alt ? data.alt : data.word);
	if (data.red) element.addClass('new');

	return element;
}

function updateGroupSelection() {
	var groupSelectionStyle = $("style.zh-dial-map__groupSelectionStyle");
	var groupInputChecked = $("input.zh-dial-map__groupItem:checked");
	var checkedGroups = [];

	for (var i = 0; i < groupInputChecked.length; i++) {
		checkedGroups.push($(groupInputChecked[i]).attr('data-group'));
	}

	if (checkedGroups.length === 0) {
		groupSelectionStyle.text('');
	} else {
		var checkedGroupsNotSelector = '';
		for (var i = 0; i < checkedGroups.length; i++) {
			checkedGroupsNotSelector += ':not([data-group="' + checkedGroups[i] + '"])'
		}

		groupSelectionStyle.text('.zh-dial-map__dot' + checkedGroupsNotSelector + ' { display: none; }');
	}
}

function main() {
	var dots;
	var redLinks = {}; // Record which pages don't exist (for the "other terms" popup links)
	var wordLocations = {}; // Initialize object that will contain locations for each dialectal word. Cache instead of regenerating all the time
	var otherTerms = [];
	var activePopup;
	var popups = [];

	var mapImg = $('.zh-dial-map__map img');

	// take original pixel dimensions from the image
	// and give them to the image and the container holding the image
	$('.zh-dial-map__map, .zh-dial-map__map img')
		.css('width', mapImg.attr('width') + 'em')
		.css('height', mapImg.attr('height') + 'em')
	;

	// <s>change image source to svg</s>
	// and remove dimension attributes
	mapImg
		.removeAttr('width')
		.removeAttr('height')

		.attr('src', function(index, attr) {
			return attr.replace('1200px', '2400px');
		})
		.removeAttr('srcset')
	
		/*
		.attr('src', function(index, attr) {
			return attr.replace('thumb/', '').replace(/(svg).+/, '$1')
		})
		.removeAttr('srcset')
		*/
	;


	var inputContainer = $('<span>');
	inputContainer
		.addClass('zh-dial-map__inputContainer')
		.prependTo('.zh-dial-map__container')
	;

	var zoomContainer = $('<span>');
	var zoomController = $('<input>');
	zoomContainer
		.addClass('zh-dial-map__zoomContainer')
		.appendTo('.zh-dial-map__inputContainer')
	;
	zoomController
		.addClass('zh-dial-map__zoomController')
		.attr('type', 'range')
		.attr('min', 0.1)
		.attr('max', 4)
		.attr('step', 0.1)
		.val(1) // default zoom at 100%
		.on("change", function() {
			//console.log($(this).val());
			$('.zh-dial-map__map').css("font-size", $(this).val() + "px");
		})
		.appendTo('.zh-dial-map__zoomContainer')
	;

	var groupSelectionContainer = $('<span>');
	var groupSelectionStyle = $("<style>");
	groupSelectionContainer
		.addClass('zh-dial-map__groupSelectionContainer')
		.appendTo('.zh-dial-map__inputContainer')
	;
	groupSelectionStyle
		.addClass('zh-dial-map__groupSelectionStyle')
		.appendTo('head')
	;
	for (var group in groupZH) {
		var groupItem = $('<input>');
		var groupLabel = $('<label>');
		var id = 'zh-dial-map__groupItem--' + group.replace(' ', '-');
		groupItem
			.attr('type', 'checkbox')
			.addClass('zh-dial-map__groupItem')
			.attr('id', id)
			.attr('name', 'zh-dial-map__groupItem')
			.attr('data-group', group)
			.on("change", updateGroupSelection)
			.appendTo('.zh-dial-map__groupSelectionContainer')
		;
		groupLabel
			.attr('for', id)
			.text(group)
			.appendTo('.zh-dial-map__groupSelectionContainer')
		;
	}

	$('.zh-dial-map__legend-row').on("touchstart mouseenter",
		function() {
			var word = $(this).data('word'); // Get the dialectal word for the active legend row
			var isOther = (word == 'other'); // Is our row the "other terms" row?

			if (isOther) {
				dots = $('.zh-dial-map__dot-other'); // Get all grey dots
			} else {
				dots = $('.zh-dial-map__dot[data-word=' + word + ']'); // Get every dot corresponding to our word
			}

			$(this).addClass(classes.legendRowHover); // Add hovered class to the legend row
			dots.addClass(classes.dotHover); // Add hovered class to every corresponding dot

			if (! wordLocations[word]) {
				wordLocations[word] = [];

				dots.each(
					function(index) {
						var location = [$(this).data('location-en'), $(this).data('location-zh'), $(this).data('group')]; // Get location of dot
						wordLocations[word].push(location);

						if (isOther) {
							var otherWord = $(this).data('word'); // For "other terms", collect the terms too
							otherTerms.push(otherWord);

							if (! redLinks[otherWord]) redLinks[otherWord] = $(this).parent().hasClass('new');
							//console.log(otherWord, redLinks[otherWord]);
						}
					}
				);
				//console.log(word, wordLocations[word]);
				//if (isOther) { console.log(otherTerms) };
			}

			if (! popups[word]) {
				var locationsList = $('<ul>').addClass('zh-dial-map__legend-row-locations'); // Popup contents

				$.each(wordLocations[word], function(index, value) { // Do stuff related to each location recorded for this word
					if (isOther) word = otherTerms[index];

					var locationEn = value[0], locationZh = value[1], group = value[2];
					var listItem = $('<li>');

					listItem.append(zh({type: 'lang', word: locationZh + groupZH[group]})).append(' (' + locationEn + ' ' + group + ')'); // FIXME: this feels really hacky
					if (isOther) listItem.append(': ').append(zh({type: 'link', word: otherTerms[index], red: redLinks[word]})); // FIXME: this too

					var locationDot = $('[data-location-zh=' + locationZh + '][data-word=' + word + ']'); // Find the dot for this word at this location
					//console.log('[data-location-zh=' + locationZh + '][data-word=' + word + ']', locationDot);
					listItem.hover(
						function() {
							locationDot.addClass(classes.dotSuperHover);
						},
						function() {
							locationDot.removeClass(classes.dotSuperHover);
						}
					);

					listItem.appendTo(locationsList);
				});

				popups[word] = new OO.ui.PopupWidget({
					$content: locationsList,
					$container: $('.zh-dial-map__container'),
				});

				$(this).append(popups[word].$element); // Add cool popup to DOM
			}

			popups[word].toggle(true); // Make the popup visible
			activePopup = popups[word];
		}
	);

	$('.zh-dial-map__legend-row').on("touchend mouseleave",
		function() {
			$(this).removeClass(classes.legendRowHover); // Remove hovered class from legend row
			dots.removeClass(classes.dotHover); // Remove hovered class from every dot that corresponds to that word
			activePopup.toggle(false); // Hide popup
		}
	);
}

if ($('.zh-dial-map__map').length) {
	main();
}