Wikipedia IPA Helper

By Juan Lin Last update Dec 12, 2008 — Installed 170 times. Daily Installs: 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0

There are 5 previous versions of this script.

// ==UserScript==
// @name           Wikipedia IPA Helper
// @namespace      http://dodob.shackspace.com/greasemonkey/
// @description    Popup an IPA pronunciation key when hovering over an IPA  link on Wikipedia.
// @include        http://*.wikipedia.org/*
// @include        http://wikipedia.org/*
// @exclude        http://*.wikipedia.org/wiki/Wikipedia:IPA
// @exclude        http://wikipedia.org/wiki/Wikipedia:IPA
// ==/UserScript==

(function() {
    function getTime() { benchmarkTimer = new Date(); return benchmarkTimer.getTime(); }
    var benchmarkTimer = null;
    var scriptStartTime = getTime();
	const MAX_SYM_LEN = 3;
	var ipa_popup = {};
    
	var ipa_table = {
		'b': '<b>b</b>ut, we<b>b</b>',
		'd': '<b>d</b>o, o<b>dd</b>',
		'ð': '<b>th</b>is, brea<b>the</b>, fa<b>th</b>er',
		'dʒ': '<b>g</b>in, <b>j</b>oy, e<b>dge</b>',
		'f': '<b>f</b>ool, enou<b>gh</b>, lea<b>f</b>, <b>ph</b>oto',
		'ɡ': '<b>g</b>o, <b>g</b>et, be<b>g</b>',
		'h': '<b>h</b>am, a<b>h</b>ead',
		'j': '<b>y</b>es, hallelu<b>j</b>ah',
		'k': '<b>c</b>at, <b>k</b>ill, s<b>k</b>in, <b>q</b>ueen, thi<b>ck</b>',
		'l': '<b>l</b>eft, be<b>ll</b>',
		'm': '<b>m</b>an, ha<b>m</b>',
		'n': '<b>n</b>o, ti<b>n</b>',
		'ŋ': 'ri<b>ng</b>er, si<b>ng</b>, si<b>n</b>k',
		'ŋɡ': 'fi<b>ng</b>er',
		'θ': '<b>th</b>ing, tee<b>th</b>',
		'p': '<b>p</b>en, s<b>p</b>in, ti<b>p</b>',
		'r': '<b>r</b>un, ve<b>r</b>y',
		's': '<b>s</b>ee, <b>c</b>ity, pa<b>ss</b>, <b>sc</b>issors',
		'ʃ': '<b>sh</b>e, <b>s</b>ure, emo<b>ti</b>on, lea<b>sh</b>, se<b>ss</b>ion',
		't': '<b>t</b>wo, s<b>t</b>ing, be<b>t</b>',
		'tʃ': '<b>ch</b>air, na<b>t</b>ure, tea<b>ch</b>',
		'v': '<b>v</b>oice, ha<b>ve</b>',
		'w': '<b>w</b>e, q<b>u</b>een',
		'ʍ': '<b>wh</b>at',
		'z': '<b>z</b>oo, ro<b>s</b>e',
		'ʒ': 'plea<b>s</b>ure, vi<b>si</b>on, bei<b>g</b>e',
		'x': 'u<b>gh</b>, lo<b>ch</b>, <b>Ch</b>anukah',
		'ʔ': 'uh<b>-</b>oh /ˈʌʔoʊ/, Hawai<b>‘</b>i /həˈwaɪʔiː/',
		'æ': 'b<b>a</b>d, p<b>a</b>t',
		'ær': 'b<b>arr</b>ow, m<b>arr</b>y',
		'ɑː': 'b<b>a</b>lm, f<b>a</b>ther, p<b>a</b>',
		'ɑr': 'b<b>ar</b>, m<b>ar</b>',
		'ɒ': 'b<b>o</b>d, p<b>o</b>t, c<b>o</b>t',
		'ɒr': 'm<b>or</b>al, f<b>or</b>age',
		'ɔː': 'b<b>aw</b>d, p<b>aw</b>, c<b>augh</b>t',
		'ɔr': 'b<b>or</b>n, f<b>or</b>',
		'oʊ': 'b<b>eau</b>, h<b>oe</b>, p<b>o</b>ke',
		'ɔər': 'b<b>oar</b>, f<b>our</b>, m<b>ore</b>',
		'ʊ': 'g<b>oo</b>d, f<b>oo</b>t, p<b>u</b>t',
		'ʊər': 'b<b>oor</b>, m<b>oor</b>',
		'uː': 'b<b>oo</b>ed, f<b>oo</b>d',
		'ʌ': 'b<b>u</b>d, b<b>u</b>tt',
		'ʌr': 'h<b>urr</b>y, M<b>urr</b>ay',
		'ɜr': 'b<b>ir</b>d, m<b>yrrh</b>, f<b>urr</b>y',
		'ɝː': 'b<b>ir</b>d, m<b>yrrh</b>, f<b>urr</b>y',
		'ɛ': 'b<b>e</b>d, p<b>e</b>t',
		'ɛr': 'b<b>err</b>y, m<b>err</b>y',
		'eɪ': 'b<b>ay</b>, h<b>ey</b>, f<b>a</b>te',
		'ɛər': 'b<b>ear</b>, m<b>are</b>, M<b>ar</b>y',
		'ɪ': 'b<b>i</b>d, p<b>i</b>t',
		'ɪr': 'm<b>irr</b>or',
		'i': 'happ<b>y</b>, cit<b>y</b>, toff<b>ee</b>',
		'ɪər': 'b<b>eer</b>, m<b>ere</b>',
		'iː': 'b<b>ea</b>d, p<b>ea</b>t, f<b>ee</b>t',
		'аɪ': 'b<b>uy</b>, h<b>igh</b>, r<b>i</b>de, wr<b>i</b>te',
		'ə': 'Ros<b>a</b>’s, <b>a</b>bove',
		'aʊ': 'b<b>ough</b>, h<b>ow</b>, p<b>ou</b>t',
		'əl': 'bott<b>le</b>',
		'ɔɪ': 'b<b>oy</b>, h<b>oy</b>',
		'əm': 'rhyth<b>m</b>',
		'juː': 'b<b>eau</b>ty, h<b>ue</b>, p<b>ew</b>, d<b>ew</b>',
		'ən': 'butt<b>on</b>',
		'ər': 'runn<b>er</b>, merc<b>er</b>',
		'ɚ': 'runn<b>er</b>, merc<b>er</b>',
		'.': '<i>syllabification</i>, e.g.: moai /ˈmoʊ.аɪ/',
		'ɨ': 'ros<b>e</b>s, busin<b>e</b>ss ',
		'ˈ': '<i>into<b>na</b>tion</i> /ˌɪntəˈneɪʃən/',
		'ˌ': '<i><b>in</b>tonation</i> /ˌɪntəˈneɪʃən/'
	};
	
	function parse_ipa(ipatext) {
		ipatext = ipatext.replace(/\[/,'').replace(/\]/,'').replace(/^\s\s*/,'').replace(/\s\s*$/, '');
		ipatext = ipatext.replace(':','ː');	// use triangular colons
		ipatext = ipatext.replace(/\//g,'');
		return ipatext;
	}

	function add_key(key, example) {
		var tr = document.createElement('tr');
		var td_left = document.createElement('td');
		var td_right = document.createElement('td');
		td_left.innerHTML = '/' + key + '/';
		td_right.innerHTML = example;
		td_left.setAttribute('class', 'ipahelper_key');
		td_right.setAttribute('class', 'ipahelper_example');
		tr.appendChild(td_left);
		tr.appendChild(td_right);
		return tr;
	}

	function getpos(ele) {
		var x = ele.offsetLeft;
		var y = ele.offsetTop;
		while (ele = ele.offsetParent) {
			x += ele.offsetLeft;
			y += ele.offsetTop;
		}
		return [x, y];
	}

	function toggle_popup(event) {
		var ipatext = parse_ipa(get_text(this));
		if (!ipa_popup[ipatext]) { ipa_popup[ipatext] = create_popup(ipatext); }

		var popup = ipa_popup[ipatext];
		// getpos every time in case page is zoomed in/out
		popup.style.left = getpos(this)[0] + 'px';
		popup.style.top = getpos(this)[1]+this.scrollHeight+1 + 'px';	// +1 prevents flicker
		popup.style.display = (popup.style.display == 'none')? 'block':'none';
		event.stopPropagation();
		event.preventDefault();
	}

	function create_popup(ipatext) {
		//GM_log('creating popup for ' + ipatext);
		var popup;
		popup = document.createElement('div');
		popup.style.display = 'none';
		popup.setAttribute('class', 'ipahelper_popup');
		
		var legend;
		legend = document.createElement('table');

		var key;
		for (var c=0; c<ipatext.length;) {
			for (var s=MAX_SYM_LEN; s>0; s--) {
				if (c+s > ipatext.length) { continue; }
				key = ipatext.substr(c, s);
				//GM_log(key);

				if (ipa_table[key]) {
					//GM_log('/' + key + '/ '+ ipa_table[key]);
					legend.appendChild(add_key(key, ipa_table[key]));
					break;
				}
			}
			if (s == 0) {
				//GM_log(key);
				legend.appendChild(add_key(key, ''));
				c++;
			} else {
				c = c + s;
			}
		}
		
		popup.appendChild(legend);
		popup.addEventListener('mouseover', function(e) { this.style.display='block'; }, true);
		popup.addEventListener('mouseout', function(e) { this.style.display='none'; }, true);
		document.getElementsByTagName('body')[0].appendChild(popup);
		return popup;
	}

	function get_text(ele) {
		var text;
		text = document.evaluate(
				'.//text()',
				ele,
				null,
				XPathResult.FIRST_ORDERED_NODE_TYPE,
				null).singleNodeValue.data;
		return text;
	}

	var ipas = document.evaluate(
		'//span[@class="IPA"]',
		document,
		null,
		XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
		null);

	for (var i=0, ipalink=null; ipalink = ipas.snapshotItem(i); i++) {
		//GM_log(ipalink.text);
		var ipatext = parse_ipa(get_text(ipalink));
		//GM_log(ipatext);

		//if (!ipa_popup[ipatext]) { ipa_popup[ipatext] = create_popup(ipatext); }

		ipalink.title = '';
		if (ipalink.firstChild.tagName == 'A') { ipalink.firstChild.title = ''; }
		//ipalink.addEventListener('click', toggle_popup, true);
		ipalink.addEventListener('click', function(e){e.preventDefault();e.stopPropagation();}, true);
		ipalink.addEventListener('mouseover', toggle_popup, true);
		ipalink.addEventListener('mouseout', toggle_popup, true);
	}

	GM_addStyle(<><![CDATA[
		div.ipahelper_popup {
			position: absolute;
			border: 1px solid rgb(170,170,170);
			padding: 2px;
			background-color: LightYellow;
			z-index: 99;
		}
		div.ipahelper_popup > table {
			background-color: transparent;
			border-spacing: 0px;
			font-size: 12.7px;
			padding: 2px;
		}
		.ipahelper_key {
			background-color: rgb(204, 204, 255);
			/*background-color: PaleGreen;*/
			font-weight: bold;
		}
		.ipahelper_example {
			/*align: right;*/
		}
	]]></>);
	
    GM_log((getTime() - scriptStartTime) + 'ms');

})();