var tmrDelay;
var glbTabs;



// Path to the blank image must point to a valid location on your server
	  Ext.BLANK_IMAGE_URL = glbSiteRoot + 'images/s.gif';

	  // Main application entry point
	  Ext.onReady(function() {
		OnStartup();
});

function OnBodyLoad()
{
	try {
		document.body.scrollTop = 0;

	} catch (ex) {}
}



function OnStartup()
{
	Ext.QuickTips.init();
	// START OF EXT

   glbTabs = new Ext.TabPanel({
		id: 'DashboardTabs',
		stateful: true,

		renderTo: document.getElementById("Tabs"),
		activeTab: 0,
		autoWidth: true,
		plain:true,
		defaults:{autoScroll: true},
		height: 320,
		
		listeners: {beforetabchange: {fn: BeforeMainTabChange}},
		
		items: [
			{id: "tab_overview", contentEl:'Headline', title: 'Overview'},
			{id: "tab_chart", contentEl:'ChartBlock', title: 'Charts', gridId: "HeadlineChart"},
			{id: "tab_openclosed", title: 'Open/Closed', layout: "fit", gridId: "OpenClosed", tabTip: "Analysis of open vs closed trades"},
			{id: "tab_symbol", title: 'Symbol', layout: "fit", gridId: "Symbol", tabTip: "Analysis by symbol (closed trades only)"},
			{id: "tab_direction", title: 'Direction', layout: "fit", gridId: "Direction", tabTip: "Analysis by direction (closed trades only)"},
			{id: "tab_symboldirection", title: 'Sym+dir', layout: "fit", gridId: "SymbolDirection", tabTip: "Analysis by direction and symbol (closed trades only)"},
			{id: "tab_month", title: 'Month', layout: "fit", gridId: "Month", tabTip: "Analysis by month of closure (closed trades only)"},
			{id: "tab_week", title: 'Week', layout: "fit", gridId: "Week", tabTip: "Analysis by week of closure (closed trades only)"},
			{id: "tab_day", title: 'Day', layout: "fit", gridId: "Day", tabTip: "Analysis by day of closure (closed trades only)"},
			{id: "tab_openhour", title: 'Hour', layout: "fit", gridId: "Hour", tabTip: "Analysis by hour of opening (closed trades only)"},
			{id: "tab_dow", title: 'DoW', layout: "fit", gridId: "DoW", tabTip: "Analysis by day of week (closed trades only)"},
			{id: "tab_ea", title: 'EA', layout: "fit", gridId: "EA", tabTip: "Analysis by EA (closed trades only)"}
		]

	});

	if (glbIsMultiUser) {
		glbTabs.add({id: "tab_usernames", title: 'Username', layout: "fit", gridId: "Username", tabTip: "Analysis by sub-account in portfolio (closed trades only)"});
	}

	OnBodyResize();


	var strHash = document.location.hash;
	if (strHash != "") {
		if (strHash.substr(0,1) == "#") strHash = strHash.substr(1);
	}
	
	if (strHash.indexOf("chart_") == 0) {
		strHash = strHash.substr(6);
		var combo = document.getElementById("ChartType");
		for (var iFindChart = 0; iFindChart < combo.options.length; iFindChart++) {
			if (combo.options[iFindChart].value == strHash) {
				combo.selectedIndex = iFindChart;
				iFindChart = 9999;
			}
		}
		glbTabs.setActiveTab(1);
	} else {
		switch (strHash) {
			case "chart":
			case "tabChart":
				glbTabs.setActiveTab(1);
				break;
			case "openclosed":
			case "tabOpenClosed":
				glbTabs.setActiveTab(2);
				break;
			case "symbol":
			case "tabSymbol":
				glbTabs.setActiveTab(3);
				break;
			case "direction":
			case "tabDirection":
				glbTabs.setActiveTab(4);
				break;
			case "symboldirection":
			case "tabSymbolDirection":
				glbTabs.setActiveTab(5);
				break;
			case "month":
			case "tabMonth":
				glbTabs.setActiveTab(6);
				break;
			case "week":
			case "tabWeek":
				glbTabs.setActiveTab(7);
				break;
			case "day":
			case "tabDay":
				glbTabs.setActiveTab(8);
				break;
			case "openhour":
			case "tabOpenHour":
				glbTabs.setActiveTab(9);
				break;
			case "dow":
			case "tabDoW":
				glbTabs.setActiveTab(10);
				break;
			case "ea":
			case "tabEA":
				glbTabs.setActiveTab(11);
				break;
			case "username":
			case "tabUsername":
				glbTabs.setActiveTab(12);
				break;
		}
	}
}


function OnBodyResize()
{
	document.body.scrollTop = 0;

	var tabsY = glbTabs.getPosition()[1];
	glbTabs.setHeight(document.body.offsetHeight - tabsY - 20);
	
	ResizeChart();
}

function ResizeChart()
{
	var tabsY = glbTabs.getPosition()[1];
	document.getElementById("Chart1").style.width = "850px";
	document.getElementById("Chart1").style.height = (document.body.offsetHeight - tabsY - 100) + "px";
	OnChartChange2(true);	
}

function BeforeMainTabChange(Tab, NewPanel)
{
	if (NewPanel.gridId) {
		if (!NewPanel.doneGrid) {
		
			switch (NewPanel.gridId) {
				case "HeadlineChart":
					ResizeChart();
					break;
			
				case "OpenClosed":
					DoOpenClosedGrid(NewPanel);
					break;
		
				case "Symbol":
					DoGenericStatsGrid(arrSymbols, "Symbol", "Symbol", 320, NewPanel);
					break;
				
				case "Direction":
					DoGenericStatsGrid(arrDirections, "Direction", "Direction", 320, NewPanel);
					break;
				
				case "SymbolDirection":
					DoGenericStatsGrid(arrSymbolDirections, "Symbol/Direction", "Symbol/Direction", 320, NewPanel);
					break;
				
				case "Month":
					DoGenericStatsGrid(arrMonths, "Month", "Month", 320, NewPanel);
					break;
				
				case "Week":
					DoGenericStatsGrid(arrWeeks, "Week", "Week", 320, NewPanel);
					break;
				
				case "Day":
					DoGenericStatsGrid(arrDays, "Day", "Day", 320, NewPanel);
					break;
				
				case "Hour":
					DoGenericStatsGrid(arrOpenHours, "Hour", "Open-hour", 320, NewPanel);
					break;

				case "DoW":
					DoGenericStatsGrid(arrDow, "DoW", "Day of week", 320, NewPanel);
					break;

				case "Username":
					DoGenericStatsGrid(arrUsernames, "Username", "Username", 320, NewPanel);
					break;

				case "EA":
					if (GotEAs()) {
						DoGenericStatsGrid(arrMagicNumbers, "Magic number", "EA", 320, NewPanel);
					} else {
						DoEAHelpPanel(NewPanel);
					}
					break;
			
			}
		
			NewPanel.doneGrid = true;
		}
	}
}


function OnChartChange(elem)
{
	clearInterval(tmrDelay);
	tmrDelay = setInterval("OnChartChange2()", 200);
}

function OnChartChange2(bForce)
{
	clearInterval(tmrDelay);
	var strNewChart = GetFieldValue(document.getElementById("ChartType"));
	if ((strNewChart != glbLastChart || bForce) && strNewChart != "") {
		glbLastChart = strNewChart;
		
		var strUrl = glbSiteRoot + "charts/ResultChart.aspx?id=" + glbUserId + "&type=" + strNewChart;
		strUrl += "&w=" + parseInt(document.getElementById("Chart1").style.width);
		strUrl += "&h=" + parseInt(document.getElementById("Chart1").style.height);
		if (glbFilterRequest != "") {strUrl += "&" + glbFilterRequest;}

		document.getElementById("Chart1").src = RefreshableUrl(strUrl);
	}
}



function DoOpenClosedGrid(NewPanel)
{
	var arrFieldList = [
	   {name: 'type'},
	   {name: 'bankedProfit', type: 'float'},
	   {name: 'bankedLoss', type: 'float'},
	   {name: 'netProfit', type: 'float'},
	   {name: 'trades', type: 'int'},
	   {name: 'winners', type: 'int'},
	   {name: 'losers', type: 'int'},
	   {name: 'winpc', type: 'float'},
	   {name: 'losepc', type: 'float'},
	   {name: 'averagewin', type: 'float'},
	   {name: 'averageloss', type: 'float'},
	   {name: 'averagetrade', type: 'float'},
	   {name: 'largestwin', type: 'float'},
	   {name: 'largestloss', type: 'float'},
	   {name: 'lotstraded', type: 'float'}
	];


    // create the Grid
    var grid = new Ext.grid.GridPanel({
		store: new Ext.data.GroupingStore({
			reader: new Ext.data.ArrayReader({}, arrFieldList),
			data: arrOpenClosed,
			sortInfo:{field: arrFieldList[0].name, direction: "ASC"}
		}),

        columns: [
            {id:'type', locked: true, header: 'Open/Closed', align: 'left', width: 160, sortable: true, dataIndex: 'type', summaryType: 'count', summaryRenderer: function(v, params, data){return ((v === 0 || v > 1) ? '(' + v +' categories)' : '(1 category)');}},
            {header: 'Gross profit', tooltip:'Gross profit on winning trades', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'bankedProfit', summaryType: 'sum'},
            {header: 'Gross loss', tooltip:'Gross loss on losing trades', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'bankedLoss', summaryType: 'sum'},
            {header: 'Net profit', tooltip:'Net profit - profits minus losses', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'netProfit', summaryType: 'sum'},
            {header: 'Trades', tooltip:'Number of trades', align: 'right', width: 75, sortable: true, dataIndex: 'trades', summaryType: 'sum'},
            {header: 'Winners', tooltip:'Number of winning trades', align: 'right', width: 75, sortable: true, dataIndex: 'winners', summaryType: 'sum'},
            {header: 'Losers', tooltip:'Number of losing trades', align: 'right', width: 75, sortable: true, dataIndex: 'losers', summaryType: 'sum'},
            {header: 'Winner%', tooltip:'Winning trades as % of total', renderer: DP1, align: 'right', width: 75, sortable: true, dataIndex: 'winpc', summaryType: 'special'},
            {header: 'Loser%', tooltip:'Losing trades as % of total', renderer: DP1, align: 'right', width: 75, sortable: true, dataIndex: 'losepc', summaryType: 'special'},
            {header: 'Avg win', tooltip:'Average win (gross profit divided by winners)', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'averagewin', summaryType: 'special'},
            {header: 'Avg loss', tooltip:'Average loss (gross loss divided by losers)', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'averageloss', summaryType: 'special'},
            {header: 'Avg trade', tooltip:'Average trade (net profit divided by total positions)', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'averagetrade', summaryType: 'special'},
            {header: 'Best trade', tooltip:'Largest winning trade', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'largestwin', summaryType: 'max'},
            {header: 'Worst trade', tooltip:'Largest losing trade', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'largestloss', summaryType: 'min'},
            {header: 'Lots traded', tooltip:'Total lots closed/open', align: 'right', width: 100, sortable: true, renderer: DP2, dataIndex: 'lotstraded', summaryType: 'sum'}
        ],

		view: new Ext.grid.GroupingView({
			groupTextTpl: '{text}  ({[values.rs.length]} {[values.rs.length > 1 ? "categories" : "category"]})'
		}),

        plugins: [new Ext.ux.grid.GroupSummary(), new Ext.ux.grid.GridSummary()],

		hasFilter: function() {if (this.currentFilter) if (this.currentFilter.length) return true; else return false; else return false; },

		listeners: {
			cellcontextmenu: {fn: OnCellContextMenu}
		},

		tbar: {
			items: [
			   	{id: "OpenClosebtnExcel", text: 'Download', tooltip: 'Export this data in CSV format', icon: glbSiteRoot + "images/siExcel.gif", handler: DownloadGrid},
				{icon: glbSiteRoot + "images/siPrint.gif", tooltip: 'Print this grid, via a new window', text: 'Print',iconCls: 'print',handler: function() {Ext.ux.GridPrinter.print(grid);}},
				{id: "OpenClosebtnChart", text: 'Bespoke Chart', tooltip: 'View a chart of selected columns in this data', icon: glbSiteRoot + "images/siStats.gif", handler: ChartOfGrid},
			   	{id: "clearFilterButton", text: 'Clear grid filter', tooltip: 'Right-click over cell values to filter the grid on those values. Use this button to clear the filter and reset the grid.', disabled: true, icon: glbSiteRoot + "images/siClearFilter.gif", handler: OnGridClearFilterButton}
			]
		},

        stripeRows: true,
        height: 150,
        width: 870,
        title: "Open/Closed",
        header: false
    });


    // render the grid to the specified div in the page
    NewPanel.add(grid);
}



function DoGenericStatsGrid(ArrayName, RowName, GridTitle, GridHeight, RenderToElem)
{
	// Work out whether we're dealing with the day/week/month grids which contain % change etc
	var bIsDayWeekMonth = false;
	switch (GridTitle) {
		case "Day":
		case "Week":
		case "Month":
			bIsDayWeekMonth = true;
			break;
	}
	

    var arrFieldList = [
	   {name: 'type'},
	   {name: 'trades', type: 'int'},
	   {name: 'bankedProfit', type: 'float'},
	   {name: 'bankedLoss', type: 'float'},
	   {name: 'netProfit', type: 'float'},
	   {name: 'profitFactor', type: 'float'},
	   {name: 'winners', type: 'int'},
	   {name: 'winpc', type: 'float'},
	   {name: 'losers', type: 'int'},
	   {name: 'losepc', type: 'float'},
	   {name: 'averagewin', type: 'float'},
	   {name: 'averageloss', type: 'float'},
	   {name: 'averagetrade', type: 'float'},
	   {name: 'largestwin', type: 'float'},
	   {name: 'largestloss', type: 'float'},
	   {name: 'lotsTraded', type: 'float'},
	   {name: 'longestDuration', type: 'float'},
	   {name: 'shortestDuration', type: 'float'},
	   {name: 'averageDuration', type: 'float'},
	   {name: 'winningBankedPips', type: 'float'},
	   {name: 'losingBankedPips', type: 'float'},
	   {name: 'totalBankedPips', type: 'float'},
	   {name: 'averageBankedPips', type: 'float'},
	   {name: 'averagePipWin', type: 'float'},
	   {name: 'averagePipLoss', type: 'float'},
	   {name: 'averageLotsTraded', type: 'float'},
	   {name: 'averageWinnerDuration', type: 'float'},
	   {name: 'averageLoserDuration', type: 'float'},
	   {name: 'percentageChange', type: 'float'}
	];
        
    // create the Grid
    var grid = new Ext.grid.GridPanel({
		store: new Ext.data.GroupingStore({
			reader: new Ext.data.ArrayReader({}, arrFieldList),
			data: ArrayName,
			storeId: GridTitle, 
			sortInfo:{field: arrFieldList[0].name, direction: "ASC"}
		}),

		deferRowRender: true,

        columns: [
            {id:'type', renderer: RenderIdColumn, locked: true, header: RowName, align: 'left', width: 100, sortable: true, dataIndex: 'type', summaryType: 'count', summaryRenderer: function(v, params, data){return ((v === 0 || v > 1) ? '(' + v +' categories)' : '(1 category)');}},
            {header: 'Trades', tooltip: 'Number of closed trades', align: 'right', width: 75, sortable: true, dataIndex: 'trades', summaryType: 'sum'},
            {header: 'Gross profit', tooltip: 'Total profit on winning trades', align: 'right', width: 100, renderer: DPCash, sortable: true, dataIndex: 'bankedProfit', summaryType: 'sum'},
            {header: 'Gross loss', tooltip: 'Total loss on losing trades', align: 'right', width: 100, renderer: DPCash, sortable: true, dataIndex: 'bankedLoss', summaryType: 'sum'},
            {header: 'Net profit', tooltip: 'Total net profit', align: 'right', width: 100, renderer: DPCash, sortable: true, dataIndex: 'netProfit', summaryType: 'sum'},
            {header: '% change', tooltip: 'Compound % change in balance during period', align: 'right', width: 75, renderer: DP1, sortable: true, dataIndex: 'percentageChange', summaryType: 'average', hidden: !bIsDayWeekMonth},
            {header: 'Profit factor', tooltip: 'MT4-style profit factor', renderer: DP2, align: 'right', width: 85, sortable: true, dataIndex: 'profitFactor', summaryType: 'special'},
            {header: 'Winners', tooltip: 'Number of winning trades', align: 'right', width: 75, sortable: true, dataIndex: 'winners', summaryType: 'sum'},
            {header: 'Winner%', tooltip: 'Winning trades as % of total', align: 'right', width: 75, renderer: DP1, sortable: true, dataIndex: 'winpc', summaryType: 'special'},
            {header: 'Losers', tooltip: 'Number of losing trades', align: 'right', width: 75, sortable: true, dataIndex: 'losers', summaryType: 'sum'},
            {header: 'Loser%', tooltip: 'Losing trades as % of total', align: 'right', width: 75, sortable: true, renderer: DP1, dataIndex: 'losepc', summaryType: 'special'},
            {header: 'Avg win', tooltip: 'Average win (gross profit divided by winners)', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'averagewin', summaryType: 'special'},
            {header: 'Avg loss', tooltip: 'Average loss (gross loss divided by losers)', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'averageloss', summaryType: 'special'},
            {header: 'Avg trade', tooltip: 'Average trade result (net profit divided by positions)', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'averagetrade', summaryType: 'special'},
            {header: 'Best trade', tooltip: 'Best winning trade', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'largestwin', summaryType: 'max'},
			{header: 'Worst trade', tooltip: 'Worst losing trade', align: 'right', width: 100, sortable: true, renderer: DPCash, dataIndex: 'largestloss', summaryType: 'min'},
			{header: 'Longest (hours)', tooltip: 'Longest trade length in hours', renderer: DP2Hours, align: 'right', width: 100, sortable: true, dataIndex: 'longestDuration', summaryType: 'max'},
			{header: 'Shortest (hours)', tooltip: 'Shortest trade length in hours', renderer: DP2Hours, align: 'right', width: 100, sortable: true, dataIndex: 'shortestDuration', summaryType: 'min'},
			{header: 'Average length (hours)', tooltip: 'Average trade length in hours', renderer: DP2Hours, align: 'right', width: 100, sortable: true, dataIndex: 'averageDuration'},
			{header: 'Total win pips', tooltip: 'Total pips banked on winning trades', renderer: DP1, align: 'right', width: 100, sortable: true, dataIndex: 'winningBankedPips', summaryType: 'sum'},
			{header: 'Total loss pips', tooltip: 'Total pips banked on losing trades', renderer: DP1, align: 'right', width: 100, sortable: true, dataIndex: 'losingBankedPips', summaryType: 'sum'},
			{header: 'Net pips', tooltip: 'Total net pips banked', renderer: DP1, align: 'right', width: 100, sortable: true, dataIndex: 'totalBankedPips', summaryType: 'sum'},
			{header: 'Avg win pips', tooltip: 'Average pips on each winning trade', renderer: DP1, align: 'right', width: 100, sortable: true, dataIndex: 'averagePipWin', summaryType: 'special'},
			{header: 'Avg loss pips', tooltip: 'Average pips on each losing trade', renderer: DP1, align: 'right', width: 100, sortable: true, dataIndex: 'averagePipLoss', summaryType: 'special'},
			{header: 'Avg pips per trade', tooltip: 'Average net pips banked per trade', renderer: DP1, align: 'right', width: 100, sortable: true, dataIndex: 'averageBankedPips', summaryType: 'special'},
			{header: 'Lots traded', tooltip: 'Total number of lots closed', renderer: DP2, align: 'right', width: 100, sortable: true, dataIndex: 'lotsTraded', summaryType: 'sum'},
			{header: 'Avg lots', tooltip: 'Avg number of lots closed per trade', renderer: DP2, align: 'right', width: 100, sortable: true, dataIndex: 'averageLotsTraded', summaryType: 'special'},
			{header: 'Average winner length (hours)', tooltip: 'Average length of winning trades in hours', renderer: DP2Hours, align: 'right', width: 100, sortable: true, dataIndex: 'averageWinnerDuration'},
			{header: 'Average loser length (hours)', tooltip: 'Average length of losing trades in hours', renderer: DP2Hours, align: 'right', width: 100, sortable: true, dataIndex: 'averageLoserDuration'}
        ],
        stripeRows: true,
        height: GridHeight,
        layout: 'fit',
        header: false,

		view: new Ext.grid.GroupingView({
			groupTextTpl: '{text}  ({[values.rs.length]} {[values.rs.length > 1 ? "categories" : "category"]})'
		}),

        plugins: [new Ext.ux.grid.GroupSummary(), new Ext.ux.grid.GridSummary()],

		hasFilter: function() {if (this.currentFilter) if (this.currentFilter.length) return true; else return false; else return false; },

		listeners: {
			cellcontextmenu: {fn: OnCellContextMenu}
		},


		tbar: {
			items: [
			   	{xtype: 'tbspacer', width: 5},
			   	new Ext.form.Label({text: "  (Closed trades only)", style: "font-weight: bold", tooltip: "This grid only looks at closed trades"}),
			   	{xtype: 'tbspacer', width: 20},
			   	{id: ArrayName + "btnExcel", text: 'Download', tooltip: 'Export this data in CSV format', icon: glbSiteRoot + "images/siExcel.gif", handler: DownloadGrid},
				{icon: glbSiteRoot + "images/siPrint.gif", tooltip: 'Print this grid, via a new window', text: 'Print',iconCls: 'print',handler: function() {Ext.ux.GridPrinter.print(grid);}},
				{id: ArrayName + "btnItemChart", text: 'Filtered chart', tooltip: 'View a chart of trades matching the selected items in this grid', icon: glbSiteRoot + "images/siGraph.gif", handler: ItemChartOptions},
				{id: ArrayName + "btnChart", text: 'Bespoke chart', tooltip: 'View a chart of selected columns in this data', icon: glbSiteRoot + "images/siStats.gif", handler: ChartOfGrid},
			   	{id: "clearFilterButton", text: 'Clear grid filter', tooltip: 'Right-click over cell values to filter the grid on those values. Use this button to clear the filter and reset the grid.', disabled: true, icon: glbSiteRoot + "images/siClearFilter.gif", handler: OnGridClearFilterButton}
			]
		},

        title: GridTitle
    });
    
    RenderToElem.add(grid);
}

function DP1(val)
{
    return Ext.util.Format.number(val, "0,000.0");
}

function DP2(val)
{
	if (val == 0) {
		return "-";
	} else {
        return Ext.util.Format.number(val, "0,000.00");
    }
}

function DPCash(val)
{
	return Ext.util.Format.number(val, "0,000.00");
}

function DP2Hours(val)
{
	if (val == 0) {
		return "-";
	} else {
        return Ext.util.Format.number(val, "0,000.00") + "h";
    }
}

function RenderIdColumn(id, metadata, record, rowIndex, colIndex, store)
{
	switch (store.storeId) {
		case "Username":	

			return "<a href='" + glbSiteRoot + "/users/" + id + "/stats'>" + id + "</a>";		
		
		default:

			return id;
	}
}


function DownloadGrid(Item)
{
	var strData = new Array();
	
	var strX = "";
	var grid = Item.findParentByType("grid");
	var cols = grid.getColumnModel();
	
	for (var i = 0; i < cols.getColumnCount(); i++) {
		strData.push(cols.getColumnHeader(i) + "|");
	}

	var store = grid.store;
	
	for (var iRec = 0; iRec < store.getCount(); iRec++) {
		strData.push("^");
	
		var Rec = store.getAt(iRec);
		var strRow = "";
		
		for (var iCol = 0; iCol < cols.getColumnCount(); iCol++) {
			strRow += Rec.get(cols.getDataIndex(iCol)) + "|";
		}
		strData.push(strRow);
	}

	document.getElementById("CSVPostbackForm").data.value = strData.join("");
	document.getElementById("CSVPostbackForm").submit();
}


var ChartOptionsWindow;
var ChartGrid;

function ChartOfGrid(Item)
{
	// Build the list of columns
	var grid = Item.findParentByType("grid");
	ChartGrid = grid;
	var cols = grid.getColumnModel();


	var strOptions = new Array();	
	
	strOptions.push("<div style='padding: 20px'>");
		strOptions.push("<p><b>Draw a bespoke chart of any data in the grid</b></p>");
		strOptions.push("<p>Choose the fields to include on the chart:</p>");

		// Build the list of fields in the grid. The function below requires the
		// order of this to match the order in the grid, and to include the
		// first field containing the row key - though this is hidden by the following code
		strOptions.push("<div style='padding-left: 10px; margin-bottom: 1em' id='ChartFieldList'>");
			strOptions.push("<div style='height: 250px; overflow-y: auto; border: solid 1px #F0F0F0; padding: 5px'>");
				strOptions.push("<table>");
				for (var i = 0; i < cols.getColumnCount(); i++) {
					strOptions.push("<tr" + (i == 0 ? " style='display: none'": "") + ">");
					strOptions.push("<td><input columnindex='" + i + "' type='checkbox' id='cfield" + i +"' value='1' title='" + cols.getColumnTooltip(i) + "'/></td>");
					strOptions.push("<td><label for='cfield" + i + "' title='" + cols.getColumnTooltip(i) + "'>" + cols.getColumnHeader(i) + "</label></td>");
					strOptions.push("<td style='padding-left: 30px'><input onclick='if (this.checked) document.getElementById(\42cfield" + i + "\42).checked = true' type='checkbox' id='cumulative" + i +"' value='1' title='Show this field as a cumulative running total'/> <label for='cumulative" + i +"' style='color: #808080; font-size: 8pt'>Running total</label></td>");
					strOptions.push("</tr>");
				}
				strOptions.push("</table>");
			strOptions.push("</div>");
		strOptions.push("</div>");

		strOptions.push("<p>Choose the style for the chart: ");
			strOptions.push("<select id='BespokeChartStyle' name='BespokeChartStyle' style='font-size: 8pt'>")
				strOptions.push("<option value='0'>Bar</option>")
				strOptions.push("<option value='1'>Line</option>")
				strOptions.push("<option value='2'>Pie</option>")
			strOptions.push("</select>")
		strOptions.push("</p>");

		strOptions.push("<p><input type='button' name='btnDrawBespokeChart' value='Draw chart' onclick='DrawBespokeChart()'/></p>");

		strOptions.push("<div style='display: none'>");
		strOptions.push("<form id='ChartPostbackForm' method='post' action='index.aspx'>");
		strOptions.push("</form>");

	strOptions.push("</div>");
	
	document.getElementById("ChartOptionsColumnList").innerHTML = strOptions.join("");



	// Need to build the list of options
	if (!ChartOptionsWindow) {
		ChartOptionsWindow = new Ext.Window({
			contentEl: "ChartOptionsForm",
			closeAction: "hide",
			width: 800,
			height: 540,
			title: "Bespoke chart - " + grid.title,
			autoScroll: true,
			modal: true,
			bodyStyle: "background: white",
			resizable: false
		});
	}

	ChartOptionsWindow.setTitle("Bespoke chart - " + grid.title);
	ChartOptionsWindow.setPagePosition(((document.body.offsetWidth - 800) / 2) + document.body.scrollLeft, ((document.body.offsetHeight - 540) / 2) + document.body.scrollTop);
	

	ChartOptionsWindow.show();
}


function DrawBespokeChart()
{
	// Get references to the grid and its columns
	var grid = ChartGrid;
	var cols = grid.getColumnModel();

	// Get a list of the checkboxes for each field
	var arrFields = document.getElementById("ChartFieldList").getElementsByTagName("INPUT");

	// Run through the field, building a list of names and also adding the data-indices to the arrFieldIds array

	// N.B. This expects the fields to be in the same order as the grid, including a HIDDEN first field
	var ctSel = 0;

	var arrFieldIds = new Array();

	var strFieldList = "";
	for (var i = 1; i < arrFields.length; i++) {
		if (arrFields[i].id.indexOf("cfield") == 0) {
			if (arrFields[i].checked) {
				ctSel++;
				if (strFieldList != "") strFieldList += "|";
				var idxColumn = arrFields[i].getAttribute("columnindex");
				
				var bIsCumulative = document.getElementById("cumulative" + idxColumn).checked;
				
				
				strFieldList += cols.getColumnHeader(idxColumn);
				arrFieldIds.push({dataIndex: cols.getDataIndex(idxColumn), cumulative: bIsCumulative});
			}
		}
	}

	// Check that something has been selected 
	if (!ctSel) {
		alert("Please select one or more fields to include on the chart");
		return;
	}

	// Start building the post data
	var strPostData = new Array();
	strPostData.push(grid.title + "|" + GetFieldValue(document.getElementById("BespokeChartStyle")) + "^");
	strPostData.push(strFieldList);

	// Get the data
	var store = grid.store;

	// Calculate running totals
	var arrTotals = new Array(arrFieldIds.length);
	for (var iC = 0; iC < arrFieldIds.length; iC++) {
		arrTotals[iC] = 0;
	}

	// Run through the store getting the field values for the selected records	
	for (var iRec = 0; iRec < store.getCount(); iRec++) {
		// Add a separator to the data 
		strPostData.push("^");
	
		// Get the nth record and start building a description of its data 
		var Rec = store.getAt(iRec);
		var strRow = "";
		
		// Get the key for the item
		strRow += Rec.get(cols.getDataIndex(0));
		
		// Get the value of the selected field(s) for the item
		for (var iC = 0; iC < arrFieldIds.length; iC++) {
			var v = Rec.get(arrFieldIds[iC].dataIndex);
			arrTotals[iC] += v;
			
			strRow += "|" + (arrFieldIds[iC].cumulative ? arrTotals[iC] : v);
		}
		
		strPostData.push(strRow);
	}
	

	// Need to build the data to display
	XMLTransmission("POST", glbSiteRoot + "_BespokeChartPostback.aspx", "data=" + strPostData.join(""), OnBespokeChartPostback);
}

function OnBespokeChartPostback(objXML)
{
	if (IsXmlHttpResponseSuccess(objXML)) {
		document.getElementById("ChartOptionsColumnList").innerHTML = "<img src='" + glbSiteRoot + "charts/BespokeChart.aspx?id=" + GetXmlHttpResponseData(objXML) + "'/>";
	} else {
		HandleXmlHttpResponse(objXML);
	}
}





function OnCellContextMenu(Grid, rowIndex, cellIndex, e)
{
	e.stopEvent();

	var strDataFieldName = Grid.initialConfig.columns[cellIndex].dataIndex;
	var objDataField = Grid.store.fields.get(strDataFieldName);

	Grid.filterOnColumnName = Grid.initialConfig.columns[cellIndex].header;
	Grid.filterOnField = objDataField;
	Grid.filterCompareToValue = Grid.store.getAt(rowIndex).get(objDataField.name);


	var glbFilterMenu = new Ext.menu.Menu({
		id: "filtermenu" + Grid.uniqueId,
		items: [
			{
				id: "EQ",
				icon: glbSiteRoot + "images/siEqual.gif",
				grid: Grid,
				text: 'Equal to',
				handler: OnFilterMenuClick
			},
			{
				id: "GE",
				icon: glbSiteRoot + "images/siGE.gif",
				grid: Grid,
				text: 'Greater or equal',
				handler: OnFilterMenuClick
			},
			{
				id: "LE",
				icon: glbSiteRoot + "images/siLE.gif",
				grid: Grid,
				text: 'Less or equal',
				handler: OnFilterMenuClick
			},
			{
				id: "NE",
				grid: Grid,
				icon: glbSiteRoot + "images/siNE.gif",
				text: 'Not equal',
				handler: OnFilterMenuClick
			}
			,"-"
			,{
				id: "ColumnStats",
				grid: Grid,
				icon: glbSiteRoot + "images/siSum.gif",
				text: 'Column Stats',
				handler: OnColumnStatsClick
			}
		],
		
		listeners: {
			hide: {fn: OnGridMenuHidden},
			show: {fn: OnGridMenuShown}
		}
	});


	var pos = new Array();
	pos.push(e.getPageX());
	pos.push(e.getPageY());
	Ext.getCmp("filtermenu" + Grid.uniqueId).showAt(pos);
}


function OnFilterMenuClick(Item, e)
{
	try {
		var operator = Item.id;
		var grid = Item.grid;
		var store = grid.store;
		var field = grid.filterOnField;
		var compareAgainst = grid.filterCompareToValue;
		
		// Need to add an item to the grid's filtering
		if (!grid.currentFilter) {
			grid.currentFilter = new Array();
		}
		
		// Create a new object
		grid.currentFilter.push({
			field: field,
			operator: operator,
			compareAgainst: compareAgainst
		});
		
		
		Item.grid.store.filterBy(function(Record) {return FilterComparison(Record, store, grid);});

		try {
			Item.grid.getTopToolbar().items.get("clearFilterButton").enable();
			Item.grid.getTopToolbar().addClass("x-filtered-grid");
		} catch (ex) {}

	} catch (ex) {alert(ex);}
}

function FilterComparison(Record, store, grid)
{
	var filter = grid.currentFilter;
	if (!filter) return true;
	
	for (var i = 0; i < filter.length; i++) {
		var filterElem = filter[i];
		if (!CompareRecordAgainstFilter(Record, filterElem.field, filterElem.operator, filterElem.compareAgainst)) {
			return false;
		} 
	}

	return true;
}

function OnColumnStatsClick(Item, e)
{
	var grid = Item.grid;
	var store = grid.store;
	var field = grid.filterOnField;
	switch (field.type) {
		case "float":
		case "int":
			break;

		default:
			alert("Stats are only available for numeric columns.");
			return;
	}

	// Run through...
	var ctRecords = store.getCount();
	var ctNumeric = 0, ctNotNumeric = 0, ctGreaterThanZero = 0, ctLessThanZero = 0, ctZero = 0;

	var vSum = 0, vLargest = -999999999, vSmallest = 999999999;

	var vMean = 0, vMedian = 0;
	

	var arrV = new Array();
	
	for (var i =0; i < ctRecords; i++) {
		var r = store.getAt(i);
		var v = r.get(field.name);
		
		if (isNaN(v)) {
			ctNotNumeric++;
		} else {
			ctNumeric++;
			vSum += v;
			
			if (v > 0) {
				ctGreaterThanZero++;
			} else if (v < 0) {
				ctLessThanZero++;
			} else {
				ctZero++;
			}
	

			if (vLargest < v) vLargest = v;
			if (vSmallest > v) vSmallest = v;
			
			arrV.push(v);
		}
	}

	if (ctNumeric) {
		vMean = Math.round(vSum / ctNumeric * 100) / 100;
		
		// Get median by sorting the array
		arrV.sort(function(a, b){ return (a-b)});
		if (ctNumeric % 2 == 0) {
			vMedian = (arrV[Math.floor(ctNumeric/2) - 1] + arrV[Math.floor(ctNumeric/2)]) / 2;
		} else {
			vMedian = arrV[Math.floor(ctNumeric/2)];
		}
	
		// Do the stdev
		var vStdev = 0;
		if (ctNumeric > 1) {
			var vSumDiff = 0;
			for (var i = 0; i < ctNumeric; i++) {
				vSumDiff += ((arrV[i] - vMean) * (arrV[i] - vMean));
			}
			vStdev = Math.round(Math.sqrt(vSumDiff / (ctNumeric - 1)) * 100) / 100;
		}
		
		vSum = Math.round(vSum * 100) / 100;
	}



	var arrData = [
		["Records in column", ctRecords],
		["Non-numeric", ctNotNumeric],
		["Numeric", ctNumeric]
	];
	
	if (ctNumeric > 0) {
		arrData.push(
		["Greater than zero", ctGreaterThanZero],
		["Less than zero", ctLessThanZero],
		["Zero value", ctZero],
		["Largest", vLargest],
		["Smallest", vSmallest],
		["Total", vSum],
		["Average (mean)", vMean],
		["Median", vMedian],
		["StDev", vStdev]
		);	
	}

	var g =	new Ext.grid.GridPanel({
		store: new Ext.data.ArrayStore({
			fields: [{name: "field", type: "string"},{name: "value", type:"float"}],
			data: arrData
		}),
		
		columns: [
			{id:'type', locked: true, header: "Statistic", align: 'left', width: 200, sortable: true, dataIndex: 'field'},
			{id:'value', locked: true, header: "Value", align: 'right', width: 100, sortable: true, dataIndex: 'value', renderer: DP2}
		],
		stripeRows: true,
		layout: 'fit',
		header: false,
		frame: false
	});


	var ColumnStatsWindow = new Ext.Window({
		closeAction: "close",
		width: 400,
		height: 350,
		layout: 'fit',
		title: "Column stats: " + grid.filterOnColumnName,
		autoScroll: true,
		modal: true,
		bodyStyle: "background: white",
		resizable: false
	});
	ColumnStatsWindow.add(g);
	ColumnStatsWindow.show();
}

function CompareRecordAgainstFilter(Record, field, operator, compareAgainst)
{
	var v = Record.get(field.name);
	
	switch (operator) {
		case "EQ":
			return (v == compareAgainst);		

		case "NE":
			return (v != compareAgainst);		

		case "LE":
			return (v <= compareAgainst);		

		case "GE":
			return (v >= compareAgainst);		

		case "LT":
			return (v < compareAgainst);		

		case "GT":
			return (v > compareAgainst);		
			
		default:
			return false;
	}
}

function OnGridMenuHidden(Menu)
{
	if (Menu.shown) Menu.destroy();
}

function OnGridMenuShown(Menu)
{
	Menu.shown = true;
}

function OnGridClearFilterButton(Item)
{
	var grid = Item.findParentByType("grid");
	if (grid.hasFilter()) {
		grid.currentFilter = null;
		grid.store.clearFilter();

		try {
			grid.getTopToolbar().items.get("clearFilterButton").disable();
			grid.getTopToolbar().removeClass("x-filtered-grid");
		} catch (ex) {}
	} else {
		alert("There is no filtering on this grid");
	}
}

function PrintChart()
{
	var win = window.open(document.getElementById("Chart1").src, "printchart");
	win.focus();
	win.print();
}

function PrintHeadline()
{
    var win = window.open(glbSiteRoot + "users/" + glbUserId + "/statsheadline" + (glbFilterRequest == "" ? "" : "?" + glbFilterRequest), "printheadline");
    win.focus();
    win.print();
}


function GotEAs()
{
	if (arrMagicNumbers.length > 1) return true;
	if (filter_magic != "") return true;
	if (arrMagicNumbers.length == 0) return false;
	if (arrMagicNumbers[0][0] == 0) return false;
	return true;
}

function DoEAHelpPanel(NewPanel)
{
	var p = new Ext.Panel({contentEl: 'divNoEAs'});
	NewPanel.add(p);
}


function ShowFiltering()
{
	var arrDateRanges = [["",""], ["today", "Today"], ["last3days", "Last 3 days"], ["last7days", "Last 7 days"], ["currentweek", "Current week"], ["lastweek", "Previous week"], ["currentmonth", "Current month"], ["lastmonth", "Previous month"], ["currentquarter", "Current quarter"], ["lastquarter", "Previous quarter"], ["currentyear", "Current year"], ["lastyear", "Previous year"]];
	var arrBuySell = [["","(Both)"], ["buy", "Buys only"], ["sell", "Sells only"]];
	var arrDoW= [["","(All)"], ["0", "Sunday"], ["1", "Monday"], ["2", "Tuesday"], ["3", "Wednesday"], ["4", "Thursday"], ["5", "Friday"], ["6", "Saturday"]];

	var f = new Ext.form.FormPanel({
    	labelWidth: 75,
        frame:true,
        bodyStyle:'padding:5px 5px 0',
        width: 350,
        xdefaults: {width: 230},
        defaultType: 'textfield',

        items: [
			new Ext.form.Label({
				html: "<div style='padding-bottom: 2em'>This filtering applies to <b>all tabs</b> of the Stats page. (You can also filter the contents of <b>individual</b> grids by right-clicking over cells in the grid)</div>"
			}),

			new Ext.form.ComboBox({
				fieldLabel: "Date range", 
				triggerAction: 'all',
				id:'DateRangeListCombo', 
				store: arrDateRanges,
				width:200, 
				forceSelection:true,
				listeners:{select: SetDateRange}
			}),
			
			new Ext.form.Label({
				html: "<div style='padding-bottom: 0.5em'></div>"
			}),
			
			new Ext.form.DateField({
				fieldLabel: 'Start date',
				id: 'startdate',
				width:190,
 				format: 'Y/m/d',
 				value: Date.parseDate(filter_startDate, "Y-m-d"),
				allowBlank:true,
 				listeners: {render: function(c) {Ext.QuickTips.register({target: c, text: 'Choose the start date for orders to include (based on the date when orders were opened).'});}}
				}),

			new Ext.form.DateField({
				fieldLabel: 'End date',
				id: 'enddate',
				width:190,
 				format: 'Y/m/d',
 				value: Date.parseDate(filter_endDate, "Y-m-d"),
				allowBlank:true,
 				listeners: {render: function(c) {Ext.QuickTips.register({target: c, text: 'Choose the end date for orders to include (based on the date when orders were opened).'});}}
			}),

            new Ext.ux.form.SpinnerField({
                fieldLabel: 'Min lots',
                id: 'minlot',
            	minValue: 0,
            	maxValue: 1000,
            	allowDecimals: true,
            	decimalPrecision: 2,
            	incrementValue: 0.01,
            	alternateIncrementValue: 1,
            	accelerate: true,
            	value: filter_minLot,
 				listeners: {render: function(c) {Ext.QuickTips.register({target: c, text: 'Choose the minimum lot size of orders to include.'});}}
            }),
            
            new Ext.ux.form.SpinnerField({
                fieldLabel: 'Max lots',
                id: 'maxlot',
            	minValue: 0,
            	maxValue: 1000,
            	allowDecimals: true,
            	decimalPrecision: 2,
            	incrementValue: 0.01,
            	alternateIncrementValue: 1,
            	accelerate: true,
            	value: filter_maxLot,
 				listeners: {render: function(c) {Ext.QuickTips.register({target: c, text: 'Choose the maximum lot size of orders to include.'});}}
            }),

            new Ext.form.TextField({
                fieldLabel: 'Symbol(s)',
                id: 'symbol',
                width: 250,
            	value: filter_symbol,
 				listeners: {render: function(c) {Ext.QuickTips.register({target: c, text: 'Enter a list of symbols to include, separated by commas. The list is not case-sensitive.'});}}
 			}),

			new Ext.form.ComboBox({
				fieldLabel: "Buy/sell", 
				triggerAction: 'all',
				id:'buysellcombo', 
				store: arrBuySell,
				width:200, 
            	value: filter_buysell,
				forceSelection:true
			}),

			new Ext.form.ComboBox({
				fieldLabel: "Day of week", 
				triggerAction: 'all',
				id:'dowcombo', 
				store: arrDoW,
				width:200, 
            	value: filter_dow,
				forceSelection:true
			}),

            new Ext.form.TextField({
                fieldLabel: 'Magic #',
                id: 'magic',
                width: 250,
            	value: filter_magic,
 				listeners: {render: function(c) {Ext.QuickTips.register({target: c, text: 'Enter a list of magic numbers to include, separated by commas.'});}}
 			})
			
       	],
		

        buttons: [{
            text: 'Filter',
            handler: SaveFiltering
        },{
            text: 'Clear filter',
            handler: ClearFiltering
        },{
            text: 'Cancel',
            handler: function(b) {b.findParentByType("window").close();}
        }]		
	});

	var ColumnStatsWindow = new Ext.Window({
		closeAction: "close",
		width: 400,
		height: 400,
		title: 'Filter the results',
		layout: 'fit',
		autoScroll: true,
		modal: true,
		bodyStyle: "background: white",
		resizable: false
	});
	ColumnStatsWindow.add(f);
	ColumnStatsWindow.show();
}

function SaveFiltering(b)
{
	var strFilter = "";

	// Get the form in the filtering window
	var f = b.findParentByType("form");
	

	// Process start date filtering
	var dtStart = f.get("startdate").getValue();
	if (dtStart) {
		strFilter += (strFilter ? "&" : "") + "start=" + DateToText(dtStart);
	}

	// Process end date filtering
	var dtEnd = f.get("enddate").getValue();
	if (dtEnd) {
		strFilter += (strFilter ? "&" : "") + "end=" + DateToText(dtEnd);
	}

	// Minimum lot size
	var vMinLot = f.get("minlot").getValue();
	if (vMinLot) {
		strFilter += (strFilter ? "&" : "") + "minlot=" + vMinLot;
	}

	// Maximum lot size
	var vMaxLot = f.get("maxlot").getValue();
	if (vMaxLot) {
		strFilter += (strFilter ? "&" : "") + "maxlot=" + vMaxLot;
	}

	// Symbol
	var strSymbol = f.get("symbol").getValue();
	if (strSymbol) {
		strFilter += (strFilter ? "&" : "") + "symbol=" + strSymbol;
	}

	// Magic
	var strMagic = f.get("magic").getValue();
	if (strMagic) {
		strFilter += (strFilter ? "&" : "") + "magic=" + strMagic;
	}

	// Buys and sells
	var strBuySell = f.get("buysellcombo").getValue();
	if (strBuySell) {

		strFilter += (strFilter ? "&" : "") + "buysell=" + strBuySell;
	}

	// Buys and sells
	var strDoW = f.get("dowcombo").getValue();
	if (strDoW) {
		strFilter += (strFilter ? "&" : "") + "dow=" + strDoW;
	}

	// Get and close the filtering window
	var w = b.findParentByType("window");
	w.close();


	var strHash = GetHashForCurrentTab();
	
	var strUrl = RefreshableUrl(glbSiteRoot + "users/" + glbUserId + "/stats?" + strFilter) + "#" + strHash;
	
	Goto2(strUrl);
}

function ClearFiltering()
{
	var strHash = GetHashForCurrentTab();
	var strUrl = RefreshableUrl(glbSiteRoot + "users/" + glbUserId + "/stats") + "#" + strHash;
	Goto2(strUrl);
}

function GetHashForCurrentTab()
{

	var strTabId = glbTabs.getActiveTab().getId();
	return (strTabId.substr(4));	// Strip the tab_ bit leaving only the id used on initialisation
}

function DateToText(d)
{
	return d.getFullYear() + "/" + (d.getMonth() + 1) + "/" + d.getDate();
}

function SetDateRange(combo, record)
{
	var dStart = new Date();

	dStart = new Date(Math.floor(dStart.valueOf() / 86400000) * 86400000);
	var dEnd = dStart;
	
	switch (combo.getValue()) {
		case "today":
			// Already set
			break;
			
		case "last3days":
			dStart = new Date(dStart.valueOf() - (86400000 * 2));
			break;
		
		case "last7days":
			dStart = new Date(dStart.valueOf() - (86400000 * 6));
			break;
	
		case "currentweek":
			var lDow = dStart.getDay();
			dStart = new Date(dStart.valueOf() - (lDow * 86400000));
			break;

		case "lastweek":
			var lDow = dStart.getDay() + 7;
			dStart = new Date(dStart.valueOf() - (lDow * 86400000));
			dEnd = new Date(dStart.valueOf() + (6 * 86400000));
			break;

		case "currentmonth":
			dStart = new Date(dStart.getFullYear(), dStart.getMonth(), 1);
			break;
		
		case "lastmonth":
			var m = dStart.getMonth();
			var y = dStart.getFullYear();
			m--;
			if (m < 0) {m += 12; y -= 1;}
			dStart = new Date(y, m, 1);
			dEnd = new Date(y, m, GetEndOfMonth(m + 1, y));
			break;

		case "currentquarter":
			var m = Math.floor(dStart.getMonth() / 3) * 3;
			dStart = new Date(dStart.getFullYear(), m, 1);
			break;

		case "lastquarter":
			var m = Math.floor(dStart.getMonth() / 3) * 3;
			var y = dStart.getFullYear();
			m -= 3;
			if (m < 0) {m += 12; y -= 1;}
			dStart = new Date(y, m, 1);
			dEnd = new Date(y, m + 2, GetEndOfMonth(m + 3, y));
			break;

		case "currentyear":
			dStart = new Date(dStart.getFullYear(), 0, 1);
			break;

		case "lastyear":
			dStart = new Date(dStart.getFullYear() - 1, 0, 1);
			dEnd = new Date(dStart.getFullYear(), 11, 31);
			break;
	}

	// Get the form in the filtering window
	var f = combo.findParentByType("form");

	// Get the start and end date fields
	f.get("startdate").setValue(dStart);
	f.get("enddate").setValue(dEnd);

}


// Gets the last day of a month. EXPECTS normal 1-12 months, not javascript 0-11
function GetEndOfMonth(m, y)
{
	switch (m) {
		case 4:
		case 6:
		case 9:
		case 11:
			return 30;
		
		case 2:
			if (y % 4 != 0) {
				return 28;
			} else {
				if (y % 100 != 0) { 
					return 28;
				} else {
					return 28;
				}
			}
			
		default:
			return 31;
	}
}



var QuickFilteredChartWindow;
var QuickFilterSelectionList;
var QuickFilterType;

function ItemChartOptions(Item)
{
	var grid = Item.findParentByType("grid");
	var store = grid.store;
	var cols = grid.getColumnModel();

	var objSelections = grid.getSelectionModel().getSelections();
	if (!objSelections.length) {
		alert("Please select the item(s) in the grid which you want to draw a chart for");
		return false;
	}

	// Store the grid name for use in DrawQuickFilteredChart()
	QuickFilterType = store.storeId;

	// Handle the selected items
	QuickFilterSelectionList = "";
	switch (QuickFilterType) {
		case "Symbol/Direction":
			alert("Filtered charts cannot be created from the symbol/direction grid");
			return;
	
		case "Day":
		case "Week":
		case "Month":
			// Only use the earliest and latest options
			var strFirst = "zzzzzzzzz";
			var strLast = " ";
			for (var iRec = 0; iRec < objSelections.length; iRec++) {
				var Rec = objSelections[iRec];
				var date = Rec.get(cols.getDataIndex(0));
				if (strLast < date) strLast = date;
				if (strFirst > date) strFirst = date;
			}
			QuickFilterSelectionList = strFirst + "," + strLast;
			break;			
		
		default:
			// Normal processing where the user can select any set of options
			for (var iRec = 0; iRec < objSelections.length; iRec++) {
				var Rec = objSelections[iRec];
				QuickFilterSelectionList += (QuickFilterSelectionList ? "," : "") + Rec.get(cols.getDataIndex(0));
			}
			break;
	
	}
	
	// Hide the chart element, and show the options
	document.getElementById("QuickFilteredChartForm1").style.display = "";
	document.getElementById("QuickFilteredChartForm2").style.display = "none";
	
	if (!QuickFilteredChartWindow) {
		QuickFilteredChartWindow = new Ext.Window({
			contentEl: "QuickFilteredChartForm",
			closeAction: "hide",
			width: 800,
			height: 540,
			title: "Filtered chart",
			autoScroll: true,
			modal: true,
			bodyStyle: "background: white",
			resizable: false
		});
	}

	QuickFilteredChartWindow.setTitle("Filtered chart");
	QuickFilteredChartWindow.setPagePosition(((document.body.offsetWidth - 800) / 2) + document.body.scrollLeft, ((document.body.offsetHeight - 540) / 2) + document.body.scrollTop);
	

	QuickFilteredChartWindow.show();

}

function DrawQuickFilteredChart()
{
	// Get the type of chart
	var strNewChart = GetFieldValue(document.getElementById("QuickFilteredChartType"));
	if (!strNewChart) {alert("Please select a chart to draw");return;}

	// Build the basic URL
	var strUrl = glbSiteRoot + "charts/ResultChart.aspx?id=" + glbUserId + "&type=" + strNewChart;
	strUrl += "&w=760&h=490";
	
	// Need to take the list of selections and, potentially, _remove_ items in the 
	// global filter which refer to the same element
	var RevisedGlobalFilter = glbFilterRequest;

	var StartDate, EndDate, DateRange;
	
	switch (QuickFilterType) {
		case "Symbol":
			strUrl += "&symbol=" + QuickFilterSelectionList;		
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "symbol");
			break;

		case "Direction":
			strUrl += "&buysell=" + QuickFilterSelectionList;		
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "buysell");
			break;

		case "DoW":
		case "Day of week":
			strUrl += "&dow=" + QuickFilterSelectionList;		
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "buysell");
			break;

		case "EA":
			strUrl += "&magic=" + QuickFilterSelectionList;		
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "magic");
			break;

		case "Username":
			strUrl += "&username=" + QuickFilterSelectionList;		
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "username");
			break;

		case "Open-hour":
			strUrl += "&hour=" + QuickFilterSelectionList;		
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "hour");
			break;

		case "Month":
			StartDate = ConvertGridDate(QuickFilterSelectionList.split(",")[0]);
			EndDate = ConvertGridDate(QuickFilterSelectionList.split(",")[1]);
			// Need to force the end date to the end of the month
			var NextMonthStart = new Date(EndDate.getFullYear() + (EndDate.getMonth() == 11 ? 1 : 0), (EndDate.getMonth() == 11 ? 0 : EndDate.getMonth() + 1) , 1);
			EndDate = new Date(NextMonthStart.valueOf() - 86400);
			strUrl += "&start=" + FormatDateForFilter(StartDate);
			strUrl += "&end=" + FormatDateForFilter(EndDate);
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "start");
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "end");
			break;
		
		case "Week":
			DateRange = QuickFilterSelectionList.split(",");
			StartDate = ConvertGridDate(DateRange[0]);
			EndDate = ConvertGridDate(DateRange[1]);
			// Need to revise the end date
			EndDate = new Date(EndDate.valueOf() + ((7 * 86400 * 1000) - 10));
			strUrl += "&start=" + FormatDateForFilter(StartDate);
			strUrl += "&end=" + FormatDateForFilter(EndDate);
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "start");
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "end");
			break;
			
		case "Day":
			StartDate = ConvertGridDate(QuickFilterSelectionList.split(",")[0]);
			EndDate = ConvertGridDate(QuickFilterSelectionList.split(",")[1]);
			strUrl += "&start=" + FormatDateForFilter(StartDate);
			strUrl += "&end=" + FormatDateForFilter(EndDate);
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "start");
			RevisedGlobalFilter = RemoveFromGlobalFilter(RevisedGlobalFilter, "end");
			break;
		
		default:
			alert(QuickFilterType);
			break;
	}
	

	// Add in any remaining revised global filter
	if (RevisedGlobalFilter != "") {strUrl += "&" + RevisedGlobalFilter;}

	// Set the chart URL	
	document.getElementById("ItemChart").src = RefreshableUrl(strUrl);

	// Un-hide the chart
	document.getElementById("QuickFilteredChartForm1").style.display = "none";
	document.getElementById("QuickFilteredChartForm2").style.display = "";
}


function RemoveFromGlobalFilter(Filter, RemoveKey)
{
	if (!Filter) return "";

	var arrParts = Filter.split("&");
	
	var RevisedFilter = "";
	for (var iP = 0; iP < arrParts.length; iP++) {
		var iEq = arrParts[iP].indexOf("=");
		if (iEq >= 0) {
			var strKey = arrParts[iP].substr(0, iEq);
			var strVal = arrParts[iP].substr(iEq + 1);
		
			if (strKey.toLowerCase() == RemoveKey.toLowerCase()) {
			
			} else {
				RevisedFilter += (RevisedFilter ? "&" : "") + strKey + "=" + strVal;	
			}
		}
	}
	
	return RevisedFilter;
}

function FormatDateForFilter(vDate)
{
	return vDate.getFullYear() + "/" + (vDate.getMonth() + 1) + "/" + vDate.getDate();
}

function ConvertGridDate(DateText)
{
	var arrP = DateText.split("/");
	switch (arrP.length) {
		case 2:
			return new Date(parseInt(arrP[0], 10), parseInt(arrP[1], 10) - 1, 1);
		case 3:
			return new Date(parseInt(arrP[0], 10), parseInt(arrP[1], 10) - 1, parseInt(arrP[2], 10));
		
		default:
			return new Date();
	}
}
