/*
 * Unicode/IPA Popup
 * Written by Joshua M. Thompson <joshua@linguistlist.org>
 * August 1st, 2007
 */

Ext.namespace('LINGUIST');

LINGUIST.UnicodePopup = {
	// This maps ASCII characters to zero or more alternate characters. For convenience we have
	// separate "IPA" and "Unicode" (anything non-IPA) mappings. They used to be displayed
	// separately and may be again in the future, so keep them separate for now.
	//
	// To prevent this file being trashed when run through an non-UTF8 editor the alternates are
	// given as Unicode escapes.

	codes: {
		"unicode": {
			"A": [ "\u00C0","\u00C1","\u00C2","\u00C3","\u00C4","\u00C5","\u00C6","\u0100","\u0102","\u0104","\u01FC" ],
			"C": [ "\u00C7","\u0106","\u0108","\u010A","\u010C" ],
			"D": [ "\u010E","\u0110" ],
			"E": [ "\u00C8","\u00C9","\u00CA","\u00CB","\u0112","\u0114","\u0116","\u0118" ],
			"G": [ "\u011C","\u011E","\u0120","\u0122","\u01F4","\u021C" ],
			"H": [ "\u0124","\u0126","\u01F6" ],
			"I": [ "\u00CC","\u00CD","\u00CE","\u00CF","\u0128","\u012A","\u012C","\u0130" ],
			"J": [ "\u0134" ],
			"K": [ "\u0136" ],
			"L": [ "\u0139","\u013B","\u013D","\u013F","\u0141" ],
			"N": [ "\u00D1","\u0143","\u0145","\u0147","\u014A" ],
			"O": [ "\u00D2","\u00D3","\u00D4","\u00D5","\u00D6","\u00D8","\u014C","\u014E","\u0150","\u0152","\u01EA" ],
			"R": [ "\u0154","\u0156","\u0158" ],
			"S": [ "\u015A","\u015C","\u015E","\u0160" ],
			"T": [ "\u00DE","\u0162","\u0164","\u0166" ],
			"U": [ "\u00D9","\u00DA","\u00DB","\u00DC","\u0168","\u016A","\u016C","\u016E","\u0170","\u0172" ],
			"W": [ "\u0174" ],
			"Y": [ "\u00DD","\u0176","\u0178","\u0232","\u1EF8" ],
			"Z": [ "\u0179","\u017B","\u017D" ],
			"a": [ "\u00E0","\u00E1","\u00E2","\u00E3","\u00E4","\u00E5","\u00E6","\u0101","\u0103","\u0105","\u01CE","\u01FD" ],
			"b": [ "\u0062" ],
			"c": [ "\u00E7","\u0107","\u0109","\u010B","\u010D" ],
			"d": [ "\u00F0","\u010F","\u0111" ],
			"e": [ "\u00E8","\u00E9","\u00EA","\u00EB","\u0113","\u0115","\u0117","\u0119","\u011B" ],
			"g": [ "\u011D","\u011F","\u0121","\u0123","\u01F5","\u021D" ],
			"h": [ "\u0125","\u0127","\u0195" ],
			"i": [ "\u00EC","\u00ED","\u00EE","\u00EF","\u0129","\u012B","\u012D","\u0131","\u01D0" ],
			"j": [ "\u0135","\u01F0" ],
			"k": [ "\u0137" ],
			"l": [ "\u013A","\u013C","\u013E","\u0140","\u0142" ],
			"n": [ "\u00F1","\u0144","\u0146","\u0148","\u014B" ],
			"o": [ "\u00F2","\u00F3","\u00F4","\u00F5","\u00F6","\u00F8","\u014D","\u014F","\u0151","\u0153","\u01D2","\u01EB" ],
			"r": [ "\u0155","\u0157","\u0159" ],
			"s": [ "\u00DF","\u015B","\u015D","\u015F","\u0161" ],
			"t": [ "\u00FE","\u0163","\u0165","\u0167" ],
			"u": [ "\u00F9","\u00FA","\u00FB","\u00FC","\u0169","\u016B","\u016D","\u016F","\u0171","\u0173","\u01D4","\u01D6","\u01D8","\u01DA","\u01DC" ],
			"w": [ "\u0175" ],
			"y": [ "\u00FD","\u00FF","\u0177","\u0233","\u1EF9" ],
			"z": [ "\u017A","\u017C","\u017E" ]
		},

		"ipa": {
			"'": [ "\u030B","\u0301","\u0304","\u0300","\u030F" ],
			":": [ "\u02D0","\u02D1","\u0306" ],
			"?": [ "\u0294","\u0295","\u0296","\u02A1","\u02A2","\u02E4","\u02C0","\u02C1" ],
			"`": [ "\u030B","\u0301","\u0304","\u0300","\u030F" ],
			"a": [ "\u0061","\u0061\u0308","\u00E6","\u0105","\u0250","\u0251","\u0252","\u0252\u0308","\u028C" ],
			"b": [ "\u0062","\u0253","\u0299","\u03B2" ],
			"c": [ "\u0063","\u00E7","\u0255","\u0297" ],
			"d": [ "\u0064","\u0064\u02B1","\u00F0","\u0256","\u0257","\u02A3","\u02A4","\u02A5" ],
			"e": [ "\u0065","\u0065\u0303","\u0065\u0308","\u0258","\u0259","\u025A","\u025B","\u025C","\u025D","\u025E","\u029A" ],
			"f": [ "\u0066","\u0278" ],
			"g": [ "\u0067","\u0260","\u0262","\u0263","\u029B" ],
			"h": [ "\u0068","\u0127","\u0265","\u0266","\u0267","\u029C","\u02B0","\u02B1" ],
			"i": [ "\u0069","\u0069\u0308","\u0268","\u0269","\u026A" ],
			"j": [ "\u006A","\u025F","\u029D","\u02B2" ],
			"k": [ "\u006B","\u006B\u2019","\u029E" ],
			"l": [ "\u006C","\u0142","\u026C","\u026D","\u026E","\u028E","\u029F","\u02E1" ],
			"m": [ "\u006D","\u006D\u0325","\u0271" ],
			"n": [ "\u006E","\u006E\u0325","\u0144","\u014B","\u0272","\u0273","\u0274","\u207F" ],
			"o": [ "\u006F","\u006F\u0308","\u00F8","\u0153","\u0254","\u0254\u0308","\u0275","\u0276","\u0325" ],
			"p": [ "\u0070","\u0070\u2019" ],
			"q": [ "\u0071" ],
			"r": [ "\u0072","\u0279","\u027A","\u027B","\u027C","\u027D","\u027E","\u0280","\u0281","\u02DE","\u02B3","\u02B4","\u02B5","\u02B6" ],
			"s": [ "\u0073","\u0073\u2019","\u0282","\u0283","\u0284","\u0286","\u02E2" ],
			"t": [ "\u0074","\u0074\u02B0","\u0074\u032A","\u0074\u2019","\u0287","\u0288","\u02A7","\u02A8","\u03B8" ],
			"u": [ "\u0075","\u0075\u0308","\u0289","\u028A","\u028C" ],
			"v": [ "\u0076","\u028B","\u028C" ],
			"w": [ "\u0077","\u0077\u0308\u0308","\u026F","\u0270","\u028D","\u02B7" ],
			"x": [ "\u0078","\u03C7","\u02E3","\u033D" ],
			"y": [ "\u0079","\u0079\u0308","\u0264","\u028F","\u02E0" ],
			"z": [ "\u007A","\u0290","\u0291","\u0292","\u0293" ],
			"|": [ "\u02D0","\u02D1","\u02C8","\u02CC","\u0306","\u007C","\u2016" ],
			"~": [ "\u032C","\u02B0","\u0339","\u031C","\u031F","\u0331","\u0308","\u033D","\u0323","\u032F","\u02DE","\u0324","\u0330","\u033C","\u02B7","\u02B2","\u02E0","\u02E4","\u0334","\u031D","\u031E","\u0318","\u0319","\u032A","\u033A","\u033B","\u0303","\u207F","\u02E1","\u031A" ]
		}
	},

	// This initializes the popup by building its DOM structure and then attaching event handlers
	// to all input fields on the page with a CSS class of "unicode"

	init: function() {
		var parent = Ext.get('content');

		if (parent == null) {
			parent = document.body;
		}

		this.popup = Ext.DomHelper.append(parent, { tag: 'div', id: 'unicode-popup' }, true);
		this.popup.hide();

		var els = Ext.select('.unicode');
		els.on('focus', this.attach, this, true);

		this.isOpen = false;
	},

	// Attach the popup to an input field. Called as an event handler.

	attach: function(e) {
		var target = Ext.get(e.getTarget());

		if (this.target == target) {
			return;
		} else if (this.target != null) {
			this.detach();
		}

		this.target = target;
		this.target.on('keypress', this.handleKey, this, true);

		if (this.isOpen) {
			this.popup.anchorTo(this.target, 'tl-bl?');
		}
	},

	// Detach from the current target.

	detach: function() {
		this.target.un('keypress', this.handleKey);
	},

	// Open the popup and anchor it to the target.

	open: function() {
		var width = this.target.getWidth();

		if (width < 150) {
			width = 150;
		}

		this.popup.setWidth(width);
		this.popup.anchorTo(this.target, 'tl-bl?');
		this.update('<h2>Type a character to see available Unicode replacements</h2>');
		this.popup.show();

		this.isOpen = true;
	},

	// Close (hide) the popup

	close: function() {
		this.popup.hide();

		this.isOpen = false;
	},

	// Event handler that monitors keypress events and acts accordingly.
	// We use escape to toggle the popup; when the popup is open keypresses
	// are captured and used to update the poup.

	handleKey: function(e) {
		var key = e.getKey();

		// Ignore any keys pressed with a modifier (other than shift) and any keys
		// that are special keys like delete/backspace.

		if ((e.ctrlKey || e.altKey || (key < 32) || (key > 126)) && (key != 27)) {
			return;
		}

		if (this.isOpen) {
			if (key == 27) {		// escape closes the popup again
				this.close();
			} else {
				var c   = String.fromCharCode(key);
				var ipa = this.codes.ipa[c];
				var uni = this.codes.unicode[c];
				var html;

				if (ipa || uni) {
					html = '<h2>Click on a symbol to select it</h2><p>';

					if (ipa) {
						html += this.renderCodes(ipa);
					}

					if (uni) {
						html += this.renderCodes(uni);
					}

					html += '</p>';
				} else {
					html = '<h2 class="important">No replacements found for \'' + c + '\'</h2>';
				}

				this.update(html);
			}
		} else {
			if (key == 27) {	// escape opens the popup
				e.preventDefault();

				this.open();
			}
		}
	},

	// Update the body of the popup with new HTML and resize the height to match.

	update: function(html) {
		this.popup.update(html);
		this.popup.autoHeight();
	},

	// Put a character back to the current target. This is called when a user clicks
	// on a character in the generated HTML inside the popup div.

	putChar: function(c) {
		var str = this.target.dom.value;

		this.target.dom.value = str.substring(0, str.length - 1) + c;
		this.target.focus();
	},

	// Private method for rendering a list of replacement characters as a series of HTML anchor tags.

	renderCodes: function(codes) {
		var html = '';

		for (var i = 0; i < codes.length; i++) {
			html += '<input type="button" style="cursor:pointer;font-weight:bold;margin:3px;" onclick="LINGUIST.UnicodePopup.putChar(\'' + codes[i] + '\')" value="'+codes[i]+'" />';
		}

		return html;
	}
};

// Add a callback hook to initialize the popup once the document has finished loading.

Ext.onReady(LINGUIST.UnicodePopup.init, LINGUIST.UnicodePopup, true);
