From 900f2511c700d28f3666cf501e4030a16ee7f4aa Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Mon, 20 Apr 2026 21:54:47 +0100 Subject: [PATCH 01/19] split out js for readability --- embed.php | 5 + graph.csv.js | 125 +++++ graph.editor.js | 456 ++++++++++++++++ graph.histogram.js | 98 ++++ graph.js | 1276 -------------------------------------------- graph.legend.js | 89 +++ graph.saved.js | 448 ++++++++++++++++ graph.utils.js | 72 +++ view.php | 6 + 9 files changed, 1299 insertions(+), 1276 deletions(-) create mode 100644 graph.csv.js create mode 100644 graph.editor.js create mode 100644 graph.histogram.js create mode 100644 graph.legend.js create mode 100644 graph.saved.js create mode 100644 graph.utils.js diff --git a/embed.php b/embed.php index 688b9f7..e3eca3b 100644 --- a/embed.php +++ b/embed.php @@ -99,7 +99,12 @@ + + + + + + + + + + + - - - - - - - - - - - - - -
- - -
- -

Data viewer

- - - - - - - -
-
-
- - -
-
- - - - - - From 5ca2f93310af43a5781a52611c4be2e425bb34e8 Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Tue, 21 Apr 2026 08:01:20 +0100 Subject: [PATCH 03/19] =?UTF-8?q?refactor:=20phase=201=20cleanup=20-=20ext?= =?UTF-8?q?ract=20HTML=20builders,=20consolidate=20inline=20scripts,=20var?= =?UTF-8?q?=E2=86=92const/let?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- graph.csv.js | 108 ++++++++----------- graph.editor.js | 175 +++++++++++++++--------------- graph.histogram.js | 124 ++++++++++----------- graph.js | 264 +++++++++++++++++++++------------------------ graph.saved.js | 40 +++---- view.php | 142 ++++++++++++------------ vis.helper.js | 92 ++++++++-------- 7 files changed, 444 insertions(+), 501 deletions(-) diff --git a/graph.csv.js b/graph.csv.js index 7c2528d..c494a4e 100644 --- a/graph.csv.js +++ b/graph.csv.js @@ -4,93 +4,71 @@ function printcsv() { - if (typeof(feedlist[0]) === "undefined" ) {return}; + if (typeof feedlist[0] === "undefined") return; - var timeformat = $("#csvtimeformat").val(); - var nullvalues = $("#csvnullvalues").val(); - var headers = $("#csvheaders").val(); + const timeformat = $("#csvtimeformat").val(); + const nullvalues = $("#csvnullvalues").val(); + const headers = $("#csvheaders").val(); - var csvout = ""; + let csvout = ""; - var value = []; - var line = []; - var lastvalue = []; - var start_time = feedlist[0].data[0][0]; - var end_time = feedlist[feedlist.length-1].data[feedlist[feedlist.length-1].data.length-1][0]; - var showName=false; - var showTag=false; + const value = []; + let line = []; + const lastvalue = []; + const start_time = feedlist[0].data[0][0]; + let showName = false; + let showTag = false; switch (headers) { - case "showNameTag": - showName=true; - showTag=true; - break; - case "showName": - showName=true; - break; + case "showNameTag": showName = true; showTag = true; break; + case "showName": showName = true; break; } - if (showName || showTag ) { + if (showName || showTag) { switch (timeformat) { - case "unix": - line = ["Unix timestamp"]; - break; - case "seconds": - line = ["Seconds since start"]; - break; - case "datestr": - line = ["Date-time string"]; - break; + case "unix": line = ["Unix timestamp"]; break; + case "seconds": line = ["Seconds since start"]; break; + case "datestr": line = ["Date-time string"]; break; } - - for (var f in feedlist) { - line.push((showTag ? feedlist[f].tag : "")+(showTag && showName ? ":" : "")+(showName ? feedlist[f].name : "")); + for (const f in feedlist) { + line.push((showTag ? feedlist[f].tag : "") + (showTag && showName ? ":" : "") + (showName ? feedlist[f].name : "")); } - csvout = "\"" + line.join("\", \"")+"\"\n"; + csvout = '"' + line.join('", "') + '"\n'; } - for (var z in feedlist[0].data) { + for (const z in feedlist[0].data) { line = []; - // Different time format options for csv output - if (timeformat=="unix") { + if (timeformat === "unix") { line.push(Math.round(feedlist[0].data[z][0] / 1000)); - } else if (timeformat=="seconds") { - line.push(Math.round((feedlist[0].data[z][0]-start_time)/1000)); - } else if (timeformat=="datestr") { - // Create date time string - var t = new Date(feedlist[0].data[z][0]); - var year = t.getFullYear(); - var month = t.getMonth()+1; - if (month<10) month = "0"+month; - var day = t.getDate(); - if (day<10) day = "0"+day; - var hours = t.getHours(); - if (hours<10) hours = "0"+hours; - var minutes = t.getMinutes(); - if (minutes<10) minutes = "0"+minutes; - var seconds = t.getSeconds(); - if (seconds<10) seconds = "0"+seconds; - - var formatted = year+"-"+month+"-"+day+" "+hours+":"+minutes+":"+seconds; - line.push(formatted); + } else if (timeformat === "seconds") { + line.push(Math.round((feedlist[0].data[z][0] - start_time) / 1000)); + } else if (timeformat === "datestr") { + const t = new Date(feedlist[0].data[z][0]); + const year = t.getFullYear(); + const month = String(t.getMonth() + 1).padStart(2, '0'); + const day = String(t.getDate()).padStart(2, '0'); + const hours = String(t.getHours()).padStart(2, '0'); + const minutes = String(t.getMinutes()).padStart(2, '0'); + const seconds = String(t.getSeconds()).padStart(2, '0'); + line.push(`${year}-${month}-${day} ${hours}:${minutes}:${seconds}`); } - var nullfound = false; - for (var f in feedlist) { - if (value[f]==undefined) value[f] = null; + let nullfound = false; + for (const f in feedlist) { + if (value[f] === undefined) value[f] = null; lastvalue[f] = value[f]; - if (feedlist[f].data[z]!=undefined) { - if (feedlist[f].data[z][1]==null) nullfound = true; - if (feedlist[f].data[z][1]!=null || nullvalues=="show") value[f] = feedlist[f].data[z][1]; - if (value[f]!=null) value[f] = (value[f]*1.0).toFixed(feedlist[f].dp); - line.push(value[f]+""); + if (feedlist[f].data[z] !== undefined) { + if (feedlist[f].data[z][1] === null) nullfound = true; + if (feedlist[f].data[z][1] !== null || nullvalues === "show") value[f] = feedlist[f].data[z][1]; + if (value[f] !== null) value[f] = (value[f] * 1.0).toFixed(feedlist[f].dp); + line.push(value[f] + ""); } } - if (nullvalues=="remove" && nullfound) { + if (nullvalues === "remove" && nullfound) { // pass } else { - csvout += line.join(", ")+"\n"; + csvout += line.join(", ") + "\n"; } } $("#csv").val(csvout); diff --git a/graph.editor.js b/graph.editor.js index eb9f809..0172e97 100644 --- a/graph.editor.js +++ b/graph.editor.js @@ -3,6 +3,49 @@ // Only loaded by view.php (not embed.php) //---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +// buildFeedSelectorHTML - builds the HTML for the sidebar feed selector table +// Returns { html, feedsbytag, numberoftags } +//---------------------------------------------------------------------------------------- +function buildFeedSelectorHTML(feeds) { + const feedsbytag = {}; + let numberoftags = 0; + for (const feed of feeds) { + if (feedsbytag[feed.tag] === undefined) { + feedsbytag[feed.tag] = []; + numberoftags++; + } + feedsbytag[feed.tag].push(feed); + } + + let out = ` + + + + `; + + for (const tag in feedsbytag) { + const tagname = tag || 'undefined'; + out += ` + + ${tagname} + + + `; + for (const feed of feedsbytag[tag]) { + let name = feed.name; + if (name && name.length > 20) name = name.substr(0, 20) + '..'; + out += ` + ${name} + + + `; + } + out += ''; + } + return { html: out, feedsbytag, numberoftags }; +} + //---------------------------------------------------------------------------------------- // Side bar feed selector and events associated with editor only, not loaded in embed mode //---------------------------------------------------------------------------------------- @@ -10,48 +53,8 @@ function graph_init_editor() { if (!feeds) feeds = feedlist; - var numberoftags = 0; - feedsbytag = {}; - for (var z in feeds) { - if (feedsbytag[feeds[z].tag]==undefined) { - feedsbytag[feeds[z].tag] = []; - numberoftags++; - } - feedsbytag[feeds[z].tag].push(feeds[z]); - } - // Draw sidebar feed selector ------------------------------------------- - - var out = ""; - out += ""; - out += ""; - out += ""; - out += ""; - out += ""; - - for (var tag in feedsbytag) { - tagname = tag; - if (tag=="") tagname = "undefined"; - out += ""; - out += ""; - out += ""+tagname+""; - out += ""; - out += ""; - out += ""; - for (var z in feedsbytag[tag]) - { - out += ""; - var name = feedsbytag[tag][z].name; - if (name && name.length>20) { - name = name.substr(0,20)+".."; - } - out += ""+name+""; - out += ""; - out += ""; - out += ""; - } - out += ""; - } + const { html, feedsbytag, numberoftags } = buildFeedSelectorHTML(feeds); // --------------------------------------------------------------- // Writting direct to the menu system here @@ -61,14 +64,14 @@ function graph_init_editor() // 2. Clear original hidden element $("#sidebar_html").html(""); // 3. Populate with feed list selector - $("#feeds").html(out); + $("#feeds").html(html); // 4. Show l3 menu - if (menu.width>=576) menu.show_l3(); + if (menu.width >= 576) menu.show_l3(); // 5. Enable l3 menu so that collapsing and re-expanding works - if (menu.obj.setup!=undefined) { - if (menu.obj.setup.l2!=undefined) { - if (menu.obj.setup.l2.graph!=undefined) { - menu.obj.setup.l2.graph.l3 = [] + if (menu.obj.setup != undefined) { + if (menu.obj.setup.l2 != undefined) { + if (menu.obj.setup.l2.graph != undefined) { + menu.obj.setup.l2.graph.l3 = []; menu.active_l3 = true; } } @@ -76,7 +79,7 @@ function graph_init_editor() if (session_write) load_saved_graphs_menu(); // --------------------------------------------------------------- - if (feeds.length>12 && numberoftags>2) { + if (feeds.length > 12 && numberoftags > 2) { $(".tagbody").hide(); } @@ -106,21 +109,21 @@ function graph_init_editor() showmissing = false; showtag = true; showlegend = true; - floatingtime=1; - yaxismin="auto"; - yaxismax="auto"; - yaxismin2="auto"; - yaxismax2="auto"; - csvtimeformat="datestr"; - csvnullvalues="show"; - csvheaders="showNameTag"; + floatingtime = 1; + yaxismin = "auto"; + yaxismax = "auto"; + yaxismin2 = "auto"; + yaxismax2 = "auto"; + csvtimeformat = "datestr"; + csvnullvalues = "show"; + csvheaders = "showNameTag"; current_graph_id = ""; current_graph_name = ""; previousPoint = 0; active_histogram_feed = 0; - var timeWindow = 3600000*24.0*7; - var now = Math.round(+new Date * 0.001)*1000; + const timeWindow = 3600000 * 24.0 * 7; + const now = Math.round(+new Date() * 0.001) * 1000; view.start = now - timeWindow; view.end = now; view.calc_interval(); @@ -223,42 +226,42 @@ function graph_init_editor() }); $("body").on("click",".feed-select-left",function(){ - var feedid = $(this).data("feedid"); - var checked = $(this)[0].checked; + const feedid = $(this).data("feedid"); + const checked = $(this)[0].checked; - var loaded = false; - for (var z in feedlist) { - if (feedlist[z].id==feedid) { - if (!checked) { - feedlist.splice(z,1); - } else { - feedlist[z].yaxis = 1; - loaded = true; - $(".feed-select-right[data-feedid="+feedid+"]")[0].checked = false; - } - } + let loaded = false; + for (const z in feedlist) { + if (feedlist[z].id == feedid) { + if (!checked) { + feedlist.splice(z, 1); + } else { + feedlist[z].yaxis = 1; + loaded = true; + $(".feed-select-right[data-feedid="+feedid+"]")[0].checked = false; + } + } } - if (loaded==false && checked) pushfeedlist(feedid, 1); + if (!loaded && checked) pushfeedlist(feedid, 1); graph_reload(); }); $("body").on("click",".feed-select-right",function(){ - var feedid = $(this).data("feedid"); - var checked = $(this)[0].checked; + const feedid = $(this).data("feedid"); + const checked = $(this)[0].checked; - var loaded = false; - for (var z in feedlist) { - if (feedlist[z].id==feedid) { - if (!checked) { - feedlist.splice(z,1); - } else { - feedlist[z].yaxis = 2; - loaded = true; - $(".feed-select-left[data-feedid="+feedid+"]")[0].checked = false; - } - } + let loaded = false; + for (const z in feedlist) { + if (feedlist[z].id == feedid) { + if (!checked) { + feedlist.splice(z, 1); + } else { + feedlist[z].yaxis = 2; + loaded = true; + $(".feed-select-left[data-feedid="+feedid+"]")[0].checked = false; + } + } } - if (loaded==false && checked) pushfeedlist(feedid, 2); + if (!loaded && checked) pushfeedlist(feedid, 2); graph_reload(); }); diff --git a/graph.histogram.js b/graph.histogram.js index 97e3784..8d35513 100644 --- a/graph.histogram.js +++ b/graph.histogram.js @@ -3,96 +3,88 @@ //---------------------------------------------------------------------------------------- // Launch histogram mode for a given feed -$("body").on("click",".histogram",function(){ - $("#navigation").hide(); - $("#histogram-controls").show(); - var feedid = $(this).attr("feedid"); +$('body').on('click', '.histogram', function() { + $('#navigation').hide(); + $('#histogram-controls').show(); + const feedid = $(this).attr('feedid'); active_histogram_feed = feedid; - var type = $("#histogram-type").val(); - var resolution = 1; - - var index = 0; - for (var z in feedlist) { - if (feedlist[z].id==feedid) { - index = z; - break; - } + const type = $('#histogram-type').val(); + let resolution = 1; + + let index = 0; + for (const z in feedlist) { + if (feedlist[z].id == feedid) { index = z; break; } } - if (feedlist[index].stats.diff<5000) resolution = 10; - if (feedlist[index].stats.diff<100) resolution = 0.1; - $("#histogram-resolution").val(resolution); + if (feedlist[index].stats.diff < 5000) resolution = 10; + if (feedlist[index].stats.diff < 100) resolution = 0.1; + $('#histogram-resolution').val(resolution); - histogram(feedid,type,resolution); + histogram(feedid, type, resolution); }); // Change the histogram resolution -$("#histogram-resolution").change(function(){ - var type = $("#histogram-type").val(); - var resolution = $("#histogram-resolution").val(); - histogram(active_histogram_feed,type,resolution); +$('#histogram-resolution').change(function() { + const type = $('#histogram-type').val(); + const resolution = $('#histogram-resolution').val(); + histogram(active_histogram_feed, type, resolution); }); -// time at value or power to kwh -$("#histogram-type").change(function(){ - var type = $("#histogram-type").val(); - var resolution = $("#histogram-resolution").val(); - histogram(active_histogram_feed,type,resolution); +// Time at value or power to kWh +$('#histogram-type').change(function() { + const type = $('#histogram-type').val(); + const resolution = $('#histogram-resolution').val(); + histogram(active_histogram_feed, type, resolution); }); -// return to power graph -$("#histogram-back").click(function(){ - $("#navigation").show(); - $("#histogram-controls").hide(); +// Return to power graph +$('#histogram-back').click(function() { + $('#navigation').show(); + $('#histogram-controls').hide(); graph_draw(); }); // Draw the histogram -function histogram(feedid,type,resolution) +function histogram(feedid, type, resolution) { - var histogram = {}; - var total_histogram = 0; - var val = 0; + const histData = {}; + let val = 0; // Get the feedlist index of the feedid - var index = -1; - for (var z in feedlist) - if (feedlist[z].id==feedid) index = z; - if (index==-1) return false; + let index = -1; + for (const z in feedlist) { + if (feedlist[z].id == feedid) { index = z; } + } + if (index === -1) return false; // Load data from feedlist object - var data = feedlist[index].data; - - for (var i=1; ib[0]) return 1; else return -1;}); - histogram = tmp; - - var options = { - series: { bars: { show: true, barWidth:resolution*0.8 } }, - grid: {hoverable: true} + const tmp = []; + for (const z in histData) tmp.push([z * 1, histData[z]]); + tmp.sort(function(a, b) { return a[0] > b[0] ? 1 : -1; }); + + const options = { + series: { bars: { show: true, barWidth: resolution * 0.8 } }, + grid: { hoverable: true } }; - var label = ""; - if (showtag) label += feedlist[index].tag+": "; + let label = ''; + if (showtag) label += feedlist[index].tag + ': '; label += feedlist[index].name; - $.plot("#placeholder",[{label:label, data:histogram}], options); -} + $.plot('#placeholder', [{label: label, data: tmp}], options); +} \ No newline at end of file diff --git a/graph.js b/graph.js index c11706a..c65a023 100644 --- a/graph.js +++ b/graph.js @@ -175,32 +175,32 @@ function reloadDatetimePrep() } function pushfeedlist(feedid, yaxis) { - var f = getfeed(feedid); - var dp=0; + let f = getfeed(feedid); + let dp = 0; - if (f==false) f = getfeedpublic(feedid); - if (f!=false) { - if (f.value % 1 !== 0 ) dp=1; - feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:yaxis, fill:0, scale: 1.0, offset: 0.0, delta:0, average:0, dp:dp, plottype:'lines'}); + if (f === false) f = getfeedpublic(feedid); + if (f !== false) { + if (f.value % 1 !== 0) dp = 1; + feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:yaxis, fill:0, scale:1.0, offset:0.0, delta:0, average:0, dp:dp, plottype:'lines'}); } } function graph_reload() { - var ds = new Date(view.start); - var de = new Date(view.end); + const ds = new Date(view.start); + const de = new Date(view.end); // Round start and end time if (view.mode=="daily" || view.mode=="weekly") { view.start = (new Date(ds.getFullYear(), ds.getMonth(), ds.getDate(), 0,0,0,0)).getTime(); view.end = (new Date(de.getFullYear(), de.getMonth(), de.getDate(), 0,0,0,0)).getTime(); } else if (view.mode=="monthly") { - var month_offset = 0; + let month_offset = 0; if (ds.getMonth()==de.getMonth()) month_offset = 1; view.start = (new Date(ds.getFullYear(), ds.getMonth(), 1, 0,0,0,0)).getTime(); view.end = (new Date(de.getFullYear(), de.getMonth()+month_offset, 1, 0,0,0,0)).getTime(); } else if (view.mode=="annual") { - var year_offset = 0; + let year_offset = 0; if (ds.getFullYear()==de.getFullYear()) year_offset = 1; view.start = (new Date(ds.getFullYear(), 0, 1, 0,0,0,0)).getTime(); view.end = (new Date(de.getFullYear()+1, 0, 1, 0,0,0,0)).getTime(); @@ -222,18 +222,18 @@ function graph_reload() $("#request-limitinterval").attr("checked",view.limitinterval); // Convert feedlist into csv properties - var ids = []; - var averages = []; - var deltas = []; - for (var z in feedlist) { + const ids = []; + const averages = []; + const deltas = []; + for (const z in feedlist) { ids.push(feedlist[z].id); if (feedlist[z].average==false) feedlist[z].average = 0; - averages.push(feedlist[z].average) + averages.push(feedlist[z].average); if (feedlist[z].delta==false) feedlist[z].delta = 0; - deltas.push(feedlist[z].delta) + deltas.push(feedlist[z].delta); } - var data = { + const data = { ids: ids.join(','), start: view.start, end: view.end, @@ -264,7 +264,7 @@ function graph_reload() $('#cloned_toggle').remove(); // get feedlist data - $.getJSON(path+"feed/data.json", data, addFeedlistData) + $.getJSON(path + "feed/data.json", data, addFeedlistData) .fail(handleFeedlistDataError) .done(checkFeedlistData); } @@ -272,11 +272,11 @@ function graph_reload() function addFeedlistData(response){ // loop through feedlist and add response data to data property - var valid = false; - for (i in feedlist) { - let feed = feedlist[i]; - for (j in response) { - let item = response[j]; + let valid = false; + for (const i in feedlist) { + const feed = feedlist[i]; + for (const j in response) { + const item = response[j]; if (parseInt(feed.id) === parseInt(item.feedid) && item.data!=undefined) { feed.postprocessed = false; feed.data = item.data; @@ -334,23 +334,17 @@ function checkFeedlistData(response){ function set_feedlist() { + const remove_null = embed ? false : $(".remove-null")[0].checked; + const remove_null_max_duration = embed ? 900 : $(".remove-null-max-duration").val(); - var remove_null = false; - var remove_null_max_duration = 900; - if (!embed) { - remove_null = $(".remove-null")[0].checked; - remove_null_max_duration = $(".remove-null-max-duration").val(); - } - - for (var z in feedlist) - { - var scale = $(".scale[feedid="+feedlist[z].id+"]").val(); - if (scale!=undefined) feedlist[z].scale = scale; - var offset = $(".offset[feedid="+feedlist[z].id+"]").val(); - if (offset!=undefined) feedlist[z].offset = offset; + for (const z in feedlist) { + const scale = $(".scale[feedid="+feedlist[z].id+"]").val(); + if (scale !== undefined) feedlist[z].scale = scale; + const offset = $(".offset[feedid="+feedlist[z].id+"]").val(); + if (offset !== undefined) feedlist[z].offset = offset; // check to ensure feed scaling and data are only applied once - if (feedlist[z].postprocessed==false) { + if (feedlist[z].postprocessed === false) { feedlist[z].postprocessed = true; console.log("postprocessing feed "+feedlist[z].id+" "+feedlist[z].name); @@ -360,32 +354,88 @@ function set_feedlist() { } // Apply a scale to feed values - if (feedlist[z].scale!=undefined && feedlist[z].scale!=1.0) { - for (var i=0; i + `` + ).join(''); + out += ` + + ${z > 0 ? `` : ''} + ${z < feedlist.length - 1 ? `` : ''} + + ${getFeedName(feed)} + + + + + + + + + + + `; + } + return out; +} + +//---------------------------------------------------------------------------------------- +// buildFeedStatsHTML - builds the HTML for the feed statistics table rows +//---------------------------------------------------------------------------------------- +function buildFeedStatsHTML(feedlist, time_in_window) { + let out = ''; + for (const feed of feedlist) { + const quality = Math.round(100 * (1 - (feed.stats.npointsnull / feed.stats.npoints))); + const dp = feed.dp; + out += ` + + ${getFeedName(feed)} + ${quality}% (${feed.stats.npoints - feed.stats.npointsnull}/${feed.stats.npoints}) + ${!isNaN(Number(feed.stats.minval)) ? feed.stats.minval.toFixed(dp) : ''} + ${!isNaN(Number(feed.stats.maxval)) ? feed.stats.maxval.toFixed(dp) : ''} + ${feed.stats.diff.toFixed(dp)} + ${feed.stats.mean.toFixed(dp)} + ${feed.stats.stdev.toFixed(dp)} + ${Math.round((feed.stats.mean * time_in_window) / 3600)} + `; + } + return out; +} + function graph_draw() { - var options = { + const options = { lines: { fill: false }, xaxis: { mode: "time", @@ -434,38 +484,37 @@ function graph_draw() if (yaxismax!='auto' && yaxismax!='') { options.yaxes[0].max = yaxismax; } if (yaxismax2!='auto' && yaxismax2!='') { options.yaxes[1].max = yaxismax2; } - var time_in_window = (view.end - view.start) / 1000; - var hours = Math.floor(time_in_window / 3600); - var mins = Math.round(((time_in_window / 3600) - hours)*60); - if (mins!=0) { - if (mins<10) mins = "0"+mins; + const time_in_window = (view.end - view.start) / 1000; + const hours = Math.floor(time_in_window / 3600); + let mins = Math.round(((time_in_window / 3600) - hours) * 60); + if (mins !== 0) { + if (mins < 10) mins = "0" + mins; } else { mins = ""; } - if (!embed) $("#window-info").html(""+_lang['Window']+": "+printdate(view.start)+" "+printdate(view.end)+"
"+_lang['Length']+": "+hours+"h"+mins+" ("+time_in_window+" seconds)"); + if (!embed) $("#window-info").html(`${_lang['Window']}: ${printdate(view.start)} ${printdate(view.end)}
${_lang['Length']}: ${hours}h${mins} (${time_in_window} seconds)`); plotdata = []; let num_left = 0; let num_right = 0; - for (var z in feedlist) { + for (const z in feedlist) { - var data = feedlist[z].data; + let data = feedlist[z].data; // Hide missing data (only affects the plot view) if (!showmissing) { - var tmp = []; - for (var n in data) { - if (data[n][1]!=null) tmp.push(data[n]); + const tmp = []; + for (const n in data) { + if (data[n][1] !== null) tmp.push(data[n]); } data = tmp; } // Add series to plot - var label = ""; - if (showtag) label += feedlist[z].tag+": "; + let label = ""; + if (showtag) label += feedlist[z].tag + ": "; label += feedlist[z].name; - // label += ' '+getFeedUnit(feedlist[z].id); - var stacked = (typeof(feedlist[z].stack) !== "undefined" && feedlist[z].stack); - var plot = {label:label, data:data, yaxis:feedlist[z].yaxis, color: feedlist[z].color, stack: stacked}; + const stacked = (typeof feedlist[z].stack !== "undefined" && feedlist[z].stack); + const plot = {label:label, data:data, yaxis:feedlist[z].yaxis, color:feedlist[z].color, stack:stacked}; if (feedlist[z].plottype=="lines") { plot.lines = { show: true, fill: (feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), fill: feedlist[z].fill } }; if (feedlist[z].plottype=="bars") { plot.bars = { align: "center", fill: (feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), show: true, barWidth: view.interval * 1000 * 0.75 } }; @@ -499,87 +548,27 @@ function graph_draw() if (!embed) { - for (var z in feedlist) { + for (const z in feedlist) { feedlist[z].stats = stats(feedlist[z].data); } - var default_linecolor = "000"; - var out = ""; - for (var z in feedlist) { - var dp = feedlist[z].dp; - - out += ""; - out += ""; - if (z > 0) { - out += ""; - } - if (z < feedlist.length-1) { - out += ""; - } - out += ""; - - out += ""+getFeedName(feedlist[z])+""; - out += ""; - out += ""; - out += ""; - out += ""; - - for (var i=0; i<11; i++) out += ""; - out += ""; - out += ""; - out += ""; - out += ""; - out += ""; - out += ""; - out += ""; - // out += ""; - out += ""; - } - $("#feed-controls").html(out); - - var out = ""; - for (var z in feedlist) { - out += ""; - out += ""; - out += ""+getFeedName(feedlist[z])+""; - var quality = Math.round(100 * (1-(feedlist[z].stats.npointsnull/feedlist[z].stats.npoints))); - out += ""+quality+"% ("+(feedlist[z].stats.npoints-feedlist[z].stats.npointsnull)+"/"+feedlist[z].stats.npoints+")"; - var dp = feedlist[z].dp; - if(!isNaN(Number(feedlist[z].stats.minval))) out += ""+feedlist[z].stats.minval.toFixed(dp)+""; - if(!isNaN(Number(feedlist[z].stats.maxval))) out += ""+feedlist[z].stats.maxval.toFixed(dp)+""; - out += ""+feedlist[z].stats.diff.toFixed(dp)+""; - out += ""+feedlist[z].stats.mean.toFixed(dp)+""; - out += ""+feedlist[z].stats.stdev.toFixed(dp)+""; - out += ""+Math.round((feedlist[z].stats.mean*time_in_window)/3600)+""; - out += ""; - } - $("#feed-stats").html(out); + $("#feed-controls").html(buildFeedControlsHTML(feedlist)); + $("#feed-stats").html(buildFeedStatsHTML(feedlist, time_in_window)); if (feedlist.length) $(".feed-options").show(); else $(".feed-options").hide(); - for (var z in feedlist) { + for (const z in feedlist) { $(".decimalpoints[feedid="+feedlist[z].id+"]").val(feedlist[z].dp); - if ($(".average[feedid="+feedlist[z].id+"]")[0]!=undefined) + if ($(".average[feedid="+feedlist[z].id+"]")[0] !== undefined) $(".average[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].average; - if ($(".delta[feedid="+feedlist[z].id+"]")[0]!=undefined) + if ($(".delta[feedid="+feedlist[z].id+"]")[0] !== undefined) $(".delta[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].delta; $(".scale[feedid="+feedlist[z].id+"]").val(feedlist[z].scale); - $(".offset[feedid="+feedlist[z].id+"]").val(feedlist[z].offset); + $(".offset[feedid="+feedlist[z].id+"]").val(feedlist[z].offset); $(".linecolor[feedid="+feedlist[z].id+"]").val(feedlist[z].color); - if ($(".fill[feedid="+feedlist[z].id+"]")[0]!=undefined) + if ($(".fill[feedid="+feedlist[z].id+"]")[0] !== undefined) $(".fill[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].fill; - if ($(".stack[feedid="+feedlist[z].id+"]")[0]!=undefined) + if ($(".stack[feedid="+feedlist[z].id+"]")[0] !== undefined) $(".stack[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].stack; } @@ -636,12 +625,9 @@ function getfeedindex(id) return false; } -function getFeedUnit(id){ - let unit = '' - for(let key in feeds) { - if (feeds[key].id == id){ - unit = feeds[key].unit || '' - } +function getFeedUnit(id) { + for (const key in feeds) { + if (feeds[key].id == id) return feeds[key].unit || ''; } - return unit + return ''; } diff --git a/graph.saved.js b/graph.saved.js index 44d37fd..0e72b4f 100644 --- a/graph.saved.js +++ b/graph.saved.js @@ -162,8 +162,8 @@ function load_saved_graphs_menu() if (typeof list === 'undefined' || typeof property === 'undefined' || typeof value === 'undefined') { return -1 } - for (n in list) { - var item = list[n] + for (const n in list) { + const item = list[n] if (item.hasOwnProperty(property)) { if (item[property] === value) { return n @@ -288,8 +288,8 @@ $(function(){ function load_saved_graph(graph) { // @todo: unload_saved_graph() - if(typeof graph === 'undefined') return; - if (graph.mode==undefined) graph.mode = 'interval'; + if (typeof graph === 'undefined') return; + if (graph.mode == undefined) graph.mode = 'interval'; // view settings view.start = graph.start; @@ -298,17 +298,17 @@ function load_saved_graph(graph) { view.mode = graph.mode; view.limitinterval = graph.limitinterval; view.fixinterval = graph.fixinterval; - floatingtime = graph.floatingtime, + floatingtime = graph.floatingtime; yaxismin = graph.yaxismin; yaxismin2 = graph.yaxismin2 || 'auto'; yaxismax = graph.yaxismax; yaxismax2 = graph.yaxismax2 || 'auto'; // CSV display settings - csvtimeformat = (typeof(graph.csvtimeformat)==="undefined" ? "datestr" : graph.csvtimeformat); - csvnullvalues = (typeof(graph.csvnullvalues)==="undefined" ? "show" : graph.csvnullvalues); - csvheaders = (typeof(graph.csvheaders)==="undefined" ? "showNameTag" : graph.csvheaders); - var tmpCsv = (typeof(graph.showcsv)==="undefined" ? "0" : graph.showcsv.toString()); + csvtimeformat = (typeof graph.csvtimeformat === 'undefined' ? 'datestr' : graph.csvtimeformat); + csvnullvalues = (typeof graph.csvnullvalues === 'undefined' ? 'show' : graph.csvnullvalues); + csvheaders = (typeof graph.csvheaders === 'undefined' ? 'showNameTag' : graph.csvheaders); + const tmpCsv = (typeof graph.showcsv === 'undefined' ? '0' : graph.showcsv.toString()); // show settings showmissing = graph.showmissing; @@ -316,15 +316,15 @@ function load_saved_graph(graph) { showlegend = graph.showlegend; // graph details - current_graph_id = graph.id - current_graph_name = graph.name + current_graph_id = graph.id; + current_graph_name = graph.name; // feedlist feedlist = graph.feedlist; if (floatingtime) { - var timewindow = view.end - view.start; - var now = Math.round(+new Date * 0.001)*1000; + const timewindow = view.end - view.start; + const now = Math.round(+new Date() * 0.001) * 1000; view.end = now; view.start = view.end - timewindow; } @@ -340,7 +340,7 @@ function load_saved_graph(graph) { $("#showlegend")[0].checked = showlegend; $("#request-type").val(view.mode); - if (view.mode!="interval") { + if (view.mode !== "interval") { $(".fixed-interval-options").hide(); } else { $(".fixed-interval-options").show(); @@ -356,14 +356,14 @@ function load_saved_graph(graph) { csvShowHide(tmpCsv); } -function get_graph_data () { +function get_graph_data() { - var now = Math.round(+new Date * 0.001)*1000; - if (Math.abs(now - view.end)<120000) { + const now = Math.round(+new Date() * 0.001) * 1000; + if (Math.abs(now - view.end) < 120000) { floatingtime = 1; } - var graph_to_save = { + const graph_to_save = { name: current_graph_name, start: view.start, end: view.end, @@ -391,8 +391,8 @@ function get_graph_data () { function graph_update(data) { // Clean feedlist of data and stats that dont need to be saved - for (var i in data.feedlist) { - delete data.feedlist[i].data + for (const i in data.feedlist) { + delete data.feedlist[i].data; delete data.feedlist[i].stats; } diff --git a/view.php b/view.php index 692eb19..a4600d0 100644 --- a/view.php +++ b/view.php @@ -12,7 +12,7 @@ global $path, $embed, $session, $settings; $userid = 0; - $v = 28; + $v = 30; $feedidsLH = ""; if (isset($_GET['feedidsLH'])) $feedidsLH = $_GET['feedidsLH']; @@ -54,7 +54,6 @@ --> - @@ -275,12 +274,6 @@ - - @@ -289,19 +282,14 @@ - - diff --git a/vis.helper.js b/vis.helper.js index 6e5f4a2..ccf7079 100644 --- a/vis.helper.js +++ b/vis.helper.js @@ -98,50 +98,47 @@ var view = function stats(data) { - var sum = 0; - var i=0; - var minval = 0; - var maxval = 0; - var npoints = 0; - var npointsnull = 0; - - var val = null; - for (var z in data) + let sum = 0; + let i = 0; + let minval = 0; + let maxval = 0; + let npoints = 0; + let npointsnull = 0; + + for (const z in data) { - var val = data[z][1]; // 1) only calculated based on present values - //if (data[z][1]!=null) val = data[z][1]; // 2) if value is missing use last value - if (val!=null) + const val = data[z][1]; + if (val !== null) { - if (i==0) { + if (i === 0) { maxval = val; minval = val; } - if (val>maxval) maxval = val; - if (val maxval) maxval = val; + if (val < minval) minval = val; sum += val; i++; } - if (data[z][1]==null) npointsnull++; - - npoints ++; + if (data[z][1] === null) npointsnull++; + npoints++; } - var mean = sum / i; - sum = 0, i=0; - for (z in data) + const mean = sum / i; + sum = 0; i = 0; + for (const z in data) { sum += (data[z][1] - mean) * (data[z][1] - mean); i++; } - var stdev = Math.sqrt(sum / i); - + const stdev = Math.sqrt(sum / i); + return { - "minval":minval, - "maxval":maxval, - "diff":maxval-minval, - "mean":mean, - "stdev":stdev, - "npointsnull":npointsnull, - "npoints":npoints + "minval": minval, + "maxval": maxval, + "diff": maxval - minval, + "mean": mean, + "stdev": stdev, + "npointsnull": npointsnull, + "npoints": npoints }; }; @@ -161,36 +158,33 @@ var urlParams; function tooltip(x, y, contents, bgColour) { - var offset = 15; // use higher values for a little spacing between `x,y` and tooltip - var elem = $('
' + contents + '
').css({ + const offset = 15; + const elem = $('
' + contents + '
').css({ position: 'absolute', display: 'none', - 'font-weight':'bold', + 'font-weight': 'bold', border: '1px solid rgb(255, 221, 221)', padding: '2px', 'background-color': bgColour, opacity: '0.8' }).appendTo("body").fadeIn(200); - var elemY = y - elem.height() - offset; - var elemX = x - elem.width() - offset; - if (elemY < 0) { elemY = 0; } - if (elemX < 0) { elemX = 0; } - elem.css({ - top: elemY, - left: elemX - }); + let elemY = y - elem.height() - offset; + let elemX = x - elem.width() - offset; + if (elemY < 0) elemY = 0; + if (elemX < 0) elemX = 0; + elem.css({ top: elemY, left: elemX }); }; -function parseTimepickerTime(timestr){ - var tmp = timestr.split(" "); - if (tmp.length!=2) return false; +function parseTimepickerTime(timestr) { + const tmp = timestr.split(" "); + if (tmp.length !== 2) return false; - var date = tmp[0].split("/"); - if (date.length!=3) return false; + const date = tmp[0].split("/"); + if (date.length !== 3) return false; - var time = tmp[1].split(":"); - if (time.length!=3) return false; + const time = tmp[1].split(":"); + if (time.length !== 3) return false; - return new Date(date[2],date[1]-1,date[0],time[0],time[1],time[2],0).getTime() / 1000; + return new Date(date[2], date[1] - 1, date[0], time[0], time[1], time[2], 0).getTime() / 1000; } From d76ad14c88af1c0a470768210a56c9f03f12664d Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Thu, 23 Apr 2026 22:22:09 +0100 Subject: [PATCH 04/19] minor cleanup --- graph.js | 23 ++++++--------------- graph.utils.js | 22 ++++++++++++++++++++ view.php | 55 ++++++++++++++++++++++++++------------------------ 3 files changed, 57 insertions(+), 43 deletions(-) diff --git a/graph.js b/graph.js index c65a023..c2f86fc 100644 --- a/graph.js +++ b/graph.js @@ -286,8 +286,8 @@ function addFeedlistData(response){ } } } - // alter feedlist base on user selection - if (valid) set_feedlist(); + + if (valid) processFeedlistData(); } function handleFeedlistDataError(jqXHR, error, message){ error = error === 'parsererror' ? _('Received data not in correct format. Check the logs for more details'): error; @@ -332,7 +332,7 @@ function checkFeedlistData(response){ } } -function set_feedlist() { +function processFeedlistData() { const remove_null = embed ? false : $(".remove-null")[0].checked; const remove_null_max_duration = embed ? 900 : $(".remove-null-max-duration").val(); @@ -340,6 +340,7 @@ function set_feedlist() { for (const z in feedlist) { const scale = $(".scale[feedid="+feedlist[z].id+"]").val(); if (scale !== undefined) feedlist[z].scale = scale; + const offset = $(".offset[feedid="+feedlist[z].id+"]").val(); if (offset !== undefined) feedlist[z].offset = offset; @@ -354,22 +355,10 @@ function set_feedlist() { } // Apply a scale to feed values - if (feedlist[z].scale !== undefined && feedlist[z].scale != 1.0) { - for (let i = 0; i < feedlist[z].data.length; i++) { - if (feedlist[z].data[i][1] !== null) { - feedlist[z].data[i][1] = feedlist[z].data[i][1] * feedlist[z].scale; - } - } - } + feedlist[z].data = scale_values(feedlist[z].data, feedlist[z].scale); // Apply an offset to feed values - if (feedlist[z].offset !== undefined && feedlist[z].offset != 0.0) { - for (let i = 0; i < feedlist[z].data.length; i++) { - if (feedlist[z].data[i][1] !== null) { - feedlist[z].data[i][1] = feedlist[z].data[i][1] + 1 * feedlist[z].offset; - } - } - } + feedlist[z].data = offset_values(feedlist[z].data, feedlist[z].offset); } } // call graph_draw() once feedlist is altered diff --git a/graph.utils.js b/graph.utils.js index 69aa71a..c819acd 100644 --- a/graph.utils.js +++ b/graph.utils.js @@ -70,3 +70,25 @@ function remove_null_values(data, interval, max_duration = 900) { } return data; } + +function scale_values(data, scale) { + if (scale !== undefined && scale != 1.0) { + for (let i = 0; i < data.length; i++) { + if (data[i][1] !== null) { + data[i][1] = data[i][1] * scale; + } + } + } + return data; +} + +function offset_values(data, offset) { + if (offset !== undefined && offset != 0.0) { + for (let i = 0; i < data.length; i++) { + if (data[i][1] !== null) { + data[i][1] = data[i][1] + 1 * offset; + } + } + } + return data; +} diff --git a/view.php b/view.php index a4600d0..11c3264 100644 --- a/view.php +++ b/view.php @@ -65,8 +65,8 @@
@@ -162,7 +162,7 @@ - +
: @@ -170,10 +170,10 @@ - +
- - + + - - - - - - +

From 3a41f01c0eb9b30af3939f2615c2a60f800c6949 Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Wed, 29 Apr 2026 10:40:48 +0100 Subject: [PATCH 06/19] refactor: phase 2 - introduce Vue.observable graphState, replace scattered globals --- embed.php | 20 ++--- graph.csv.js | 30 +++---- graph.editor.js | 184 ++++++++++++++++++-------------------- graph.histogram.js | 24 ++--- graph.js | 217 +++++++++++++++++++++++---------------------- graph.legend.js | 2 +- graph.saved.js | 82 ++++++++--------- view.php | 11 ++- 8 files changed, 280 insertions(+), 290 deletions(-) diff --git a/embed.php b/embed.php index e3eca3b..ba4c670 100644 --- a/embed.php +++ b/embed.php @@ -145,19 +145,19 @@ view.interval = result.interval; view.limitinterval = result.limitinterval; view.fixinterval = result.fixinterval; - floatingtime = result.floatingtime, - yaxismin = result.yaxismin; - yaxismax = result.yaxismax; - yaxismin2 = result.yaxismin2; - yaxismax2 = result.yaxismax2; - feedlist = result.feedlist; + graphState.floatingtime = result.floatingtime, + graphState.yaxismin = result.yaxismin; + graphState.yaxismax = result.yaxismax; + graphState.yaxismin2 = result.yaxismin2; + graphState.yaxismax2 = result.yaxismax2; + graphState.feedlist = result.feedlist; // show settings - showmissing = result.showmissing; - showtag = result.showtag; - showlegend = result.showlegend; + graphState.showmissing = result.showmissing; + graphState.showtag = result.showtag; + graphState.showlegend = result.showlegend; - if (floatingtime) { + if (graphState.floatingtime) { var timewindow = view.end - view.start; var now = Math.round(+new Date * 0.001)*1000; view.end = now; diff --git a/graph.csv.js b/graph.csv.js index c494a4e..75ced08 100644 --- a/graph.csv.js +++ b/graph.csv.js @@ -4,7 +4,7 @@ function printcsv() { - if (typeof feedlist[0] === "undefined") return; + if (typeof graphState.feedlist[0] === "undefined") return; const timeformat = $("#csvtimeformat").val(); const nullvalues = $("#csvnullvalues").val(); @@ -15,7 +15,7 @@ function printcsv() const value = []; let line = []; const lastvalue = []; - const start_time = feedlist[0].data[0][0]; + const start_time = graphState.feedlist[0].data[0][0]; let showName = false; let showTag = false; @@ -30,20 +30,20 @@ function printcsv() case "seconds": line = ["Seconds since start"]; break; case "datestr": line = ["Date-time string"]; break; } - for (const f in feedlist) { - line.push((showTag ? feedlist[f].tag : "") + (showTag && showName ? ":" : "") + (showName ? feedlist[f].name : "")); + for (const f in graphState.feedlist) { + line.push((showTag ? graphState.feedlist[f].tag : "") + (showTag && showName ? ":" : "") + (showName ? graphState.feedlist[f].name : "")); } csvout = '"' + line.join('", "') + '"\n'; } - for (const z in feedlist[0].data) { + for (const z in graphState.feedlist[0].data) { line = []; if (timeformat === "unix") { - line.push(Math.round(feedlist[0].data[z][0] / 1000)); + line.push(Math.round(graphState.feedlist[0].data[z][0] / 1000)); } else if (timeformat === "seconds") { - line.push(Math.round((feedlist[0].data[z][0] - start_time) / 1000)); + line.push(Math.round((graphState.feedlist[0].data[z][0] - start_time) / 1000)); } else if (timeformat === "datestr") { - const t = new Date(feedlist[0].data[z][0]); + const t = new Date(graphState.feedlist[0].data[z][0]); const year = t.getFullYear(); const month = String(t.getMonth() + 1).padStart(2, '0'); const day = String(t.getDate()).padStart(2, '0'); @@ -54,13 +54,13 @@ function printcsv() } let nullfound = false; - for (const f in feedlist) { + for (const f in graphState.feedlist) { if (value[f] === undefined) value[f] = null; lastvalue[f] = value[f]; - if (feedlist[f].data[z] !== undefined) { - if (feedlist[f].data[z][1] === null) nullfound = true; - if (feedlist[f].data[z][1] !== null || nullvalues === "show") value[f] = feedlist[f].data[z][1]; - if (value[f] !== null) value[f] = (value[f] * 1.0).toFixed(feedlist[f].dp); + if (graphState.feedlist[f].data[z] !== undefined) { + if (graphState.feedlist[f].data[z][1] === null) nullfound = true; + if (graphState.feedlist[f].data[z][1] !== null || nullvalues === "show") value[f] = graphState.feedlist[f].data[z][1]; + if (value[f] !== null) value[f] = (value[f] * 1.0).toFixed(graphState.feedlist[f].dp); line.push(value[f] + ""); } } @@ -90,12 +90,12 @@ function csvShowHide(set) if (action==="show") { printcsv() - showcsv = 1; + graphState.showcsv = 1; $("#csv").show(); $(".csvoptions").show(); $("#showcsv").html(_lang["Hide CSV Output"]); } else { - showcsv = 0; + graphState.showcsv = 0; $("#csv").hide(); $(".csvoptions").hide(); $("#showcsv").html(_lang["Show CSV Output"]); diff --git a/graph.editor.js b/graph.editor.js index 0172e97..ed75059 100644 --- a/graph.editor.js +++ b/graph.editor.js @@ -51,10 +51,10 @@ function buildFeedSelectorHTML(feeds) { //---------------------------------------------------------------------------------------- function graph_init_editor() { - if (!feeds) feeds = feedlist; + if (!graphState.feeds) graphState.feeds = graphState.feedlist; // Draw sidebar feed selector ------------------------------------------- - const { html, feedsbytag, numberoftags } = buildFeedSelectorHTML(feeds); + const { html, feedsbytag, numberoftags } = buildFeedSelectorHTML(graphState.feeds); // --------------------------------------------------------------- // Writting direct to the menu system here @@ -79,14 +79,14 @@ function graph_init_editor() if (session_write) load_saved_graphs_menu(); // --------------------------------------------------------------- - if (feeds.length > 12 && numberoftags > 2) { + if (graphState.feeds.length > 12 && numberoftags > 2) { $(".tagbody").hide(); } $("#info").show(); - if ($("#showmissing")[0]!=undefined) $("#showmissing")[0].checked = showmissing; - if ($("#showtag")[0]!=undefined) $("#showtag")[0].checked = showtag; - if ($("#showlegend")[0]!=undefined) $("#showlegend")[0].checked = showlegend; + if ($("#showmissing")[0]!=undefined) $("#showmissing")[0].checked = graphState.showmissing; + if ($("#showtag")[0]!=undefined) $("#showtag")[0].checked = graphState.showtag; + if ($("#showlegend")[0]!=undefined) $("#showlegend")[0].checked = graphState.showlegend; datetimepickerInit(); @@ -101,26 +101,26 @@ function graph_init_editor() $("#clear").click(function(){ - feedlist = []; + graphState.feedlist = []; plotdata = []; - skipmissing = 0; + graphState.skipmissing = 0; requesttype = "interval"; - showcsv = 0; - showmissing = false; - showtag = true; - showlegend = true; - floatingtime = 1; - yaxismin = "auto"; - yaxismax = "auto"; - yaxismin2 = "auto"; - yaxismax2 = "auto"; - csvtimeformat = "datestr"; - csvnullvalues = "show"; - csvheaders = "showNameTag"; - current_graph_id = ""; - current_graph_name = ""; + graphState.showcsv = 0; + graphState.showmissing = false; + graphState.showtag = true; + graphState.showlegend = true; + graphState.floatingtime = 1; + graphState.yaxismin = "auto"; + graphState.yaxismax = "auto"; + graphState.yaxismin2 = "auto"; + graphState.yaxismax2 = "auto"; + graphState.csvtimeformat = "datestr"; + graphState.csvnullvalues = "show"; + graphState.csvheaders = "showNameTag"; + graphState.current_graph_id = ""; + graphState.current_graph_name = ""; previousPoint = 0; - active_histogram_feed = 0; + graphState.active_histogram_feed = 0; const timeWindow = 3600000 * 24.0 * 7; const now = Math.round(+new Date() * 0.001) * 1000; @@ -138,15 +138,11 @@ function graph_init_editor() $(".csvoptions").hide(); $("body").on("click",".average",function(){ - var feedid = $(this).attr("feedid"); + const feedid = $(this).attr("feedid"); - for (var z in feedlist) { - if (feedlist[z].id==feedid) { - if ($(this)[0].checked) { - feedlist[z].average = 1; - } else { - feedlist[z].average = 0; - } + for (const z in graphState.feedlist) { + if (graphState.feedlist[z].id==feedid) { + graphState.feedlist[z].average = $(this)[0].checked ? 1 : 0; break; } } @@ -154,26 +150,22 @@ function graph_init_editor() }); $("body").on("click", ".move-feed", function(){ - var feedid = $(this).attr("feedid")*1; - var curpos = parseInt(feedid); - var moveby = parseInt($(this).attr("moveby")); - var newpos = curpos + moveby; - if (newpos>=0 && newpos=0 && newpos { - feedlist = feedlist.filter((feed)=>!badfeeds.find((id)=>feed.id === id)); + graphState.feedlist = graphState.feedlist.filter((feed)=>!badfeeds.find((id)=>feed.id === id)); graph_reload(); }); } else { @@ -337,28 +338,28 @@ function processFeedlistData() { const remove_null = embed ? false : $(".remove-null")[0].checked; const remove_null_max_duration = embed ? 900 : $(".remove-null-max-duration").val(); - for (const z in feedlist) { - const scale = $(".scale[feedid="+feedlist[z].id+"]").val(); - if (scale !== undefined) feedlist[z].scale = scale; + for (const z in graphState.feedlist) { + const scale = $(".scale[feedid="+graphState.feedlist[z].id+"]").val(); + if (scale !== undefined) graphState.feedlist[z].scale = scale; - const offset = $(".offset[feedid="+feedlist[z].id+"]").val(); - if (offset !== undefined) feedlist[z].offset = offset; + const offset = $(".offset[feedid="+graphState.feedlist[z].id+"]").val(); + if (offset !== undefined) graphState.feedlist[z].offset = offset; // check to ensure feed scaling and data are only applied once - if (feedlist[z].postprocessed === false) { - feedlist[z].postprocessed = true; - console.log("postprocessing feed "+feedlist[z].id+" "+feedlist[z].name); + if (graphState.feedlist[z].postprocessed === false) { + graphState.feedlist[z].postprocessed = true; + console.log("postprocessing feed "+graphState.feedlist[z].id+" "+graphState.feedlist[z].name); // Remove null values if (remove_null) { - feedlist[z].data = remove_null_values(feedlist[z].data, view.interval, remove_null_max_duration); + graphState.feedlist[z].data = remove_null_values(graphState.feedlist[z].data, view.interval, remove_null_max_duration); } // Apply a scale to feed values - feedlist[z].data = scale_values(feedlist[z].data, feedlist[z].scale); + graphState.feedlist[z].data = scale_values(graphState.feedlist[z].data, graphState.feedlist[z].scale); // Apply an offset to feed values - feedlist[z].data = offset_values(feedlist[z].data, feedlist[z].offset); + graphState.feedlist[z].data = offset_values(graphState.feedlist[z].data, graphState.feedlist[z].offset); } } // call graph_draw() once feedlist is altered @@ -465,13 +466,13 @@ function graph_draw() } } - if (showlegend) options.legend.show = true; + if (graphState.showlegend) options.legend.show = true; - if (yaxismin!='auto' && yaxismin!='') { options.yaxes[0].min = yaxismin; } - if (yaxismin2!='auto' && yaxismin2!='') { options.yaxes[1].min = yaxismin2; } + if (graphState.yaxismin!='auto' && graphState.yaxismin!='') { options.yaxes[0].min = graphState.yaxismin; } + if (graphState.yaxismin2!='auto' && graphState.yaxismin2!='') { options.yaxes[1].min = graphState.yaxismin2; } - if (yaxismax!='auto' && yaxismax!='') { options.yaxes[0].max = yaxismax; } - if (yaxismax2!='auto' && yaxismax2!='') { options.yaxes[1].max = yaxismax2; } + if (graphState.yaxismax!='auto' && graphState.yaxismax!='') { options.yaxes[0].max = graphState.yaxismax; } + if (graphState.yaxismax2!='auto' && graphState.yaxismax2!='') { options.yaxes[1].max = graphState.yaxismax2; } const time_in_window = (view.end - view.start) / 1000; const hours = Math.floor(time_in_window / 3600); @@ -487,11 +488,11 @@ function graph_draw() plotdata = []; let num_left = 0; let num_right = 0; - for (const z in feedlist) { + for (const z in graphState.feedlist) { - let data = feedlist[z].data; + let data = graphState.feedlist[z].data; // Hide missing data (only affects the plot view) - if (!showmissing) { + if (!graphState.showmissing) { const tmp = []; for (const n in data) { if (data[n][1] !== null) tmp.push(data[n]); @@ -500,23 +501,23 @@ function graph_draw() } // Add series to plot let label = ""; - if (showtag) label += feedlist[z].tag + ": "; - label += feedlist[z].name; - const stacked = (typeof feedlist[z].stack !== "undefined" && feedlist[z].stack); - const plot = {label:label, data:data, yaxis:feedlist[z].yaxis, color:feedlist[z].color, stack:stacked}; - - if (feedlist[z].plottype=="lines") { plot.lines = { show: true, fill: (feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), fill: feedlist[z].fill } }; - if (feedlist[z].plottype=="bars") { plot.bars = { align: "center", fill: (feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), show: true, barWidth: view.interval * 1000 * 0.75 } }; - if (feedlist[z].plottype == 'points') plot.points = {show: true, radius: 3}; - if (feedlist[z].plottype=="steps") { plot.lines = { steps: true, show: true, fill: (feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), fill: feedlist[z].fill } }; - plot.isRight = feedlist[z].yaxis === 2; - plot.id = feedlist[z].id; + if (graphState.showtag) label += graphState.feedlist[z].tag + ": "; + label += graphState.feedlist[z].name; + const stacked = (typeof graphState.feedlist[z].stack !== "undefined" && graphState.feedlist[z].stack); + const plot = {label:label, data:data, yaxis:graphState.feedlist[z].yaxis, color:graphState.feedlist[z].color, stack:stacked}; + + if (graphState.feedlist[z].plottype=="lines") { plot.lines = { show: true, fill: (graphState.feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), fill: graphState.feedlist[z].fill } }; + if (graphState.feedlist[z].plottype=="bars") { plot.bars = { align: "center", fill: (graphState.feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), show: true, barWidth: view.interval * 1000 * 0.75 } }; + if (graphState.feedlist[z].plottype == 'points') plot.points = {show: true, radius: 3}; + if (graphState.feedlist[z].plottype=="steps") { plot.lines = { steps: true, show: true, fill: (graphState.feedlist[z].fill ? (stacked ? 1.0 : 0.5) : 0.0), fill: graphState.feedlist[z].fill } }; + plot.isRight = graphState.feedlist[z].yaxis === 2; + plot.id = graphState.feedlist[z].id; plot.index = z; plotdata.push(plot); - if (feedlist[z].yaxis == 1) { + if (graphState.feedlist[z].yaxis == 1) { num_left++; - } else if (feedlist[z].yaxis == 2) { + } else if (graphState.feedlist[z].yaxis == 2) { num_right++; } } @@ -537,31 +538,31 @@ function graph_draw() if (!embed) { - for (const z in feedlist) { - feedlist[z].stats = stats(feedlist[z].data); + for (const z in graphState.feedlist) { + graphState.feedlist[z].stats = stats(graphState.feedlist[z].data); } - $("#feed-controls").html(buildFeedControlsHTML(feedlist)); - $("#feed-stats").html(buildFeedStatsHTML(feedlist, time_in_window)); - - if (feedlist.length) $(".feed-options").show(); else $(".feed-options").hide(); - - for (const z in feedlist) { - $(".decimalpoints[feedid="+feedlist[z].id+"]").val(feedlist[z].dp); - if ($(".average[feedid="+feedlist[z].id+"]")[0] !== undefined) - $(".average[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].average; - if ($(".delta[feedid="+feedlist[z].id+"]")[0] !== undefined) - $(".delta[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].delta; - $(".scale[feedid="+feedlist[z].id+"]").val(feedlist[z].scale); - $(".offset[feedid="+feedlist[z].id+"]").val(feedlist[z].offset); - $(".linecolor[feedid="+feedlist[z].id+"]").val(feedlist[z].color); - if ($(".fill[feedid="+feedlist[z].id+"]")[0] !== undefined) - $(".fill[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].fill; - if ($(".stack[feedid="+feedlist[z].id+"]")[0] !== undefined) - $(".stack[feedid="+feedlist[z].id+"]")[0].checked = feedlist[z].stack; + $("#feed-controls").html(buildFeedControlsHTML(graphState.feedlist)); + $("#feed-stats").html(buildFeedStatsHTML(graphState.feedlist, time_in_window)); + + if (graphState.feedlist.length) $(".feed-options").show(); else $(".feed-options").hide(); + + for (const z in graphState.feedlist) { + $(".decimalpoints[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].dp); + if ($(".average[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) + $(".average[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].average; + if ($(".delta[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) + $(".delta[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].delta; + $(".scale[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].scale); + $(".offset[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].offset); + $(".linecolor[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].color); + if ($(".fill[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) + $(".fill[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].fill; + if ($(".stack[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) + $(".stack[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].stack; } - if (showcsv) printcsv(); + if (graphState.showcsv) printcsv(); } } function getFeedName(item) { @@ -582,9 +583,9 @@ function getFeedName(item) { } function getfeed(id) { - for (var z in feeds) { - if (feeds[z].id == id) { - return feeds[z]; + for (const z in graphState.feeds) { + if (graphState.feeds[z].id == id) { + return graphState.feeds[z]; } } return false; @@ -606,8 +607,8 @@ function getfeedpublic(feedid) { function getfeedindex(id) { - for (var z in feeds) { - if (feeds[z].id == id) { + for (const z in graphState.feeds) { + if (graphState.feeds[z].id == id) { return z; } } @@ -615,8 +616,8 @@ function getfeedindex(id) } function getFeedUnit(id) { - for (const key in feeds) { - if (feeds[key].id == id) return feeds[key].unit || ''; + for (const key in graphState.feeds) { + if (graphState.feeds[key].id == id) return graphState.feeds[key].unit || ''; } return ''; } diff --git a/graph.legend.js b/graph.legend.js index fb9d8d5..248c81c 100644 --- a/graph.legend.js +++ b/graph.legend.js @@ -60,7 +60,7 @@ function onClickLegendLink(event) { // re-draw the chart with the plot lines hidden/shown var index = link.dataset.index; var current_data = plot_statistics.getData() - var feed = feedlist.find(function(item) { return item.id == this; }, current_data[index].id); + var feed = graphState.feedlist.find(function(item) { return item.id == this; }, current_data[index].id); if (feed == undefined) return; switch (feed.plottype) { case 'lines': current_data[index].lines.show = !current_data[index].lines.show; break; diff --git a/graph.saved.js b/graph.saved.js index 0e72b4f..3304c39 100644 --- a/graph.saved.js +++ b/graph.saved.js @@ -298,46 +298,46 @@ function load_saved_graph(graph) { view.mode = graph.mode; view.limitinterval = graph.limitinterval; view.fixinterval = graph.fixinterval; - floatingtime = graph.floatingtime; - yaxismin = graph.yaxismin; - yaxismin2 = graph.yaxismin2 || 'auto'; - yaxismax = graph.yaxismax; - yaxismax2 = graph.yaxismax2 || 'auto'; + graphState.floatingtime = graph.floatingtime; + graphState.yaxismin = graph.yaxismin; + graphState.yaxismin2 = graph.yaxismin2 || 'auto'; + graphState.yaxismax = graph.yaxismax; + graphState.yaxismax2 = graph.yaxismax2 || 'auto'; // CSV display settings - csvtimeformat = (typeof graph.csvtimeformat === 'undefined' ? 'datestr' : graph.csvtimeformat); - csvnullvalues = (typeof graph.csvnullvalues === 'undefined' ? 'show' : graph.csvnullvalues); - csvheaders = (typeof graph.csvheaders === 'undefined' ? 'showNameTag' : graph.csvheaders); + graphState.csvtimeformat = (typeof graph.csvtimeformat === 'undefined' ? 'datestr' : graph.csvtimeformat); + graphState.csvnullvalues = (typeof graph.csvnullvalues === 'undefined' ? 'show' : graph.csvnullvalues); + graphState.csvheaders = (typeof graph.csvheaders === 'undefined' ? 'showNameTag' : graph.csvheaders); const tmpCsv = (typeof graph.showcsv === 'undefined' ? '0' : graph.showcsv.toString()); // show settings - showmissing = graph.showmissing; - showtag = graph.showtag; - showlegend = graph.showlegend; + graphState.showmissing = graph.showmissing; + graphState.showtag = graph.showtag; + graphState.showlegend = graph.showlegend; // graph details - current_graph_id = graph.id; - current_graph_name = graph.name; + graphState.current_graph_id = graph.id; + graphState.current_graph_name = graph.name; // feedlist - feedlist = graph.feedlist; + graphState.feedlist = graph.feedlist; - if (floatingtime) { + if (graphState.floatingtime) { const timewindow = view.end - view.start; const now = Math.round(+new Date() * 0.001) * 1000; view.end = now; view.start = view.end - timewindow; } - $("#yaxis-min").val(yaxismin); - $("#yaxis-max").val(yaxismax); - $("#yaxis-min2").val(yaxismin2); - $("#yaxis-max2").val(yaxismax2); + $("#yaxis-min").val(graphState.yaxismin); + $("#yaxis-max").val(graphState.yaxismax); + $("#yaxis-min2").val(graphState.yaxismin2); + $("#yaxis-max2").val(graphState.yaxismax2); $("#request-fixinterval")[0].checked = view.fixinterval; $("#request-limitinterval")[0].checked = view.limitinterval; - $("#showmissing")[0].checked = showmissing; - $("#showtag")[0].checked = showtag; - $("#showlegend")[0].checked = showlegend; + $("#showmissing")[0].checked = graphState.showmissing; + $("#showtag")[0].checked = graphState.showtag; + $("#showlegend")[0].checked = graphState.showlegend; $("#request-type").val(view.mode); if (view.mode !== "interval") { @@ -350,9 +350,9 @@ function load_saved_graph(graph) { graph_reload(); load_feed_selector(); // Placed after graph load as values only available after the graph is redrawn - $("#csvtimeformat").val(csvtimeformat); - $("#csvnullvalues").val(csvnullvalues); - $("#csvheaders").val(csvheaders); + $("#csvtimeformat").val(graphState.csvtimeformat); + $("#csvnullvalues").val(graphState.csvnullvalues); + $("#csvheaders").val(graphState.csvheaders); csvShowHide(tmpCsv); } @@ -360,31 +360,31 @@ function get_graph_data() { const now = Math.round(+new Date() * 0.001) * 1000; if (Math.abs(now - view.end) < 120000) { - floatingtime = 1; + graphState.floatingtime = 1; } const graph_to_save = { - name: current_graph_name, + name: graphState.current_graph_name, start: view.start, end: view.end, interval: view.interval, mode: view.mode, limitinterval: view.limitinterval, fixinterval: view.fixinterval, - floatingtime: floatingtime, - yaxismin: yaxismin, - yaxismax: yaxismax, - yaxismin2: yaxismin2, - yaxismax2: yaxismax2, - showmissing: showmissing, - showtag: showtag, - showlegend: showlegend, - showcsv: showcsv, - csvtimeformat: csvtimeformat, - csvnullvalues: csvnullvalues, - csvheaders: csvheaders, - feedlist: JSON.parse(JSON.stringify(feedlist)), - id: current_graph_id, + floatingtime: graphState.floatingtime, + yaxismin: graphState.yaxismin, + yaxismax: graphState.yaxismax, + yaxismin2: graphState.yaxismin2, + yaxismax2: graphState.yaxismax2, + showmissing: graphState.showmissing, + showtag: graphState.showtag, + showlegend: graphState.showlegend, + showcsv: graphState.showcsv, + csvtimeformat: graphState.csvtimeformat, + csvnullvalues: graphState.csvnullvalues, + csvheaders: graphState.csvheaders, + feedlist: JSON.parse(JSON.stringify(graphState.feedlist)), + id: graphState.current_graph_id, }; return graph_to_save } diff --git a/view.php b/view.php index 14df367..52c141c 100644 --- a/view.php +++ b/view.php @@ -290,7 +290,6 @@ var feedidsLH = ""; var feedidsRH = ""; var load_savegraphs = ""; - var feeds = false; var _user = { lang: "" @@ -332,13 +331,13 @@ const public_username_str = public_userid ? public_username + "/" : ""; $.ajax({ url: path + public_username_str + "feed/list.json", async: false, dataType: "json", - success: function(data_in) { feeds = data_in; } + success: function(data_in) { graphState.feeds = data_in; } }); } else { // Load user feeds $.ajax({ url: path + "feed/list.json" + apikeystr, async: false, dataType: "json", - success: function(data_in) { feeds = data_in; } + success: function(data_in) { graphState.feeds = data_in; } }); } @@ -353,7 +352,7 @@ if (feedid) { let f = getfeed(feedid); if (f === false) f = getfeedpublic(feedid); - if (f !== false) feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:1, fill:0, scale:1.0, average:0, delta:0, dp:1, plottype:'lines'}); + if (f !== false) graphState.feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:1, fill:0, scale:1.0, average:0, delta:0, dp:1, plottype:'lines'}); } } } @@ -366,7 +365,7 @@ if (feedid) { let f = getfeed(feedid); if (f === false) f = getfeedpublic(feedid); - if (f !== false) feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:1, fill:0, scale:1.0, average:0, delta:0, dp:1, plottype:'lines'}); + if (f !== false) graphState.feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:1, fill:0, scale:1.0, average:0, delta:0, dp:1, plottype:'lines'}); } } } @@ -379,7 +378,7 @@ if (feedid) { let f = getfeed(feedid); if (f === false) f = getfeedpublic(feedid); - if (f !== false) feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:2, fill:0, scale:1.0, average:0, delta:0, dp:1, plottype:'lines'}); + if (f !== false) graphState.feedlist.push({id:feedid, name:f.name, tag:f.tag, yaxis:2, fill:0, scale:1.0, average:0, delta:0, dp:1, plottype:'lines'}); } } } From 461162968b519de5f477835359af8d88a4bc5ca8 Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Wed, 29 Apr 2026 11:09:09 +0100 Subject: [PATCH 07/19] migrate feedcontrols to vue.js --- graph.editor.js | 96 ---------------------------------- graph.feedcontrols.js | 47 +++++++++++++++++ graph.js | 56 -------------------- view.php | 116 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 147 insertions(+), 168 deletions(-) create mode 100644 graph.feedcontrols.js diff --git a/graph.editor.js b/graph.editor.js index ed75059..215341c 100644 --- a/graph.editor.js +++ b/graph.editor.js @@ -137,77 +137,6 @@ function graph_init_editor() }); $(".csvoptions").hide(); - $("body").on("click",".average",function(){ - const feedid = $(this).attr("feedid"); - - for (const z in graphState.feedlist) { - if (graphState.feedlist[z].id==feedid) { - graphState.feedlist[z].average = $(this)[0].checked ? 1 : 0; - break; - } - } - graph_draw(); - }); - - $("body").on("click", ".move-feed", function(){ - const feedid = $(this).attr("feedid")*1; - const curpos = parseInt(feedid); - const moveby = parseInt($(this).attr("moveby")); - const newpos = curpos + moveby; - if (newpos>=0 && newpos which requires a full 6-digit '#rrggbb' value. + feedColor(feed) { + const c = feed.color; + if (!c) return '#000000'; + return c.startsWith('#') ? c : '#' + c; + }, + moveFeed(index, by) { + const newpos = index + by; + if (newpos >= 0 && newpos < this.state.feedlist.length) { + this.state.feedlist = arrayMove(this.state.feedlist, index, newpos); + graph_draw(); + } + }, + // Controls that only affect rendering — call graph_draw() immediately. + setPlottype(feed, e) { feed.plottype = e.target.value; graph_draw(); }, + setColor(feed, e) { feed.color = e.target.value; graph_draw(); }, + setFill(feed, e) { feed.fill = e.target.checked; graph_draw(); }, + setStack(feed, e) { feed.stack = e.target.checked; graph_draw(); }, + setDelta(feed, e) { feed.delta = e.target.checked ? 1 : 0; graph_draw(); }, + setAverage(feed, e) { feed.average = e.target.checked ? 1 : 0; graph_draw(); }, + setDp(feed, e) { feed.dp = e.target.value; graph_draw(); }, + // Scale and offset are applied to data in processFeedlistData() during + // graph_reload(). Updating graphState here is enough — no immediate redraw. + setScale(feed, e) { feed.scale = e.target.value; }, + setOffset(feed, e) { feed.offset = e.target.value; }, + }, + }); +} diff --git a/graph.js b/graph.js index 348d136..c7db250 100644 --- a/graph.js +++ b/graph.js @@ -339,12 +339,6 @@ function processFeedlistData() { const remove_null_max_duration = embed ? 900 : $(".remove-null-max-duration").val(); for (const z in graphState.feedlist) { - const scale = $(".scale[feedid="+graphState.feedlist[z].id+"]").val(); - if (scale !== undefined) graphState.feedlist[z].scale = scale; - - const offset = $(".offset[feedid="+graphState.feedlist[z].id+"]").val(); - if (offset !== undefined) graphState.feedlist[z].offset = offset; - // check to ensure feed scaling and data are only applied once if (graphState.feedlist[z].postprocessed === false) { graphState.feedlist[z].postprocessed = true; @@ -366,40 +360,6 @@ function processFeedlistData() { graph_draw(); } -//---------------------------------------------------------------------------------------- -// buildFeedControlsHTML - builds the HTML for the feed options control table rows -//---------------------------------------------------------------------------------------- -function buildFeedControlsHTML(feedlist) { - const defaultLinecolor = '000'; - let out = ''; - for (let z = 0; z < feedlist.length; z++) { - const feed = feedlist[z]; - const plotTypes = ['lines', 'bars', 'points', 'steps']; - const plotTypeLabels = [_lang['Lines'], _lang['Bars'], _lang['Points'], _lang['Steps']]; - const plottypeOptions = plotTypes.map((type, i) => - `` - ).join(''); - out += ` - - ${z > 0 ? `` : ''} - ${z < feedlist.length - 1 ? `` : ''} - - ${getFeedName(feed)} - - - - - - - - - - - `; - } - return out; -} - //---------------------------------------------------------------------------------------- // buildFeedStatsHTML - builds the HTML for the feed statistics table rows //---------------------------------------------------------------------------------------- @@ -542,26 +502,10 @@ function graph_draw() graphState.feedlist[z].stats = stats(graphState.feedlist[z].data); } - $("#feed-controls").html(buildFeedControlsHTML(graphState.feedlist)); $("#feed-stats").html(buildFeedStatsHTML(graphState.feedlist, time_in_window)); if (graphState.feedlist.length) $(".feed-options").show(); else $(".feed-options").hide(); - for (const z in graphState.feedlist) { - $(".decimalpoints[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].dp); - if ($(".average[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) - $(".average[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].average; - if ($(".delta[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) - $(".delta[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].delta; - $(".scale[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].scale); - $(".offset[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].offset); - $(".linecolor[feedid="+graphState.feedlist[z].id+"]").val(graphState.feedlist[z].color); - if ($(".fill[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) - $(".fill[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].fill; - if ($(".stack[feedid="+graphState.feedlist[z].id+"]")[0] !== undefined) - $(".stack[feedid="+graphState.feedlist[z].id+"]")[0].checked = graphState.feedlist[z].stack; - } - if (graphState.showcsv) printcsv(); } } diff --git a/view.php b/view.php index 52c141c..4263b58 100644 --- a/view.php +++ b/view.php @@ -193,22 +193,104 @@
- - - - - - - - - - - - - - - -
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
@@ -274,6 +356,7 @@ load_js("Modules/graph/graph.histogram.js"); load_js("Modules/graph/graph.saved.js"); load_js("Modules/graph/graph.editor.js"); +load_js("Modules/graph/graph.feedcontrols.js"); load_js("Lib/moment.min.js"); load_js("Lib/user_locale.js"); load_js("Lib/misc/gettext.js"); @@ -385,6 +468,7 @@ } graph_init_editor(); + initFeedControlsApp(); load_feed_selector(); graph_resize(); From 6d0f83d7fecac6bb5f8f44676b235eabf7ed3f9d Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Wed, 29 Apr 2026 12:38:41 +0100 Subject: [PATCH 08/19] move stats to vue --- graph.editor.js | 6 ++---- graph.feedcontrols.js | 10 +++++++++- graph.js | 30 ++++------------------------ view.php | 46 ++++++++++++++++++++++++++++--------------- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/graph.editor.js b/graph.editor.js index 215341c..3ad2164 100644 --- a/graph.editor.js +++ b/graph.editor.js @@ -293,16 +293,14 @@ function graph_init_editor() }); $(".feed-options-show-stats").click(function(event){ - $("#feed-options-table").hide(); - $("#feed-stats-table").show(); + graphState.showStats = true; $(".feed-options-show-options").removeClass('hide'); $(".feed-options-show-stats").addClass('hide'); event.preventDefault(); }); $(".feed-options-show-options").click(function(event){ - $("#feed-options-table").show(); - $("#feed-stats-table").hide(); + graphState.showStats = false; $(".feed-options-show-options").addClass('hide'); $(".feed-options-show-stats").removeClass('hide'); event.preventDefault(); diff --git a/graph.feedcontrols.js b/graph.feedcontrols.js index 39c5914..4ed6088 100644 --- a/graph.feedcontrols.js +++ b/graph.feedcontrols.js @@ -10,7 +10,9 @@ function initFeedControlsApp() { return { state: graphState }; }, computed: { - feedlist() { return this.state.feedlist; }, + feedlist() { return this.state.feedlist; }, + showStats() { return this.state.showStats; }, + time_in_window(){ return this.state.time_in_window; }, }, methods: { feedName(feed) { @@ -42,6 +44,12 @@ function initFeedControlsApp() { // graph_reload(). Updating graphState here is enough — no immediate redraw. setScale(feed, e) { feed.scale = e.target.value; }, setOffset(feed, e) { feed.offset = e.target.value; }, + feedQuality(feed) { + return Math.round(100 * (1 - (feed.stats.npointsnull / feed.stats.npoints))); + }, + feedWh(feed) { + return Math.round((feed.stats.mean * this.time_in_window) / 3600); + }, }, }); } diff --git a/graph.js b/graph.js index c7db250..a645b81 100644 --- a/graph.js +++ b/graph.js @@ -25,6 +25,8 @@ const graphState = Vue.observable({ current_graph_name: '', skipmissing: 0, active_histogram_feed: 0, + showStats: false, + time_in_window: 0, }); // Non-reactive module-level variables (transient state, jQuery instances, config flags) @@ -360,29 +362,6 @@ function processFeedlistData() { graph_draw(); } -//---------------------------------------------------------------------------------------- -// buildFeedStatsHTML - builds the HTML for the feed statistics table rows -//---------------------------------------------------------------------------------------- -function buildFeedStatsHTML(feedlist, time_in_window) { - let out = ''; - for (const feed of feedlist) { - const quality = Math.round(100 * (1 - (feed.stats.npointsnull / feed.stats.npoints))); - const dp = feed.dp; - out += ` - - - - - - - - - - `; - } - return out; -} - function graph_draw() { const options = { @@ -435,6 +414,7 @@ function graph_draw() if (graphState.yaxismax2!='auto' && graphState.yaxismax2!='') { options.yaxes[1].max = graphState.yaxismax2; } const time_in_window = (view.end - view.start) / 1000; + graphState.time_in_window = time_in_window; const hours = Math.floor(time_in_window / 3600); let mins = Math.round(((time_in_window / 3600) - hours) * 60); if (mins !== 0) { @@ -499,11 +479,9 @@ function graph_draw() if (!embed) { for (const z in graphState.feedlist) { - graphState.feedlist[z].stats = stats(graphState.feedlist[z].data); + Vue.set(graphState.feedlist[z], 'stats', stats(graphState.feedlist[z].data)); } - $("#feed-stats").html(buildFeedStatsHTML(graphState.feedlist, time_in_window)); - if (graphState.feedlist.length) $(".feed-options").show(); else $(".feed-options").hide(); if (graphState.showcsv) printcsv(); diff --git a/view.php b/view.php index 4263b58..1a9f228 100644 --- a/view.php +++ b/view.php @@ -194,7 +194,7 @@
-
${getFeedName(feed)}${quality}% (${feed.stats.npoints - feed.stats.npointsnull}/${feed.stats.npoints})${!isNaN(Number(feed.stats.minval)) ? feed.stats.minval.toFixed(dp) : ''}${!isNaN(Number(feed.stats.maxval)) ? feed.stats.maxval.toFixed(dp) : ''}${feed.stats.diff.toFixed(dp)}${feed.stats.mean.toFixed(dp)}${feed.stats.stdev.toFixed(dp)}${Math.round((feed.stats.mean * time_in_window) / 3600)}
+
@@ -290,22 +290,36 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ feedQuality(feed) }}% ({{ feed.stats.npoints - feed.stats.npointsnull }}/{{ feed.stats.npoints }}){{ !isNaN(Number(feed.stats.minval)) ? feed.stats.minval.toFixed(feed.dp) : '' }}{{ !isNaN(Number(feed.stats.maxval)) ? feed.stats.maxval.toFixed(feed.dp) : '' }}{{ feed.stats.diff.toFixed(feed.dp) }}{{ feed.stats.mean.toFixed(feed.dp) }}{{ feed.stats.stdev.toFixed(feed.dp) }}{{ feedWh(feed) }}
- - - - - - - - - - - - - - -

From fff97685ac8cb4bdc5d6219ec840709514163aa2 Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Wed, 29 Apr 2026 13:25:03 +0100 Subject: [PATCH 09/19] refinements --- graph.css | 46 ++------ graph.editor.js | 2 + view.php | 306 ++++++++++++++++++++++++++---------------------- 3 files changed, 180 insertions(+), 174 deletions(-) diff --git a/graph.css b/graph.css index f16136f..0233133 100644 --- a/graph.css +++ b/graph.css @@ -69,7 +69,6 @@ /* end of sidebar #mygraphs */ .feed-options { - background-color:#eee; overflow-x: scroll; } table#feeds thead tr th .caret{ @@ -80,43 +79,20 @@ table#feeds thead tr th .caret{ table#feeds tbody tr th { font-weight: normal } -.feed-options-header { - height:40px; - background-color:#ccc; - font-weight:bold; - color:#fff; - position: relative; -} -.feed-options-header .caret { - /* margin: 1.2em 0 0 .7em; */ - margin: .5em .6em .5em .3em; - transition: transform 300ms ease-in-out; - transform: rotate(0deg); +.feed-options-show-options, +.feed-options-show-stats { + margin-left: auto; + flex-shrink: 0; } -.feed-options-header .caret.open { - transform: rotate(-90deg); +/* .hide has no !important so app-btn display:inline-flex wins — override here */ +.feed-options-show-options.hide, +.feed-options-show-stats.hide { + display: none !important; } -.feed-options-title:link, -.feed-options-title:focus { - padding:10px; - color: white; +#feed-options-table td input[type="checkbox"], +#feed-stats-table td input[type="checkbox"] { display: block; - text-decoration: none; - outline: 0; -} -.feed-options-title:hover { - color: currentColor; - opacity: .8; -} -.feed-options-title:active{ - opacity: 1!important; -} -.feed-options-show-options, -.feed-options-show-stats{ - margin: 5px; - position: absolute; - right: 0; - z-index: 2; + margin: 0 auto; } .feed-options #tables { diff --git a/graph.editor.js b/graph.editor.js index 3ad2164..8dac5f4 100644 --- a/graph.editor.js +++ b/graph.editor.js @@ -297,6 +297,7 @@ function graph_init_editor() $(".feed-options-show-options").removeClass('hide'); $(".feed-options-show-stats").addClass('hide'); event.preventDefault(); + event.stopPropagation(); }); $(".feed-options-show-options").click(function(event){ @@ -304,6 +305,7 @@ function graph_init_editor() $(".feed-options-show-options").addClass('hide'); $(".feed-options-show-stats").removeClass('hide'); event.preventDefault(); + event.stopPropagation(); }); // Reload feeds if remove-null is changed or remove-null-max-duration is changed diff --git a/view.php b/view.php index 1a9f228..492cbfc 100644 --- a/view.php +++ b/view.php @@ -183,142 +183,167 @@

-
-
-
- - - - -
+
+
+ +
+
+ +
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ feedQuality(feed) }}% ({{ feed.stats.npoints - feed.stats.npointsnull }}/{{ feed.stats.npoints }}){{ !isNaN(Number(feed.stats.minval)) ? feed.stats.minval.toFixed(feed.dp) : '' }}{{ !isNaN(Number(feed.stats.maxval)) ? feed.stats.maxval.toFixed(feed.dp) : '' }}{{ feed.stats.diff.toFixed(feed.dp) }}{{ feed.stats.mean.toFixed(feed.dp) }}{{ feed.stats.stdev.toFixed(feed.dp) }}{{ feedWh(feed) }}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ feedQuality(feed) }}% ({{ feed.stats.npoints - feed.stats.npointsnull }}/{{ feed.stats.npoints }}){{ !isNaN(Number(feed.stats.minval)) ? feed.stats.minval.toFixed(feed.dp) : '' }}{{ !isNaN(Number(feed.stats.maxval)) ? feed.stats.maxval.toFixed(feed.dp) : '' }}{{ feed.stats.diff.toFixed(feed.dp) }}{{ feed.stats.mean.toFixed(feed.dp) }}{{ feed.stats.stdev.toFixed(feed.dp) }}{{ feedWh(feed) }}
+
@@ -499,10 +524,13 @@ // manually add hide/show $('#tables').collapse(); - // trigger hide/show - $('.feed-options-title').on('click', function(event) { - event.preventDefault(); - event.target.querySelector('.caret').classList.toggle('open'); + // trigger hide/show on the whole header bar + $('.feed-options-header').on('click', function(event) { + var icon = document.querySelector('.feed-options-header .collapse-icon'); + if (icon) { + icon.classList.toggle('icon-chevron-down'); + icon.classList.toggle('icon-chevron-right'); + } $('#tables').collapse('toggle'); }); }); From 58711cdde8683249f7e41a69a746c7f54cc658ad Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Wed, 29 Apr 2026 13:41:58 +0100 Subject: [PATCH 10/19] minor, but headings still wrapping and responsivness poor --- graph.css | 6 ++++-- view.php | 27 +++++++++++++++++---------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/graph.css b/graph.css index 0233133..41a82ae 100644 --- a/graph.css +++ b/graph.css @@ -91,8 +91,10 @@ table#feeds tbody tr th { } #feed-options-table td input[type="checkbox"], #feed-stats-table td input[type="checkbox"] { - display: block; - margin: 0 auto; + vertical-align: middle; + margin: 0; + position: relative; + top: -1px; } .feed-options #tables { diff --git a/view.php b/view.php index 492cbfc..26eaee7 100644 --- a/view.php +++ b/view.php @@ -39,6 +39,13 @@ [v-cloak] { visibility: hidden } + #feed-options-table input { + margin-bottom: 0; + } + + #feed-options-table select { + margin-bottom: 0; + } - - - + + + - - + + - + @@ -295,9 +302,9 @@ - - @@ -313,7 +320,7 @@ - + From 34ffc4e9d57c45ce60fa9dfc2baef3e25e8ff724 Mon Sep 17 00:00:00 2001 From: Trystan Lea Date: Wed, 29 Apr 2026 14:02:52 +0100 Subject: [PATCH 11/19] clean up includes but not quite right --- embed.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/embed.php b/embed.php index ba4c670..e3c4abd 100644 --- a/embed.php +++ b/embed.php @@ -29,17 +29,16 @@ - - - - - - - - - - - + +