tempStatsMulti.html HTML Source View


<!!DOCTYPE html>>
<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
	<link rel="icon" type="image/png" href="favicon196.png" sizes="196x196">
	<link rel="stylesheet" href="styles/jquery.mobile-1.0b2.min.css" />
	<link rel="stylesheet" href="styles/eth.css" />
	<script src="styles/jquery-1.6.4.min.js" type="text/javascript"></script> 
	<script src="styles/jquery.mobile-1.0b2.min.js" type="text/javascript"></script> 
	<script src="styles/highcharts-custom.js" type="text/javascript"></script> 
			<script type="text/javascript" src="styles/jquery.mobile.simpledialog.min.js"></script> 
	<link rel="stylesheet" type="text/css" href="styles/jquery.mobile.simpledialog.min.css" />
	<style>	.ui-checkbox {		display:inline-block;	}
		.inline-datepicker {
			width: 140px; padding: .4em;
    line-height: 1.4;    font-size: 16px;    display: inline; vertical-align:top; margin-top: 7px;
		}
	</style> 
</head>
<body>
	<div data-role="page" id="tempStatTopPage" data-title="Temperature Statistics - Wireless Tag List">

		<div data-role="header" data-theme="b" data-position="inline">
			<a href="javascript:closeGraph();" data-icon="delete" data-iconpos="notext" data-ajax="false">Close</a>
			<h1 id="stat_title"></h1>
			<a class="ui-btn-right" id="top_right_btn" data-icon="share" data-ajax=0 data-theme="b">Share</a>
		</div>
		<div id="tempStatGraphs" style="width: 100%; ">
		</div>
		<center>
			<span id="lightOnlyOptions">
				<input type="checkbox" name="log_axis" id="log_axis" class="custom" checked /><label for="log_axis">Log Scale</label>
			</span>
			<span class="loggedInOnly">
			<span id="editModeAvailable">
				<input type="checkbox" name="edit_mode" id="edit_mode" class="custom" /><label for="edit_mode">Click to remove point</label>
				<span id="editModeOnly" style="display:none">
					<button data-icon="arrow-l" data-inline=1 data-theme="a" disabled id="edit_mode_undo">Undo</button>
					<button data-icon="check" data-inline=1 data-theme="e" disabled id="edit_mode_save">Save</button>
				</span>
			</span>
			<button data-icon="arrow-d" data-inline=1 data-theme="b" id="downloadLogBtn">Download CSV</button></span><span id="tempCapOnlyOptions">
			<input type="checkbox" name="since_calibration" id="since_calibration" class="custom" /><label for="since_calibration">Show since last calibration</label>
			</span><span id="tempCapLightOnlyOptions">
			<input type="checkbox" name="hourly_minmax" id="hourly_minmax" class="custom" /><label for="hourly_minmax">Show hourly highs &amp; lows</label></span>
			<input type="date" name="custom_min" id="custom_min" class="inline-datepicker ui-body-null ui-corner-all ui-shadow-inset ui-body-c" />
			<input type="date" name="custom_max" id="custom_max" class="inline-datepicker ui-body-null ui-corner-all ui-shadow-inset ui-body-c" />

	<button data-icon="search" data-inline=1 data-theme="b" id="zoomBtn">Zoom To</button>

	<button data-icon="arrow-r" data-inline=1 data-theme="c" onclick="location.replace('tempStatsMultiDaily.html'+window.location.search)">View Daily Graph</button>
</center>
		<center class="sharedOnly">
			Captured by <a href="http://wirelesstag.net">Wireless Sensor Tags</a>
		</center>

		<script src="styles/client.js" type="text/javascript"></script> 
		<script src="styles/rawDataChart.js" type="text/javascript"></script> 
		<script type="text/javascript">
			var tzoffset = (new Date()).getTimezoneOffset() * 60000;
			var minmaxChartLines = [];
			var editMode = false;
			var editModeStack = [];
			function updateRawSeries(tagId) {
				var i = $.inArray(tagId, arrayOfIds);
				if(i>=0)
					chart.series[i * (hourly_minmax ? 2 : 1)].setData(id2RawSeries[tagId], true);
			}
			function removeFromRawSeries(point) {
				editModeStack.push(point);
				var i = 0;
				for (; i < id2RawSeries[point.id].length;i++){
					if (id2RawSeries[point.id][i][0] == point.x) {
						id2RawSeries[point.id].splice(i, 1);
						updateRawSeries(point.id);
						return;
					}
				}
			}
			function undoRemoveFromRawSeries() {
				var i = 0, point = editModeStack.pop();
				for (; i < id2RawSeries[point.id].length; i++) {
					if (id2RawSeries[point.id][i][0] > point.x) {
						id2RawSeries[point.id].splice(i, 0, [point.x, point.y]);
						updateRawSeries(point.id);
						return;
					}
				}
			}			
			$("#edit_mode_undo").click(function () {
				undoRemoveFromRawSeries();
				if (editModeStack.length == 0) $("#edit_mode_save, #edit_mode_undo").button("disable");
			});
			$("#edit_mode_save").click(function () {
				$("#edit_mode_save, #edit_mode_undo").button("disable");
				$.ajax({
					url: WSROOT + "ethLogs.asmx/DeleteRawDataPoints",
					data: JSON.stringify({ "ids": slaveIds, "type": statType, "withMinMax": hourly_minmax, "sinceLastCalibration": since_calibration, "stack": editModeStack }),
					success: function (retval, textStatus) {
						editModeStack = [];
						hourlyDataCache[since_calibration] = retval.d.temps;
						updateMetadata(retval.d);
						statData = null; clearHourlyData();
					},
					error: function (xhr, textStatus, exception) {
						ajaxErrorHandler(xhr, exception);
						$("#edit_mode_save, #edit_mode_undo").button("enable");
					}
				});
			});
			$("#edit_mode").change(function () {
				if (this.checked) $("#editModeOnly").show();
				else $("#editModeOnly").hide();
				editMode = this.checked;
			});

			$("#log_axis").change(function () {
				chart.yAxis[0].update({
					type: this.checked ? "logarithmic" : "linear"
				});
			});
			function resizeChart() {
				holder.highcharts().setSize(
				   $(window).width(),
				   $(window).height() - 42,
				   false
				);
			}
			var holder = $("#tempStatGraphs");
			$(window).resize(function () {
				resizeChart();
			});

			var slaveIds, temp_unit;
			
			if(window.location.search.length > 0 ){
				var params = window.location.search.substring(1).split('&');
				slaveIds = params[0].split(':');
				statType = params.length > 1 ? params[1] : "temperature";
				temp_unit = params.length > 2 ? (params[2] == "F" ? 1 : 0) : -1;
			}
			else{
				slaveIds = localStorage["mytaglist.stats.slaveids"].split(':');
				statType = localStorage["mytaglist.stats.type"] || "temperature";
				temp_unit = -1;
			}

			var since_calibration = false, hourly_minmax= false;
			if (statType == "temperature" || statType=="cap") {
				since_calibration = localStorage["mytaglist.since_calibration"] == "true";
				hourly_minmax = localStorage["mytaglist.hourly_minmax"] == "true";
				$("#since_calibration").attr("checked", since_calibration);
				$("#hourly_minmax").attr("checked", hourly_minmax);
				$("#since_calibration").change(function () {
					since_calibration = localStorage["mytaglist.since_calibration"] = this.checked ? "true" : "false";
					statData = null;
					clearHourlyData();
					loadFirstData();
				});
				$("#hourly_minmax").change(function () {
					hourly_minmax = localStorage["mytaglist.hourly_minmax"] = this.checked ? "true" : "false";
					if (hourly_minmax)
						location.reload();
					else {
						minmaxChartLines.forEach(function (s) { s.remove(false); });
					}
				});
			} else {
				$("#tempCapOnlyOptions").hide();
				if (statType != "light") $("#tempCapLightOnlyOptions").hide();
			}

			if (statType != "light") $("#lightOnlyOptions").hide();

			if (statType == "motion")
				$("#editModeAvailable").hide();

			function previewEmbedHTML2() {
				$.ajax({
					url: WSROOT + "ethLogs.asmx/EditSharePermissions",
					data: JSON.stringify({
						"ids": slaveIds,
						"shareTemperature": shareInfo.shareTemperature.map(function () { return $("#shareTemp").is(":checked"); }),
						"shareMotion": shareInfo.shareMotion.map(function () { return $("#shareMotion").is(":checked"); })
					}),
					success: function () {
						$("#embdedHTMLPreview").html($("#embedHTML").val());
					},
					error: function (xhr, textStatus, exception) {
						popup_error(xhr, null);
					}
				});
			}

			var isUUID = slaveIds[0].length > 4;
			var shareInfo;
			if (window.location.search.length > 0) {
				$(".loggedInOnly").hide();
				$(".sharedOnly").show();

				var btn = $("#top_right_btn");
				btn.data("icon", "arrow-d");
				btn[0].innerHTML = "Download";
				btn.click(function () {
					window.location = WSROOT + "ethDownloadMultiStatsCSV.aspx?uuids=" + slaveIds.join(":") + "&type=" + statType + "&fromDate=" + $("#custom_min").val() + "&toDate=" + $("#custom_max").val();
				});
			} else {
				$(".sharedOnly").hide();
				$("#downloadLogBtn").click(function () {
					window.location = WSROOT + "ethDownloadMultiStatsCSV.aspx?ids=" + slaveIds.join(":") + "&type=" + statType + "&fromDate=" + $("#custom_min").val() + "&toDate=" + $("#custom_max").val();
				});
				$("#top_right_btn").click(function () {
					var btn2 = $("#top_right_btn").find(".ui-btn-inner");
					var oldhtml2 = show_finding(btn2, "Loading...");
					$.ajax({
						url: WSROOT + "ethLogs.asmx/GetSharePermissions",
						data: JSON.stringify({ "ids": slaveIds, "type": statType=="motion"?"motion_new":statType }),
						complete: function () { restore_finding(btn2, oldhtml2); },
						success: function (retval, textStatus) {
							shareInfo = retval.d;

							var html = "<div style='padding: 15px; width: " + (window.innerWidth - 200) + "px'><b>Share this data</b>" +
							"<div data-role='fieldcontain'><label for='graphURL'>Link to open graph in Web:</label><input type='text' id='graphURL'></div>"+
							"<div data-role='fieldcontain'><label for='downloadURL'>CSV download link:</label><input type='text' id='downloadURL'></div>"+
							"<div data-role='fieldcontain'><label for='iosURL'>Link to open graph in iOS app:</label><input type='text' id='iosURL'></div>"+
							"<div data-role='fieldcontain'><label for='embedHTML'>HTML to embed latest readings:</label><input type='text' id='embedHTML'><button onclick='previewEmbedHTML2()' data-theme='d'>Preview HTML</button></div><div id='embdedHTMLPreview'></div>" +
							"<form><center><input type='checkbox' id='shareTemp'><label for='shareTemp'>Anyone with link can access temperature data for these tags</label> " +
							"<input type='checkbox' id='shareMotion'><label for='shareMotion'>Anyone with link can access motion log data for these tags</label>"+
							"<a rel='close' data-role='button' data-theme='b' data-inline=1 href='#'>Apply Permissions</a></center></form></div>";

							var holder = $("#tempStatGraphs");
							if (holder.data('simpledialog')) {
								holder.data('simpledialog').options.fullHTML = html;
								holder.simpledialog('refresh').simpledialog('open');
							} else {
								holder.simpledialog({
									'mode': 'blank',
									'left': 80, 'top': '0px',
									'prompt': false,
									'forceInput': false,
									'useModal': true,
									pickPageTheme: 'c',
									'fullHTML': html,
									onClosed: function () {
										if (shareInfo.shareMotion.every(function (e) { return e; }) != $("#shareMotion").is(":checked") ||
											shareInfo.shareTemperature.every(function (e) { return e; }) != $("#shareTemp").is(":checked")) {
										
											$.ajax({
												url: WSROOT + "ethLogs.asmx/EditSharePermissions",
												data: JSON.stringify({
													"ids": slaveIds,
													"shareTemperature": shareInfo.shareTemperature.map(function () { return $("#shareTemp").is(":checked"); }),
													"shareMotion": shareInfo.shareMotion.map(function () { return $("#shareMotion").is(":checked"); })
												}),
												error: function (xhr, textStatus, exception) {
													popup_error(xhr, null);
												}
											});
										}
									}
								});
							}

							$("#shareMotion").attr("checked", shareInfo.shareMotion[0]).checkboxradio("refresh");
							$("#shareTemp").attr("checked", shareInfo.shareTemperature[0]).checkboxradio("refresh");

							$("#graphURL").val(shareInfo.graphUrl).click(function () { return selectedURL(this, statType == "motion"); });
							$("#downloadURL").val(shareInfo.downloadUrl).click(function () { return selectedURL(this, statType == "motion"); });
							$("#iosURL").val(shareInfo.iosAppUrl).click(function () { return selectedURL(this, statType == "motion"); });
							$("#embedHTML").val(shareInfo.embedHTML).click(function () { return selectedURL(this, statType == "motion"); });

						},
						error: function (xhr, textStatus, exception) {
							popup_error(xhr, null);
						}
					});
				});
			}



			var statTypeTranslationLUT = { "temperature": 
				{name:"Temperature",
					unitGen: function () {return temp_unit ? '°F' : '°C'; }, 
					preProcess: function (degC, i) { if (degC == 0.0) return NaN; return temp_unit == 1 ? degC * 9.0 / 5.0 + 32.0 : degC; },
					ymaxInit: function () { return temp_unit == 1 ? -28 : -40;},
					ymaxPost: function (ymax) { return Math.ceil(Math.min(temp_unit ? 220 : 125, ymax) / 5 + 0.2) * 5; },
					yminInit: function () { return temp_unit == 1 ? 220 : 115;},
					yminPost: function (ymin) { return Math.floor(ymin / 5 - 0.2) * 5; },
					decimals: 1
				},
				"cap": 
					{
						name: "Moisture/RH",
						unitGen: function () { return "%"; },
						preProcess: function (val, i) { if (val == 0 || val==-99) return NaN; return val; },
						ymaxInit: function () { return 0;},
						ymaxPost: function (ymax) { return ymax;},
						yminInit: function () { return 100;},
						yminPost: function (ymin) { return ymin; },
						decimals: 1
					},
				"batteryVolt": 
					{
						name: "Battery Voltage",
						unitGen: function () { return "V"; },
						preProcess: function (val, i) { if (val == 0) return NaN; return val; },
						ymaxInit: function () { return 2.0; },
						ymaxPost: function (ymax) { return ymax; },
						yminInit: function () { return 3.5; },
						yminPost: function (ymin) { return ymin; },
						decimals: 2
					},
				"light":
					{
						name: "Ambient Light",
						unitGen: function () { return " lux"; },
						preProcess: function (val, i) { if (val <= 0) return 0.005; return val; },
						ymaxInit: function () { return 0.005; },
						ymaxPost: function (ymax) { return ymax; },
						yminInit: function () { return 30000; },
						yminPost: function (ymin) { return ymin; },
						decimals: 2
					},
				"signal":
					{
						name: "Received Signal Level + TX Back-Off",
						unitGen: function () { return "dBm"; },
						preProcess: function (val, i) { if (val == 0) return NaN; return val; },
						ymaxInit: function () { return -120; },
						ymaxPost: function (ymax) { return ymax; },
						yminInit: function () { return 0; },
						yminPost: function (ymin) { return ymin; },
						decimals: 1
					},
				"motion":
					{
						name: "Motion/Door",
						unitGen: function () { return " times"; },
						preProcess: function (val, i) { return val; },
						ymaxInit: function () { return 0; },
						ymaxPost: function (ymax) { return ymax; },
						yminInit: function () { return 100; },
						yminPost: function (ymin) { return ymin; },
						decimals: 0,
						total: 1
					}

			};
			var statTypeTranslation = statTypeTranslationLUT[statType];
			var id2Series = {}, id2RawSeries = {}, id2BandSeries = {}, id2nameMapping = {};
			var id2minmaxSeries = {};
			var arrayOfIds=[];

			function processOneDay(day, baseDate, prepend) {
				
				var tagCount = day.ids.length;
				var tods = day.tods;

				for (var tagi = 0; tagi < tagCount; tagi++) {

					var tagv = day.values[tagi];
					var tagId = day.ids[tagi];

					if (-1 == $.inArray(tagId, arrayOfIds)) {
						arrayOfIds.push(tagId);
						var option = {
							name: id2nameMapping[tagId],
							tagId:tagId,
							tooltip: {
								valueSuffix: statTypeTranslation.unitGen()
							},
							point:{
								events: {
									click: function () {
										//alert("x=" + this.x + " y=" + this.y + " tagId=" + this.series.options.tagId);
										if ($("#edit_mode_undo").is(":visible")) {
											$("#edit_mode_save, #edit_mode_undo").button("enable");
											removeFromRawSeries({ x: this.x, y: this.y, id: this.series.options.tagId });
										}
									}
								}
							}
						};
						
						if (statType == "motion") {
							option.step = "center";
						}
						var s = chart.addSeries(option);
						if (hourly_minmax)
							minmaxChartLines.push(chart.addSeries({
								name: id2nameMapping[tagId] + " Highs/Lows",
								type: "arearange",
								color: s.color, turboThreshold:1e6, 
								tooltip: {
									valueSuffix: statTypeTranslation.unitGen()
								}
							}));
					}
					var series = null, rawSeries = null, tod = null, minmaxSeries=null;
					if (tods) {
						tod = tods[tagi];
						rawSeries = id2RawSeries[tagId];
						if (rawSeries == null) {
							id2RawSeries[tagId] = rawSeries = [];
						}
					} else {
						series = id2Series[tagId];
						if (series == null) {
							id2Series[tagId] = series = [];
						}
						if (hourly_minmax) {
							minmaxSeries = id2minmaxSeries[tagId];
							if (minmaxSeries == null) id2minmaxSeries[tagId] = minmaxSeries = [];
						}
					}
					var ymin = statTypeTranslation.yminInit(), ymax = statTypeTranslation.ymaxInit();
					var avg = 0;
					for (var j = 0; j < tagv.length; j++) {
						var n = prepend ? tagv.length - j - 1 : j;
						var val = statTypeTranslation.preProcess(tagv[n]);
						if (!isNaN(val)) {							
							var dataPoint = [baseDate + (tod ? tod[n] * 1000 : HOUR * (n+0.5)), val];
							if (dataPoint[0] > dataRange.max) dataRange.max = dataPoint[0];  
							
							if (tods) {
								if (prepend)
									rawSeries.unshift(dataPoint);
								else
									rawSeries.push(dataPoint);
								chart.series[$.inArray(tagId, arrayOfIds) * (hourly_minmax?2:1)].addPoint(dataPoint, false);
							} else {
								if (prepend) {
									series.unshift(dataPoint);
									if (minmaxSeries) minmaxSeries.unshift( [dataPoint[0], statTypeTranslation.preProcess(day.min[tagi][n]), statTypeTranslation.preProcess(day.max[tagi][n])] );
								} else {
									series.push(dataPoint);
									if (minmaxSeries) minmaxSeries.push( [dataPoint[0], statTypeTranslation.preProcess(day.min[tagi][n]), statTypeTranslation.preProcess(day.max[tagi][n])] );
								}

							}
							ymax = Math.max(ymax, val); ymin = Math.min(ymin, val);
							avg += val;
						}
					}
					if (!tods) {
						var bandSeries = id2BandSeries[tagId];
						if (!bandSeries) {
							id2BandSeries[tagId] = bandSeries = [];
						}
						var dataPoint;
						if (statType == "motion") {
							dataPoint = [baseDate + HOUR * 12, avg];
						} else {
							dataPoint = [baseDate + HOUR * 12, ymin, ymax];
						}
						bandSeries.push(dataPoint);
					}
				}
			}
			
			var $loader = $("<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1>Loading Data...</h1></div>");
			$body = $("body");
			$body.addClass("ui-loading");

			function ajaxErrorHandler(xhr, exception) {
				if (xhr.responseText.toLowerCase().indexOf("unauthorized") != -1 || exception.toLowerCase().indexOf("unauthorized") != -1 || xhr.responseText.toLowerCase().indexOf("authentication failed") != -1)
					location.replace("signin.html?ReturnUrl=" + encodeURIComponent(window.location.pathname + window.location.search));
				else {
					$loader.remove();
					popup_error(xhr, $body);
				}
			}
			function dataLoader(fromDate, toDate, onData) {
				$.ajax({
					url: WSROOT + (isUUID ? "ethLogShared.asmx/GetMultiTagStatsRawByUUIDs" : "ethLogs.asmx/GetMultiTagStatsRaw"),
					data: JSON.stringify({ ids: slaveIds, type: statType, fromDate: fromDate, toDate: toDate }),
					success: function (retval, textStatus) {
						updateMetadata(retval.d);
						onData(retval.d.stats);
					},
					error: function (xhr, textStatus, exception) {
						ajaxErrorHandler(xhr, exception);
					}
				});
			}
			var motionEventTypes = ["Disarmed", "Armed", "Moved", "Opened", "Closed", "Detected", "Timed Out", "Stabilizing..."];
			function motionEventFormatter(){
				return motionEventTypes[this.value];
			}
			function setMotionRawYAxis() {
				if (statType == "motion" ) {
					chart.yAxis[0].update({
						labels: {
							formatter: motionEventFormatter
						}
					});
					for (var i = 0; i < chart.series.length; i++)
						chart.series[i].update({ type: "scatter" },i==chart.series.length-1);
				}
			}
			function revertMotionRawYAxis() {
				if (statType == "motion" ) {
					chart.yAxis[0].update({
						labels: {
							formatter: null
						}
					});

					/*for (var i = 0; i < chart.series.length; i++)
						chart.series[i].update({
							type: "line"
						});*/
				}

			}
			function updateChartType(zoomLevel) {

				var step = (hourly_minmax ? 2 : 1);
				if (zoomLevel > ChartZoomLevelNormal) {
					$("#editModeAvailable").hide(); 
					var type = statType == "motion" ? "line" : "arearange";
					
					for (var i = 0; i < chart.series.length; i += step) {
						if(chart.series[i].type!=type)chart.series[i].update({ type: type }, false);
						chart.series[i].setData(id2BandSeries[arrayOfIds[i/step]], false);
						if (hourly_minmax) {
							chart.series[i + 1].setData(id2minmaxSeries[arrayOfIds[i / step]], false);
							chart.series[i + 1].hide();
						}
					}
					chart.redraw();
				}
				else if (zoomLevel == ChartZoomLevelNormal) {
					$("#editModeAvailable").hide();
					for (var i = 0; i < chart.series.length; i += step) {
						if (chart.series[i].type != "line") chart.series[i].update({ type: "line" }, false);
						chart.series[i].setData(id2Series[arrayOfIds[i / step]], false);

						if (hourly_minmax) {
							chart.series[i + 1].setData(id2minmaxSeries[arrayOfIds[i / step]], false);
							chart.series[i + 1].show();
						}
					}
					chart.redraw();
				}
				else {
					if (statType != "motion")
						$("#editModeAvailable").show();

					for (var i = 0; i < chart.series.length; i += step) {
						if (chart.series[i].type != "line") chart.series[i].update({ type: "line" }, false);
						if (hourly_minmax) {
							chart.series[i + 1].setData(id2minmaxSeries[arrayOfIds[i / step]], false);
							chart.series[i + 1].hide();
						}
						//chart.series[i].setData(id2RawSeries[arrayOfIds[i]], i == chart.series.length - 1);   // already done in processOneDay addPoint
					}
					chart.redraw();
				}
				if (zoomLevel != ChartZoomLevelRaw) revertMotionRawYAxis();
			}
			function clearHourlyData() {
				id2minmaxSeries = {};
				id2Series = {};
				id2BandSeries = {};
			}
			function clearCachedRawData() {
				id2RawSeries = {};
				for (var i = 0; i < chart.series.length; i++)
					chart.series[i].setData([], i==chart.series.length-1);
				setMotionRawYAxis();
			}
			function loadCachedRawData() {
				for (var i = 0; i < chart.series.length; i++)
					chart.series[i].setData(id2RawSeries[arrayOfIds[i]], i == chart.series.length - 1);
				setMotionRawYAxis();
			}

			//var availableColors = ['red', 'yellow', 'green', 'blue', '#FF00FF', '#13919F', '#660F8B', '#848B0F', '#0F368B', '#D53366', '#151B8D', '#F778A1', '#F87431',
				//'#20E42D', '#F6A40C', '#4079CA', '#40CA93', '#DDE469', '#979F0F', '#C4522C', '#29BA7C', '#4B29BA', '#4A3A72', '#00FFFF', '#808000'];
			$("#stat_title").text(statTypeTranslation.name + " Charts");

			var options = {
				title: { text: null },
				subtitle: {
					text: "Drag to zoom, hold Shift key and drag to pan"
				},
				animation: false,
				chart: {
					zoomType: 'x', panning: true, panKey: 'shift',
					type: "line",
					events: {
						selection: function (event) {
							updateZoom(this, event.xAxis ? event.xAxis[0] : dataRange, function () { });
						}
					}
				},
				xAxis: {
					type: 'datetime',
					dateTimeLabelFormats: {
						hour: '%I:%M %p'
					},
					events: {
						setExtremes: function (event) {
							if (event.trigger == "pan") updatePan(this.chart, event);
							try{
								$("#custom_min").val(new Date(event.min - tzoffset).toISOString().substring(0, 10));
								$("#custom_max").val(new Date(event.max - tzoffset).toISOString().substring(0, 10));
							} catch (e) { }
						}
					}
				},
				yAxis: [{
					title: { text: statTypeTranslation.name }, allowDecimals: statType == "batteryVolt", type: statType == "light" ? "logarithmic" : "linear"
					}
                ],
				tooltip: {
					borderColor: "gray",
					valueDecimals: statTypeTranslation.decimals,
					crosshairs: true,
					followPointer: false,
					dateTimeLabelFormats: {
						second: "%A, %b %e, %I:%M:%S %p",
					}
				},
				plotOptions: {
					scatter: {
						tooltip: {
							crosshairs: true,
							xDateFormat: "%A, %b %e, %H:%M:%S",
							headerFormat: '<b>{point.key}</b>',
							pointFormat: ""
						}
					}
				},
				legend: {
					layout: 'horizontal', align: 'left', x: 75, verticalAlign: 'top', y: 0, floating: true,
					backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF'
				},
				series: [],
				exporting: {
					buttons: {
						dayButton: {
							text: 'Last 24 Hours',
							onclick: function () {
								var range = { min: dataRange.max - DAY, max: dataRange.max };
								updateZoom(holder.highcharts(), range, function () {
									holder.highcharts().xAxis[0].setExtremes(dataRange.max - DAY, dataRange.max);
								});
							}
						},
						weekButton: {
							text: 'Last Week',
							onclick: function () {
								var range = { min: dataRange.max - DAY * 7, max: dataRange.max };
								updateZoom(holder.highcharts(), range, function () {
									holder.highcharts().xAxis[0].setExtremes(dataRange.max - DAY * 7, dataRange.max);
								});
							}
						},
						monthButton: {
							text: 'Last 30 Days',
							onclick: function () {
								var range = { min: dataRange.max - DAY * 30, max: dataRange.max };
								updateZoom(holder.highcharts(), range, function () {
									holder.highcharts().xAxis[0].setExtremes(dataRange.max - DAY * 30, dataRange.max);
								});
							}
						},
						allButton: {
							text: 'All',
							onclick: function () {
								updateZoom(holder.highcharts(), dataRange, function () {
									holder.highcharts().xAxis[0].setExtremes(dataRange.min, dataRange.max);
								});
							}
						}
					}
				}
			};
			if (statType == "light") {
				options.exporting.buttons["luxAxisButton"] = {
					text: "Log/Linear Scale",
					onclick: function () {
						if (chart.yAxis[0].options.type == "linear")
							chart.yAxis[0].update({ type: "logarithmic" });
						else
							chart.yAxis[0].update({ type: "linear" });
					}
				};
			}
			holder.highcharts(options);
			resizeChart();
			chart = holder.highcharts();

			$("#zoomBtn").click(function () {
				var range = { min: new Date($("#custom_min").val()).getTime() + tzoffset, max: new Date($("#custom_max").val()).getTime() + 3600 * 1000 * 23.9 + tzoffset };
				updateZoom(holder.highcharts(), range, function () {
					holder.highcharts().xAxis[0].setExtremes(range.min, range.max);
				});
			});


			function updateMetadata(d) {
				for (var i = 0; i < d.ids.length; i++) {
					id2nameMapping[d.ids[i]] = d.names[i];
				}
				if (temp_unit == -1) 
					temp_unit = d.temp_unit;

				chart.yAxis[0].update({ labels: { format: '{value}' + statTypeTranslation.unitGen() } });
			}
			var hourlyDataCache = {};
			function hourlyDataLoader(onData) {
				if (hourlyDataCache[since_calibration] != null) {
					onData(hourlyDataCache[since_calibration]);
					return;
				}

				$.ajax({
					url: WSROOT + (isUUID? "ethLogShared.asmx/GetHourlyStatsByUUIDs2":"ethLogs.asmx/GetHourlyStats2"),
					data: JSON.stringify({ "ids": slaveIds, "type": statType, "withMinMax": hourly_minmax, "sinceLastCalibration": since_calibration }),
					success: function (retval, textStatus) {
						hourlyDataCache[since_calibration] = retval.d.temps;

						updateMetadata(retval.d);
						onData(retval.d.stats);

					},
					error: function (xhr, textStatus, exception) {
						ajaxErrorHandler(xhr, exception);
					}
				});
			}
			function dataSpanLoader(onData){
				$.ajax({
					url: WSROOT + (isUUID ? "ethLogShared.asmx/GetMultiTagStatsSpanByUUIDs2" : "ethLogs.asmx/GetMultiTagStatsSpan2"),
					data: JSON.stringify({ "ids": slaveIds, "type": statType, "sinceLastCalibration": since_calibration }),
					success: function (retval, textStatus) {
						updateMetadata(retval.d);
						onData(fileTimeToDate(retval.d.from).getTime(), fileTimeToDate(retval.d.to).getTime());
					},
					error: function (xhr, textStatus, exception) {
						ajaxErrorHandler(xhr, exception);
					}
				});
			}

			if (params != null && params.length > 2) {
				$.ajax({
					url: WSROOT + "ethAccount.asmx/ReflectAspxAuthCookie",
					data: JSON.stringify({ "cookie": params[2] }),
					success: function (retval, textStatus) {
						loadFirstData();
					}
				});
			}
			else {
				loadFirstData();
			}

		</script> 
	</div>

</body>
</html>