// $Id: chartlibrary5.js 12927 2008-04-30 18:12:57Z dave $
//
// FM Chart Library JS
// (c) Stockcube Research Ltd, 2004-2006.
// All rights reserved.


// this is the only global variable; tracking the most
// recently highlighted chart for keyboard focus purposes.
var keyboardFocus = null;

// debug console
var showConsole = false;
var bConsole = false;

 
// count of updates to chart
var updateCount = 0;

function makeNewUrl(url) {
    var re = new RegExp("&__n=[^&]*");
    if (re.test(url)) {
	url = url.replace(re, "&__n=" + updateCount);
    } else
	url = url + "&__n=" + updateCount;
    updateCount++;
    return url;
}

// http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
function addEvent( obj, type, fn )
{
    if (obj.addEventListener)
	obj.addEventListener( type, fn, false );
    else if (obj.attachEvent) {
	obj["e"+type+fn] = fn;
	obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
	obj.attachEvent( "on"+type, obj[type+fn] );
    }
}

function removeEvent( obj, type, fn )
{
    if (obj.removeEventListener)
	obj.removeEventListener( type, fn, false );
    else if (obj.detachEvent) {
	obj.detachEvent( "on"+type, obj[type+fn] );
	obj[type+fn] = null;
	obj["e"+type+fn] = null;
    }
}


function killMouseEvent(ev) {
    if (ev.stopPropagation) {
	ev.stopPropagation();
	ev.preventDefault();
    } else if (window.event) {
	window.event.returnValue = false;
	window.event.cancelBubble = true;
    }
}

function consoleToggle() {
    if (showConsole) {
	var c = document.getElementById("chartConsole");
	if (c) c.style.display = "none";
	showConsole = false;
    } else {
	showConsole = true;
	chartConsole("Show console");
    }
}

function chartConsole(x) {
    if (!showConsole)
	return;
    var c = document.getElementById("chartConsole");
    if (c) {
	if (!c.ccount) c.ccount = 0;
	c.style.display = 'block';
	var s = c.innerHTML;
	if (s.length > 500) s = s.substring(0, 500);
	c.innerHTML = "" + c.ccount++ + " " + x + "<br>" + s;
    }
}

function filterNaN(label, value, suffix) {
    if (value == "nan") {
        return "";
    } else {
        return label + value + suffix;
    }
}


function chartTracker() {
    
    this.chartX = 0;
    this.chartY = 0;
    this.orig_url = null;
    this.priceArray = 0;
    this.p1 = -1;
    this.p2;
    this.itemwidth = 1;
    this.cx1 = 0; 
    this.cy1 = 0; 
    this.cx2 = 0;
    this.cy2 = 0;
    this.cwidth = 0
    this.cheight = 0;
    this.yMin = 0
    this.yMax = 0;
    this.count = 0;
    this.trackX = -1; 
    this.trackY = -1;
    this.trackDiv = 0;
    this.priceDataOHLC = true;
    this.yLogScale = false;
    this.bIE = 0;
    this.decPlaces = 2;
    if (window.ActiveXObject) this.bIE = 1;

    this.bSafari = false;
    if (navigator && navigator.userAgent && navigator.userAgent.indexOf("Safari") != -1) this.bSafari = true;

    this.cumMonths = [ 0,31,59,90,120,151,181,212,243,273,304,334];
    this.priceBoxTrackingX = -1;
    this.priceBoxTrackingY = -1;
    this.isBusy = false;
    this.isTracking = false;
    this.currentDialog = false;

    addEvent(window, "unload", this.onUnload);
}

chartTracker.prototype.create = function(img) {
    //console.info("Create tracker " + img.src);
    this.fromImg(img);
}

chartTracker.prototype.fromImg = function(img) {
    this.el = img;

    var src = img.src;
    var p = src.indexOf("?");
    var opts = src.substring(p);


    if (this.count == 0) {
	if (location.href.indexOf("&charttype=") >= 0) {
	    saveChartSettings(img.src);
	    // console.info("save settings");
	}
    }
    this.count++;

    src = "http://" + location.host + "/x/charts/chartdata" + opts;
    this.id = img.id;
    chartConsole("src = " + src);
    chartConsole("id = " + this.id);
    this.showMessage(null);
    this.setup();

    if ((src.indexOf("type=pnf") >= 0) ||
        (src.indexOf("type=rpnf") >= 0) || 
	(!document.getElementById(this.id + "Tracking"))) {
	this.hideAll();
	this.p1 = -1; // disable tracking
    } else {
	this.loadXML(src);
    }
}

function nullFn() {}

chartTracker.prototype.loadXML = function(url) {
    // code for Mozilla, etc.
    try {
	var _this = this;
	if (window.XMLHttpRequest) {
	    this.xmlhttp = new XMLHttpRequest();
	    this.xmlhttp.onreadystatechange = function() { _this.xmlhttpStateChange();}
	    this.xmlhttp.open("GET", url, true);
	    this.xmlhttp.send(null);
	} else if (window.ActiveXObject) {
	    // code for IE
	    this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
	    if (this.xmlhttp) {
	        this.xmlhttp.onreadystatechange = function() { _this.xmlhttpStateChange();}
		this.xmlhttp.open("GET",url,true);
		this.xmlhttp.send();
	    }
	}
    } catch (e) {
	//alert("error retrieving chart details\r\n(" + e + ")");
	this.xmlhttp.onreadystatechange = nullFn; // IE doesn't allow "null"
	this.xmlhttp = null;
    }
}

chartTracker.prototype.xmlhttpStateChange = function(url) {
    // if xmlhttp shows "loaded"
    if (this.xmlhttp.readyState==4) { // if "OK"
	if (this.xmlhttp.status==200) {
	    this.setupTracking(this.xmlhttp.responseText);
	} else {
	    alert("Problem retrieving XML data");
	}
	this.xmlhttp.onreadystatechange = nullFn; // IE doesn't allow "null"
	this.xmlhttp = null;
    }
}

chartTracker.prototype.xpos = function(el) {
    var x = 0;
    while (el) {
	x += el.offsetLeft;
	el = el.offsetParent;
    }
    return x;
}

chartTracker.prototype.ypos = function(el) {
    var y = 0;
    while (el) {
	y += el.offsetTop;
	el = el.offsetParent;
    }
    return y;
}

chartTracker.prototype.setup = function(s) {
    var el = this.el
    el.tracker = this;

    this.chartX = this.xpos(el);
    this.chartY = this.ypos(el);
// DNS - Disable keys for the moment
//    addEvent(document, "keypress", onKeyPress);

    if (!keyboardFocus) keyboardFocus = this.el;
}

chartTracker.prototype.setupTracking = function(s) {
    var el = this.el

    if (bConsole) console.info("getTracking(" + s.length + ")");
    this.showMessage(null);

    if (!el.orig_url) {
	el.zoomed = false;
	el.orig_url = el.src;
	el.priceBoxAutoMove = true;
    }
    this.zoomed = (el.orig_url != el.src);
    this.priceArray = s.split("\n");
    var s1 = this.priceArray[0];
    if (s1.indexOf("area") == 0) {
	this.priceArray.shift();
	// area x1 y1 x2 y2   - area of graph containing the chart
	var d = s1.split(" ");
	this.cwidth = parseInt(d[1]);
	this.cheight = parseInt(d[2]);
	this.cx1 = parseInt(d[3]);
	this.cy1 = parseInt(d[4]);
	this.cx2 = parseInt(d[5]);
	this.cy2 = parseInt(d[6]);
	this.itemwidth = parseInt(d[7].substring(2));
    } else {
	if (bConsole) console.error("getTracking: no area data");
	this.priceArray = 0;
	return;
    }
    
    s1 = this.priceArray[0];
    if (s1.indexOf("yrange") == 0) {
	this.priceArray.shift();
	var d = s1.split(" ");
	this.ymin = parseFloat(d[1]);
	this.ymax = parseFloat(d[2]);
	s1 = this.priceArray[0];
    }

    this.priceDataOHLC = true;
    this.yLogScale = false;
    while (s1.indexOf("data ") == 0) {
	if (s1 == "data C") 
	    this.priceDataOHLC = false;
	else if (s1 == "data ylog") 
	    this.yLogScale = true;
	this.priceArray.shift();
	s1 = this.priceArray[0];
    }

    var len = this.priceArray.length;
    while (len > 0 && this.priceArray[len-1] == "")
	len--;
    if (this.priceArray.length > 0) {
	this.p1 = parseInt(this.priceArray[0]);
	this.p2 = parseInt(this.priceArray[len-1]);
	
	// Scan the first few values counting the decimal places,
	// use this as a heueristic to define how much precison 
	// is used on the Y margin.
	var i;
	for(i = 1; i < this.priceArray.length; i++) {
	    var s = this.priceArray[i];
	    var p = s.lastIndexOf(" ");
	    if (p > 0) {
		s = s.substring(p+1);
		p = s.indexOf(".");
		if (p > 0) {
		    var dp = s.length - p-1;
		    if (dp > this.decPlaces) this.decPlaces = dp;
		}
	    }
	    if (i > 3) break;
	}
    } else
	this.p1 = -1;

    this.setupCandleStatus();

    // Setup event handlers in parent div
    if (!this.chartDiv) {
	// Initialisation

	this.chartDiv = this.el.parentNode;
	this.chartDiv.tracker = this;
	
	addEvent(this.chartDiv, "mousemove", this.chartDivMouseMove);
	addEvent(this.chartDiv, "mouseup", this.chartDivMouseUp);
	addEvent(this.chartDiv, "mousedown", this.chartDivMouseDown);
	addEvent(this.chartDiv, "mouseout", this.chartDivMouseOut);
	
	if (this.bIE) el.ondrag = doDrag;
	
	// Create h/vlines
	var hline = document.createElement("img");
	hline.id = "hline";
	hline.className = "chartLine";
	hline.src = "/img/hline.gif";
	this.chartDiv.appendChild(hline);
	this.hline = hline;

	var vline = document.createElement("img");
	vline.id = "vline";
	vline.className = "chartLine";
	vline.src = "/img/vline.gif";
	this.chartDiv.appendChild(vline);
	this.vline = vline;
	
	this.hstatus = document.createElement("div");
	this.hstatus.className = "chartStatus";
	this.chartDiv.appendChild(this.hstatus);
	
	this.vstatus = document.createElement("div");
	this.vstatus.className = "chartStatus";
	this.chartDiv.appendChild(this.vstatus);
    }

    this.isBusy = false;
    this.isTracking = true;
    var c = readCookie("chartTrack");

    if (c && c == "n")
	this.toggleTracking();
    else {
	document.getElementById(this.id + "Tracking").checked = true;
    }
}

chartTracker.prototype.onUnload = function(s) {

    if (this.chartDiv) {
    	if (this.hstatus) {
	    this.chartDiv.removeChild(this.hstatus);
	    this.hstatus = null;
	}
	if (this.vstatus) {
	    this.chartDiv.removeChild(this.vstatus);
	    this.vstatus = null;
	}
	if (this.vline) {
	    this.chartDiv.removeChild(this.vline);
	    this.vline = null;
	}
	if (this.hline) {
	    this.chartDiv.removeChild(this.hline);
	    this.hline = null;
	}

	if (this.trackDiv) {
	    this.chartDiv.removeChild(this.trackDiv);
	    this.trackDiv = null;
	}

	removeEvent(this.chartDiv, "mousemove", this.chartDivMouseMove);
	removeEvent(this.chartDiv, "mouseup", this.chartDivMouseUp);
	removeEvent(this.chartDiv, "mousedown", this.chartDivMouseDown);
	removeEvent(this.chartDiv, "mouseout", this.chartDivMouseOut);
    }
    var el = document.getElementById(this.id + "infodiv");
    if (el) {
	removeEvent(el, "mouseup", this.priceBoxMouseUp);
	removeEvent(el, "mousedown", this.priceBoxMouseDown);
    }

// DNS - Disable keys for the moment    
//    removeEvent(document, "keypress", onKeyPress);

    if (this.xmlhttp) 
	this.xmlhttp.onreadystatechange = nullFn; // IE doesn't allow "null"
}


function createCookie(name,value,days)
{
    if (days) {
	var date = new Date();
	date.setTime(date.getTime()+(days*24*60*60*1000));
	var expires = "; expires="+date.toGMTString();
    }
    else var expires = "";
    document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name)
{
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
	var c = ca[i];
	while (c.charAt(0)==' ') c = c.substring(1,c.length);
	if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

function eraseCookie(name)
{
	createCookie(name,"",-1);
}

function report(ev, n) {
    if (!ev) ev = window.event;
    var target = ev.srcElement;
    if (!target) target = ev.target;
    chartConsole(n + " "+ target.tagName + "." + target.id + " [" + ev.clientX + "]");
}

function onKeyPress(ev) {
    if (!ev) ev = window.event;
    var code = ev.charCode;
    if (!code) code = ev.keyCode;
    chartConsole("keypress " + code);

    // Check the target isnt an element which needs the key...
    var target = ev.target;
    if (!target) target = ev.srcElement;
    if (target) {
	var ignore = false;
	if (target.tagName == "SELECT") {
	   ignore = true;
	}  else if (target.tagName == "INPUT" && target.type == "text") {
	   ignore = true;
	}
	if (ignore)
	    return true;
    }

    if (ev.ctrlKey)
	return true;
    if (code == 39 && keyboardFocus) {
	keyboardFocus.tracker.zoomLeftRight(false);
	killMouseEvent(ev);
	return false;
    } else if (code == 37 && keyboardFocus) {
	keyboardFocus.tracker.zoomLeftRight(true);
	killMouseEvent(ev);
	return false;
    }
    if ((code == 90 || code == 122) && keyboardFocus) {  // Z
	keyboardFocus.tracker.resetZoom();
    } else if ((code == 120 || code == 88) && keyboardFocus) {  // X
	keyboardFocus.tracker.toggleTracking();
    } else if ((code == 67 || code == 99) && keyboardFocus) { // C
	keyboardFocus.tracker.chartingDialog();
    } else if ((code == 82 || code == 114)) { // R
	resetChartSettings();
    } else if ((code == 84 || code == 116) && keyboardFocus) { // T
	keyboardFocus.tracker.toggleTracking();
    } else if ((code == 79 || code == 111) && keyboardFocus) { // "O"
	keyboardFocus.tracker.optionsDialog();
    } else if (code == 65)
	consoleToggle();
    else {
	if (bConsole) console.log("key = ", code);
    }
    return true;
}

chartTracker.prototype.chartDivMouseMove = function(ev) {
    this.tracker.trackMouse(ev);
    killMouseEvent(ev);
}

chartTracker.prototype.chartDivMouseDown = function(ev) {
    this.tracker.trackMouseDown(ev);
    killMouseEvent(ev);
}

chartTracker.prototype.chartDivMouseUp = function(ev) {
    this.tracker.trackMouseUp(ev);
    killMouseEvent(ev);
}

chartTracker.prototype.chartDivMouseOut = function(ev) {
    this.tracker.trackMouseOut(ev);
    killMouseEvent(ev);
}

chartTracker.prototype.chartDivDrag = function(ev) {
    killMouseEvent(ev);
}

chartTracker.prototype.priceBoxMouseDown = function(ev) {
    this.tracker.onPriceBoxDown(ev);
    killMouseEvent(ev);
}

chartTracker.prototype.priceBoxMouseUp = function(ev) {
    this.tracker.onPriceBoxMoveEnd(ev);
    killMouseEvent(ev);
}

chartTracker.prototype.mouseX = function(ev) {
    // Returns X position of mouse relative to chart.
    var x;
    if (this.bIE)
        x = ev.clientX + document.body.scrollLeft -2;
    else
        x = ev.pageX;
    return x - this.chartX-1;
}

chartTracker.prototype.mouseY = function(ev) {
    // Returns Y position of mouse relative to chart.
    var y;
    if (this.bIE)
        y = ev.clientY + document.body.scrollTop -2;
    else
        y = ev.pageY;
    return y - this.chartY -1;
}

function doDrag(ev) {
    if (!ev) ev = window.event;
    report(ev, "drag");
    if (ev) 
	ev.returnValue = false;
    return false; 
}

function doDrag2() { 
    alert("doDrag2");
    return false;
}

chartTracker.prototype.reset = function() 
{
    if (bConsole) console.info("reset tracker");
    this.hideAll();
}

chartTracker.prototype.trackingOff = function() { 
    this.hideAll();
    this.isTracking = false;
    var report = document.getElementById(this.id + "Tracking");
    if (report) report.checked = false;
}

chartTracker.prototype.trackingOn = function() { 
    this.isTracking = true;
    var report = document.getElementById(this.id + "Tracking");
    if (report) report.checked = true;
}

chartTracker.prototype.toggleTracking = function(ev) { 

    if (this.currentDialog)
	return;

    if (this.isTracking) {
	chartConsole("Tracking off");
	this.trackingOff();
	createCookie("chartTrack","n",365);
    } else {
	chartConsole("Tracking on");
	this.trackingOn();
	eraseCookie("chartTrack")
    }
}

chartTracker.prototype.trackMouseDown = function(ev) { 

    if (!this.priceArray)
	return;

    if (this.isBusy || !this.isTracking) return; // busy

    if (!ev) ev = window.event;

    var x = this.mouseX(ev);
    var y = this.mouseY(ev);
    
    if (y > (this.cheight - this.cy1) || y < (this.cheight - this.cy2)) {
	this.resetZoom();
	return;
    }
    
    if (bConsole) console.info("start tracking");
    this.trackX = x;
    this.trackY = y;
    if (!this.trackDiv) {
	this.trackDiv = document.createElement("div");
	this.trackDiv.className = "chartTrackDiv";
	this.chartDiv.appendChild(this.trackDiv);
    }
    this.trackDiv.tracker = this;
// CHANGE
//    this.trackDiv.style.left = this.chartX + x;
    this.trackDiv.style.left = x + "px";
    this.trackDiv.style.top = (this.cheight-this.cy2) + "px";
    this.trackDiv.style.display = "block";
    this.trackDiv.style.width = "1px";
    this.trackDiv.style.height = (this.cy2 - this.cy1) + "px";
}

chartTracker.prototype.resetZoom = function() { 
    chartConsole("resetZoom");
    this.trackX = -1;
    this.el.zoomed = false;
    this.vlineAt(-1);
    this.hlineAt(-1);
    this.showMessage("Working...");
    this.isBusy = true;
    this.el.src = makeNewUrl(this.el.orig_url);
    this.updateZoomStatus();
}
		       
chartTracker.prototype.trackMouseUp = function(ev) { 
    if (!this.priceArray)
	return;

    if (this.priceBoxTrackingX >= 0) {
	this.onPriceBoxMoveEnd(ev);
	return;
    }

    if (this.isBusy || !this.isTracking) return; // busy
    
    if (this.trackX < 0)
	return;

    if (!ev) ev = window.event;
    var x = this.mouseX(ev);

    if ((x < this.cx1 || x > this.cx2) && this.el.zoomed) {
	this.trackEnd();
	this.zoomLeftRight(x < this.cx1);
	return;
    }
    
    if (x < this.trackX)
	this.rescale(x, this.trackX);
    else
	this.rescale(this.trackX, x);
    
    this.trackEnd();
}

chartTracker.prototype.trackEnd = function(ev) { 
    this.trackX = -1;
    this.trackY = -1;
    if (this.trackDiv) {
	this.trackDiv.style.display = "none";
	this.trackDiv = null;
    }
}

chartTracker.prototype.rescale = function(p1, p2) { 
    // alert("p1 = " + p1 + " p2 = "+p2);
    var d1 = this.pos2date(p1);
    var d2 = this.pos2date(p2, true);
    // alert("d1 = " + d1 + " d2 = "+d2);
    if (d1 == 0 || d2 == 0 || d2 - d1 < 5) return;
    if (d2 - d1 < 20) d2 = d1 + 20;
    // alert("rescape " + this.sDate(d1) + " " + this.sDate(d2));
    this.el.orig_url = this.el.src;
    var url = this.el.src + "&";
    url = url.replace(/&_cdate=[^&]*/g, "");
    url = url.replace(/&_period=[^&]*/g, "");
    url = url.replace(/&_sample=[^&]*/g, "");
    
    url += "_cdate=" + escape(this.sDate(d2)) + "&_period=" + (d2-d1) + "d";
    this.el.zoomed = true;
    this.isBusy = true;
    this.hlineAt(-1);
    this.vlineAt(-1);
    this.showMessage("Working..");

    this.el.src = makeNewUrl(url);
    this.updateZoomStatus();
}

chartTracker.prototype.showMessage = function(msg) { 
   var el = document.getElementById("chartStatusBox");
   if (msg) {
       el.style.top = (this.chartY + this.cy2/2) + "px";
// CHANGE
//       el.style.left = (this.chartX + this.cx1 + (this.cx2-200)/2) + "px";
       el.style.left = (this.cx1 + (this.cx2-200)/2) + "px";

       el.innerHTML = msg;
       el.style.display = "block";
    } else
	el.style.display = "none";
}


chartTracker.prototype.zoomLeftRight = function(bLeft) { 
    var z = this.priceArray.length
    
    var d1 = this.priceArray[0].split(' ');
    var date1 = d1[1];
    while (z > 0) {
	var d = this.priceArray[--z]
	if (d != "") {
	    var dd = d.split(' ');
	    var date = dd[1];
	    var url = this.el.src + "&";
	    url = url.replace(/&_cdate=[^&]*/g, "");
	    var days = +date - date1;
	    if (days > 5) days = parseInt((4 * days)/5);
	    if(bLeft)
		date = +date - days;
	    else
		date = +date + days;

	    // ### be nice to detect when we're at the end and
	    //     display a message

	    url += "_cdate=" + escape(this.sDate(date));
	    this.trackEnd();
	    this.el.src = makeNewUrl(url);
	    return;
	}
    }
}


chartTracker.prototype.trackMouse = function(ev) { 
    if (this.p1 < 0)
	return;
    if (this.isBusy || !this.isTracking) return; // busy

    keyboardFocus = this.el;
    
    if (this.priceBoxTrackingX >= 0) {
	this.onPriceBoxMove(ev);
	return;
    }
    
    if (!ev) ev = window.event;
    var x = this.mouseX(ev);
    var y = this.mouseY(ev);
    // this.showStatus("trackMouse " + x + " " + y + " " + (this.movingStatusBox ? "status":""));

    var i;
    var d1 = this.itemwidth/2;
    var d2 = this.itemwidth -d1;
    if (x < this.cx1 || x > this.cx2) {
	this.vlineAt(-1);
	//this.showPriceValues(-1, -1);
	return;
    } 
    if (this.hlineAt(y)) {
	var yval = this.ymin + ((this.cheight- y - this.cy1)*(this.ymax-this.ymin) / (this.cy2-this.cy1));
	if (this.yLogScale)
	    yval = Math.pow(10, yval);
	this.vStatus(y, new Number(yval).toFixed(this.decPlaces));
    } else 
	this.vStatus(-1);
    
    this.vlineAt(x);
    if (this.trackDiv) {
	if (x < this.trackX) {
	    this.trackDiv.style.left = (this.chartX + x) + "px";
	    this.trackDiv.style.width = (this.trackX - x) + "px";
	} else 
	    this.trackDiv.style.width = (x - this.trackX) + "px";
	//trackDiv.style.height = y - trackY;
    }
    
    for(i =0; i < this.priceArray.length; i++) {
	var x1 = parseInt(this.priceArray[i]);
	if (x >= x1-d1 && x <= x1 + d2) {
	    // showStatus("" + x  + " " + y + " " + priceArray[i]);
	    this.showPriceValues(x, y, this.priceArray[i]);
	    return;
	}
    }
}

chartTracker.prototype.trackMouseOut = function(ev) { 

    if (!ev) ev = window.event;
    var x = this.mouseX(ev);
    var y = this.mouseY(ev);

    if ((x > 0 && x < this.cwidth) && (y > 0 && y < this.cheight)) {
	chartConsole("Still on chart");
	return;
    }
    this.hideAll();
}

chartTracker.prototype.hideAll = function(ev) { 
    if (bConsole) console.info("hideAll");
    this.vlineAt(-1);
    this.hlineAt(-1);
    this.vStatus(-1);
    this.vStatus(-1);
    this.showPriceValues(-1, -1);
}

chartTracker.prototype.lastPrice = function() { 
    var sz = this.priceArray.length;
    while (sz > 0) {
	var d = this.priceArray[--sz];
	if (d != "") return d;
    }
    return null;
}

chartTracker.prototype.pos2date = function(x, bRelaxed) { 
    var d1 = this.itemwidth/2;
    var d2 = this.itemwidth -d1;
    var x1 = 0;
    for(i =0; i < this.priceArray.length; i++) {
	x1 = parseInt(this.priceArray[i]);
	if (x <= x1+d2) {
	    var d = this.priceArray[i].split(" ");
	    return parseInt(d[1]);
	}
    }
    if (bRelaxed) {
	var d = this.lastPrice().split(" ");
	if (x > parseInt(d[0])) 
	    return parseInt(d[1]);
    }
    return 0;
}
		       
chartTracker.prototype.getRange = function(x1, x2, bHigh) { 
    var h = bHigh ? -1e30 : 1e30;
    for(i =0; i < this.priceArray.length; i++) {
	x = parseInt(this.priceArray[i]);
	if (x > x1) {
	    if (x > x2) break;
	    var l = this.priceArray[i].split(" ");
	    var v = parseFloat(l[bHigh ? 5:6]);
	    if (bHigh) {
		if (v > h) h = v;
	    } else {
		if (v < h) h = v;
	    }
	}
    }
    var ch = this.cy2 - this.cy1;
    var y = (h - this.ymin) * ch / (this.ymax - this.ymin);
    y = this.cheight - this.cy1 -y;
    chartConsole("cy1 = " + this.cy1 + " cy2 = " + this.cy2);
    chartConsole("ch = " + ch + " y = " + y);
    return parseInt(y);
}

chartTracker.prototype.vlineAt = function(x) { 
    if (this.vline) {
	var vline = this.vline;
	if (x < this.cx1 || x > this.cx2) {
	    vline.style.display = "none";
	} else {
	    vline.style.display = "block";
// CHANGE
//	    vline.style.left = x + this.chartX;
	    vline.style.left = x + "px";
	    vline.style.top = (this.cheight-this.cy2) + "px";
	    vline.style.height = (this.cy2 - this.cy1+5) + "px";
	    vline.style.width = "1px";
	    return true;
	}
    }
    return false;
}


chartTracker.prototype.hlineAt = function(y) { 
    if (this.hline) {
	var hline = this.hline;
	if (y < 0 || y > (this.cheight - this.cy1) || y < (this.cheight -this.cy2)) {
	    hline.style.display = "none";
	} else {
	    hline.style.display = "block";
	    hline.style.top = y + "px";
// CHANGE
//	    hline.style.left = this.cx1 + this.chartX+1;
	    hline.style.left = this.cx1 + "px";
	    hline.style.width = (this.cx2 - this.cx1 + 2) + "px";
	    hline.style.height = "1px";
	    return true;
	}
    }
    return false;
}


chartTracker.prototype.setupCandleStatus = function() {
    var el = document.getElementById(this.id + "infodiv");
    if (!el) {
	el = document.createElement("div");
	el.className = "chartInfoDiv";
	el.id = this.id + "infodiv";
	el.tracker = this;
	this.el.parentNode.appendChild(el);
	el.style.cursor = "pointer";

	addEvent(el, "mouseup", this.priceBoxMouseUp);
	addEvent(el, "mousedown", this.priceBoxMouseDown);
    }
    el.style.display = "none";
    el.tracker = this;
    this.infodiv = el;

    if (this.el.priceBoxAutoMove) {
	var priceBoxLeft = (+this.chartX + this.cx1 + 4);
	var h = this.getRange(0, priceBoxLeft + 120, false);
	var priceBoxTop = this.cy2 - 95 - this.chartY;
	if (this.el.zoomed) priceBoxTop -= 14;
	// chartConsole("pos = " + h + " priceBoxTop = " + priceBoxTop);
	if (h > priceBoxTop) {
	    // Move to top
	    priceBoxTop = +this.cy1 + 15 - this.chartY;
	}
	el.style.left = priceBoxLeft + "px";
	el.style.top =  (this.chartY + priceBoxTop) + "px";
    }
}

chartTracker.prototype.updateZoomStatus = function() {
    var el = document.getElementById("btnZoomReset_" + this.id);
    el.className = this.el.zoomed ? "btnZoomResetOn" : "btnZoomResetOff";
}

function resetZoom(id) {
    var el = document.getElementById(id + "infodiv");
    if (el) {
	tracker = el.tracker;
	if (tracker)
	    tracker.resetZoom();
    }
}
		       
chartTracker.prototype.showPriceValues = function(x, y, s) {

    var el = this.infodiv;
    if (!el) return;
    if (x < 0) {
	el.style.display = "none";
	this.hStatus(-1);
	return;
    }
    
    var l = s.split(" ");
    var day = parseInt(l[1]);
    var date = this.sDate(day);
    var zoom = this.el.zoomed ? "<a href=javascript:resetZoom('"+this.id+"') style=color:red;font-weight:bold><u>Z</u>oom Out</a><br>" : "";
    var pricesText;
    if (this.priceDataOHLC) {
        pricesText = filterNaN("O: ", l[4], "<br>")
         + filterNaN("H: ", l[5], "<br>")
         + filterNaN("L: ", l[6], "<br>")
         + filterNaN("C: ", l[7], "");
    } else {
        pricesText = filterNaN("", l[4], "");
    }

    if (pricesText == "") {
        pricesText = "No data";
    }

    el.innerHTML = zoom + "<b>" + date +  "</b><br>"+ pricesText;

    el.tracker = this;
    if (el.style.display != "block")
	el.style.display = "block";

    this.hStatus(x, date);
}

chartTracker.prototype.onPriceBoxDown = function(ev) {
    if (!ev) ev = window.event;
    var el = this.infodiv;
    if (el) {
	this.priceBoxTrackingX = this.mouseX(ev) - el.offsetLeft;
	this.priceBoxTrackingY = this.mouseY(ev) - el.offsetTop;
	this.movingStatusBox = true;
    }
    return false;
}
		       
chartTracker.prototype.onPriceBoxMove = function(ev) {
    if (!ev) ev = window.event;
    var x = this.mouseX(ev);
    var y = this.mouseY(ev);
    if (this.priceBoxTrackingX > 0) {
	if (x < 0) x = 0;
	if (y < 0) y = 0;
	var el = this.infodiv;
	if (el) {
	    el.style.left = (x-this.priceBoxTrackingX) + "px";
	    el.style.top = (y-this.priceBoxTrackingY) + "px";
	    if (window.event) {
		window.event.returnValue = false;
		window.event.cancelBubble = true;
	    }
	    this.el.priceBoxAutoMove = false;
	}
	return true;
    }
    this.trackMouse(ev);
    return false;
}

chartTracker.prototype.onPriceBoxMoveEnd = function(ev) {
    this.priceBoxTrackingX = this.priceBoxTrackingY = -1;
}
		       
chartTracker.prototype.vStatus = function(y, s) {
    var el = this.vstatus;
    if (el) {
	if (y < 0)
	    el.style.display = "none";
	else {
	    el.innerHTML = s;
// CHANGE
//	    el.style.left = this.cx2 + this.chartX + 3;
	    el.style.left = (this.cx2 + 3) + "px";
	    el.style.top = y + "px";
	    el.style.display = "block";
	}
    }
}
		       
chartTracker.prototype.hStatus = function(x, s) {
    var el = this.hstatus;
    if (el) {
	if (x < 0) {
	    el.style.display = "none";
	} else {
	    el.innerHTML = s;
	    el.style.left = x + "px";
	    el.style.top = (this.cheight - this.cy1 + 4) + "px";
	    el.style.display = "block";
	}
    }
}
   
chartTracker.prototype.sDate = function(d) {
    var d0 = d;
    if (d-- == 0) return "";
    var year = parseInt(d/146097)*400;
    d = d % 146097;
    
    if (d == 146096) {
	year += 300;
	d = 36524;
    } else {
	year = year + parseInt(d/36524) * 100;
	d = d % 36524;
    }
    year = year + parseInt(d/1461) * 4;
    d = d % 1461;
    
    var bLeap = (d > 3 * 365);
    if (bLeap && ((year+4) % 100) == 0)
	bLeap = (((year+4) % 400) == 0);
    
    if (bLeap) {
	year += 3;
	d = ((d-1) % 365) + 1;
    } else {
	year = year + parseInt(d/365);
	d = d % 365;
    }
    
    var month = +parseInt(d / 31);
    if (month < 11) {
	var cd = this.cumMonths[month+1];
	if (bLeap && month >= 1) cd++;
	if (d >= cd)
	    month++;
    }
    // return "year = " + year + " month = " + month + " day = " + d + " leap " + bLeap;
    
    d = d - this.cumMonths[month];
    if (bLeap && month >= 2)
	d--;
    
    return (d+1) + " " + this.monthName(month+1) + " " + (year+1);
}

chartTracker.prototype.monthName = function(m) {
    var monthNames = "JanFebMarAprMayJunJulAugSepOctNovDec";
    return monthNames.substr((m-1)*3, 3);
}

chartTracker.prototype.showStatus = function(x) {
    if (x == "") x = "&nbsp;";
    document.getElementById("statusLine").style.display = 'block';
    document.getElementById("statusLine").innerHTML = x;
}

chartTracker.prototype.getDlgFrame = function(name, classname) {
    // Get Dialog Frame
    var el = document.getElementById(this.id + name);
    if (!el) {
	el = document.createElement("iframe");
	if (typeof(el.frameBorder) != "undefined") el.frameBorder = 0; //##IE
	el.className = "chartDialog " + classname;
	el.id = this.id + name;
	el.tracker = this;
	this.el.parentNode.appendChild(el);
    }

    if (this.bSafari) {
	// Safari "hidden iframes dont exist" bug
	el.style.display = "block";
    }
    return el
}

chartTracker.prototype.optionsDialog = function(x) {
    // display options iframe
    // pull in contents
    var el = this.getDlgFrame("optionsDlg", "chartOptionsDialog");
    el.src = "/x/chartlibrary/optionsDialog.html?n=" + this.id;
}

chartTracker.prototype.chartingDialog = function(x) {
    // display Charting Dialog
    var el = this.getDlgFrame("chartingDlg", "chartChartingDialog");

    var url = this.el.src;
    var p = url.indexOf('?');
    var args = url.substring(p+1);
    if (args.substring(0,1) == "&") args = args.substring(1);
    el.src = "/x/chartlibrary/chartingDialog.html?n=" + this.id + "&" + args;
}

chartTracker.prototype.instrumentDialog = function(x) {
    // display instrument iframe
    var el = this.getDlgFrame("instrumentDlg", "chartInstrumentDialog");

    var url = this.el.src;
    var p = url.indexOf('?');
    var args = url.substring(p+1);
    el.src = "/x/chartlibrary/instrumentDialog.html?n=" + this.id + "&" + args;
}

chartTracker.prototype.sendEmailDialog = function(x) {
    // display "Send Email" dialog
    var el = this.getDlgFrame("emailDlg", "chartEmailDialog");

    var url = this.el.src;
    el.src = "/x/chartlibrary/emailDialog.html?n=" + this.id + "&url=" + escape(url);
}

chartTracker.prototype.onDisplayDialog = function(dlg, bShow) {
    if (bShow) {
	// Hide any existing dialog.
	if (this.currentDialog && this.currentDialog != dlg) {
	    this.onDisplayDialog(this.currentDialog, false);
	}
	
	this.wasTracking = this.isTracking;
	if (this.wasTracking) this.trackingOff();
	this.currentDialog = dlg;
	dlg.style.display = "block";
    } else {
	dlg.style.display = "none";
	this.currentDialog = null;
	if (this.wasTracking) {
	    this.wasTracking = null;
	    this.trackingOn();
	}
    }
}


function showChartDialog(dlgId, bShow) {
    var dlg = document.getElementById(dlgId);
    if (dlg && dlg.tracker) 
	dlg.tracker.onDisplayDialog(dlg, bShow);
}

function resizeChartDialog(dlgId, v) {
    var dlg = document.getElementById(dlgId);
    if (dlg) {
	// console.info("Resize " + dlgId + " by " + delta + " (" + dlg.offsetHeight + ")");
	dlg.style.height = "" + v + "px";
    }
}

function showEvent(ev, t) {
    console.info (t+ ": (body4) target = "+ ev.target.id +  ", phase = "+ ev.eventPhase + ", related = " + (ev.relatedTarget ? ev.relatedTarget.id : "-"));
}

function chartToggleTracker(n)
{
    var img = document.getElementById("chart" + n);
    if (img && img.tracker)
	img.tracker.toggleTracking();
}

function chartResetZoom(n)
{
    var img = document.getElementById("chart" + n);
    if (img && img.tracker)
	img.tracker.resetZoom();
}

function chartOptionsDialog(n)
{
    var img = document.getElementById("chart" + n);
    if (img && img.tracker)
	img.tracker.optionsDialog();
}

function chartChartingDialog(n)
{
    var img = document.getElementById("chart" + n);
    if (img && img.tracker)
	img.tracker.chartingDialog();
}

function chartInstrumentDialog(n)
{
    var img = document.getElementById("chart" + n);
    if (img && img.tracker)
	img.tracker.instrumentDialog();
}

function chartEmailDialog(n)
{
    var img = document.getElementById("chart" + n);
    if (img && img.tracker)
	img.tracker.sendEmailDialog();
}

function saveChartSettings(url) {
    url= url.replace(/^.*\?/, "&");
    url= url.replace(/&bFM=[^&]*/, "");
    url= url.replace(/&[wh]=[^&]*/g, "");
    url= url.replace(/&id=[^&]*/, "");
    url= url.replace(/&.margin=[^&]*/g, "");
    url= url.replace(/&axisfontsize=[^&]*/, "");
    url= url.replace(/&detail=[^&]*/, "");
    url= url.replace(/&_cdate=[^&]*/, "");
    url= url.replace(/&el=[^&]*/, "");
    url= url.replace(/&__n=[^&]*/, "");
    url= url.replace(/&_label=[^&]*/, "");
    url= url.replace(/&_cscale=[^&]*/, "");
    url= url.replace(/&&/g, "&");
    if (bConsole) console.info("Save ["+ url, "]");
    createCookie("chset", url, 365 * 10);
}

function resetChartSettings(url) {
    if (confirm("Reset all chart settings to defaults?")) {
	eraseCookie("chset");
	location.reload();
    }
}

function chartUpdateArgs(name, args) {
    var img = document.getElementById(name);
    if (img) {
	if (img.tracker) img.tracker.reset();

	var u = img.src.replace(/\?.*$/, "") + "?" + args;
	img.orig_url = u;
	if (bConsole) console.log("new url: " + u);
	img.tracker.trackingOff();
	saveChartSettings(u);
	img.src = makeNewUrl(u);
    }
}


// ---------------------------------------------------------------------

function chartTrack(el) {
    var tracker = el.parentNode.tracker;
    if (!tracker) 
	tracker = new chartTracker();
    tracker.fromImg(el);
}

function onLoadChart(el) {
    if (typeof(chartTracker) != 'undefined') {
      chartTrack(this); // get the element event was called on
    }
}
