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?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" type="text/javascript"></script> <script src="styles/highcharts-custom.js?11" 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" /> </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%; touch-action: pan-y; "> </div> <span id="moreButtonTip" onclick="moreButtonTipClicked();"><span></span>Scroll for Options</span> <center id="moreButtons" style="display:none;"> <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 & 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?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 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"); if (statType == "temperature" && temp_unit == 1) { for (var i = 0; i < editModeStack.length; i++) { editModeStack[i].y = (editModeStack[i].y - 32.0) * 5.0 / 9.0; } } $.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) { if (retval.d.deleted_count) { popup("Attempted to delete " + editModeStack.length + " data points, " + retval.d.deleted_count + " deleted. "); } 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 ); holder.highcharts().options.exporting.chartOptions.chart = { width: $(window).width(), height: $(window).height() - 42, marginTop: 50 }; } var holder = $("#tempStatGraphs"); var slaveIds, temp_unit; 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; } } } if (old_style_query) { 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" || statType == "dp") { //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" && statType!="tvoc") $("#tempCapLightOnlyOptions").hide(); } if (statType != "light" && statType!="tvoc") $("#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 (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 + "ethDownloadMultiStatsCSV.aspx?uuids=" + slaveIds.join(":") + "&type=" + statType + "&fromDate=" + $("#custom_min").val() + "&toDate=" + $("#custom_max").val() + "&useDegF=" + temp_unit; }); } else { $(".sharedOnly").hide(); $("#downloadLogBtn").click(function () { window.location = WSROOT + "ethDownloadMultiStatsCSV.aspx?ids=" + slaveIds.join(":") + "&type=" + statType + "&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": slaveIds, "type": statType=="motion"?"motion_new":statType }), 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>"+ "<div data-role='fieldcontain'><label for='embedHTML'>HTML to embed latest readings:</label><input type='text' id='embedHTML'>" + copy_icon + "<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 }, "dp": { name: "Dew Point", 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; if (val < 0) return NaN; return val; }, ymaxInit: function () { return 0.005; }, ymaxPost: function (ymax) { return ymax; }, yminInit: function () { return 30000; }, yminPost: function (ymin) { return ymin; }, decimals: 2 }, "tvoc": { name: "Volatile Organic Compound", unitGen: function () { return " ppm"; }, preProcess: function (val, i) { if (val <= 0) return NaN; return val; }, ymaxInit: function () { return 0; }, 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_base64 != null ? day.tods_base64.map(s => new Uint32Array(new Uint8Array([...atob(s)].map(c => c.charCodeAt(0))).buffer) ) : day.tods; for (var tagi = 0; tagi < tagCount; tagi++) { var tagv = day.values_base64!=null ? new Float64Array(new Uint8Array([...atob(day.values_base64[tagi])].map(c => c.charCodeAt(0))).buffer): 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) { addRawDataPoint(dataPoint, prepend, rawSeries, chart.series[$.inArray(tagId, arrayOfIds) * (hourly_minmax ? 2 : 1)]); } 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 { var addDiscon = false; if (series.length > 0 && dataPoint[0] - series[series.length - 1][0] > 5 * HOUR) { series.push([dataPoint[0] - 1000, null]); addDiscon = true; } series.push(dataPoint); if (minmaxSeries) { if (addDiscon) minmaxSeries.push([dataPoint[0] - 1000, null, null]); 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]; } if (bandSeries.length > 0 && dataPoint[0] - bandSeries[bandSeries.length - 1][0] > 25 * HOUR) { if (statType == "motion") bandSeries.push([dataPoint[0] - HOUR, null]); else bandSeries.push([ dataPoint[0] - HOUR, null, null]); } 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 id='loader_title'>Loading Data...</h1></div>"); $body = $("body"); $loader.appendTo($body).css({ top: 100 }); $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, null); } } 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); //chart.yAxis[0].update({ labels: { format: '{value}' + statTypeTranslation.unitGen() } }); }, 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" });*/ } } var markerLoaded = false; 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 loadMarkers() { arrayOfIds.forEach(function (tagId) { var series = chart.series[$.inArray(tagId, arrayOfIds) * (hourly_minmax ? 2 : 1)]; $.ajax({ url: WSROOT + (isUUID ? "ethLogShared.asmx/LoadMarkers" : "ethLogs.asmx/LoadMarkers"), data: JSON.stringify({ "id": isUUID ? slaveIds[tagId] : tagId, "type": statType }), success: function (retval, textStatus) { retval.d.forEach(function (m) { restoreMarker(series, m); }); }, error: function (xhr, textStatus, exception) { } }); }); } 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() { var step = (hourly_minmax ? 2 : 1); for (var i = 0; i < chart.series.length; i += step) { chart.series[i].setData(id2RawSeries[arrayOfIds[i/step]], i == chart.series.length - step); } setMotionRawYAxis(); } function saveMarker(series, filetime, comment) { if (-1 == chart.series.indexOf(series)) return; var step = (hourly_minmax ? 2 : 1); $.ajax({ url: WSROOT + "ethClient.asmx/MergeMarker", data: JSON.stringify({ id: isUUID? slaveIds[arrayOfIds[chart.series.indexOf(series)/step]] : arrayOfIds[chart.series.indexOf(series)/step], filetime: filetime, type: statType, comment: comment }) }); } function deleteMarker(series, filetime) { if (-1 == chart.series.indexOf(series)) return; var step = (hourly_minmax ? 2 : 1); $.ajax({ url: WSROOT + "ethClient.asmx/DeleteMarker", data: JSON.stringify({ id: isUUID? slaveIds[arrayOfIds[chart.series.indexOf(series)/step]] : arrayOfIds[chart.series.indexOf(series)/step], filetime: filetime, type: statType }) }); } //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"); 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', type: "line", style: { fontFamily: "ProximaNovaLight, Arial" }, events: { selection: function (event) { updateZoom(this,event.xAxis? event.xAxis[0]:dataRange,function() {}); }, redraw: onChartRedraw } }, xAxis: { type: 'datetime', 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 - 1000).toISOString().substring(0, 10)); } catch (e) { } } } }, yAxis: [{ title: { text: statTypeTranslation.name }, allowDecimals: statType == "batteryVolt", type: (statType == "light" || statType == "tvoc") ? "logarithmic" : "linear" } ], tooltip: { animation: false, borderColor: "gray", valueDecimals: statTypeTranslation.decimals, crosshairs: true, followPointer: false, footerFormat:old_style_query?"<br/><b>.</b>":"<br/><b>Press M to place a marker...</b>", style: {pointerEvents: 'all' }, dateTimeLabelFormats: { second: "%A, %b %e, %I:%M:%S %p", } }, plotOptions: { series: { events: { afterAnimate: onAnimationEnd }, marker: { enabled: false } }, 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: [], lang: lang, exporting: { libURL:"https://code.highcharts.com/6.1.0/lib", chartOptions: { subtitle: { text: null }, legend: { itemDistance: 50 }, chart: { width: $(window).width(), height: $(window).height() - 42 } }, filename: statTypeTranslation.name + " Charts", buttons: buttons } }; if (statType == "light" || statType == "tvoc") { options.exporting.buttons["luxAxisButton"] = { text: "Log/Linear Scale", _titleKey: "logScale", 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); 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(); $("#moreButtons").show(); $(window).resize(function () { resizeChart(); }); 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 updateMetadata(d) { for (var i = 0; i < d.ids.length; i++) { id2nameMapping[d.ids[i]] = d.names[i]; } tzo = d.tzo; if (temp_unit == -1) temp_unit = d.temp_unit; } 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); //chart.yAxis[0].update({ labels: { format: '{value}' + statTypeTranslation.unitGen() } }); }, 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()); chart.yAxis[0].update({ labels: { format: '{value}' + statTypeTranslation.unitGen() } }); }, error: function (xhr, textStatus, exception) { ajaxErrorHandler(xhr, exception); } }); } 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) { s.points[i - 1].onMouseOver(); createMarker(m.comment, m.filetime); chart.tooltip.hide(); return true; } } return false; } </script> </div> </body> </html>