/////////////////////////////////////////////////////////////////////////
//
// insiderchart.js
//
// Copyright (c) 2008 Richard L. Lynch <rich@richlynch.com>
// All Rights Reserved.
//
// Not For Redistribution.
//
// $Id$
//
/////////////////////////////////////////////////////////////////////////

var maxCharts = 5;
var maxDataSets = 10;

// Chart type currently being shown. Updated by setSetType right after updating HTML for set.
var setActiveType = new Array();

// Last symbol that was entered. Updated by setSetType right after updating HTML for set.
var setActiveSymbol = new Array();

// Default colors for each set
var defaultColors = new Array('#599d00', '#00309d', '#9b009d', '#9d0000', '#9d8900', '#000000', '#00b3b1', '#65c0ff', '#86ff65', '#ff8965');

// Assoc array indexed by symbol.  Elements indicate if options are available for a particular symbol.
var optionsAvailability = new Object();

// Assoc array indexed by symbol.  One char entry indicating symbol type.
var symbolType = new Object();

// Assoc array indexed by symbol.  Elements are assoc array mapping insider names to their ID.
var insiderList = new Object();

// Loaded with default HTML to show a chart.
var chartHTML = '';

// Indicates if a particular chart is currently being shown
var chartShown = new Array();

// Indicates if the settings for a particular chart is currently being shown.
var chartEntryShown = new Array();

// Indicates if a particular chart has any sets turned on
var chartContainsSets = new Array();

// Available chart types
// Fields:
// - Name to supply to chartData.php
// - User visible name
// - Requires insider data
// - Requires options data
var chartTypes = new Array(
	'none', 		'None', 				0, 0,
	'stockprice', 	'Stock Price', 			0, 0,
	'stockvolume', 	'Stock Volume', 		0, 0, 
	'insider', 		'Insider Activity', 	1, 0, 
	'optvol', 		'Options Volume', 		0, 1,
	'callput', 		'Call/put Ratio', 		0, 1
);
var chartTypesUserVisible = 1;
var chartTypesInsiderRequired = 2;
var chartTypesOptionsRequired = 3;
var chartTypesElementSize = 4;


///////////////////////////////////////////////////////////////////////////
// Fetch the information about a stock into the following variables:
// - insiderList
// - optionsAvailability
///////////////////////////////////////////////////////////////////////////
function fetchStockInfo(symbol)
{
	var tokenNumber = 0;
	
	///////////////////////////////////////////////////////////////////////////
	// Check for cached data
	///////////////////////////////////////////////////////////////////////////
	if (insiderList[symbol] != undefined)
	{
		return;
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Cache the results
	///////////////////////////////////////////////////////////////////////////
	insiderList[symbol] = new Object();
	optionsAvailability[symbol] = false;
	symbolType[symbol] = '-';
	
	if (symbol == '')
	{
		return;
	}

	var request = makeHttpObject();
	request.open("GET", "insiderchart/insiderList.php?symbol=" + symbol, false);
	request.send(null);
	
	if (request.responseText == null || request.responseText == undefined)
	{
		return;
	}
	
	tokens = request.responseText.split("\t");
	
	if (tokens[tokenNumber] == '1')
	{
		optionsAvailability[symbol] = true;
	}
	
	tokenNumber++;
	
	symbolType[symbol] = tokens[tokenNumber];
	tokenNumber++;

	///////////////////////////////////////////////////////////////////////////
	// Tokens alternate between ID and name
	///////////////////////////////////////////////////////////////////////////
	for (; tokenNumber < tokens.length; tokenNumber += 2)
	{
		if (tokens[tokenNumber] != '' && tokens[tokenNumber + 1] != '')
		{
			insiderList[symbol][tokens[tokenNumber]] = tokens[tokenNumber + 1];
		}
	}
	
	return;
}

///////////////////////////////////////////////////////////////////////////
// Returns if options are supported for a particular symbol
///////////////////////////////////////////////////////////////////////////
function isOptionsEnabledForStock(symbol)
{
	if (symbol == '')
	{
		return true;
	}
	
	fetchStockInfo(symbol);
	return optionsAvailability[symbol];
}

///////////////////////////////////////////////////////////////////////////
// Returns S for stock, E for ETF, M for mutual fund
///////////////////////////////////////////////////////////////////////////
function getSymbolType(symbol)
{
	fetchStockInfo(symbol);
	return symbolType[symbol];
}

function isInsiderSupported(symbol)
{
	if (getSymbolType(symbol) == 'E')
	{
		return false;
	}
	return true;
}

///////////////////////////////////////////////////////////////////////////
// Returns a hash that maps insider IDs to insider names for the given symbol
///////////////////////////////////////////////////////////////////////////
function getInsiderList(symbol)
{
	fetchStockInfo(symbol);
	return insiderList[symbol];
}

///////////////////////////////////////////////////////////////////////////
// Return the arguments required to recreate this chart
///////////////////////////////////////////////////////////////////////////
function buildURLArgs()
{
	///////////////////////////////////////////////////////////////////////////
	// Global chart ids
	///////////////////////////////////////////////////////////////////////////
	var globalArgNames = new Array("width", 
									"startDate", 
									"endDate");
	
	///////////////////////////////////////////////////////////////////////////
	// Global chart ids
	///////////////////////////////////////////////////////////////////////////
	var globalCheckBoxNames = new Array("unlockPerSetSymbols");
	
	///////////////////////////////////////////////////////////////////////////
	// Chart specific ids
	///////////////////////////////////////////////////////////////////////////
	var chartArgNames = new Array(	"height", 
									"backgroundColor",
									"gridColor",
									"axisLabelColor");
	
	///////////////////////////////////////////////////////////////////////////
	// Set specific ids
	///////////////////////////////////////////////////////////////////////////
	var setArgNames = new Array("setType", 
								"symbol",
								"color",
								"smaWindow");

	///////////////////////////////////////////////////////////////////////////
	// IDs for multiselects
	///////////////////////////////////////////////////////////////////////////
	var setArgMultiSelectNames = new Array("insiderIds");
	var ret = '';
	
	for (var i = 0; i < globalArgNames.length; i++)
	{
		var name = globalArgNames[i];
		if (document.getElementById(name) != undefined)
		{
			var value = document.getElementById(name).value;
			ret += "&" + escape(name) + "=" + escape(value);
		}
	}

	for (var i = 0; i < globalCheckBoxNames.length; i++)
	{
		var name = globalCheckBoxNames[i];
		if (document.getElementById(name) != undefined)
		{
			var value = document.getElementById(name).checked;
			ret += "&" + escape(name) + "=" + escape(value);
		}
	}

	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var i = 0; i < chartArgNames.length; i++)
		{
			var name = chartArgNames[i] + chartNumber;
			if (document.getElementById(name) != undefined)
			{
				var value = document.getElementById(name).value;
				ret += "&" + escape(name) + "=" + escape(value);
			}
		}

		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			for (var i = 0; i < setArgNames.length; i++)
			{
				var name = setArgNames[i] + chartNumber + "_" + setNumber;
				if (document.getElementById(name) != undefined)
				{
					var value = document.getElementById(name).value;
					ret += "&" + escape(name) + "=" + escape(value);
				}
			}
			for (var i = 0; i < setArgMultiSelectNames.length; i++)
			{
				var name = setArgMultiSelectNames[i] + chartNumber + "_" + setNumber;
				if (document.getElementById(name) != undefined)
				{
					var multi = document.getElementById(name);
					for (var row = 0; row < multi.length; row++)
					{
						var item = multi.item(row);
						if (item.selected)
						{
							ret += "&" + name + "[]=" + item.value;
						}
					}
				}
			}
		}
	}
	
	if (ret == '')
	{
		return ret;
	}
	
	return "?" + ret.substring(1);
}

function updateLinks(symbol)
{
	html = '<br/>';

	if (symbol != '')
	{
		html += "<a href=\"insidertradingstats?symbol=" + symbol + "\">[Switch to " + symbol + " insider scores]</a>\n";
		html += "<a href=\"" + buildURLArgs() + "\">[Link to this chart]</a>\n";
		html += "<a onclick=\"resetAll();\">[Reset chart]</a>\n";
		html += "<br/>\n";
		html += '<a href="http://finance.yahoo.com/q?s=' + symbol + '">[Yahoo]</a> ';
		html += '<a href="http://finance.google.com/finance?q=' + symbol + '">[Google]</a> ';
		html += '<a href="http://moneycentral.msn.com/detail/stock_quote?Symbol=' + symbol + '">[MSN]</a> ';
		html += '<a href="http://www.marketwatch.com/quotes/' + symbol + '">[MarketWatch]</a> ';
		html += '<a href="http://money.cnn.com/quote/quote.html?symb=' + symbol + '">[CNN]</a> ';
		html += '<a href="http://stocks.us.reuters.com/stocks/overview.asp?symbol=' + symbol + '">[Reuters]</a> ';
		
		html += "<br/>\n";
		html += "<br/>\n";
	}
	
	document.getElementById('links').innerHTML = html;

	document.getElementById('addthisRSSFeed').innerHTML = addThisFeed(getDirURL() + 'insiderchart/rss.php?symbol=' + symbol);
	
	newTitle = '';
	if (symbol != '')
	{
		newTitle += symbol + " ";
	}
	newTitle += "Insider Trading Chart";

	document.title = newTitle;
}

function hideChart()
{
	document.getElementById('chartDiv').innerHTML = 'Please select a stock symbol.';
}

// url must be an Object, not an Array
function reloadChart(url, width, height) 
{
	var allNewHTML = '';
	
	for (chartNumber in url)
	{
		oldHTML = chartHTML;
		newHTML = oldHTML.replace(/chartData\.php.*?\"/g, escape(url[chartNumber]) + '"');
		newHTML = newHTML.replace(/width="100"/g, 'width="' + width[chartNumber] + '"');
		newHTML = newHTML.replace(/width=100/g, 'width=' + width[chartNumber]);
		newHTML = newHTML.replace(/height="100"/g, 'height="' + height[chartNumber] + '"');
		newHTML = newHTML.replace(/height=100/g, 'height=' + height[chartNumber]);
		allNewHTML += newHTML;
	}
	document.getElementById('chartDiv').innerHTML = allNewHTML;
}

function optionText(selectedType, thisType, visibleName)
{
	var sel = '';
	if (selectedType == thisType)
	{
		sel = ' selected';
	}
	return '<option value="' + thisType + '"' + sel + '>' + visibleName + '</option>';
}

function chartTypeHTML(chartNumber, setNumber, type, defaultSymbol)
{
	var ret = '';
	var isInsiderSupportedThis = isInsiderSupported(defaultSymbol);
	var isOptionsEnabledForStockThis = isOptionsEnabledForStock(defaultSymbol);

	ret += '<select name="setType' + chartNumber + "_" + setNumber + '" id="setType' + chartNumber + "_" + setNumber + '" onchange="refresh();">';
	
	for (var i = 0; i < chartTypes.length; i += chartTypesElementSize)
	{
		if ( (isInsiderSupportedThis || !chartTypes[i + chartTypesInsiderRequired]) &&
			(isOptionsEnabledForStockThis || !chartTypes[i + chartTypesOptionsRequired]) )
		{
			ret += optionText(type, chartTypes[i], chartTypes[i + chartTypesUserVisible]);
		}
	}
	
	ret += '</select>';
		
	return ret;
}

function typeSpecificHTML(chartNumber, setNumber, type, defaultSymbol, defaultColor)
{
	var preLabel = '<tr class="rowA"><td>';
	var postLabel = '</td><td>';
	var postEntry = '</td></tr>';
	var ret = '';
	
	ret += preLabel + 'Symbol:' + postLabel + '<input type="text" name="symbol' + chartNumber + "_" + setNumber + '" id="symbol' + chartNumber + "_" + setNumber + '" value="' + defaultSymbol + '" onchange="refresh()" title="Specify a stock symbol to chart"/>' + postEntry;
	ret += preLabel + 'Color:'  + postLabel + '<input type="text" name="color'  + chartNumber + "_" + setNumber + '" id="color' +  chartNumber + "_" + setNumber + '" value="' + defaultColor  + '" onchange="refresh()" title="Specify either the name of a color, e.g. blue, or the RGB code of a color, e.g. #0000ff"/>' + postEntry;

	switch (type)
	{
		case 'stockprice':
		case 'stockvolume':
			ret += preLabel + 'SMA Window:' + postLabel + '<input type="text" name="smaWindow' + chartNumber + "_" + setNumber + '" id="smaWindow' + chartNumber + "_" + setNumber + '" value="" onchange="refresh()" title="Select the SMA window size to smooth the data set"/>' + postEntry;
			break;
		case 'insider':
			var insiderListForThis = getInsiderList(defaultSymbol);
			
			ret += preLabel + "Insider:" + postLabel;
			ret += "<select size=\"5\" multiple=\"multiple\" name=\"insiderIds" + chartNumber + "_" + setNumber + "\" id=\"insiderIds" + chartNumber + "_" + setNumber + "\" onchange=\"refresh()\">";
			for (var i in insiderListForThis)
			{
				ret += "<option value=\"" + i + "\">" + insiderListForThis[i] + "</option>";
			}
			ret += "</select>" + postEntry;
			break;
	}

	return ret;
}

function setSetType(chartNumber, setNumber, type, defaultSymbol, defaultColor)
{
	var desiredSetHTML;
	var previousSymbolEnabled = false;
	
	// Remember old settings
	if (document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
	{
		previousSymbolEnabled = document.getElementById('symbol' + chartNumber + "_" + setNumber).disabled;
	}

	// Input validation
	for (var i = 0; i < chartTypes.length; i += chartTypesElementSize)
	{
		if (chartTypes[i] == type)
		{
			if (chartTypes[i + chartTypesOptionsRequired] && isOptionsEnabledForStock(defaultSymbol) == false)
			{
				type = 'none';
			}
			if (chartTypes[i + chartTypesInsiderRequired] && isInsiderSupported(defaultSymbol) == false)
			{
				type = 'none';
			}
			break;
		}
	}
	
	desiredSetHTML = '<table>';
	desiredSetHTML += '<tr class="rowH"><td>Data Set ' + (setNumber + 1) + ' Type:</td>';
	desiredSetHTML += '<td>' + chartTypeHTML(chartNumber, setNumber, type, defaultSymbol) + '</td></tr>';
	desiredSetHTML += typeSpecificHTML(chartNumber, setNumber, type, defaultSymbol, defaultColor);
	desiredSetHTML += '</table>';

	document.getElementById('set' + chartNumber + "_" + setNumber).innerHTML = desiredSetHTML;

	setActiveSymbol[chartNumber][setNumber] = defaultSymbol;
	setActiveType[chartNumber][setNumber] = type;
	
	if (chartEntryShown[chartNumber] == false)
	{
		setHTML = '<table>';
		setHTML += '<tr class="rowH"><td>Chart ' + (chartNumber + 1) + ' Settings</td><td></td></tr>';
		setHTML += '<tr class="rowA"><td>Height</td><td><input type="text" name="height' + chartNumber + '" id="height' + chartNumber + '" value="100" onchange="refresh()" title="Height of this chart in pixels"/></td></tr>';
		setHTML += '<tr class="rowA"><td>Background Color</td><td><input type="text" name="backgroundColor' + chartNumber + '" id="backgroundColor' + chartNumber + '" value="#ffffff" onchange="refresh()" title="Specify the background color of this chart either in text form, e.g. blue, or in RGB form, e.g. #0000ff"/></td></tr>';
		setHTML += '<tr class="rowA"><td>Grid Color</td><td><input type="text" name="gridColor' + chartNumber + '" id="gridColor' + chartNumber + '" value="#e0e0e0" onchange="refresh()" title="Specify the color of the grid lines for this chart either in text form, e.g. blue, or in RGB form, e.g. #0000ff"/></td></tr>';
		setHTML += '<tr class="rowA"><td>Axis Label Color</td><td><input type="text" name="axisLabelColor' + chartNumber + '" id="axisLabelColor' + chartNumber + '" value="#000040" onchange="refresh()" title="Specify the color of the axis labels for this chart either in text form, e.g. blue, or in RGB form, e.g. #0000ff"/></td></tr>';
		document.getElementById('set' + chartNumber).innerHTML = setHTML;
		document.getElementById('set' + chartNumber).style.height = 'auto';
		document.getElementById('set' + chartNumber).style.clear = 'both';
		chartEntryShown[chartNumber] = true;
	}

	// Restore old settings
	if (document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
	{
		document.getElementById('symbol' + chartNumber + "_" + setNumber).disabled = previousSymbolEnabled;
	}
}

function getSetType(chartNumber, setNumber)
{
	return setActiveType[chartNumber][setNumber];
}

function hideSet(chartNumber, setNumber)
{
	document.getElementById('set' + chartNumber + "_" + setNumber).innerHTML = '';
	setActiveType[chartNumber][setNumber] = undefined;
}

function hideChartSettings(chartNumber)
{
	document.getElementById('set' + chartNumber).innerHTML = '';
	document.getElementById('set' + chartNumber).style.height = 0;
	document.getElementById('set' + chartNumber).style.clear = 'none';
	chartEntryShown[chartNumber] = false;
}

function resetAll()
{
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			setSetType(chartNumber, setNumber, 'none', '', '');
		}
	}
	loadDefaultCharts();
	refresh();
}

function loadDefaultCharts()
{
	setSetType(0, 0, 'stockprice', '', defaultColors[0]);
	setSetType(0, 1, 'insider', '', defaultColors[1]);
	setSetType(1, 0, 'stockvolume', '', defaultColors[0]);
}

function onLoad()
{
	var setArgMultiSelectNames = new Array("insiderIds");

	globalHTML = '<table>';
	globalHTML += '<tr class="rowH"><td>Global Settings</td><td></td></tr>';
	globalHTML += '<tr class="rowA"><td>Width</td><td><input type="text" name="width" id="width" value="100%" onchange="refresh()" title="Specify the width of all of the charts in either pixels or percentages"/></td></tr>';
	globalHTML += '<tr class="rowA"><td>Start Date</td><td><input type="text" name="startDate" id="startDate" value="" onchange="refresh()" title="Specify the earliest date to show either as an absolute date, e.g. 01/01/2008, or as a relative date, e.g. -1 year"/></td></tr>';
	globalHTML += '<tr class="rowA"><td>End Date</td><td><input type="text" name="endDate" id="endDate" value="" onchange="refresh()" title="Specify the latest date to show either as an absolute date, e.g. 01/01/2008, or as a relative date, e.g. -1 year"/></td></tr>';
	globalHTML += '<tr class="rowA"><td>Unlock Per Set Symbols</td><td><input type="checkbox" name="unlockPerSetSymbols" id="unlockPerSetSymbols" onchange="refresh()" title="If checked, each data set can show a different stock symbol. If unchecked, all data sets will use the same stock symbol."/></td></tr>';
	document.getElementById('setGlobal').innerHTML = globalHTML;
	
	chartHTML = document.getElementById('chartDiv').innerHTML;
	
	for (chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		setActiveType[chartNumber] = new Array();
		setActiveSymbol[chartNumber] = new Array();
		chartShown[chartNumber] = false;
		hideChartSettings(chartNumber);
	}
	
	args = splitURL();
	
	///////////////////////////////////////////////////////////////////////////
	// Default settings
	///////////////////////////////////////////////////////////////////////////
	loadDefaultCharts();
	
	if (windowHeight() > (600 + 300))
	{
		document.getElementById('height0').value = windowHeight() - 600;
	}
	else
	{
		document.getElementById('height0').value = 300;
	}

	///////////////////////////////////////////////////////////////////////////
	// Read settings from URL
	///////////////////////////////////////////////////////////////////////////
	
	///////////////////////////////////////////////////////////////////////////
	// First data set types
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (args['setType' + chartNumber + "_" + setNumber] != undefined)
			{
				setSetType(chartNumber, setNumber, args['setType' + chartNumber + "_" + setNumber], '', defaultColors[setNumber]);
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Now the data
	///////////////////////////////////////////////////////////////////////////
	for (var i in args)
	{
		if (document.getElementById(i) != undefined)
		{
			document.getElementById(i).value = args[i];
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Checkboxes have to be handled differently
	///////////////////////////////////////////////////////////////////////////
	document.getElementById("unlockPerSetSymbols").checked = (args["unlockPerSetSymbols"] == "true");
	
	refresh();

	///////////////////////////////////////////////////////////////////////////
	// Now multiselects
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			for (var i = 0; i < setArgMultiSelectNames.length; i++)
			{
				var name = setArgMultiSelectNames[i] + chartNumber + "_" + setNumber;
				var multi = document.getElementById(name);

				sel = getURLMulti(name);
				for (var i = 0; i < sel.length; i++)
				{
					var insiderId = sel[i];
					
					for (var row = 0; row < multi.length; row++)
					{
						var item = multi.item(row);
						
						if (item.value == insiderId)
						{
							item.selected = true;
						}
					}
				}
			}
		}
	}
	
	refresh();
	
	///////////////////////////////////////////////////////////////////////////
	// Set default focus to first stock symbol
	///////////////////////////////////////////////////////////////////////////
	if (document.getElementById('symbol0_0') != undefined)
	{
		document.getElementById('symbol0_0').focus();
	}
}

///////////////////////////////////////////////////////////////////////////
// Refresh the chart
///////////////////////////////////////////////////////////////////////////
function refresh()
{
	var defaultSymbol = '';

	///////////////////////////////////////////////////////////////////////////
	// Show that we're busy
	///////////////////////////////////////////////////////////////////////////
	document.body.style.cursor = "wait";
	
	///////////////////////////////////////////////////////////////////////////
	// Start by tweaking inputs
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
			{
				// Upper case stock symbol
				document.getElementById('symbol' + chartNumber + "_" + setNumber).value = document.getElementById('symbol' + chartNumber + "_" + setNumber).value.toUpperCase();
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Determine default symbol
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (document.getElementById('setType' + chartNumber + "_" + setNumber) != undefined && document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
			{
				if (document.getElementById('setType' + chartNumber + "_" + setNumber).value != 'none' && document.getElementById('symbol' + chartNumber + "_" + setNumber).value != '')
				{
					if (defaultSymbol == '')
					{
						defaultSymbol = document.getElementById('symbol' + chartNumber + "_" + setNumber).value;
					}
				}
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Fill in empty symbols with default symbol
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
			{
				if (document.getElementById('symbol' + chartNumber + "_" + setNumber).value == '')
				{
					document.getElementById('symbol' + chartNumber + "_" + setNumber).value = defaultSymbol;
				}
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Update HTML if any set types have changed
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (document.getElementById('setType' + chartNumber + "_" + setNumber) != undefined)
			{
				var desiredSetType = document.getElementById('setType' + chartNumber + "_" + setNumber).value;
				
				///////////////////////////////////////////////////////////////////////////
				// see if we need to create a chart type for the next data set
				///////////////////////////////////////////////////////////////////////////
				if (desiredSetType != 'none' && getSetType(chartNumber, setNumber + 1) == undefined && (setNumber + 1) < maxDataSets)
				{
					setSetType(chartNumber, setNumber + 1, 'none', '', defaultColors[setNumber + 1]);
				}
				
				///////////////////////////////////////////////////////////////////////////
				// what about the next chart?
				///////////////////////////////////////////////////////////////////////////
				if (desiredSetType != 'none' && getSetType(chartNumber + 1, 0) == undefined && (chartNumber + 1) < maxCharts)
				{
					setSetType(chartNumber + 1, 0, 'none', '', defaultColors[0]);
				}
				
				///////////////////////////////////////////////////////////////////////////
				// set up this data set's HTML
				///////////////////////////////////////////////////////////////////////////
				if (desiredSetType != getSetType(chartNumber, setNumber))
				{
					setSetType(chartNumber, setNumber, desiredSetType, defaultSymbol, defaultColors[setNumber]);
				}
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////
	// Clean up series of nones at the end
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = maxDataSets - 2; setNumber >= 0; setNumber--)
		{
			if (getSetType(chartNumber, setNumber) == 'none' &&
				getSetType(chartNumber, setNumber + 1) == 'none' &&
				getSetType(chartNumber, setNumber + 2) == undefined)
			{
				hideSet(chartNumber, setNumber + 1);
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////
	// Support for single stock symbol mode
	///////////////////////////////////////////////////////////////////////////
	var oneSymbolForAll = !document.getElementById('unlockPerSetSymbols').checked;
	var onFirstSymbol = true;
		
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
			{
				if (onFirstSymbol == true)
				{
					onFirstSymbol = false;

					document.getElementById('symbol' + chartNumber + "_" + setNumber).disabled = false;
				}
				else
				{
					document.getElementById('symbol' + chartNumber + "_" + setNumber).disabled = oneSymbolForAll;
					
					if (oneSymbolForAll == true)
					{
						// Make all symbols equal to the default
						if (document.getElementById('symbol' + chartNumber + "_" + setNumber).value != defaultSymbol)
						{
							document.getElementById('symbol' + chartNumber + "_" + setNumber).value = defaultSymbol;
						}
					}
				}
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// See if we need to update the list of insiders for any sets
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (document.getElementById('setType' + chartNumber + "_" + setNumber) != undefined)
			{
				var desiredSetType = document.getElementById('setType' + chartNumber + "_" + setNumber).value;
				var desiredSymbol = defaultSymbol;
				var desiredColor = defaultColors[setNumber];
				
				if (document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
				{
					desiredSymbol = document.getElementById('symbol' + chartNumber + "_" + setNumber).value;
				}
				
				if (document.getElementById('color' + chartNumber + "_" + setNumber) != undefined)
				{
					defaultColor = document.getElementById('color' + chartNumber + "_" + setNumber).value;
				}
				
				if (setActiveSymbol[chartNumber][setNumber] != desiredSymbol)
				{
					setSetType(chartNumber, setNumber, desiredSetType, desiredSymbol, desiredColor);
				}
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Figure out which charts to show
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
	{
		chartShown[chartNumber] = false;
		chartContainsSets[chartNumber] = false;
		
		for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
		{
			if (document.getElementById('setType' + chartNumber + "_" + setNumber) != undefined && document.getElementById('symbol' + chartNumber + "_" + setNumber) != undefined)
			{
				if (document.getElementById('setType' + chartNumber + "_" + setNumber).value != 'none')
				{
					chartContainsSets[chartNumber] = true;
					if (document.getElementById('symbol' + chartNumber + "_" + setNumber).value != '')
					{
						chartShown[chartNumber] = true;
					}
				}
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Clean up extra charts at end
	///////////////////////////////////////////////////////////////////////////
	for (var chartNumber = maxCharts - 1; chartNumber > 0; chartNumber--)
	{
		if (chartContainsSets[chartNumber] == false && chartContainsSets[chartNumber - 1] == false && chartEntryShown[chartNumber] == true)
		{
			hideChartSettings(chartNumber);
			for (var setNumber = 0; setNumber < maxDataSets; setNumber++)
			{
				hideSet(chartNumber, setNumber);
			}
		}
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Construct URL and tweak inputs
	///////////////////////////////////////////////////////////////////////////
	if (defaultSymbol != '')
	{
		var urls = new Object();
		var chartWidths = new Object();
		var chartHeights = new Object();
		for (var chartNumber = 0; chartNumber < maxCharts; chartNumber++)
		{
			if (chartShown[chartNumber])
			{
				urls[chartNumber] = "insiderchart/chartData.php" + buildURLArgs() + "&chartNumber=" + chartNumber;
				chartWidths[chartNumber] = document.getElementById('width').value;
				chartHeights[chartNumber] = document.getElementById('height' + chartNumber).value;
			}
		}
		reloadChart(urls, chartWidths, chartHeights);
	}
	else
	{
		hideChart();
	}
	
	updateLinks(defaultSymbol);

	///////////////////////////////////////////////////////////////////////////
	// Show that we're ready again
	///////////////////////////////////////////////////////////////////////////
	document.body.style.cursor = "default";
}
