
//Variables

var arrayRequests = new Array();  //2D array - holds timeout information for each request
var nRequests = 0;
var arrayStatusRequests = new Array();  //2D array - status request info
var nStatusRequests = 0;
var bInited = false;
var g_bRequestError = false;
var g_strDefaultService = "";
var g_bReplaceLinefeeds = false;
var g_bTypewriterText = false;
var g_bPageLinking = false;
var g_strUpdateCol = '#B4B4D4';
var g_ajaxClient = null;
var g_bActiveX = false;


//Constants

//array index indentifiers
var AI_REQUEST_LOCATION = 0;   
var AI_TIMEOUTID		= 1;
var AI_REQUEST_RECORD	= 2;
var AI_REQUEST_FIELD	= 3;
var AI_REQUEST_SERVICE	= 4;
var AI_REQUEST_FORMAT	= 5;
var AI_BGCOLOR		= 6;
var AI_CLASS		= 7;
var CELL_VALUE_INITIAL	= "Pending...";
var CELL_VALUE_NOTAVAIL = "N/A";
var SAI_LOCATION		= 0;						//SAI = Status Array Index
var SAI_TYPE			= 1;

var STATUSREQUEST_CONNECT	= "stateConnect"
var STATUSREQUEST_LATENCY	= "stateLatency"
var STATUSREQUEST_BANDWIDTH	= "stateBandwidth"

var AUTO_FORMAT_NUMBERS = true;

var ID_SEL_PAGE_REF   = "idSelPageRef";
var ID_SEL_SARRTREQ   = "idSelSarRtReq";

var CSSNAME_CELL_UPDATE = "rtcellupd";
var CSSNAME_CELL_NORMAL = "rtcell";
var CSSNAME_CELL_SELECTED = "rtcell_selected";  //by default, we do not update colour of a selected cell
var CSSNAME_CELL_UPDATE_UP = "rtcellupd_up";
var CSSNAME_CELL_UPDATE_DOWN = "rtcellupd_down";
var CSSNAME_CELL_UPDATE_SAME = "rtcellupd_same";
var CSSNAME_CELL_UPDATE_AFTER_UP = "rtcellupd_after_up";
var CSSNAME_CELL_UPDATE_AFTER_DOWN = "rtcellupd_after_down";
var CSSNAME_CELL_UPDATE_AFTER_SAME = "rtcellupd_after_same";

var ID_STATUS_AREA = "rt-status";

//Functions

function init()
{
	requestStatusRecords();

	if (g_ajaxClient)
	{
		//When streaming we want to send record requests in the initial connect message, so request before connect.
		if (g_ajaxClient.IsStreaming())
		  requestRecords();  
		bInited = g_ajaxClient.Connect();
	}
	else
		bInited = RtWebClient.InitializeAsync();
}

function onClose()
{
  if (g_ajaxClient)
    g_ajaxClient.Disconnect();
}

function formatDecimal(argvalue, addzero, decimaln) 
{
  var fValue = parseFloat(argvalue);
  if (isNaN(fValue))
	return argvalue;

  if (addzero == null)
	return ("" + fValue);

  var numOfDecimal = (decimaln == null) ? 2 : decimaln;
  var number = 1;

  number = Math.pow(10, numOfDecimal);

  argvalue = Math.round(fValue * number) / number;
  argvalue = "" + argvalue;

  if (argvalue.indexOf(".") == 0)
    argvalue = "0" + argvalue;

  if (addzero == true) {
    if (argvalue.indexOf(".") == -1)
      argvalue = argvalue + ".";

    while ((argvalue.indexOf(".") + 1) > (argvalue.length - numOfDecimal))
      argvalue = argvalue + "0";
  }

  return argvalue;
}

function formatValue(strValue, strFormat)
{
	var strReturn = strValue;

	if (strFormat != null)
	{
		if (strFormat.substring(0,2) == "%.")
		{
			nDigitsAfterPoint = strFormat.substring(2, strFormat.length);
			return formatDecimal(strReturn, true, nDigitsAfterPoint);
		}
		else if (strFormat == "MinToTime")  //used in Ten4 time fields
		{
			//Minutes to 24hr time.

			var hours = Math.floor(strValue / 60);
			if (hours < 10)
			   hours = '0' + hours;
			var mins = (strValue % 60);
			if (mins < 10)
			   mins = '0' + mins;

			return hours + ":" + mins;  
		}
	}

	//auto format works on numbers beginning with '+'
	if ((AUTO_FORMAT_NUMBERS) && (strReturn.charAt(0) == '+')) 
		strReturn = formatDecimal(strReturn);

	if (g_bReplaceLinefeeds)
	{
		//strReturn = strReturn.replace(/</g, "&#60;");
		//strReturn = strReturn.replace(/>/g, "&#62;");
		strReturn = strReturn.replace(/\n/g, "<br>");
		//strReturn = strReturn.replace(/\t/g, "&#09;");  //tabs don't work
		strReturn = strReturn.replace(/\s/g, "&nbsp");  //replace spaces
		//strReturn = strReturn.replace(/#/g, "&#35;");  //hash signs
	}

	//page links <> should be replaced before other tags (<br>'s and <tt>'s) are inserted
	if (g_bPageLinking)
	{
		//Replace <xxx> with <a href="news.html?xxx"> &#60;xxx&#62;</A>
		strReturn = strReturn.replace(/(<)([ ]{0,1})(>)/g, "&#60;$2&#62;"); //non-link exceptions
		strReturn = strReturn.replace(/(<)([^ >]+)(>)/g, "<a href=\"page.htm?$2\">&#60;$2&#62;</A>");
	}

	if (g_bTypewriterText)
	{
		strReturn = "<tt>" + strReturn + "</tt>";
	}

	return strReturn;
}

function updateReceived(strLocation, strValue)
{
	resetTimeout(strLocation);

//	document.getElementById("testarea").innerHTML = strLocation;  //DEBUG TEST

	strFormat = arrayRequests[getRequestIndex(strLocation)][AI_REQUEST_FORMAT];
	var strValueFormatted = formatValue(strValue, strFormat);

	elem = document.getElementById(strLocation);
    var strOldValue = elem.innerHTML;

//Update the value:
	elem.innerHTML = strValueFormatted;

//Update the cell/background colour/style:

	//if this is a selected cell, don't apply the update style
	if (isStyleDefined(CSSNAME_CELL_SELECTED) && parentStyleIs(elem, CSSNAME_CELL_SELECTED))
	  return; 

    //Figure out the CSS class value for the updated cell
    var strClassUpdate = CSSNAME_CELL_UPDATE;
	if (isStyleDefined(CSSNAME_CELL_UPDATE_UP) && valueUp(strOldValue, strValueFormatted))
	   strClassUpdate = CSSNAME_CELL_UPDATE_UP;
	else if (isStyleDefined(CSSNAME_CELL_UPDATE_DOWN) && valueDown(strOldValue, strValueFormatted))
	   strClassUpdate = CSSNAME_CELL_UPDATE_DOWN;
	else if (isStyleDefined(CSSNAME_CELL_UPDATE_SAME))
	   strClassUpdate = CSSNAME_CELL_UPDATE_SAME;

	//elem.parentElement.bgColor = g_strUpdateCol;
	xParentNode(elem).className = strClassUpdate; //CSSNAME_CELL_UPDATE;
	strCommand = "clearUpdateColour('" + strLocation + "')";
	timerId = setTimeout(strCommand, 3000);

	rememberTimeout(strLocation, timerId);
}

function valueUp(strOld, strNew)
{
  var nOld = parseFloat(strOld);
  var nNew = parseFloat(strNew);

  if (isNaN(nOld) || isNaN(nNew))
     return false;

  return (nNew > nOld);
}

function valueDown(strOld, strNew)
{
  var nOld = parseFloat(strOld);
  var nNew = parseFloat(strNew);

  if (isNaN(nOld) || isNaN(nNew))
     return false;

  return (nNew < nOld);
}

function valueSame(strOld, strNew)
{
  var nOld = parseFloat(strOld);
  var nNew = parseFloat(strNew);

  if (isNaN(nOld) || isNaN(nNew))
     return false;

  return (nNew == nOld);
}

function valueUp(strOld, strNew)
{
  var nOld = parseFloat(strOld);
  var nNew = parseFloat(strNew);

  if (isNaN(nOld) || isNaN(nNew))
     return false;

  return (nNew > nOld);
}

function getStyleClass(className) 
{
	for (var s = 0; s < document.styleSheets.length; s++)
	{
		if(document.styleSheets[s].rules)
		{
			for (var r = 0; r < document.styleSheets[s].rules.length; r++)
			{
				if (document.styleSheets[s].rules[r].selectorText == '.' + className)
				{
					return document.styleSheets[s].rules[r];
				}
			}
		}
		else if(document.styleSheets[s].cssRules)
		{
			for (var r = 0; r < document.styleSheets[s].cssRules.length; r++)
			{
				if (document.styleSheets[s].cssRules[r].selectorText == '.' + className)
					return document.styleSheets[s].cssRules[r];
			}
		}
	}
	
	return null;
}

//cached call (much quicker than findStyle/getStyleClass)
function isStyleDefined(strClassName)
{
	return g_styleCache.isStyleDefined(strClassName);
}

function findStyle(strClassName)
{
	return (getStyleClass(strClassName) != null);
}

function onConnected()
{
	if ((g_ajaxClient != null) && g_ajaxClient.IsStreaming())  //we've already reqeusted records in the connect message when streaming.
		return;

	requestRecords();
}

function stateReceived(strLocation, strValue)
{
    whichEl = eval("document.all." + strLocation);
    whichEl.innerHTML = strValue;
}

function resetTimeout(strLocation)
{
	//find timeout in array
	index = getRequestIndex(strLocation);
	if (index == -1)
		return;
	timerId = arrayRequests[index][AI_TIMEOUTID];

	clearTimeout(timerId);

	arrayRequests[index][AI_TIMEOUTID] = 0;
}

function rememberTimeout(strLocation, timerId)
{
	index = getRequestIndex(strLocation);
	if (index == -1)
		return;
	arrayRequests[index][AI_TIMEOUTID] = timerId;
}

function clearUpdateColour(strLocation)
{
	resetTimeout(strLocation);

	el = document.getElementById(strLocation);
	if (el != null)
	{
    	//if this is a selected cell, don't apply the update style
	    if (isStyleDefined(CSSNAME_CELL_SELECTED) && parentStyleIs(el, CSSNAME_CELL_SELECTED))
	      return; 

		var className = xParentNode(el).className;
		if ((className == CSSNAME_CELL_UPDATE_UP) && (isStyleDefined(CSSNAME_CELL_UPDATE_AFTER_UP)))
			xParentNode(el).className = CSSNAME_CELL_UPDATE_AFTER_UP;
		else if ((className == CSSNAME_CELL_UPDATE_DOWN) && (isStyleDefined(CSSNAME_CELL_UPDATE_AFTER_DOWN)))
			xParentNode(el).className = CSSNAME_CELL_UPDATE_AFTER_DOWN;
		else if ((className == CSSNAME_CELL_UPDATE_SAME) && (isStyleDefined(CSSNAME_CELL_UPDATE_AFTER_SAME)))
			xParentNode(el).className = CSSNAME_CELL_UPDATE_AFTER_SAME;
		else
			xParentNode(el).className = arrayRequests[getRequestIndex(strLocation)][AI_CLASS];
	} 	

}

//go through a few levels of parents to find is style is used
function parentStyleIs(el, strClass)
{
  var parent = el;

  for (n=0; n<5; n++)
  {
	if (parent.className == CSSNAME_CELL_SELECTED)
	  return true;

    parent = xParentNode(parent);
	if (parent == null)
	  return false;
  }

  return false;
}

function xParentNode(el)
{
	return ((el.parentElement)?el.parentElement:el.parentNode);
}

function getRequestIndex(strLocation)
{
	for (i=0; i<nRequests; i++)
	{
		if (arrayRequests[i][AI_REQUEST_LOCATION] == strLocation)
			return i;
	}

	return -1;
}

function getDomElement(strLocation)
{
	//return eval("document.all." + strLocation);
	return document.getElementById(strLocation);
}

function initElement(strLocation)
{
	el = getDomElement(strLocation);
	if (bInited || g_ajaxClient.IsStreaming())
		el.innerHTML = CELL_VALUE_INITIAL;
	else
		el.innerHTML = CELL_VALUE_NOTAVAIL;
}

function requestFromActivex(strLocation, strRecord, strField, strService)
{
	initElement(strLocation);

	if (g_ajaxClient)
	{
		return g_ajaxClient.AddRequest(strLocation, strRecord, strField, strService);
	}

	return RtWebClient.AddRequestDhtml(strLocation, strRecord, strField, strService);
}

function requestStatusFromActivex(strLocation, strType)
{
	if (g_ajaxClient)
		return false;

	return RtWebClient.AddStatusRequest(strLocation, strType);
}

function setDefaultService(strService)
{
	g_strDefaultService = strService;

	if (g_ajaxClient)
		return false;

	RtWebClient.SetDefaultService(strService);
}

function getDefSvc()
{
	return g_strDefaultService;
}

function setReplaceLinefeeds(bOn)
{
	g_bReplaceLinefeeds = bOn;
}

function setTypewriterText(bOn)
{
	g_bTypewriterText = bOn;
}

function setPageLinking(bOn)
{
	g_bPageLinking = bOn;
}

function LoadUIRecords()
{
}

function setUpdateCol(strUpdateCol)
{
	g_strUpdateCol = strUpdateCol;
}

function requestRecords()
{
	//go through our request list and make the requests from the activex control

	for (i=0; i<nRequests; i++)
	{
		var strLocation, strRecord, strField, strService;

		strLocation = arrayRequests[i][AI_REQUEST_LOCATION];
		strRecord = arrayRequests[i][AI_REQUEST_RECORD];
		strField = arrayRequests[i][AI_REQUEST_FIELD];
		strService = arrayRequests[i][AI_REQUEST_SERVICE];

		//remember background color
		//whichEl = eval("document.all." + strLocation);  //NON STANDARD APPARENTLY
		el = document.getElementById(strLocation);
//		alert(whichEl.firstChild.nodeValue);
//		alert(whichEl.bgColor);
//		alert(whichEl.parentElment.bgColor);
//		arrayRequests[i][AI_BGCOLOR] = whichEl.parentElement.bgColor;
//		alert("about to requestFromActivex()");
		arrayRequests[i][AI_CLASS] = xParentNode(el).className;

		if (!requestFromActivex(strLocation, strRecord, strField, strService))
		{
			alert("Could not make request to the RtWebClient ActiveX control");
			return false;
		}

	}

	return true;
}

function requestStatusRecords()
{
	//request status records from activex control 

	for (i=0; i<nStatusRequests; i++)
	{
		var strLocation = arrayStatusRequests[i][SAI_LOCATION];
		var strType = arrayStatusRequests[i][SAI_TYPE];

		if (!requestStatusFromActivex(strLocation, strType))
			return false;
	}

	return true;
}


//request() function params:
//[1]: strRecord
//[2]: strField
//[3]: strService=defaultService  (optional parameter)
function request(strRecord, strField, strService, strFormat)
{
	if (g_bRequestError)  
		return false;  //to prevent loads of error messages being displayed

	if (strService == null)
		strService = g_strDefaultService;

	if (strService == "")
	{
		if (g_bRequestError == false)
		{
			alert("Default service has not been defined.");
			g_bRequestError = true;
		}

		return false;
	}

	//generate a location identifier
	var strLocation = "request" + nRequests;

	//store request details
	arrayRequests[nRequests] = new Array();
	arrayRequests[nRequests][AI_REQUEST_LOCATION] = strLocation;
	arrayRequests[nRequests][AI_REQUEST_RECORD] = strRecord;
	arrayRequests[nRequests][AI_REQUEST_FIELD] = strField;
	arrayRequests[nRequests][AI_REQUEST_SERVICE] = strService;
	arrayRequests[nRequests][AI_REQUEST_FORMAT] = strFormat;

	nRequests ++;

	//output HTML identifier for this request (using SPAN)
	document.write("<SPAN ID='" + strLocation + "' onmouseup='onMouseUp(this)' onDblClick='onDblClickReq(this)'>" + CELL_VALUE_INITIAL + "</SPAN>");

	return true;
}

function requestStatus(strType)
{
	var strLocation = "st" + nStatusRequests;

	arrayStatusRequests[nStatusRequests] = new Array();
	arrayStatusRequests[nStatusRequests][SAI_LOCATION] = strLocation;
	arrayStatusRequests[nStatusRequests][SAI_TYPE] = strType;

	nStatusRequests ++;

	//output HTML identifier for this request (using SPAN)
	document.write("<SPAN ID='" + strLocation + "'>" + CELL_VALUE_INITIAL + "</SPAN>");
}

function ShowEventViewer()
{
	RtWebClient.ShowEventViewer();
}

function putElem_SelSarRtReq()
{
//	document.write("<SPAN id='" + ID_SEL_SARRTREQ + "'></SPAN>");
	document.write("<INPUT type='text' name='" + ID_SEL_SARRTREQ + "' id='" + ID_SEL_SARRTREQ + "' value='' size='90'></INPUT>");
}

function onMouseUp(el)
{
	if (el == null)
		return;

	//TODO: Split this function up: OutputPageRef() and OutputPageNum()

	//attempt to output PAGE reference:

	var nRow = el.id.substring(el.id.length-1, el.id.length);

	var selRng = document.selection.createRange();
	var nSelLength = selRng.text.length;
	var elemRng = document.body.createTextRange();
	elemRng.moveToElementText(selRng.parentElement());
	var offset = 0;
	while (selRng.compareEndPoints("StartToStart", elemRng) > 0)
	{
		++offset;
		selRng.move("character", -1);
	}
	el = getDomElement(ID_SEL_PAGE_REF);
	var strPageRef = "PAGE " + nRow + " " + offset + " " + nSelLength + " NONE"; 
//	if (el != null)
//		el.innerHTML = strPageRef;

	//output page number for Telerate pages
	var nPage = getPageFromSelection();
	if (nPage != -1)
		textGoToPage.value = nPage;

	//atempt to find SaracenRT req element
	el = getDomElement(ID_SEL_SARRTREQ);
	if (el != null)
	{
		strRecord = TELERATE_PAGEREQ_PREFIX + location.search.substring(1);
		el.value = "=Rt.Rtm.1|GET!'!" + strRecord + ";" + strPageRef + ";Telerate'";
	}

}

function onDblClickReq(el)
{
	if (el == null)
		return;

	if (getPageFromSelection() != -1)
		onGo();
}

//returns -1 if invalid selection
function getPageFromSelection()
{
	var selText = document.selection.createRange().text;
	var nPage = parseInt(selText);
	if (!isNaN(nPage))
		return nPage;

	//invalid page
	return -1;	
}

function useRtClientActiveX()
{
	g_bActiveX = true;

	writeActiveXHtml();
}

function useRtClientAjax(bStreaming)
{
	g_ajaxClient = new RtWebClientAjax("g_ajaxClient", bStreaming);
}

function writeActiveXHtml()
{
	var strObjActiveX = "<OBJECT codebase='RtWebClient.cab#Version=1,0,0,96' \n" +
		            "classid='CLSID:8BB1D5E8-2A17-11D6-B830-000347CC94C6'\n" +
		            "width='0' height='0' id='RtWebClient'>" +
			"<param name='font' value='Arial'>" +
			"<PARAM NAME='fontsize' VALUE='11'>" +
			"<PARAM NAME='server' VALUE='realtimedata.derivs.org'>" +
			"<PARAM NAME='port' VALUE='8244'>" +
			"<PARAM NAME='useHttp' VALUE='0'>" +
			"<PARAM NAME='UpdateColour' VALUE='0xB4B4D4'>" +
			"<PARAM NAME='modeUI' VALUE='0'>" +
			"<PARAM NAME='serverlist' VALUE='servers.txt'>" +
			"</OBJECT>";

	document.write(strObjectActiveX);
}

//StyleCache contains an array of defined stylesheet classes. 
//This saves having to iterate through all of the stylesheet 
//objects on each RT update (which was taking up a lot of CPU time).
function StyleCache()
{
	//public methods:
	this.isStyleDefined = isStyleDefined;

	function isStyleDefined(strStyle)
	{
		var bStyle = m_array[strStyle];
		if (bStyle == null)
		{
			var bStyle = findStyle(strStyle);
			m_array[strStyle] = bStyle;
			return bStyle;
		}
		else
			return bStyle;
	}

	//private data
	var m_array = new Array();
}

var g_styleCache = new StyleCache;
