﻿// JavaScript Document which contains functions for regularly checking latin input, converting it to farsi and placing it in another text control.
// Copyright © 2009 Behnevis.com
// first lets fix IEs stupid indexOf problem:
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt /*, from*/)
  {
    var len = this.length;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}

var IE = document.all?true:false;

/*
	Cross-Browser Split 0.2.1
	By Steven Levithan <http://stevenlevithan.com>
	MIT license
*/

// fix IE String.split bug
var nativeSplit = nativeSplit || String.prototype.split;

String.prototype.split = function (s /* separator */, limit) {
	// If separator is not a regex, use the native split method
	if (!(s instanceof RegExp))
		return nativeSplit.apply(this, arguments);

	/* Behavior for limit: If it's...
	 - Undefined: No limit
	 - NaN or zero: Return an empty array
	 - A positive number: Use limit after dropping any decimal
	 - A negative number: No limit
	 - Other: Type-convert, then use the above rules */
	if (limit === undefined || +limit < 0) {
		limit = false;
	} else {
		limit = Math.floor(+limit);
		if (!limit)
			return [];
	}

	var	flags = (s.global ? "g" : "") + (s.ignoreCase ? "i" : "") + (s.multiline ? "m" : ""),
		s2 = new RegExp("^" + s.source + "$", flags),
		output = [],
		lastLastIndex = 0,
		i = 0,
		match;

	if (!s.global)
		s = new RegExp(s.source, "g" + flags);

	while ((!limit || i++ <= limit) && (match = s.exec(this))) {
		var zeroLengthMatch = !match[0].length;

		// Fix IE's infinite-loop-resistant but incorrect lastIndex
		if (zeroLengthMatch && s.lastIndex > match.index)
			s.lastIndex = match.index; // The same as s.lastIndex--

		if (s.lastIndex > lastLastIndex) {
			// Fix browsers whose exec methods don't consistently return undefined for non-participating capturing groups
			if (match.length > 1) {
				match[0].replace(s2, function () {
					for (var j = 1; j < arguments.length - 2; j++) {
						if (arguments[j] === undefined)
							match[j] = undefined;
					}
				});
			}

			output = output.concat(this.slice(lastLastIndex, match.index), (match.index === this.length ? [] : match.slice(1)));
			lastLastIndex = s.lastIndex;
		}

		if (zeroLengthMatch)
			s.lastIndex++;
	}

	return (lastLastIndex === this.length) ?
		(s.test("") ? output : output.concat("")) :
		(limit      ? output : output.concat(this.slice(lastLastIndex)));
};

String.prototype.split2 = String.prototype.split;

//------------------------- functions for handling misc tasks ----------------------

function jsonEncodeObject(obj)
{
	var s = '{';
	var key;
	for (key in obj)
	{
		switch(key)
		{
			case '\n': key='\\n';obj[key] = key; break;
			case '\\': key='\\\\';obj[key] = key;break;
			case '"' : key='\\"';obj[key] = key;break;	
		}
			
		s = s + '"' + key +'"' + ':' +'"'+obj[key] + '"' + ',' ;
	}
		
	// remove extra ,
	s = s.slice(0,-1);	
		
	s = s + '}';
return s;
};

function copyToClipboardFirefox(meintext){
	
						 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');

				   var clip = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard);
				   if (!clip) return;

				   var trans = Components.classes['@mozilla.org/widget/transferable;1'].createInstance(Components.interfaces.nsITransferable);
				   if (!trans) return;

				   trans.addDataFlavor('text/unicode');

				   var str = new Object();
				   var len = new Object();
				   
				   var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
				   
				   var copytext=meintext;
				   
				   str.data=copytext;
				   
				   trans.setTransferData("text/unicode",str,copytext.length*2);
				   
				   var clipid=Components.interfaces.nsIClipboard;
				   
				   if (!clip) return false;
				 
				    clip.setData(trans,null,clipid.kGlobalClipboard);
					    
						
						clip.getData(trans,clip.kGlobalClipboard); 
						var str = new Object(); 
						var strLength = new Object(); 
						trans.getTransferData("text/unicode",str,strLength); 
						
						if (str) str = str.value.QueryInterface(Components.interfaces.nsISupportsString); 
						if (str) pastetext = str.data.substring(0,strLength.value / 2); 
						
		}

function copyfarsiToClipboardIE()
{
	Copied = document.getElementById(farsiControlId).createTextRange();
	Copied.execCommand("Copy");
}

function getAds(action, farsiText, latinText)
{
	if (!action)
	{
		action = "search"; // default action is search
	}
	if (farsiText)
	{
		ajax("/php/adserver.php", "farsi="+ encodeURI(farsiText)+"&" + "latin="+encodeURI(latinText)+"&" + "action="+encodeURI(action));
	}
	else
	{
		ajax("/php/adserver.php", "farsi="+ encodeURI(document.getElementById(farsiControlId).value)+"&" + "latin="+encodeURI(document.getElementById(latinControlId).value)+"&" + "action="+encodeURI(action));
	}
}

//------------------------- functions for regularly updating document text objects ----------------------


// variables for accessing page elements
var lastKeyUpTime = new Date(); // last time any key has been pressed 
var lastConvertedLatinText = new String(); // last converted latin text
var latinControlId = new String;
var farsiControlId = new String;
var latinTinyMCE ;	// points to latin tinyMCE editor instance
var farsiTinyMCE ;	// points to latin tinyMCE editor instance
var lastConvertResponseTime = -1; // time to receive ajax response for words , -1 to show that the response time is not valid

var IsFirstTimeLatinInput = true; // is it the first time the latin text area has input? this is to clean it from initial text, like salam
var conversionOption = new Object;
conversionOption.useFarsiNumbers = true;
conversionOption.leaveBBCodesIntact = false;


 function setConversionFinishedStatusDisplay(isFinished)
{
	if (isFinished)
	{
		if (latinTextIsHtml)
		{
			var editor_ID = farsiTinyMCE.editorId; 	
			var pathElm = document.getElementById(editor_ID + "_path_row"); 			
		}
		else
		{
			var pathElm = document.getElementById("conversionStatus"); 
		}
		pathElm.childNodes[0].nodeValue = "آماده" ;
	}
	else
	{
		if (latinTextIsHtml)
		{
			var editor_ID = farsiTinyMCE.editorId; 				
			var pathElm = document.getElementById(editor_ID + "_path_row"); 			
//			pathElm.style.textAlign = 'right';
		}
		else
		{
			var pathElm = document.getElementById("conversionStatus"); 
		}
		pathElm.childNodes[0].nodeValue = "در حال تبدیل";
	}
		
//	document.getElementById('farsiTextArea').style.body.background = 2424;
}
 
//function for handling convert to fars button and automatic conversion checkbox 
function toggleAutomaticConversion(checkBox, button)
{
	convertAutomatically = checkBox.checked; 
	if (convertAutomatically)
	{
		button.style.visibility = "hidden";
	}
	else
	{
		button.style.visibility = "visible";
	}

}

// functions for converting TinyMCE html code to farsi

function traverseDOM(x, arr) {
		arr.push(x);
        
		for (var i = 0; i < x.childNodes.length; i++) {
                traverseDOM(x.childNodes[i], arr);
        }
        return arr;
}

function convertTextNodes(x) { // forced replace is when there are no unknown words and we want to replace with farsi
        var result = new String;
		arr = Array();
        arr = traverseDOM(x, arr);
        for (var i = 0; i < arr.length; i++) {
			//if (arr[i] && arr[i].nodeName=="#text")
			if (arr[i] && arr[i].nodeType==3)

			{
				if (arr[i].textContent)  // for firefox that uses this
				{
					arr[i].textContent = localConvertToFarsi(arr[i].textContent);			
				}
				else	// for IE
				{	
					arr[i].nodeValue = localConvertToFarsi(arr[i].nodeValue);
				}				
			}
			
        }
		return result;
}

function addTextFromAllTextNodes(x) { // forced replace is when there are no unknown words and we want to replace with farsi
        var result = new String;
		arr = Array();
        arr = traverseDOM(x, arr);
        for (var i = 0; i < arr.length; i++) {
			//if (arr[i] && arr[i].nodeName=="#text")
			if (arr[i] && arr[i].nodeType==3)

			{
				if (arr[i].textContent)  // for firefox that uses this
				{
					result = result + " " + arr[i].textContent;			
				}
				else	// for IE
				{
					result = result + " " + arr[i].nodeValue;
				}				
			}
			
        }
		return result;
}

function convertTinyMCE()
{
	if (!farsiTinyMCE)
	{
		document.write("farsiTinyMCE is not defined ");
	}
	
	if (farsiTinyMCE != latinTinyMCE)// if two editors are being used
	{
		farsiTinyMCE.setContent('<span dir="RTL">' + latinTinyMCE.getContent() + '</span>');
	}

	convertTextNodes(farsiTinyMCE.getDoc());
}

//------------ main 

var convertAutomatically = new Boolean;
var latinTextIsHtml = new Boolean;
var lastAjaxRequestTime = new Date(); // last time any key has been pressed 

function updateFarsiTextPeriodicallyIfNeeded()
{
	var thisTime = new Date();	
	
	// prevent lockdown when there has been a problem in getting ajax response 
	if (thisTime.getTime() - lastAjaxRequestTime.getTime() > 15000)
	{
		waitingForRequestResponse = false;
	}
	
	
	if (convertAutomatically && !waitingForRequestResponse)
	{

		if (latinTextIsHtml)
		{
			var currentLatinText = latinTinyMCE.getContent();
		}
		else
		{
			var currentLatinText = document.getElementById(latinControlId).value;
		}
		
		if ((thisTime.getTime() - lastKeyUpTime.getTime() > 500) && lastConvertedLatinText != currentLatinText) // was 800 ms
		{
			setConversionFinishedStatusDisplay(false);
			submitAjaxRequestIfNecessary(); 
		}
		
	}
	var t=setTimeout("updateFarsiTextPeriodicallyIfNeeded()",250); // run it again in 250 ms
}

function keyUpDetected()
{
	var thisTime = new Date();
	lastKeyUpTime.setTime(thisTime.getTime());
}


//----------------------------- core functions for conversion------------------------------

// sepaartes text into section outside any square brackets [] and text inside any of these
function separateTextBetweenSquareBrackets(latinText)
{
	var bracketLevel = 0;
	var c;
	var textParts= new Array;
	var currentPartNumber = 0;
	var currentPartIsInBracket = 0;
	textParts[currentPartNumber] = '';
	for (var i=0; i<latinText.length; i++ ) 
	{
		c = latinText.charAt(i);
		switch(c)
		{
		case '[':
			   bracketLevel++;
			    if (bracketLevel == 1) //only the top bracket is separated
				{
					currentPartNumber++;
					textParts[currentPartNumber] = '';
				}
				textParts[currentPartNumber] = textParts[currentPartNumber] + c;
			   break;
		case']':
			   textParts[currentPartNumber] = textParts[currentPartNumber] + c;
			   bracketLevel--;
			   if (bracketLevel == 0)
			   {
				   currentPartNumber++;
				   textParts[currentPartNumber] = '';
			   }
			   break;
		default:
				textParts[currentPartNumber] = textParts[currentPartNumber] + c;
		}						
	}
	return textParts;
}


//alert(separateTextBetweenSquareBrackets('avval az hame [quote[double quote]] pas az [/quote]'));

function separateLatinLetters(latinText)
{
	
	if (conversionOption.leaveBBCodesIntact)
	{
		var latinTextParts = separateTextBetweenSquareBrackets(latinText);
		var textWords = new Array;
		for (var i=0; i<latinTextParts.length; i++ ) 
		{
			if (latinTextParts[i].length>0 && latinTextParts[i].charAt(0) == '[') // if its starts with a bracket, do not split on space
			{
				conversionOption.useFarsiNumbers = false; // in case there exist a bracket, do not change numbers to farsi so the bulletin board message numbers remain intact
				textWords.push(latinTextParts[i]);
			}
			else
			{
				textWords = textWords.concat(latinTextParts[i].split2(/([\s\xA0])/)); // split on space but keep spaces);
			}																			   																				   
		}

		//latinText = latinText.replace(/\[/g,' /['); // replace [ with space+/[ to flag it with / and prevent conversion to farsi
	}
	else // if do not consider leaving bb codes intact (default)
	{
		var textWords = latinText.split2(/([\s\xA0])/); // split on space but keep spaces
	}
	
	var newTextWords= new Array;
	var counter=0;

	for (var i=0; i<textWords.length; i++ ) 
	if (shouldBeConvertedToFarsi(textWords[i].toLowerCase()))
	{
		separatedWords = textWords[i].split(/([^a-zA-Z`'\/\\])/);
		for (j=0; j<separatedWords.length; j++ ) 
		{
			newTextWords[counter] = separatedWords[j].toLowerCase();
			counter++;
		}
	}
	else
	{
		newTextWords[counter] = textWords[i];
		counter++;
	}
	return newTextWords;
}


function getAllText() // get all the text from text elements combined
{
	if (latinTextIsHtml)
		return addTextFromAllTextNodes(latinTinyMCE.getDoc());
	else
		return document.getElementById(latinControlId).value;	
}

function getUnknownWordsFromText(latinText)
{
	var unknownWords = new Array; // string array of words in the latinText we do not know their conversion
	var textWords = new Array; // contains an array with each word in the latin string
	var textFwords = new Array; // contains both latin words and also farsi translations (after they are put there)
	
	latinText = latinText.replace(/'/g,'`');

	textWords = separateLatinLetters(latinText);
	
	var wordShouldBeConvertedToFarsi = new Array;
	
	var lowerCaseTextWord = new Array;
	for (i=0; i<textWords.length; i++ ) 
	{
		lowerCaseTextWord[i] = textWords[i].toLowerCase();
		wordShouldBeConvertedToFarsi[i] = shouldBeConvertedToFarsi(lowerCaseTextWord[i]);
	}
	
	for (i=0; i<textWords.length; i++ ) // put separated word into textFwords so they can be translated
	{
		if (!knownWordsTable.hasItem(lowerCaseTextWord[i]) && unknownWords.indexOf(lowerCaseTextWord[i]) == -1 											 		&& wordShouldBeConvertedToFarsi[i])
		{
			unknownWords.push(lowerCaseTextWord[i]);
		}											  											
	}
	return unknownWords;
}

var unknownWordsInSubmittedRequest;
var waitingForRequestResponse = false;

function processAjaxResponse(responseText)
{
	var thisTime = new Date();	
	lastConvertResponseTime = 	thisTime.getTime() - lastAjaxRequestTime.getTime();
	
	waitingForRequestResponse = false;
	
	// get latin = farsi array object from responseText json string 
	var newFarsiWordsTable = eval('(' + responseText + ')');

	// add new words to the knownwords table
	for(latinWord in newFarsiWordsTable)
	{
		knownWordsTable.setItem(latinWord, newFarsiWordsTable[latinWord]);
	}
/*	
	if (behnevisStore) // if persistent local store is available, save all words into local store 
	{
		behnevisStore.set('knownWordsTable', jsonEncodeObject(knownWordsTable.items));
	
		//alert(jsonEncodeObject(knownWordsTable.items));
	}
*/
	submitAjaxRequestIfNecessary();
}


function mapCaretPositionInLatinToFarsi(caretPosition)
{
	for (var i = 0; i < latinCaretPosition.length ; i++) 
	{
		if (latinCaretPosition[i]>caretPosition)
		{
			if (i>0)
			{
				return farsiCaretPosition[i-1] + (caretPosition - latinCaretPosition[i-1]);
			}
			else
			{
				return caretPosition;
			}
		}
	}
	
	// if not between words, go to the end
	return farsiCaretPosition[farsiCaretPosition.length-1];

}


function getCaretPosition (ctrl) {

	var CaretPos = 0;
	// IE Support
	if (document.selection) {

		ctrl.focus ();
		var Sel = document.selection.createRange ();

		Sel.moveStart ('character', -ctrl.value.length);

		CaretPos = Sel.text.length;
	}
	// Firefox support
	else if (ctrl.selectionStart || ctrl.selectionStart == '0')
		CaretPos = ctrl.selectionStart;

	return (CaretPos);

}


function setCaretPosition(ctrl, pos)
{

	if(ctrl.setSelectionRange)
	{
		ctrl.focus();
		ctrl.setSelectionRange(pos,pos);
	}
	else if (ctrl.createTextRange) {
		var range = ctrl.createTextRange();
		range.collapse(true);
		range.moveEnd('character', pos);
		range.moveStart('character', pos);
		range.select();
	}
}


function convertAnyTypeOFLatinEditorTextToFarsi() // whether html or simple editor
{
		if (latinTextIsHtml)
		{
			lastConvertedLatinText = latinTinyMCE.getContent();
			convertTinyMCE();
		}
		else // simple text area or one-line-input
		{


			if (farsiControlId==latinControlId)
			{
				// remember the position of caret in the editor before updating it
				var farsiContorl = document.getElementById(farsiControlId);
				
				if (IE) // for IE
				{
					var selectionStart = getCaretPosition (farsiContorl);
				}
				else // firefox
				{
					var selectionStart = farsiContorl.selectionStart; // only works for firefox
					var selectionEnd = farsiContorl.selectionEnd;
				}
				//farsiContorl.blur();
			}
			
			lastConvertedLatinText = document.getElementById(latinControlId).value;
	
			document.getElementById(farsiControlId).value = localConvertToFarsi(document.getElementById(latinControlId).value);
//document.getElementById(latinControlId).value = localConvertToFarsi(document.getElementById(latinControlId).value);
		
			if (farsiControlId == latinControlId) // if single editor
			{
				// restore the caret position to what it was before conversion
				if (IE) // for IE
				{
				//	farsiContorl.focus();
					setCaretPosition (farsiContorl, mapCaretPositionInLatinToFarsi(selectionStart));
				}
				else // firefox
				{
					farsiContorl.selectionStart = mapCaretPositionInLatinToFarsi(selectionStart);
					farsiContorl.selectionEnd = mapCaretPositionInLatinToFarsi(selectionEnd);
				}
			}
			
			//farsiContorl.focus();
		}
	
}


function submitAjaxRequestIfNecessary() //asks server to convert unknown words, otherwise convert latin control content to  farsi
{
	var allLatinText = getAllText();
	var unknownWords = getUnknownWordsFromText(allLatinText);
	if (unknownWords.length>0)
	{
		waitingForRequestResponse = true;
		unknownWordsInSubmittedRequest = unknownWords;
				
		var thisTime = new Date(); 
		lastAjaxRequestTime.setTime(thisTime.getTime()); // save the time in which the last ajax request is sent, for detecting timer out and allowing further requests
		
		ajax("/php/convert.php?", "farsi="+  encodeURI(unknownWords.join(" "))+"&" + "resulttype=json"+"&" + "responsetime="+lastConvertResponseTime, processAjaxResponse)
	}
	else
	{
		setConversionFinishedStatusDisplay(true);
		
		// if single-line input, only allow conversion after pressing space  key
		if ((farsiControlId != latinControlId) || lastKeyPressedNumber==32)
		{
			convertAnyTypeOFLatinEditorTextToFarsi();
		}
	}
	
}
	
//----- realtime asynchronous request -----------

function ajax(url, vars, callbackFunction)
{
  var request = window.XMLHttpRequest ?
      new XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP.3.0");
  request.open("POST", url, true);
  request.setRequestHeader("Content-Type",
                           "application/x-www-form-urlencoded"); 
 
  request.onreadystatechange = function()
  {
    
	if (request.readyState == 4 && request.status == 200)
    {
      if (request.responseText && callbackFunction)
      {
          callbackFunction(request.responseText);
      }
    }
	
/*	if (request.readyState == 2 && request.status == 408)
	{
	  alert('timed out!');
	}*/
  };
  
 
  request.send(vars);
}




//------------

var knownWordsTable = new Hash(); // a hash table (latin, farsi) containing known translations
var latinFromFarsiWordsTable = new Hash(); // a hash table (farsi, latin) containing known latin words that have translated to a given (key) farsi word. This is somehow the inverse task of knownWordsTable, but since it only contains words used so far in the document whereas knownWordsTable may contain many words not used in the document 

function initKnownWordsTable()
{
	knownWordsTable.setItem(" ", " "); // add space and double space to the table so it does not ask for it from the server
	knownWordsTable.setItem("  ", "  ");
	knownWordsTable.setItem("", "" );
	knownWordsTable.setItem(".", "." );
	knownWordsTable.setItem("\\n", "\\n");
	knownWordsTable.setItem(",", "،");
	knownWordsTable.setItem("ii", "یی");
	knownWordsTable.setItem("nima","نیما");
	knownWordsTable.setItem("yushij","یوشیج");
	knownWordsTable.setItem("enshallah","انشا‌الله");
	knownWordsTable.setItem("enshaallah","انشا‌الله");
	knownWordsTable.setItem("canada","کانادا");
	knownWordsTable.setItem("4","4");
	knownWordsTable.setItem("+","+");
	knownWordsTable.setItem("<","<");
	knownWordsTable.setItem("&","&");
	knownWordsTable.setItem("s","س");
	knownWordsTable.setItem("x","خ");
	knownWordsTable.setItem(";","؛");
	
	
	knownWordsTable.setItem("angeles","انجلس");
		
	knownWordsTable.setItem("january","ژانویه");
	knownWordsTable.setItem("february","فوریه");
	knownWordsTable.setItem("march","مارث");
	knownWordsTable.setItem("april","آوریل");
	knownWordsTable.setItem("may","می");
	knownWordsTable.setItem("june","ژوئن");
	knownWordsTable.setItem("july","ژولای");
	knownWordsTable.setItem("august","آگوست");
	knownWordsTable.setItem("september","سپتامبر");
	knownWordsTable.setItem("october","اکتبر");
	knownWordsTable.setItem("november","نوامبر");
	knownWordsTable.setItem("december","دسامبر");
	knownWordsTable.setItem("\\\\","\\\\"); 
	knownWordsTable.setItem("ahmadinejad","احمدی‌نژاد"); 
	knownWordsTable.setItem("ahmadinezhad","احمدی‌نژاد"); 
	knownWordsTable.setItem("ahmadinezhaad","احمدی‌نژاد"); 
	knownWordsTable.setItem("sakht","سخت"); 
	knownWordsTable.setItem("filter","فیلتر"); 	 
	knownWordsTable.setItem("zan","زن"); 

	knownWordsTable.setItem("maa","ما"); 
	knownWordsTable.setItem("haa","ها"); 
	knownWordsTable.setItem("enshalla","انشا‌الله"); 
	knownWordsTable.setItem("chetoreh","چطوره"); 
	knownWordsTable.setItem("khanoom","خانوم");
	knownWordsTable.setItem("aazma","آزما");
	knownWordsTable.setItem("dah","ده");
	knownWordsTable.setItem("book","بوک");
	knownWordsTable.setItem("tabestan","تابستان");
	knownWordsTable.setItem("shekaste","شکسته");
    knownWordsTable.setItem("kosh","کش");
	knownWordsTable.setItem("sharab","شراب");
	knownWordsTable.setItem("sharaab","شراب");
	knownWordsTable.setItem("shebh","شبه");
	knownWordsTable.setItem("joon","جون");
	knownWordsTable.setItem("farsi","فارسی");
	knownWordsTable.setItem("w","و");
	knownWordsTable.setItem("khamenei","خامنه‌ای");
	knownWordsTable.setItem("khaamenei","خامنه‌ای");
	knownWordsTable.setItem("khaameneii","خامنه‌ای");
	knownWordsTable.setItem("khameneii","خامنه‌ای");
	knownWordsTable.setItem("gir","گیر");
}

initKnownWordsTable();

function addNewWordsToLocalTable(unknownWords, newFarsiWords)
{
	var newFarsiWord;
	for (k=0; k<unknownWords.length; k++)
	{
		newFarsiWord = newFarsiWords[k];
		// with some reason, words ending in ye have an extra virtual space at the end that should be removed before adding to the table
		if (newFarsiWord.charCodeAt(newFarsiWord.length-1) == 0x200C)
		{
			newFarsiWord = newFarsiWord.slice(0,-1); // remove last char which is an invisible space
		}
		
		knownWordsTable.setItem(unknownWords[k], newFarsiWord);
	}
}

// strings that show the word is a url

var internetSubstrings = new Array("www", "http", "ftp", ".edu", ".com", ".org",".net", ".tv", ".ws", "mailto:", ".us", ".ir", ".info", "؟", decodeURI("%C2%A0")); 

function wordIsMarkedToNotConverted(word)
{
	if (word.length>1 && (word.charAt(0) == '/' | word.charAt(0) == '\\'))
	{
		return true;
	}
	else
	{
		return false;
	}
}

function shouldBeConvertedToFarsi(word)
{
	
	if (conversionOption.leaveBBCodesIntact && word.length>0 && word.charAt(0) == '[') // when bbcodes are active, if it starst with '[', do not convert
	{
		return false;
	}
	
	for(s in internetSubstrings)
	{
		if (word.indexOf(internetSubstrings[s]) != -1)
		{
			return false;
		}
	}


	for(s in word)
	{
		if (word.charCodeAt(s) > 256) // non-latin letters should not be converted
		{
			return false;
		}
	}

	if (wordIsMarkedToNotConverted(word))
	{
		return false;
	}

	return true; // if it is not an internet address the control flow gets here
}


//----- replace space between some 'pasvand's and their word

function attachPasvands(farsiText)
{
	var pasvands = ["ترین","ها","هایم","هایت","هایشان","یی‌","هایش","هایت","هایتان", "هایمان","ای","هاشون","هاتون", "هات", "های", "هایی‌","ام","شان","اش"];
	var myregexp;

	for (p=0; p<pasvands.length; p++ ) 
	{
		myregexp = new RegExp(" "+pasvands[p]+" ", "g"); // needs to be more restriced so it does not replace all 'ha's
		farsiText = farsiText.replace(myregexp,String.fromCharCode(0x200C)+ pasvands[p]+" ");
	}

	return farsiText;
}

function attachPishvands(farsiText)
{
	var pishvands = ["می‌","نمی‌","بی"];
	var pishvandRegexp = [/(\sمی‌\s)/g,/(\sنمی‌\s)/g,/(\sبی\s)/g];

	for (p=0; p<pishvands.length; p++ ) 
	{
		farsiText = farsiText.replace(pishvandRegexp[p]," " + pishvands[p]+String.fromCharCode(0x200C));
	}

	return farsiText;
}

function replaceOneChar(s,c,n){
  (s = s.split(''))[--n] = c;
  return s.join('');
};


function replaceHyphenWithVirtualSpace(s)
{
	// replace - with virtual space
	var p;
	p = s.search(/[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]-[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]/);
	
	while (p>-1)
	{
		s = replaceOneChar(s,String.fromCharCode(0x200C),p+2);	
		p = s.search(/[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]-[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]/);
	}

	// replace -- with -
	var p;
	p = s.search(/[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]--[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]/);
	
	while (p>-1)
	{
		s = replaceOneChar(s,'',p+2);	
		p = s.search(/[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]--[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیهٔآأ]/);
	}


	return s;
}


function replaceWithFarsiNumbers(farsiText)
{
	farsiText = farsiText.replace(/1/g,'۱');
	farsiText = farsiText.replace(/2/g,'۲');
	farsiText = farsiText.replace(/3/g,'۳');
	farsiText = farsiText.replace(/4/g,'۴');
	farsiText = farsiText.replace(/5/g,'۵');
	farsiText = farsiText.replace(/6/g,'۶');
	farsiText = farsiText.replace(/7/g,'۷');
	farsiText = farsiText.replace(/8/g,'۸');
	farsiText = farsiText.replace(/9/g,'۹');
	farsiText = farsiText.replace(/0/g,'۰');
	return farsiText;
}

// variables to map each caret position in latin to farsi, used in single editor case

var latinCaretPosition;
var farsiCaretPosition;

function assembleFarsiTextUsingTable(textWords, wordShouldBeConvertedToFarsi)
{
	
	latinCaretPosition = new Array; 
	farsiCaretPosition = new Array;
	var accumulatedLatinCaretPosition = 0;
	var accumulatedFarsiCaretPosition = 0;
	latinCaretPosition.push(accumulatedLatinCaretPosition);
	farsiCaretPosition.push(accumulatedFarsiCaretPosition);

	var farsiText = new String;
	for (i=0; i<textWords.length; i++ ) // look words in the table, convert them and add all together.
	{
		lowerCaseTextWord = textWords[i].toLowerCase();
		if (knownWordsTable.hasItem(lowerCaseTextWord) && wordShouldBeConvertedToFarsi[i])
		{
			farsiText = farsiText +  knownWordsTable.getItem(lowerCaseTextWord);
			latinFromFarsiWordsTable.setItem(knownWordsTable.getItem(lowerCaseTextWord), lowerCaseTextWord);
			
			// caret position mapping
			accumulatedLatinCaretPosition = accumulatedLatinCaretPosition + lowerCaseTextWord.length;
			accumulatedFarsiCaretPosition = accumulatedFarsiCaretPosition +  knownWordsTable.getItem(lowerCaseTextWord).length;
			latinCaretPosition.push(accumulatedLatinCaretPosition);
			farsiCaretPosition.push(accumulatedFarsiCaretPosition);
			
		} else
		{
			if (wordIsMarkedToNotConverted(textWords[i])) // words with / or \ in the beginning
			{
				farsiText = farsiText +  textWords[i].substr(1); // remove the first letter, which is \ or /

				if (latinControlId == farsiControlId) // in case of si ngle editor, make place the word with / in a table so it is not converted to farsi again, but make sure to remove / from its beginning 
				{
					knownWordsTable.setItem(textWords[i].substr(1), textWords[i].substr(1));				
				}
				
				// caret position mapping
				accumulatedLatinCaretPosition = accumulatedLatinCaretPosition + textWords[i].length;
				accumulatedFarsiCaretPosition = accumulatedFarsiCaretPosition +  textWords[i].substr(1).length;
				latinCaretPosition.push(accumulatedLatinCaretPosition);
				farsiCaretPosition.push(accumulatedFarsiCaretPosition);
				
			}
			else
			{
				farsiText = farsiText +  textWords[i];

				// caret position mapping
				accumulatedLatinCaretPosition = accumulatedLatinCaretPosition + textWords[i].length;
				accumulatedFarsiCaretPosition = accumulatedFarsiCaretPosition + textWords[i].length;
				latinCaretPosition.push(accumulatedLatinCaretPosition);
				farsiCaretPosition.push(accumulatedFarsiCaretPosition);
				
			}
		}
	
	}

	farsiText = attachPasvands(farsiText);
	farsiText = attachPishvands(farsiText);
	farsiText = replaceHyphenWithVirtualSpace(farsiText);

	if (conversionOption.useFarsiNumbers)
	{
		farsiText = replaceWithFarsiNumbers(farsiText);
	}
	
	return farsiText;
}


//--------------------------- main conversion function------------------------------

function localConvertToFarsi(latinText)
{
	latinText = latinText.replace(/'/g,'`');
	//var textWords = new Array; // contains an array with each word in the latin string
	var textWords = separateLatinLetters(latinText);
//	alert(textWords);

	var wordShouldBeConvertedToFarsi = new Array;
	
	var lowerCaseTextWord = Array;
	for (i=0; i<textWords.length; i++ ) 
	{ 
		lowerCaseTextWord = textWords[i].toLowerCase();
		wordShouldBeConvertedToFarsi[i] = shouldBeConvertedToFarsi(lowerCaseTextWord);
	}
	
		farsiText = assembleFarsiTextUsingTable(textWords, wordShouldBeConvertedToFarsi);
		return farsiText;
}
	
	
//------------------------------- helper functions -----------------------------------------------------

function Hash()
{
	this.length = 0;
	this.items = new Array();
	for (var i = 0; i < arguments.length; i += 2) {
		if (typeof(arguments[i + 1]) != 'undefined') {
			this.items[arguments[i]] = arguments[i + 1];
			this.length++;
		}
	}
   
	this.removeItem = function(in_key)
	{
		var tmp_value;
		if (typeof(this.items[in_key]) != 'undefined') {
			this.length--;
			var tmp_value = this.items[in_key];
			delete this.items[in_key];
		}
	   
		return tmp_value;
	}

	this.getItem = function(in_key) {
		return this.items[in_key];
	}

	this.setItem = function(in_key, in_value)
	{
		if (typeof(in_value) != 'undefined') {
			if (typeof(this.items[in_key]) == 'undefined') {
				this.length++;
			}

			this.items[in_key] = in_value;
		}
	   
		return in_value;
	}

	this.hasItem = function(in_key)
	{
		return typeof(this.items[in_key]) != 'undefined';
	}
	
	
	this.firstKeyForValue= function(in_value)
	{
		for (key in this.items)
		{
			if (this.items[key] == in_value)
			{
				return key;
			}
		}
	}
}
		
var lastKeyPressedNumber = 0; // the ascii number of rthe last key being pressed, used in single-line conversion		
		
function cleanLatinTextonFirstKeyDownOrClick(e)
{
	if (IsFirstTimeLatinInput)
		{						
			document.getElementById(latinControlId).value = "";
			document.getElementById(farsiControlId).style.color = '#000000';
			document.getElementById(latinControlId).focus();
			IsFirstTimeLatinInput = false;
			if (latinControlId != farsiControlId)
			{
				convertAutomatically = true;
			}
		}
		
		if (latinControlId == farsiControlId)
		{
			if(window.event) // IE
				{
				keynum = e.keyCode;
				}
			else if(e.which) // Netscape/Firefox/Opera
				{
				keynum = e.which;
				}

			lastKeyPressedNumber = keynum; // use this to only allow conversion to be done after pushing space
			
			if (keynum == 32)
			{
				setConversionFinishedStatusDisplay(false);
				submitAjaxRequestIfNecessary();
			}
		}
		else
		{
			keyUpDetected();
		}
}		