//-------------------------------------------------------------------
// CopyRight Lets101 Inc 2008
//
// A simple editor for converting combined english keyboard keys to 
// vietnamese characters
//
// Approach: 
//    1) construct all the mappings 
//       vowel + dấu = vietnamese vowel
//    2) listen to keystroke and lookup from this list of mapping
//       if found a match then replace, previous character and 
//       current one with the target vietnamese vowel
//
//  Usage: 
//    Add the following property to your text object:
//       onkeypress="return handleKeyStroke(this, event);"
//    Example: 
//        <textarea rows=5 cols=100 id="text1" 
//         onkeypress="return handleKeyStroke(this, event);"></textarea>
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// List of all mappings
//-------------------------------------------------------------------
var ALL_MAPPINGS = null;

//-------------------------------------------------------------------
// Construct the mappings
// baseChar + alterChar = resultChar
// NOTE prevChar are special case where we still want to retain the
// previous character as something different from the actual result
// character.  This is very useful in case we have stress keys
// For example, we want:
//   { a | á | à | ả | ã | ạ } + alter key = { a | á | à | ả | ã | ạ }
// Thus the previous character we keep for all this set is not the
// true result key but is only 'a'
//-------------------------------------------------------------------
function addMapping(baseChar, alterChar, resultChar, prevChar) {
	ALL_MAPPINGS[ALL_MAPPINGS.length] = new Array(baseChar, alterChar.toLowerCase(), resultChar, prevChar);
}

function addSimpleMappings(baseChar, alterChars, resultChars) {
	for (var i = 0; i < alterChars.length && i < resultChars.length; i++) {
		addMapping(baseChar, alterChars[i], resultChars[i], baseChar);
	}
}

function generateMappings4Set(_sac, _huyen, _hoi, _nga, _nang, _hat_down, _hook, _hat_up, _cross) {
	var commonAlterKeys = new Array(_sac, _huyen, _hoi, _nga, _nang);
	
	addSimpleMappings('a', commonAlterKeys, new Array('á', 'à', 'ả', 'ã', 'ạ'));
	addSimpleMappings('ă', commonAlterKeys, new Array('ắ', 'ằ', 'ẳ', 'ẵ', 'ặ'));
	addSimpleMappings('â', commonAlterKeys, new Array('ấ', 'ầ', 'ẩ', 'ẫ', 'ậ'));
	addSimpleMappings('e', commonAlterKeys, new Array('é', 'è', 'ẻ', 'ẽ', 'ẹ'));
	addSimpleMappings('ê', commonAlterKeys, new Array('ế', 'ề', 'ể', 'ễ', 'ệ'));
	addSimpleMappings('i', commonAlterKeys, new Array('í', 'ì', 'ỉ', 'ĩ', 'ị'));
	addSimpleMappings('o', commonAlterKeys, new Array('ó', 'ò', 'ỏ', 'õ', 'ọ'));
	addSimpleMappings('ơ', commonAlterKeys, new Array('ớ', 'ờ', 'ở', 'ỡ', 'ợ'));
	addSimpleMappings('ô', commonAlterKeys, new Array('ố', 'ồ', 'ổ', 'ỗ', 'ộ'));
	addSimpleMappings('u', commonAlterKeys, new Array('ú', 'ù', 'ủ', 'ũ', 'ụ'));
	addSimpleMappings('ư', commonAlterKeys, new Array('ứ', 'ừ', 'ử', 'ữ', 'ự'));
	addSimpleMappings('y', commonAlterKeys, new Array('ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ'));
	
	addMapping('a', _hat_down, 'â', null);
	addMapping('a', _hat_up,   'ă', null);
	addMapping('e', _hat_down, 'ê', null);
	addMapping('o', _hat_down, 'ô', null);
	addMapping('o', _hook,     'ơ', null);
	addMapping('u', _hook,     'ư', null);
	addMapping('d', _cross,    'đ', null);
	
	addMapping('â', _hat_down, 'a', null);
	addMapping('â', _hat_up,   'a', null);
	addMapping('ă', _hat_down, 'a', null);
	addMapping('ă', _hat_up,   'a', null);
	addMapping('ê', _hat_down, 'e', null);
	addMapping('ô', _hat_down, 'o', null);
	addMapping('ơ', _hook,     'o', null);
	addMapping('ư', _hook,     'u', null);
	addMapping('đ', _cross,    'd', null);
	
	addSimpleMappings('A', commonAlterKeys, new Array('Á', 'À', 'Ả', 'Ã', 'Ạ'));
	addSimpleMappings('Ă', commonAlterKeys, new Array('Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ'));
	addSimpleMappings('Â', commonAlterKeys, new Array('Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ'));
	addSimpleMappings('E', commonAlterKeys, new Array('É', 'È', 'Ẻ', 'Ẽ', 'Ẹ'));
	addSimpleMappings('Ê', commonAlterKeys, new Array('Ế', 'Ề', 'Ể', 'Ễ', 'Ệ'));
	addSimpleMappings('I', commonAlterKeys, new Array('Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị'));
	addSimpleMappings('O', commonAlterKeys, new Array('Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ'));
	addSimpleMappings('Ơ', commonAlterKeys, new Array('Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ'));
	addSimpleMappings('Ô', commonAlterKeys, new Array('Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ'));
	addSimpleMappings('U', commonAlterKeys, new Array('Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ'));
	addSimpleMappings('Ư', commonAlterKeys, new Array('Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự'));
	addSimpleMappings('Y', commonAlterKeys, new Array('Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ'));
	
	addMapping('A', _hat_down, 'Â', null);
	addMapping('A', _hat_up,   'Ă', null);
	addMapping('E', _hat_down, 'Ê', null);
	addMapping('O', _hat_down, 'Ô', null);
	addMapping('O', _hook,     'Ơ', null);
	addMapping('U', _hook,     'Ư', null);
	addMapping('D', _cross,    'Đ', null);
	
	addMapping('Â', _hat_down, 'A', null);
	addMapping('Â', _hat_up,   'A', null);
	addMapping('Ă', _hat_down, 'A', null);
	addMapping('Ă', _hat_up,   'A', null);
	addMapping('Ê', _hat_down, 'E', null);
	addMapping('Ô', _hat_down, 'O', null);
	addMapping('Ơ', _hook,     'O', null);
	addMapping('Ư', _hook,     'U', null);
	addMapping('Đ', _cross,    'D', null);
}

function generateMappings() {
	if (ALL_MAPPINGS != null) {
		return;
	}
	
	ALL_MAPPINGS = new Array();
	generateMappings4Set('\'', '`', '?', '~', '.', '^', '*', '(', 'd');
	generateMappings4Set('1',  '2', '3', '4', '5', '6', '7', '8', '9');
	generateMappings4Set('s',  'f', 'r', 'x', 'j', '^', '+', 'w', 'd');
	
	addMapping('a', 'a', 'ă', null);
	addMapping('A', 'A', 'Ă', null);
	addMapping('e', 'e', 'ê', null);
	addMapping('E', 'E', 'Ê', null);
	addMapping('o', 'o', 'ơ', null);
	addMapping('O', 'O', 'Ơ', null);
	addMapping('u', 'u', 'ư', null);
	addMapping('U', 'U', 'Ư', null);
	
	addMapping('ă', 'a', 'a', null);
	addMapping('Ă', 'A', 'A', null);
	addMapping('â', 'a', 'a', null);
	addMapping('Â', 'A', 'A', null);	
	addMapping('ê', 'e', 'e', null);
	addMapping('Ê', 'E', 'E', null);
	addMapping('ơ', 'o', 'o', null);
	addMapping('Ơ', 'O', 'O', null);
	addMapping('ư', 'u', 'u', null);
	addMapping('Ư', 'U', 'U', null);
}

function dumpMappings() {
	var result = '';
	for (var i = 0; i < ALL_MAPPINGS.length; i++) {
		var mapping = ALL_MAPPINGS[i];
		result = result + '<br>' + mapping[0] + " + " + mapping[1] + " -> " + mapping[2] + " (" + mapping[3] + ")\n";
	}
	return result;
}

//-------------------------------------------------------------------
// Actual editing stuffs
//-------------------------------------------------------------------
function getCaretPosition(ctrl) {
	var CaretPos = 0;
	if (document.selection) {
		//-----------------------------------------------------------
	    // This is for IE it's gives value offset by some random
	    // number thus it can not be used.  But we don't care anyway
	    // We just need to know if the position has changed or not
	    //-----------------------------------------------------------
		ctrl.focus ();
		var Sel = document.selection.createRange();
		Sel.moveStart("character", -ctrl.value.length);
		CaretPos = Sel.text.length;
	}
	else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
		//-----------------------------------------------------------
		// Mozilla works perfectly well
		//-----------------------------------------------------------
		CaretPos = ctrl.selectionStart;
	}
    return CaretPos;
}

//-------------------------------------------------------------------
// Record the previous keyStroke by text object
//-------------------------------------------------------------------
var prevTextObject = null;
var prevCharacter = null;
var prevCaretPosition = null;

function handleKeyStroke(textObject, keyEvent) {
	var keyNum;
	if (window.event) {
		keyNum = keyEvent.keyCode;
	}
	else if(keyEvent.which) {
		keyNum = keyEvent.which;
	}
	
	var keyCharacter = String.fromCharCode(keyNum);
	var caretPosition = getCaretPosition(textObject);
	
	if (prevTextObject != textObject ||
		prevCaretPosition != caretPosition - 1) {
		prevTextObject = textObject;
		prevCharacter = keyCharacter;
		prevCaretPosition = caretPosition;
		return;
	}
	
	for (var i = 0; i < ALL_MAPPINGS.length; i++) {
		var mapping = ALL_MAPPINGS[i];
		var resultCharacter = null;
		
		if (prevCharacter == '\\') {
			resultCharacter = keyCharacter;
		}
		
		if (prevCharacter == mapping[0] && keyCharacter.toLowerCase() == mapping[1]) {
			resultCharacter = mapping[2];
		}
		
		if (resultCharacter != null) {
			var text = textObject.value;
			
			if (textObject.setSelectionRange) {
				//-----------------------------------------------
				// Mozilla: we'll update the text and reposition
				// the caret appropriately.  Basically perform
				// replace by add substrings
				//-----------------------------------------------
				textObject.value = textObject.value.substring(0, caretPosition - 1) + resultCharacter + textObject.value.substring(caretPosition);
				textObject.focus();
				textObject.setSelectionRange(caretPosition, caretPosition);
			}
			else {
				//-----------------------------------------------
				// IE: only insert works, anything to do with
				// caret position is hopeless.  We basically
				// move back and select one character infront
				// then set the selection to replace it.
				//-----------------------------------------------
				var range = document.selection.createRange();
				range.moveStart("character", -1);
				range.text = resultCharacter;
			}
			
			//---------------------------------------------------
			// We already take care of the character input
			// thus return false so it won't insert this key
			// and position stays the same
			//---------------------------------------------------
			prevCaretPosition = caretPosition - 1;
			if (mapping[3] != null) {
				prevCharacter = mapping[3];
			}
			else {
				prevCharacter = resultCharacter;
			}
			return false;
		}
	}
	
	//---------------------------------------------------------------
	// Return true, let the object insert character
	//---------------------------------------------------------------
	prevCharacter = keyCharacter;
	prevCaretPosition = caretPosition;
	return true;
}

//-------------------------------------------------------------------
// Initialize the mappings:
//-------------------------------------------------------------------
generateMappings();

//-------------------------------------------------------------------
// Register a text field or text area for vietnamese typing
//-------------------------------------------------------------------
function registerVietnameseCharHandler(textObjectID) {
	var textObject = document.getElementById(textObjectID);	
	var oldOnKeyPress = textObject.onkeypress;
	
 	if (typeof oldOnKeyPress != 'function') { 
		textObject.onkeypress = function (keyEvent) { 
			if (window.event) {
				keyEvent = window.event;
			}
			return handleKeyStroke(textObject, keyEvent);
		};
	} 
	else { 
		textObject.onkeypress = function (keyEvent) { 
			if (window.event) {
				keyEvent = window.event;
			}
			oldOnKeyPress(keyEvent);
			return handleKeyStroke(textObject, keyEvent);
		};
	} 
}