tempStats.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?227" /> <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?6" type="text/javascript"></script> <script src="styles/highcharts-custom.js?14" type="text/javascript"></script> <script src="styles/modules/exporting.js" type="text/javascript"></script> <script src="styles/modules/offline-exporting_x.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" /> <link rel="stylesheet" type="text/css" href="styles/charts.css?2" /> </head> <body> <div data-role="page" id="tempStatTopPage" data-title="Temperature Chart - Wireless Tags"> <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%; touch-action: pan-y; "> </div> <span id="moreButtonTip" onclick="moreButtonTipClicked();"><span></span>Scroll for Options</span> <center id="moreButtons" style="display:none;"> <span class="loggedInOnly"><button data-icon="arrow-d" data-inline=1 data-theme="b" id="downloadTemperatureLogBtn">Download CSV</button></span> <input type="checkbox" name="since_calibration" id="since_calibration" class="custom" /><label for="since_calibration">Show since last calibration</label> <input type="checkbox" name="hourly_minmax" id="hourly_minmax" class="custom" /><label for="hourly_minmax">Show hourly highs & lows</label> <span class="loggedInOnly"> <select data-inine="1" data-icon="delete" data-theme="e" id="deleteTemperatureLogBtn" data-toptext="Delete..." onchange="deleteOptions($(this)); return false;" data-native-menu="false"> <option value="1">Select range...</option><option value="2">Delete all data points </option> </select> </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('tempStatsDaily.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?22" type="text/javascript"></script> <script src="styles/rawDataChart.js?99c" type="text/javascript"></script> <script type="text/javascript"> if (localStorage["moreButtonTipSeen"] == "1") $("#moreButtonTip").hide(); function moreButtonTipClicked() { $('html, body').animate({ scrollTop: $("#moreButtons").offset().top }, 500, 'linear'); $("#moreButtonTip").hide(); localStorage["moreButtonTipSeen"] = "1"; } var old_style_query=(window.location.search.length>0); delete localStorage["mytaglist.stats.fromDate"]; if (window.location.search.length > 0) { var queryString = window.location.search.substring(1).split("&"); for (var i = 0; i < queryString.length; i++) { var pair = queryString[i].split('='); if (pair.length == 2) { localStorage["mytaglist.stats."+decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || ''); old_style_query= false; } } } var params = ((old_style_query) ? window.location.search.substring(1) : localStorage["mytaglist.stats.slaveid"]).split('&'); var slaveId = params[0]; var tagName = params.length > 1 ? decodeURIComponent(params[1]) : localStorage["mytaglist.stats.name"]; var isUUID = slaveId.length > 4; if (!tagName && params.length > 1 && isUUID) tagName = decodeURIComponent(params[1]); var temp_unit = params.length > 2 ? (params[2] == "F" ? 1 : 0) : -1; var since_calibration = localStorage["mytaglist.since_calibration"] == "true"; var hourly_minmax = localStorage["mytaglist.hourly_minmax"] == "true"; var hasALS = old_style_query ? params.length > 3 : localStorage["mytaglist.stats.hasALS"] == "true"; //var isZmod = localStorage["mytaglist.stats.hasZmod"] == "true"; var isFlat= localStorage["mytaglist.stats.isFlat"] == "true"; var hasChipTemp = localStorage["mytaglist.stats.hasChipTemp"] == "true"; var isWME = localStorage["mytaglist.stats.isWME"] == "true"; var isMoisture = localStorage["mytaglist.stats.isMoisture"] == "true"; var hideDewPoint = false; if (hasChipTemp) dewPointMode = true; if (isWME || isMoisture || isFlat) { dewPointMode = false; hideDewPoint = true; } var CapName = isFlat? "Water" : (isWME ? "WME" : (isMoisture ? "Moisture" : "Humidity")); /*var tempBL = JSON.parse(localStorage["mytaglist.stats.tempBL"]); if (hasALS) var luxBL = JSON.parse(localStorage["mytaglist.stats.luxBL"]); if (!dewPointMode) var capBL = JSON.parse(localStorage["mytaglist.stats.capBL"]); */ $("#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 { tempMinMaxChartSeries.remove(false); if (capMinMaxChartSeries) capMinMaxChartSeries.remove(true); if (luxMinMaxChartSeries) luxMinMaxChartSeries.remove(true); } }); function resizeChart() { holder.highcharts().setSize( $(window).width(), $(window).height() - 42, false ); holder.highcharts().options.exporting.chartOptions.chart = { width: $(window).width(), height: $(window).height() - 42, marginTop: 50 }; } var holder = $("#tempStatGraphs"); var shareInfo; if (old_style_query) { $(".loggedInOnly").hide(); $(".sharedOnly").show(); var btn = $("#top_right_btn"); btn.data("icon", "arrow-d"); btn[0].innerHTML = "Download"; btn.click(function () { window.location = WSROOT + "ethDownloadTempCSV.aspx?uuid=" + params[0] + "&name=" + tagName + "&fromDate=" + $("#custom_min").val() + "&toDate=" + $("#custom_max").val() + "&useDegF=" + temp_unit; }); } else { $(".sharedOnly").hide(); $("#downloadTemperatureLogBtn").click(function () { window.location = WSROOT + "ethDownloadTempCSV.aspx?id=" + params[0] + "&fromDate=" + $("#custom_min").val() + "&toDate=" + $("#custom_max").val() + "&useDegF=" + temp_unit; }); $("#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": [slaveId], "type": "temperature"}), complete: function () { restore_finding(btn2, oldhtml2); }, success: function (retval, textStatus) { shareInfo = retval.d; var copy_icon = '<svg onclick="copyURL(this);" style="width:24px;height:24px;padding-left:4px;vertical-align:text-bottom" viewBox="0 0 24 24"><path fill="#888" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /></svg >' 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'>" + copy_icon + "</div>"+ "<div data-role='fieldcontain'><label for='downloadURL'>CSV download link:</label><input type='text' id='downloadURL'>" + copy_icon + "</div>"+ "<div data-role='fieldcontain'><label for='iosURL'>Link to open graph in iOS app:</label><input type='text' id='iosURL'>" + copy_icon + "</div>"+ "<form><center><input type='checkbox' id='shareTemp'><label for='shareTemp'>Anyone with link can access temperature data for this tag</label> " + "<input type='checkbox' id='shareMotion'><label for='shareMotion'>Anyone with link can access motion log data for this tag</label>"+ "<a rel='close' data-role='button' data-inline=1 data-theme='b' data-icon='check' href='#'>Apply Permissions</a></center></form></div>"; 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[0] != $("#shareMotion").is(":checked") || shareInfo.shareTemperature[0] != $("#shareTemp").is(":checked")) { $.ajax({ url: WSROOT + "ethLogs.asmx/EditSharePermissions", data: JSON.stringify({ "ids": [slaveId], "shareTemperature": [$("#shareTemp").is(":checked")], "shareMotion":[$("#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, false); }); $("#downloadURL").val(shareInfo.downloadUrl).click(function () { return selectedURL(this, false); }); $("#iosURL").val(shareInfo.iosAppUrl).click(function () { return selectedURL(this, false); }); $("#embedHTML").val(shareInfo.embedHTML).click(function () { return selectedURL(this, false); }); }, error: function (xhr, textStatus, exception) { popup_error(xhr, null); } }); }); } var $loader=$("<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1 id='loader_title'>Loading Data...</h1></div>"); $body = $("body"); 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, null); } } $loader.appendTo($body).css({ top: 100 }); $body.addClass("ui-loading"); var tempSeries = [], tempMinMaxSeries=[]; var capSeries = [], capMinMaxSeries = []; var dpSeries = [], dpMinMaxSeries=[]; var luxSeries=[], luxMinMaxSeries=[]; var bandCapSeries = [], bandDpSeries = []; var bandTempSeries = []; var bandLuxSeries=[]; var rawTempSeries = []; var rawCapSeries = [], rawDpSeries = []; var rawLuxSeries = []; var capChartSeries = null, tempMinMaxChartSeries = null, capMinMaxChartSeries = null; var dpChartSeries = null, dpMinMaxChartSeries = null; var luxChartSeries = null, luxMinMaxChartSeries = null; var tzoffset = (new Date()).getTimezoneOffset() * 60000; var luxAxisIndex; function createLuxAxis() { chart.addAxis({ gridLineWidth: 0, title: { text: isFlat?"Analog": "Lux", style: { color: luxColor } }, labels: { style: { color: luxColor } }, opposite: true ,type: "logarithmic" }); luxAxisIndex = chart.yAxis.length - 1; luxChartSeries = chart.addSeries({ name: isFlat ? "Analog Voltage" : "Ambient Light", yAxis: luxAxisIndex, tooltip: { valueSuffix: isFlat ? " V" : " lux" }, color: luxColor, threshold: luxBL, negativeColor: luxBL? luxLowColor: luxColor }); if (hourly_minmax) { luxMinMaxChartSeries = chart.addSeries({ name: isFlat ? "Voltage Highs/Lows" : "Ambient Light Highs/Lows", yAxis: luxAxisIndex, tooltip: { valueSuffix: isFlat?" V":" lux" }, color: luxRangeColor, visible: false, threshold: luxBL, negativeColor: luxBL? luxRangeLowColor: luxRangeColor }); } } function createCapAxis() { //if (!dewPointMode) // dewPointMode only decides if dp or cap series is displayed by fault { chart.addAxis({ // Secondary yAxis gridLineWidth: 0, title: { text: CapName, style: { color: capColor } }, labels: { format: '{value} %', style: { color: capColor } }, opposite: true }); } capChartSeries = chart.addSeries({ name: CapName, yAxis: 1, tooltip: { valueSuffix: " %" }, visible: !dewPointMode, color: capColor, threshold: capBL, negativeColor: capBL ? capLowColor : capColor }); if(!hideDewPoint) dpChartSeries = chart.addSeries({ name: hasChipTemp?"Ambient Temperature":"Dew Point", yAxis: 0, tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") }, color: dpColor, visible: dewPointMode }); if (hourly_minmax) { capMinMaxChartSeries = chart.addSeries({ name: CapName+' Highs/Lows', turboThreshold: 1e6, yAxis: 1, tooltip: { valueSuffix: " %" }, color: capRangeColor, visible: false, threshold: capBL, negativeColor: capBL? capRangeLowColor: capRangeColor }); if (!hideDewPoint) dpMinMaxChartSeries = chart.addSeries({ name: hasChipTemp?"Chip Highs/Lows":'Dew Point Highs/Lows', turboThreshold: 1e6, yAxis: 0, tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") }, color: dpRangeColor, visible: false }); } } function translateCapVal(val) { return val; } function translateDpVal(val, temp) { if (hasChipTemp) return translateTempVal(val); else{ val = dewPoint(val, temp); if (temp_unit == 1) val = val * 9.0 / 5.0 + 32.0; } return val; } function translateTempVal(val) { return temp_unit==1? val* 9.0 / 5.0 + 32.0 : val; } // baseDate is Date.parse(date_string) function processOneDay(day, baseDate, prepend) { var avg = 0; var tods = day.tods_base64 != null ? new Uint32Array(new Uint8Array([...atob(day.tods_base64)].map(c => c.charCodeAt(0))).buffer) : day.tods; var count = tods ? tods.length : 24; var capMin = 100, capMax = 0, tempMin = temp_unit == 1 ? 220 : 115, tempMax = temp_unit == 1 ? -28.0 : -40.0; var dpMin = tempMin, dpMax = tempMax; var luxMin = 80000, luxMax = 0; var temps = day.temps_base64 != null ? new Float64Array(new Uint8Array([...atob(day.temps_base64)].map(c => c.charCodeAt(0))).buffer) : day.temps; var caps = day.caps_base64!=null ? new Float32Array(new Uint8Array([...atob(day.caps_base64)].map(c => c.charCodeAt(0))).buffer): day.caps; var lux= day.lux_base64!=null ? new Float64Array(new Uint8Array([...atob(day.lux_base64)].map(c => c.charCodeAt(0))).buffer): day.lux; for (var i = 0; i < count; i++) { var n = prepend ? count - i - 1 : i; var addDiscon = false; if (temps[n] != 0.0) { var val = translateTempVal( temps[n]); var dataPoint = [baseDate + (tods ? tods[n] * 1000 : HOUR * (n+0.5)), val]; if (dataPoint[0] > dataRange.max) dataRange.max = dataPoint[0]; avg+=val; if(tods){ addRawDataPoint(dataPoint, prepend, rawTempSeries, chart.series[0]); } else { if (tempSeries.length > 0 && (crossDisconBoundary(tempSeries[tempSeries.length - 1][0], dataPoint[0]) || dataPoint[0] - tempSeries[tempSeries.length - 1][0] > 5 * HOUR )) { tempSeries.push([dataPoint[0] - 1000, null]); addDiscon = true; } tempSeries.push(dataPoint); if (day.temps_min_base64 != null || day.temps_min != null) { if (addDiscon) tempMinMaxSeries.push([dataPoint[0] - 1000, null, null]); var temps_min = day.temps_min_base64 != null ? new Float64Array(new Uint8Array([...atob(day.temps_min_base64)].map(c => c.charCodeAt(0))).buffer) : day.temps_min; var temps_max = day.temps_max_base64 != null ? new Float64Array(new Uint8Array([...atob(day.temps_max_base64)].map(c => c.charCodeAt(0))).buffer) : day.temps_max; dataPoint = [dataPoint[0], translateTempVal(temps_min[n]), translateTempVal(temps_max[n])]; tempMinMaxSeries.push(dataPoint); tempMax = Math.max(tempMax, dataPoint[2]); tempMin = Math.min(tempMin, dataPoint[1]); } else { tempMax = Math.max(tempMax, val); tempMin = Math.min(tempMin, val); } } } if (caps[n] != -99) { if (capChartSeries == null) createCapAxis(); var dataPoint = [baseDate + (tods ? tods[n] * 1000 : HOUR * (n + 0.5)), translateCapVal(caps[n])]; var dataPointDp = [dataPoint[0], translateDpVal(caps[n], temps[n])]; if (tods) { addRawDataPoint(dataPoint, prepend, rawCapSeries, capChartSeries); if(!hideDewPoint) addRawDataPoint(dataPointDp, prepend, rawDpSeries, dpChartSeries); } else { if (addDiscon) { capSeries.push([dataPoint[0] - 1000, null]); if(!hideDewPoint) dpSeries.push([dataPoint[0] - 1000, null]); } capSeries.push(dataPoint); if(!hideDewPoint) dpSeries.push(dataPointDp); if (day.caps_min_base64 != null || day.caps_min != null) { if (addDiscon) { capMinMaxSeries.push([dataPoint[0] - 1000, null, null]); if(!hideDewPoint) dpMinMaxSeries.push([dataPoint[0] - 1000, null, null]); } var caps_min = day.caps_min_base64 != null ? new Float32Array(new Uint8Array([...atob(day.caps_min_base64)].map(c => c.charCodeAt(0))).buffer) : day.caps_min; var caps_max = day.caps_max_base64 != null ? new Float32Array(new Uint8Array([...atob(day.caps_max_base64)].map(c => c.charCodeAt(0))).buffer) : day.caps_max; dataPoint = [dataPoint[0], translateCapVal(caps_min[n]), translateCapVal(caps_max[n])]; capMinMaxSeries.push(dataPoint); dataPointDp = [dataPoint[0], translateDpVal(caps_min[n], temps[n]), translateDpVal(caps_max[n], temps[n])]; if(!hideDewPoint) dpMinMaxSeries.push(dataPointDp); capMax = Math.max(capMax, dataPoint[2]); capMin = Math.min(capMin, dataPoint[1]); dpMax = Math.max(dpMax, dataPointDp[2]); dpMin = Math.min(dpMin, dataPointDp[1]); } else { capMax = Math.max(capMax, dataPoint[1]); capMin = Math.min(capMin, dataPoint[1]); dpMax = Math.max(dpMax, dataPointDp[1]); dpMin = Math.min(dpMin, dataPointDp[1]); } } } if (lux != null && lux[n] > 0) { if (luxChartSeries == null) createLuxAxis(); var dataPoint = [baseDate + (tods ? tods[n] * 1000 : HOUR * (n + 0.5)), lux[n]]; if (tods) { addRawDataPoint(dataPoint, prepend, rawLuxSeries, luxChartSeries); } else { /*var addDiscon = false; if (luxSeries.length > 0 && (dataPoint[0] - luxSeries[luxSeries.length - 1][0] > 5 * HOUR || crossDisconBoundary(luxSeries[luxSeries.length - 1][0], dataPoint[0]))) { luxSeries.push([dataPoint[0] - 1000, null]); addDiscon = true; }*/ if (addDiscon) luxSeries.push([dataPoint[0] - 1000, null]); luxSeries.push(dataPoint); if (day.lux_min_base64 != null || day.lux_min != null) { if (addDiscon) luxMinMaxSeries.push([dataPoint[0] - 1000, null, null]); var lux_min = day.lux_min_base64 != null ? new Float64Array(new Uint8Array([...atob(day.lux_min_base64)].map(c => c.charCodeAt(0))).buffer) : day.lux_min; var lux_max = day.lux_max_base64 != null ? new Float64Array(new Uint8Array([...atob(day.lux_max_base64)].map(c => c.charCodeAt(0))).buffer) : day.lux_max; dataPoint = [dataPoint[0], lux_min[n], lux_max[n]]; luxMinMaxSeries.push(dataPoint); luxMax = Math.max(luxMax, dataPoint[2]); luxMin = Math.min(luxMin, dataPoint[1]); } else { luxMax = Math.max(luxMax, dataPoint[1]); luxMin = Math.min(luxMin, dataPoint[1]); } } } } avg/=count; if (!tods) { if (bandTempSeries.length > 0 && baseDate + HOUR*12 - bandTempSeries[bandTempSeries.length - 1][0] > 25 * HOUR) { addDiscon = true; bandTempSeries.push([baseDate, null, null]); } else addDiscon = false; //int bandi= findBandArrayIndexFor(baseDate,bandTempSeries); if (capSeries.length > 0) { if (addDiscon) { bandCapSeries.push([baseDate, null, null]); bandDpSeries.push([baseDate, null, null]); } bandCapSeries.push([baseDate + HOUR * 12, capMin, capMax]); bandDpSeries.push([baseDate + HOUR * 12, dpMin, dpMax]); } if (luxSeries.length > 0) { if (addDiscon) bandLuxSeries.push([baseDate, null,null]); bandLuxSeries.push([baseDate + HOUR * 12, luxMin, luxMax]); } bandTempSeries.push( [baseDate+HOUR*12, tempMin, tempMax]); } return avg; } var tempColor = "#CC2200", tempRangeColor="#FF8866", tempLowColor="#0066cc", tempRangeLowColor="#66b3ff"; var capColor = "#007F00", capRangeColor = "#66E566", capLowColor="#F7A35C", capRangeLowColor="#fac294"; var dpColor = "#00AACC", dpRangeColor = "#26D0F2"; var luxColor = "#60A0DF", luxRangeColor = "#bfd9f2", luxLowColor = "#222", luxRangeLowColor = "#aaa"; function dataLoader(fromDate, toDate, onData){ $.ajax({ url: WSROOT + (isUUID ? "ethLogShared.asmx/GetStats"+(hasALS?"Lux":"")+"RawByUUID" : "ethLogs.asmx/GetStats"+(hasALS?"Lux":"")+"Raw"), data: JSON.stringify(isUUID? {uuid: slaveId, fromDate: fromDate, toDate: toDate} : { id: slaveId, fromDate: fromDate, toDate: toDate }), success: function (retval, textStatus) { onData(retval.d); }, error: function (xhr, textStatus, exception) { ajaxErrorHandler(xhr, exception); } }); } function updateSeries(series,data,type) { if(series) { if(series.type!=type) series.update({type: type},false); if(data!=null) series.setData(data,false); } } function updateChartType(zoomLevel) { if (zoomLevel > ChartZoomLevelNormal) { updateSeries(chart.series[0], bandTempSeries, "arearange" ); if (capChartSeries!=null) updateSeries(capChartSeries, bandCapSeries, "arearange"); if (dpChartSeries!=null) updateSeries(dpChartSeries, bandDpSeries, "arearange"); if (luxChartSeries != null) updateSeries(luxChartSeries, bandLuxSeries, "arearange"); if (hourly_minmax) { tempMinMaxChartSeries.hide(); if (capMinMaxChartSeries != null) capMinMaxChartSeries.hide(); if (dpMinMaxChartSeries != null) dpMinMaxChartSeries.hide(); if (luxMinMaxChartSeries != null) luxMinMaxChartSeries.hide(); } chart.redraw(); } else if (zoomLevel == ChartZoomLevelNormal) { if (hourly_minmax) { updateSeries(chart.series[0], tempSeries, "line"); if (capChartSeries != null) updateSeries(capChartSeries, capSeries, "line"); if (dpChartSeries != null) updateSeries(dpChartSeries, dpSeries, "line"); if (luxChartSeries != null) updateSeries(luxChartSeries, luxSeries, "line"); updateSeries(tempMinMaxChartSeries, tempMinMaxSeries, "arearange"); tempMinMaxChartSeries.show(); if (capMinMaxChartSeries != null) { updateSeries(capMinMaxChartSeries, capMinMaxSeries, "arearange"); if (capChartSeries.visible) capMinMaxChartSeries.show(); } if (dpMinMaxChartSeries != null) { updateSeries(dpMinMaxChartSeries, dpMinMaxSeries, "arearange"); if (dpChartSeries.visible) dpMinMaxChartSeries.show(); } if (luxMinMaxChartSeries != null) { updateSeries(luxMinMaxChartSeries, luxMinMaxSeries, "arearange"); luxMinMaxChartSeries.show(); } } else { updateSeries(chart.series[0], tempSeries, "line"); if (capChartSeries!=null) updateSeries(capChartSeries, capSeries, "line"); if (dpChartSeries!=null) updateSeries(dpChartSeries, dpSeries, "line"); if (luxChartSeries != null) updateSeries(luxChartSeries, luxSeries, "line"); } chart.redraw(); } else { updateSeries(chart.series[0], null, "line" ); if (capChartSeries!=null) updateSeries(capChartSeries, null, "line"); if (dpChartSeries!=null) updateSeries(dpChartSeries, null, "line"); if (luxChartSeries != null) updateSeries(luxChartSeries, null, "line"); if (hourly_minmax) { tempMinMaxChartSeries.hide(); if (capMinMaxChartSeries != null) capMinMaxChartSeries.hide(); if (dpMinMaxChartSeries != null) dpMinMaxChartSeries.hide(); if (luxMinMaxChartSeries != null) luxMinMaxChartSeries.hide(); } chart.redraw(); } } function loadMarkers() { chart.yAxis.forEach(function (axis) { var series = null; for (var i = 0; i < axis.series.length; i++) { if (axis.series[i].visible) { series = axis.series[i]; break; } } if (series == null) return; $.ajax({ url: WSROOT + (isUUID ? "ethLogShared.asmx/LoadMarkers" : "ethLogs.asmx/LoadMarkers"), data: JSON.stringify({ "id": slaveId, "type": axisTitle2Type[axis.options.title.text] }), success: function (retval, textStatus) { retval.d.forEach(function (m) { restoreMarker(series, m); }); }, error: function (xhr, textStatus, exception) { } }); }); } function clearHourlyData() { tempSeries = []; tempMinMaxSeries = []; capSeries = []; capMinMaxSeries = []; dpSeries = []; dpMinMaxSeries = []; luxSeries = []; luxMinMaxSeries = []; bandCapSeries = []; bandDpSeries = []; bandTempSeries = []; bandLuxSeries = []; } function clearCachedRawData() { rawTempSeries.length = 0; rawCapSeries.length = 0; rawDpSeries.length = 0; rawLuxSeries.length = 0; chart.series[0].setData([]); if (capChartSeries!=null) capChartSeries.setData([]); if (dpChartSeries!=null) dpChartSeries.setData([]); if (luxChartSeries != null) luxChartSeries.setData([]); } function loadCachedRawData() { chart.series[0].setData(rawTempSeries); if (capChartSeries != null) capChartSeries.setData(rawCapSeries); if (dpChartSeries != null) dpChartSeries.setData(rawDpSeries); if (luxChartSeries != null) luxChartSeries.setData(rawLuxSeries); } var disconProcessed = false; function adjDiscon(d, deltaX) { if (d[2] + deltaX <= d[0]) return false; if (d[3] + deltaX >= d[1]) return false; $.ajax({ url: WSROOT + "ethLogs.asmx/UpdateDiscon", data: JSON.stringify({ "id": slaveId, "begin": dateToFileTime2(d[0]), "end": dateToFileTime2(d[1]), "offsetSec": Math.round((d[2]+deltaX-d[0])/1000)}), success: function (retval, textStatus) { d[2] += deltaX; d[3] += deltaX; [tempSeries, capSeries, dpSeries, luxSeries].forEach(function (series) { if (series != null) { series.forEach(function (dp) { if (dp[0] > d[0] && dp[0] < d[1]) dp[0] = dp[0] + deltaX; }); } }); if (hourly_minmax) { [tempMinMaxSeries, capMinMaxSeries,dpMinMaxSeries, luxMinMaxSeries].forEach(function (series) { if (series != null) { series.forEach(function (dp) { if (dp[0] > d[0] && dp[0] < d[1]) dp[0] = dp[0] + deltaX; }); } }); } //updateChartType(zoomLevel); chart.series.forEach(function (series) { series.data.forEach(function (dp) { if (dp.x > d[0] && dp.x < d[1]) { dp.update({ x: dp.x + deltaX }, false, false); } }); }); chart.xAxis[0].removePlotBand(d[0] + "L"); chart.xAxis[0].removePlotBand(d[0] + "R"); addDisconButtonPlotband(d); //chart.xAxis[0].update(); //chart.xAxis[0].isDirty = true; chart.redraw(); }, error: function (xhr, textStatus, exception) { ajaxErrorHandler(xhr, exception); } }); return true; } function addDisconButtonPlotband(d) { chart.xAxis[0].addPlotBand({ id: d[0]+"L", color: 'rgba(255,170,170,1)', from: d[0], to: d[2], label: { text: '<', verticalAlign: 'middle' }, events: { click: function (e) { adjDiscon(d, Math.max(10000, ((d[1] - d[0]) - (d[3] - d[2])) / 100) * -1); }, mouseover: function (e) { if (d[2] > d[0]) this.svgElem.attr('fill', 'rgba(255,170,170,.5)'); }, mouseout: function (e) { this.svgElem.attr('fill', this.options.color); } } }); chart.xAxis[0].addPlotBand({ id: d[0]+"R", color: 'rgba(255,170,170,1)', from: d[3], to: d[1], label: { text: '>', verticalAlign: 'middle' }, events: { click: function (e) { adjDiscon(d, Math.max(10000, ((d[1] - d[0]) - (d[3] - d[2])) / 100)); }, mouseover: function (e) { if (d[3] < d[1]) this.svgElem.attr('fill', 'rgba(255,170,170,.5)'); }, mouseout: function (e) { this.svgElem.attr('fill', this.options.color); } } }); } function processDiscon() { discons.forEach(function (d, i) { if (!disconProcessed && d[2] && d[3]) { chart.xAxis[0].addPlotBand({ color: 'rgba(255,170,170,.1)', from: d[0], to: d[1] }); disconProcessed = true; if (isUUID) return; // val moves from d[0] to d[2]-d[0]+(d[1]-d[3])+d[0] = d[2]+d[1]-d[3] addDisconButtonPlotband(d); } }); } var chartTitle=tagName.replace(/\+/g," ")+" Temperature"+((!hasChipTemp&&capSeries.length>0)? ("/"+CapName):"")+" Charts"; $("#stat_title").text(chartTitle); function createChart(buttons, lang) { var options = { title: {text: null}, subtitle: { text: isTouchScreendevice()? null: "Drag to zoom, hold Shift key and drag to pan" }, animation: false, chart: { zoomType: 'x', panning: true, panKey: 'shift', style: { fontFamily: "ProximaNovaLight, Arial" }, type: "line", events: { redraw: onChartRedraw, selectedpoints: function (event) { var fromDateString = new Date(event.from).toLocaleString(); var toDateString = new Date(event.to).toLocaleString(); var num_visible_series = chart.series.length / (hourly_minmax ? 2 : 1); var btn = $("#deleteTemperatureLogBtn"); btn.simpledialog({ mode: 'bool', prompt: "Permanently delete all temperature/RH log data between " + fromDateString + " and " + toDateString + (zoomLevel == ChartZoomLevelRaw ? " ("+Math.ceil(event.points.length/num_visible_series )+" points)?":"?"), useModal: true, forceInput: true, cleanOnClose: true, 'buttons': { 'Yes': { click: function () { var oldhtml = show_finding(btn, "Deleting...") $.ajax({ url: WSROOT + "ethLogs.asmx/DeleteTemperatureData2", data: JSON.stringify({ id: slaveId, fromFT: dateToFileTime2(event.from ), toFT: dateToFileTime2(event.to ) }), complete: function () { restore_finding(btn, oldhtml); select_to_delete_mode = false; }, success: function (retval) { Highcharts.each(event.points, function (point) { point.update(null, false, false); }); chart.redraw(); popup(retval.d + " records permanently removed.", null); } }); }, icon: "forward" }, 'Cancel': { click: function () { Highcharts.each(event.points, function (point) { point.select(false); }); select_to_delete_mode = false; }, icon: "back" } } }); }, click: function () { if (select_to_delete_mode) { var points = this.getSelectedPoints(); if (points.length > 0) { Highcharts.each(points, function (point) { point.select(false); }); select_to_delete_mode = false; } } }, selection: function (e) { if (select_to_delete_mode) { Highcharts.each(this.series, function (series) { if (series.points != null) { Highcharts.each(series.points, function (point) { if (point == null) return; if (point.x >= e.xAxis[0].min && point.x <= e.xAxis[0].max) { point.select(true, true); } }); } }); Highcharts.fireEvent(this, 'selectedpoints', { points: this.getSelectedPoints(), from: e.xAxis[0].min, to: e.xAxis[0].max }); return false; // don't zoom } else { updateZoom(this, e.xAxis ? e.xAxis[0] : dataRange, function () { }); } } } }, xAxis: { type: 'datetime', events: { afterSetExtremes: function () { processDiscon(); }, 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-1000).toISOString().substring(0, 10)); } catch (e) { } } } }, yAxis: [{ title: { text: 'Temperature' }, allowDecimals: false, labels: { format: '{value}°' + (temp_unit == 1 ? "F" : "C") } }], tooltip: { borderColor: "gray", animation:false, valueDecimals: 1, crosshairs: true, shared: true, footerFormat:old_style_query?"<br/><b>.</b>": "<br/><b>Press M to place a marker...</b>", followPointer: false, style: { pointerEvents: 'all' }, dateTimeLabelFormats: { second: "%A, %b %e, %I:%M:%S %p", } }, legend: { layout: 'vertical', align: 'left', x: 80, verticalAlign: 'top', y: 40, floating: true, backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF' }, plotOptions: { series: { marker: { enabled: false } } }, series: [{ name: 'Temperature', tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") }, events: { afterAnimate: onAnimationEnd }, color: tempColor, threshold: tempBL ? translateTempVal(tempBL) : (temp_unit == 1 ? 32 : 0), negativeColor: tempLowColor }], lang: lang, exporting: { libURL:"https://code.highcharts.com/6.1.0/lib", chartOptions: { title: {text: chartTitle}, subtitle: { text: null }, chart: { width: $(window).width(), height: $(window).height() - 42 } }, filename: chartTitle, buttons: buttons } }; if (hasALS){ options.exporting.buttons["luxAxisButton"] = { text: "Log/Linear Scale", _titleKey: "logScale", onclick: function () { if (chart.yAxis[luxAxisIndex].options.type == "linear") chart.yAxis[luxAxisIndex].update({ type: "logarithmic" }); else chart.yAxis[luxAxisIndex].update({ type: "linear" }); } }; } holder.highcharts(options); var menuItems = Highcharts.getOptions().exporting.buttons.contextButton.menuItems; menuItems.unshift({ separator: true }); for (button in options.exporting.buttons) menuItems.unshift(options.exporting.buttons[button]); resizeChart(); chart = holder.highcharts(); if (hourly_minmax) { tempMinMaxChartSeries = chart.addSeries({ name: 'Temperature Highs/Lows', turboThreshold: 1e6, tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") }, visible: false, color: tempRangeColor, threshold: tempBL ? translateTempVal(tempBL) : (temp_unit == 1 ? 32 : 0), negativeColor: tempRangeLowColor }); } $("#moreButtons").show(); $(window).resize(function () { resizeChart(); processDiscon(); }); if (localStorage["moreButtonTipSeen"] != "1") { $(window).scroll(function () { function elementScrolled(elem) { var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elemTop = $(elem).offset().top; return ((elemTop <= docViewBottom) && (elemTop >= docViewTop)); } if (elementScrolled('#moreButtons')) { $("#moreButtonTip").hide(); localStorage["moreButtonTipSeen"] = "1"; } }); } } $("#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 updateTempUnit(d) { if (temp_unit == -1) temp_unit = d.temp_unit; chart.yAxis[0].update({ labels: { format: '{value}°' + (temp_unit == 1 ? "F" : "C") } }); chart.series[0].update({ tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") } }); if (dpChartSeries!=null) dpChartSeries.update({ tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") } }); if (tempMinMaxChartSeries!=null) tempMinMaxChartSeries.update({ tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") } }); if (dpMinMaxChartSeries!=null) dpMinMaxChartSeries.update({ tooltip: { valueSuffix: "°" + (temp_unit == 1 ? "F" : "C") } }); } var hourlyDataCache = {}; function hourlyDataLoader(onData) { if (hourlyDataCache[since_calibration] != null) { onData(hourlyDataCache[since_calibration]); return; } $.ajax({ url: WSROOT + (isUUID ? "ethLogShared.asmx/GetTemperature" + (hasALS ? "Lux" : "") + "StatsByUUID3" : "ethLogs.asmx/GetTemperature" + (hasALS ? "Lux" : "") + "Stats3"), data: JSON.stringify({ "id": slaveId , "withMinMax": hourly_minmax, "sinceLastCalibration": since_calibration}), success: function (retval, textStatus) { hourlyDataCache[since_calibration] = retval.d.temps; onData(retval.d.temps); updateTempUnit(retval.d); }, error: function (xhr, textStatus, exception) { ajaxErrorHandler(xhr, exception); } }); } var axisTitle2Type = { "Temperature": "temperature", "Humidity": "cap", "Lux": "light", "VOC": "tvoc" }; function saveMarker(series, filetime, comment) { $.ajax({ url: WSROOT + "ethClient.asmx/MergeMarker", data: JSON.stringify({ id: slaveId, filetime: filetime, type: axisTitle2Type[series.yAxis.options.title.text], comment: comment }) }); } function deleteMarker(series, filetime) { $.ajax({ url: WSROOT + "ethClient.asmx/DeleteMarker", data: JSON.stringify({ id: slaveId, filetime: filetime, type: axisTitle2Type[series.yAxis.options.title.text] }) }); } var tempBL, capBL, luxBL; function dataSpanLoader(onData) { $.ajax({ url: WSROOT + (isUUID ? "ethLogShared.asmx/GetMultiTagStatsSpanByUUIDs2" : "ethLogs.asmx/GetMultiTagStatsSpan2"), data: JSON.stringify({ "ids": [slaveId], "type": "temperature", "sinceLastCalibration": since_calibration }), success: function (retval, textStatus) { tempBL = retval.d.tempBL; capBL = retval.d.capBL; luxBL = retval.d.luxBL; temp_unit = retval.d.temp_unit; tzo = retval.d.tzo; onData(fileTimeToDate(retval.d.from).getTime(), fileTimeToDate(retval.d.to).getTime()); if (retval.d.discons != null && retval.d.discons[0] != null) { discons = retval.d.discons[0].map(function (d) { return [fileTimeToDate(d[0]).getTime(), fileTimeToDate(d[1]).getTime()]; }); // only support first tag discon. } }, error: function (xhr, textStatus, exception) { ajaxErrorHandler(xhr, exception); } }); } var select_to_delete_mode = false; function deleteOptions(btn) { if (btn.val() == "2") { $("#deleteTemperatureLogBtn").simpledialog({ mode: 'bool', prompt: "This operation will permanently delete all temperature log data of this tag. Do you want to continue?", useModal: true, forceInput: true, cleanOnClose: true, 'buttons': { 'Yes': { click: function () { var oldhtml = show_finding(btn, "Deleting...") $.ajax({ url: WSROOT + "ethLogs.asmx/DeleteTemperatureData", data: "{id: " + slaveId + "}", complete: function () { restore_finding(btn, oldhtml); }, success: function (retval) { holder.empty(); holder.html("<center>" + retval.d + " records permanently removed.</center>"); } }); }, icon: "forward" }, 'Cancel': { click: function () { }, icon: "back" } } }); } else { popup("Please drag to select date range / points to be deleted. ", null); select_to_delete_mode = true; } } loadFirstData(); function attemptRestoreMarker(s, m) { if (!s.points) return; for (var i = 1; i < s.points.length; i++) { if (s.points[i - 1].x <= m.date && m.date < s.points[i].x) { var vp = []; chart.series.forEach(function (cs) { if (cs.visible && cs.points != null && cs.points[i-1]) { if(cs.points[i-1].x == s.points[i-1].x ) vp.push(cs.points[i - 1]); } }); chart.tooltip.refresh(vp); if (chart.hoverPoints != null && chart.hoverPoint == null) chart.hoverPoints.forEach(function (hp) { if (hp.series == s) chart.hoverPoint = hp; }); createMarker(m.comment, m.filetime); chart.tooltip.hide(); return true; } } return false; } </script> </div> </body> </html>