PageSourceSearch

https://joymeter.uk/js/plot.js

js joymeter.uk collected 2026-10-04 04:30:31 UTC 31,689 bytes, 753 lines download raw bytes

1function XXXFINE_DETAIL_plotEnergyAll(d) {
2    console.log(d);
3    document.getElementById('title').innerHTML = "Energy history";
4    var list = document.getElementById('list');
5    list.innerHTML    = '';
6    var bar, aDiv, hourLabel, aLabel, eLabel;
7    var colWidth = 1; //%
8    // var col = 10*colWidth - ((d.el.length+2) * colWidth); // 2 hours to the right as spare space
9    var col = 0;
10
11    var peakE = 0;
12    if (localStorage.getItem('peakE') != null) { 
13        peakE = localStorage.getItem('peakE',peakE); 
14    }
15    d.el.forEach( e => { peakE = Math.max(e.watt, peakE) })
16    localStorage.setItem('peakE',peakE); 
17
18    var r = document.querySelector(':root');
19
20    var container = document.createElement('div');
21        container.id = 'e-container';
22        container.className = 'energy-all';
23        // container.style.width  = `${d.el.length}px`;
24    const options = {
25      weekday: 'short',
26      year: 'numeric',
27      month: 'short',
28      day: 'numeric',
29    };
30    var lastDay = "";
31    var lastDT = new Date();
32    var eDay   = new Array()
33    var eDayRef = new Array()
34    d.el.forEach(
35      el => {
36        var watt = parseInt(el.watt);
37        var ref  = parseInt(el.ref);
38        var dt = new Date(el.dt.replace(/-/g, "/"));
39        const weekday = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
40        let wkday = weekday[dt.getDay()];
41        if ((dt - lastDT) > 2 * 3600 *1000) {
42            // 2 hour gap
43            col = col + 10;
44        }
45        if ((dt - lastDT) > 24 * 3600 *1000) {
46            // > 1 day gap
47            col = col + 50;
48        }
49        dtString = dt.toISOString().substring(0,10);
50        dtMonth  = dt.toISOString().substring(11,13);
51        dtHour   = dt.toISOString().substring(11,13);
52        if ((wkday == 'Mon') && (wkday != lastDay)) {
53            var day = document.createElement('div');
54                day.className    = 'energy-all-day';
55                day.innerHTML    = dt.toLocaleString('en-GB',options);
56                day.style.left   = `${col-48}px`;
57            container.appendChild(day);
58        }
59        if (wkday != lastDay) {
60            let sum = 0, ref = 0; count = 0;
61            for (let i = 0; i < eDay.length; i++) {
62                sum += parseInt(eDay[i]);
63                ref += parseInt(eDayRef[i]);
64                count++;
65            }
66            var dayBar = document.createElement('div');
67                dayBar.className = `energy-day-bar energy-day-bar-${wkday} movable`;
68                dayBar.style.height = `${(sum/count)/30}%`;
69                dayBar.style.width  = `${count}px`;
70                dayBar.style.left   = `${col-count}px`;
71            container.appendChild(dayBar);
72            var refBar = document.createElement('div');
73                refBar.className = `energy-day-bar-ref energy-item movable`;
74                refBar.style.bottom = `${(ref/count)/30}%`;
75                refBar.style.width  = `${count}px`;
76                refBar.style.left   = `${col-count}px`;
77            container.appendChild(refBar);
78            eDay = [];
79            eDayRef = [];
80        } else {
81            eDay.push(watt);
82            eDayRef.push(ref);
83        }
84
85        var bar = document.createElement('div');
86            bar.className = `energy-all-bar energy-item movable`;
87            bar.style.height = `${watt/30}%`;
88            bar.style.width  = `${colWidth}px`;
89            bar.style.left   = `${col}px`;
90        container.appendChild(bar);
91
92
93        col += colWidth;
94        lastDay = wkday;
95        lastDT  = dt;
96    });
97    container.style.width  = `${col}px`;
98    list.appendChild(container);
99
100    var shiftL = document.createElement('img');
101        shiftL.id = "shift-left";
102        shiftL.setAttribute("src","svg/back.svg");
103        shiftL.setAttribute("onclick",'document.getElementById("e-container").style.right = "";document.getElementById("e-container").style.left = "20px";');
104        list.appendChild(shiftL);
105    var shiftR = document.createElement('img');
106        shiftR.id = "shift-right";
107        shiftR.setAttribute("src","svg/next.svg");
108        shiftR.setAttribute("onclick",'document.getElementById("e-container").style.left = ""; document.getElementById("e-container").style.right = "20px"');
109        // shiftR.setAttribute("onclick",'shift(-33)');
110        list.appendChild(shiftR);
111
112    var help1 = document.createElement('div');
113        help1.className = 'help-float no-help';
114        help1.innerHTML = `
115        <p><img src="png/weather.png" alt="weather" width="30px">&nbsp; Local weather</p>
116        <p><span class="act-item" style="display:inline-block; width:30px; height:30px; opacity:1;"></span>&nbsp; Activities / appliances</p>
117        <p><span class="energy-item" style="display: inline-block; width:30px; height:30px; opacity:1;"> </span> &nbsp;&nbsp; Recent energy use</p>
118        <p>tap to <span class="act-label-add">+ Add</span>&nbsp; appliances you used</p>
119        <p>For reference</p>
120        <p>Your typical use (<span class="energy-item-self-ref" style="display:inline-block; width:10px; height:10px; opacity:1;"></span>)</p>
121        <p>Typical UK demand (<span class="energy-item-ref" style="display:inline-block; width:30px; height:10px; opacity:1;"></span>)</p>
122        `;
123        help1.style.top = '20%';
124        help1.style.left = '10%';
125        help1.setAttribute("onclick","app.toggleHelp()");
126        list.append(help1);
127    var help2 = document.createElement('div');
128        help2.className = 'help-float no-help';
129        help2.innerHTML = `Move left/right`;
130        help2.style.bottom = '0%';
131        help2.style.left = '30%';
132        help2.setAttribute("onclick","app.toggleHelp()");
133        list.append(help2);
134    }
135
136function plotEnergyAll(d) {
137    console.log(d);
138    document.getElementById('title').innerHTML = "Energy history";
139    var list = document.getElementById('list');
140    list.innerHTML    = '';
141    var bar, aDiv, hourLabel, aLabel, eLabel;
142    var colWidth = 40; //%
143    var col = 0;
144
145    var peakE = 0;
146    d.el.forEach( e => { peakE = Math.max(e.watt, peakE) })
147    d.el.forEach( e => { peakE = Math.max(e.ref, peakE) })
148
149    var container = document.createElement('div');
150        container.id = 'e-container';
151        container.className = 'energy-all';
152    const yy = { year: 'numeric' };
153    const mmm = { month: 'short' };
154    d.el.forEach(
155      el => {
156        var watt = parseInt(el.watt);
157        var ref  = parseInt(el.ref);
158        var dt = new Date(el.dt.replace(/-/g, "/"));
159        var thisMonth = dt.getMonth()+1;
160        var thisYear  = dt.getYear()+1900;
161        var month = document.createElement('div');
162            month.className    = 'energy-all-month';
163            if (thisMonth == 1) {
164                month.innerHTML    = dt.toLocaleString('en-GB', yy);
165            } else if (dt.getMonth() % 3 ==0) {
166                month.innerHTML    = dt.toLocaleString('en-GB',mmm);
167            }
168            month.style.right   = `${col}px`;
169            month.style.width   = `${colWidth}px`;
170        container.appendChild(month);
171        var thisBar = document.createElement('div');
172            thisBar.className = `energy-day-bar movable`;
173            thisBar.style.height = `${watt/peakE*90}%`;
174            thisBar.style.width  = `${colWidth}px`;
175            thisBar.style.right   = `${col}px`;
176            thisBar.setAttribute("onclick",`app.energyMonth(${thisMonth})`);
177        container.appendChild(thisBar);
178        var refBar = document.createElement('div');
179            refBar.className = `energy-day-bar-ref energy-item movable`;
180            refBar.style.bottom = `${ref/peakE*90}%`;
181            refBar.style.width  = `${colWidth}px`;
182            refBar.style.right   = `${col}px`;
183        container.appendChild(refBar);
184        col += colWidth;
185    });
186    container.style.width  = `${col}px`;
187    list.appendChild(container);
188
189    var shiftL = document.createElement('img');
190        shiftL.id = "shift-left";
191        shiftL.setAttribute("src","svg/back.svg");
192        shiftL.setAttribute("onclick",'document.getElementById("e-container").style.right = "";document.getElementById("e-container").style.left = "20px";');
193        container.appendChild(shiftL);
194    var shiftR = document.createElement('img');
195        shiftR.id = "shift-right";
196        shiftR.setAttribute("src","svg/next.svg");
197        shiftR.setAttribute("onclick",'document.getElementById("e-container").style.left = ""; document.getElementById("e-container").style.right = "20px"');
198        // shiftR.setAttribute("onclick",'shift(-33)');
199        container.appendChild(shiftR);
200
201    var monthContainer = document.createElement('div');
202        monthContainer.id = 'month-container';
203        monthContainer.className = 'energy-month';
204    list.appendChild(monthContainer);
205
206
207    var help1 = document.createElement('div');
208        help1.className = 'help-float no-help';
209        help1.innerHTML = `
210        <p><img src="png/weather.png" alt="weather" width="30px">&nbsp; Local weather</p>
211        <p><span class="act-item" style="display:inline-block; width:30px; height:30px; opacity:1;"></span>&nbsp; Activities / appliances</p>
212        <p><span class="energy-item" style="display: inline-block; width:30px; height:30px; opacity:1;"> </span> &nbsp;&nbsp; Recent energy use</p>
213        <p>tap to <span class="act-label-add">+ Add</span>&nbsp; appliances you used</p>
214        <p>For reference</p>
215        <p>Your typical use (<span class="energy-item-self-ref" style="display:inline-block; width:10px; height:10px; opacity:1;"></span>)</p>
216        <p>Typical UK demand (<span class="energy-item-ref" style="display:inline-block; width:30px; height:10px; opacity:1;"></span>)</p>
217        `;
218        help1.style.top = '20%';
219        help1.style.left = '10%';
220        help1.setAttribute("onclick","app.toggleHelp()");
221        list.append(help1);
222    var help2 = document.createElement('div');
223        help2.className = 'help-float no-help';
224        help2.innerHTML = `Move left/right`;
225        help2.style.bottom = '0%';
226        help2.style.left = '30%';
227        help2.setAttribute("onclick","app.toggleHelp()");
228        list.append(help2);
229    }
230
231function getPeak(d,peakType) {
232    var peakE = 0;
233    if (localStorage.getItem(peakType) != null) {
234        peakE = localStorage.getItem(peakType,peakE);
235    }
236    d.e.forEach( e => { peakE = Math.max(e.watt, peakE) })
237    localStorage.setItem(peakType,peakE);
238    return peakE;
239    // XXX if REF changed PEAK - update "THIS"
240}
241
242function plotEnergyThisYear(d) {
243    console.log(d);
244    var bar;
245    var lbl;
246    var m = 1;
247    let sum = 0, count = 0;
248    var thisMonth;
249    localStorage.removeItem('peakYear');
250    var peakE = getPeak(d,`peakYear_${type}`);
251    d.e.forEach(
252        e => {
253          bar = document.getElementById(`m${m}`);
254          lbl = document.getElementById(`m${m}_lbl`);
255          var watt = parseInt(e.watt);
256          var dt = new Date(e.dt.replace(/-/g, "/"));
257          thisMonth = dt.getMonth()+1;
258          if (thisMonth % 3 == 1) {
259              lbl.innerHTML = dt.toLocaleString('en-GB',{ month: 'short' });
260          }
261          bar.style.height = `${watt/peakE*90}%`;
262          bar.setAttribute("onclick",`app.energyMonth(${thisMonth})`);
263
264          if (!document.getElementById(`m${thisMonth}_ref`)) {
265              var refBar = document.createElement('div');
266                refBar.id = `m${thisMonth}_ref`;
267                refBar.className = `energy-day-bar-ref movable`;
268                refBar.style.bottom = "1px";
269                refBar.style.width  = "6%"
270                refBar.style.left   = `${m*6}%`;
271              document.getElementById('energy-year-container').appendChild(refBar);
272          }
273          sum += watt;
274          count++;
275          m+=1;
276        }
277    )
278    bar = document.getElementById(`year`);
279    line = document.getElementById(`year_avg`);
280    var avg = parseInt(sum/count);
281    bar.style.height = `${avg/peakE*90}%`;
282    line.style.bottom = `${avg/peakE*90}%`;
283    lbl = document.getElementById(`year_lbl`);
284    lbl.innerHTML = `Average<br> ${avg} Watt`;
285    lbl.style.bottom = `${avg/peakE*90}%`;
286    if (localStorage.getItem('refYear') !== null) {
287        plotEnergyRefYear(JSON.parse(localStorage.getItem('refYear'))); 
288    }
289    app.energyMonth(thisMonth);
290}
291
292function plotEnergyRefYear(d) {
293    console.log(d);
294    var bar;
295    var lbl;
296    var peakE = getPeak(d,`peakRefYear_${type}`);
297    d.e.forEach(
298        e => {
299          var dt = new Date(e.dt.replace(/-/g, "/"));
300          var thisMonth = dt.getMonth()+1;
301          bar = document.getElementById(`m${thisMonth}_ref`);
302          var watt = parseInt(e.watt);
303          bar.style.bottom = `${watt/peakE*90}%`;
304        }
305    )
306}
307
308function plotEnergyThisMonth(d) {
309    console.log("This month: ", d);
310    localStorage.removeItem('peakDay');
311    let sum = 0, count = 0;
312    var peakE = getPeak(d,`peakDay_${type}`);
313    var thisDT = new Date(d.e[0].dt.replace(/-/g, "/"));
314    var thisMonth = thisDT.toLocaleString('en-GB', { month: "long", year: "numeric" });
315
316    d.e.forEach(
317        e => {
318          var dt = new Date(e.dt.replace(/-/g, "/"));
319          var wkDay = dt.toLocaleString('en-GB', { weekday: "short"});
320          var wkDate = dt.toLocaleString('en-GB', { weekday: 'short', day: 'numeric' } );
321          var d = dt.getDate();
322          if (wkDay == 'Sat') {
323             document.getElementById(`d${d}_lbl`).innerHTML = wkDate;
324             document.getElementById(`d${d}`).style.background = "#acc";
325          } else if (wkDay == 'Sun') {
326             document.getElementById(`d${d}`).style.background = "#acc";
327          } else {
328             document.getElementById(`d${d}`).style.background = "#ace";
329          }
330          var watt = parseInt(e.watt);
331          document.getElementById(`d${d}`).style.height = `${watt/peakE*90}%`;
332        sum += watt;
333        count++;
334        })
335    bar = document.getElementById(`month`);
336    line = document.getElementById(`month_avg`);
337    var avg = parseInt(sum/count);
338    bar.style.height = `${avg/peakE*90}%`;
339    line.style.bottom = `${avg/peakE*90}%`;
340    lbl = document.getElementById(`month_lbl`);
341    lbl.innerHTML = `Average<br> ${avg} Watt`;
342    lbl.style.bottom = `${avg/peakE*90}%`;
343    document.getElementById('energy-month-label').innerHTML = thisMonth;
344}
345
346function plotEnergyRefMonth(d) {
347    console.log("Ref month: ", d);
348    var peakE = getPeak(d,`peakDay_${type}`);
349    d.e.forEach(
350        e => {
351          var dt = new Date(e.dt.replace(/-/g, "/"));
352          var d = dt.getDate();
353          var watt = parseInt(e.watt);
354          document.getElementById(`d${d}_ref`).style.bottom = `${watt/peakE*90}%`;
355        }
356    )
357}
358
359function plotEnergyMonth(d) {
360    console.log(d);
361    var container = document.getElementById('month-container');
362    container.innerHTML    = '';
363    var thisDT = new Date(d.el[0].dt.replace(/-/g, "/"));
364    var thisMonth = thisDT.toLocaleString('en-GB', { month: "long", year: "numeric" });
365
366    var graphLabel = document.createElement('div');
367        graphLabel.className    = 'energy-graph-label';
368        graphLabel.innerHTML    = thisMonth;
369    container.append(graphLabel);
370    var colWidth = 3; //%
371    var col = 3; // % right offset
372
373    var peakE = 0;
374    d.el.forEach( e => { peakE = Math.max(e.watt, peakE) })
375    d.el.forEach( e => { peakE = Math.max(e.ref, peakE) })
376
377    const wd = { weekday: 'short', day: 'numeric' };
378    d.el.forEach(
379      el => {
380        var watt = parseInt(el.watt);
381        var ref  = parseInt(el.ref);
382        var dt = new Date(el.dt.replace(/-/g, "/"));
383        var wkDay = dt.toLocaleString('en-GB', { weekday: "short"});
384        var wkDate = dt.toLocaleString('en-GB', wd );
385        var dayLabel = document.createElement('div');
386            dayLabel.className    = 'energy-day-label';
387            if (wkDay == 'Sat') {
388                dayLabel.innerHTML    = wkDate;
389            }
390            dayLabel.style.right   = `${col}%`;
391            dayLabel.style.width   = `${colWidth}%`;
392        container.appendChild(dayLabel);
393        var thisBar = document.createElement('div');
394            thisBar.className = `energy-day-bar movable energy-day-bar-${wkDay}`;
395            thisBar.style.height = `${watt/peakE*90}%`;
396            thisBar.style.width  = `${colWidth}%`;
397            thisBar.style.right   = `${col}%`;
398        container.appendChild(thisBar);
399        var refBar = document.createElement('div');
400            refBar.className = `energy-day-bar-ref energy-item movable`;
401            refBar.style.bottom = `${ref/peakE*90}%`;
402            refBar.style.width  = `${colWidth}%`;
403            refBar.style.right   = `${col}%`;
404        container.appendChild(refBar);
405        col += colWidth;
406    });
407    list.appendChild(container);
408    }
409
410function plotEnergyPerHour(d) {
411    document.getElementById('title').innerHTML = "Last 24 hours";
412    document.getElementById('list').innerHTML    = '';
413    var timeLabel = { 0: "<span class='night'>Midnight</span>", 3: "<span class='night'>3am</span>", 6: "6am", 9: "9am", 12: "Noon", 15: "3pm", 18: "6pm", 21: "<span class='night'>9pm</span>" };
414    var bar, aDiv, hourLabel, aLabel, eLabel;
415    var colWidth = 80/8; //%
416    var col = 10*colWidth - ((d.el.length+2) * colWidth); // 2 hours to the right as spare space
417    var actRow = {};  // counts activities displayed to increment height
418    var hourCol = {}; // keeps the left position for each hour value
419
420    var peakE = 0;
421    if (localStorage.getItem('peakE') != null) { 
422        peakE = localStorage.getItem('peakE',peakE); 
423    }
424    d.el.forEach( e => { peakE = Math.max(e.watt, peakE) })
425    localStorage.setItem('peakE',peakE); 
426
427    d.el.forEach(
428      el => {
429        var watt = parseInt(el.watt);
430
431        var zero = document.createElement('div');
432            zero.id = `zero_${el.hour}`;
433            zero.className = 'energy-bar energy-item-zero movable';
434            zero.style.height = `8px`;
435            zero.style.bottom = `10%`;
436            zero.style.width  = `${colWidth}%`;
437            zero.style.left   = `${col}%`;
438        list.appendChild(zero);
439
440        var bar = document.createElement('div');
441            bar.id = `bar_${el.hour}`;
442            bar.className = 'energy-bar energy-item movable';
443            bar.setAttribute("onclick",`app.showLabel('h${el.hour}')`);
444            bar.style.height = `10%`;
445            // bar.style.height = `${watt/peakE*80}%`;
446            bar.style.width  = `${colWidth}%`;
447            bar.style.left   = `${col}%`;
448        list.appendChild(bar);
449
450        var ref = document.createElement('div');
451            ref.className = 'energy-bar energy-item-ref movable';
452            ref.id = `ref_${el.hour}`;
453            ref.setAttribute("onclick",`app.showLabel('h${el.hour}')`);
454            ref.style.height = '10px';
455            ref.style.bottom = `10%`;
456            ref.style.width  = `${colWidth}%`;
457            ref.style.left   = `${col}%`;
458        list.appendChild(ref);
459
460        var self_ref = document.createElement('div');
461            self_ref.className = 'energy-bar energy-item-self-ref movable';
462            self_ref.id = `self_ref_${el.hour}`;
463            self_ref.setAttribute("onclick",`app.showLabel('h${el.hour}')`);
464            self_ref.style.height = `${colWidth/3}%`;
465            self_ref.style.bottom = `10%`;
466            self_ref.style.width  = `${colWidth/3}%`;
467            self_ref.style.left   = `${col+(colWidth/3)}%`;
468        list.appendChild(self_ref);
469
470
471        var hourLabel = document.createElement('div');
472            hourLabel.className = 'energy-bar hour-label movable';
473            hourLabel.id = `h${el.hour}`;
474            // hourLabel.style.bottom = `${15+watt/peakE*50}%`;
475            hourLabel.style.top = '10%';
476            if (col < 50) {
477                hourLabel.style.left = `${col-colWidth}%`;
478            } else {
479                hourLabel.style.right = `${90-col-colWidth}%`;
480            }
481            hourLabel.style.display = 'none';
482
483            var eLabel = document.createElement('div');
484                eLabel.className = 'e-label';
485                wattLabel = `${watt} Watt`;
486                if (watt == 0) {wattLabel = 'no data';}
487                eLabel.innerHTML = `${el.hour}:00&nbsp;&nbsp;${wattLabel}<span id='temp-${el.hour}'></span>`;
488            hourLabel.appendChild(eLabel);
489
490            var refLabel = document.createElement('div');
491                refLabel.id = `refLabel_${el.hour}`;
492                refLabel.className = 'e-label';
493                refLabel.innerHTML = '';
494            hourLabel.appendChild(refLabel);
495
496            var self_refLabel = document.createElement('div');
497                self_refLabel.id = `self_refLabel_${el.hour}`;
498                self_refLabel.className = 'e-label';
499                self_refLabel.innerHTML = '';
500            hourLabel.appendChild(self_refLabel);
501
502            var aLabel = document.createElement('div');
503                aLabel.className = 'act-label-add';
504                aLabel.innerHTML = '+ Add';
505                aLabel.setAttribute("onclick",`app.addActToHour('${el.dt}')`);
506            hourLabel.appendChild(aLabel);
507        list.appendChild(hourLabel);
508
509        if (el.hour in timeLabel) {
510            var xLabel = document.createElement('div');
511                xLabel.className = 'xLabel movable';
512                xLabel.innerHTML = timeLabel[el.hour];
513                xLabel.style.left = `${col}%`;
514                xLabel.setAttribute("onclick",`app.showLabel('h${el.hour}')`);
515            list.appendChild(xLabel);
516            if (el.hour == 0) {
517                var yesterday = document.createElement('div');
518                    yesterday.className = 'day-label movable';
519                    yesterday.innerHTML = 'Yesterday';
520                    yesterday.style.left = `${col-50}%`;
521                list.appendChild(yesterday);
522            }
523
524            var weather = document.createElement('img');
525                weather.id = `weather-${el.hour}`;
526                weather.className = 'weather-icon movable';
527                weather.style.left = `${col}%`;
528                weather.setAttribute("onclick",`app.showLabel('h${el.hour}')`);
529                list.appendChild(weather);
530        }
531        var dt = new Date(el.dt.replace(/-/g, "/"));
532        dtString = dt.toISOString().substring(0,10);
533        getWeather(dtString,el.hour);
534
535        actRow[el.hour] = 1;
536        hourCol[el.hour] = col;
537        col += colWidth;
538    });
539
540    d.act.forEach(
541      act => {
542          // add activity to popup (eLabel)
543        var actHour = new Date(act.dt.replace(" ","T") + 'Z').getHours();
544        eLabel = document.getElementById(`h${actHour}`);
545        if (eLabel != null) {
546            aLabel = document.createElement('div');
547            aLabel.className = 'act-label';
548            aLabel.innerHTML = act.activity;
549            eLabel.appendChild(aLabel);
550        }
551
552        var aDiv = document.createElement('div');
553            aDiv.className = 'energy-act-item act-item movable';
554            aDiv.setAttribute("onclick",`app.showLabel('h${actHour}')`);
555            aDiv.style.top = `${actRow[actHour]*colWidth}%`;
556            actRow[actHour] += 1;
557            aDiv.style.width  = `${colWidth}%`;
558            aDiv.style.height = `${colWidth/1.2}%`;
559            if (actHour in hourCol) {
560                aDiv.style.left = `${hourCol[actHour]}%`;
561            } else {
562                // no energy reading for this hour (yet)
563                aDiv.style.top = `${colWidth}%`;
564                aDiv.style.left = `${1-colWidth}%`;
565            }
566            var img = document.createElement('img');
567                if (act.key in app.activities) {
568                    var icon = app.activities[act.key]["icon"]
569                    img.setAttribute("src",`svg/${icon}.svg`);
570                    img.setAttribute("width", `150%`);
571                    aDiv.appendChild(img);
572                }
573        list.appendChild(aDiv);
574    });
575    var shiftL = document.createElement('img');
576        shiftL.id = "shift-left";
577        shiftL.setAttribute("src","svg/back.svg");
578        shiftL.setAttribute("onclick",'shift(33)');
579        list.appendChild(shiftL);
580    var shiftR = document.createElement('img');
581        shiftR.id = "shift-right";
582        shiftR.setAttribute("src","svg/next.svg");
583        shiftR.setAttribute("onclick",'shift(-33)');
584        list.appendChild(shiftR);
585
586    var help1 = document.createElement('div');
587        help1.className = 'help-float no-help';
588        help1.innerHTML = `
589        <p><img src="png/weather.png" alt="weather" width="30px">&nbsp; Local weather</p>
590        <p><span class="act-item" style="display:inline-block; width:30px; height:30px; opacity:1;"></span>&nbsp; Activities / appliances</p>
591        <p><span class="energy-item" style="display: inline-block; width:30px; height:30px; opacity:1;"> </span> &nbsp;&nbsp; Recent energy use</p>
592        <p>tap to <span class="act-label-add">+ Add</span>&nbsp; appliances you used</p>
593        <p>For reference</p>
594        <p>Your typical use (<span class="energy-item-self-ref" style="display:inline-block; width:10px; height:10px; opacity:1;"></span>)</p>
595        <p>Typical UK demand (<span class="energy-item-ref" style="display:inline-block; width:30px; height:10px; opacity:1;"></span>)</p>
596        `;
597        help1.style.top = '20%';
598        help1.style.left = '10%';
599        help1.setAttribute("onclick","app.toggleHelp()");
600        list.append(help1);
601    var help2 = document.createElement('div');
602        help2.className = 'help-float no-help';
603        help2.innerHTML = `Move left/right`;
604        help2.style.bottom = '0%';
605        help2.style.left = '30%';
606        help2.setAttribute("onclick","app.toggleHelp()");
607        list.append(help2);
608    }
609
610
611function shiftItems(items,shift) {
612    limit = 50
613    for (let i = 0; i < items.length; i++) {
614        left = parseInt(items[i].style.left);
615        if (!isNaN(left)) {
616            if (shift < 0) {limit = Math.max(limit,left)}
617                      else {limit = Math.min(limit,left)}
618            items[i].style.left = `${left+shift}%`;
619        }
620    }
621    return limit
622}
623
624function shift(shift) {
625    limit = shiftItems(document.getElementsByClassName('movable'), shift)
626    if ((shift > 0) && (limit > 20)) {
627        shiftItems(document.getElementsByClassName('movable'), -shift)
628    }
629    if ((shift < 0) && (limit < 80)) {
630        shiftItems(document.getElementsByClassName('movable'), -shift)
631    }
632}
633
634function energyGrid(d,type) {
635    var data = d.el;
636    if (type == 'gas') {data = d.gas}
637
638    let watts = data.map(x => parseInt(x.watt));
639    watts.sort(function(a, b){return a-b});
640    var len = watts.length;
641    var q1 = watts[Math.floor(len*.2) -1];
642    var q2 = watts[Math.floor(len*.4) -1];
643    var q3 = watts[Math.floor(len*.6) -1];
644    var q4 = watts[Math.floor(len*.8) -1];
645    var q5 = watts[Math.floor(len) -1];
646
647    switchEnergy = "<span class='feature-item' onclick=\"app.energyGrid('gas')\">&nbsp;Electricity&nbsp;</span>";
648    if (type == 'gas') {
649            switchEnergy = "<span class='feature-item' onclick=\"app.energyGrid('el')\"> &nbsp; Gas &nbsp; </span>";
650    }
651    document.getElementById('title').innerHTML =  `${switchEnergy} from <span class='feature-item q1'>&nbsp;Low&nbsp;</span> to <span class='feature-item q5'>&nbsp;High&nbsp;</span>`;
652    var list = document.getElementById('list');
653    list.innerHTML = ''; // start afresh
654    list.className = `feature-grid`;
655
656    var lastMonth = '';
657    var lastDay = 7;
658    var row = document.createElement('div');
659    var rowNumber = 0;
660
661    let acts     = d.act.map(x => x.activity);
662    let actDates = d.act.map(x => new Date(x.dt_activity.replace(/-/g, "/")).toDateString());
663    actDateDict = {};
664    for (i=0; i<actDates.length; i++) {
665        if (actDateDict[actDates[i]] === undefined) {
666            actDateDict[actDates[i]] = [acts[i]];
667        } else {
668            arr = actDateDict[actDates[i]];
669            arr.push(acts[i]);
670            actDateDict[actDates[i]] = arr;
671        }
672    }
673    data.forEach(e => {
674        var dt = new Date(e.dt.replace(/-/g, "/"));
675        var day = dt.toLocaleString(getLang(), { weekday: 'short', day: 'numeric', month: 'numeric'});
676        let elDate = dt.toDateString();
677        var month = dt.toLocaleString(getLang(), {month: 'long', year: 'numeric'});
678        var weekDay = ((dt.getDay() + 6) % 7) +0.5; // Mon 1 - Sun 7
679        if (weekDay > lastDay) { rowNumber += 1 }
680        lastDay = weekDay;
681
682        if (month != lastMonth) {
683            rowNumber += 1
684            var monthDiv = document.createElement('div');
685                monthDiv.innerHTML = month;
686                monthDiv.style.top = `${rowNumber * 7}%`;
687                monthDiv.style.left = `10%`;
688                monthDiv.style.position = `absolute`;
689            list.appendChild(monthDiv);
690            rowNumber += 0.5
691        }
692        lastMonth = month;
693
694        var quintile = 'q1';
695        if (e.watt > q1) {quintile = 'q2'}
696        if (e.watt > q2) {quintile = 'q3'}
697        if (e.watt > q3) {quintile = 'q4'}
698        if (e.watt > q4) {quintile = 'q5'}
699        var weekend = '';
700        if (weekDay > 5) {weekDay += 0.2}
701
702        var div = document.createElement('div');
703            div.className = `energy-act-item ${quintile}`;
704            div.setAttribute("onclick",`app.toggleId("label_${day}",".feature-label-float")`);
705            div.style.width  = "12%"
706            div.style.height = "7%"
707            div.style.top  = `${rowNumber * 7}%`;
708            div.style.left  = `${weekDay * 12}%`;
709        list.append(div);
710
711        var eRange = {'q1': 'low', 'q2': 'below average', 'q3': 'average', 'q4': 'above average', 'q5': 'high'}
712        var label = document.createElement('div');
713            label.className  = `feature-label-float ${quintile}`;
714            label.id         = `label_${day}`;
715            label.setAttribute("onclick",`app.toggleId("label_${day}",".feature-label-float")`);
716            var acts = '';
717            if (actDateDict[elDate] !== undefined) {
718                acts = actDateDict[elDate].join('<br>');
719                var actCnt = document.createElement('div');
720                    actCnt.className  = `act-item-count`;
721                    actCnt.innerHTML = actDateDict[elDate].length;
722                    div.appendChild(actCnt);
723            }
724            label.innerHTML  = `${day}<br><b>${parseInt(e.watt)}W (${eRange[quintile]})</b><br>${acts}`;
725            label.style.top  = `${rowNumber * 7}%`;
726            if (weekDay < 4) {
727                label.style.left  = `${(weekDay+1) * 12}%`;
728            } else {
729                label.style.right  = `${(8-weekDay) * 12}%`;
730            }
731            label.style.display = 'none';
732        list.append(label);
733    })
734
735    var tipText = document.createElement('div');
736    tipText.className = "help help-top no-help";
737    tipText.setAttribute("onclick","app.toggleHelp()");
738    tipText.innerHTML = `${switchEnergy} tap to switch between Gas and Electricity<br>
739        <br> Colours <br>
740        <span style='color: var(--ft-body)'>
741        <div class='feature-item q1'>Lowest 20% (< ${parseInt(q1)}W)</div><br>
742        <div class='feature-item q2'> < ${parseInt(q2)}W</div><br>
743        <div class='feature-item q3'> < ${parseInt(q3)}W</div><br>
744        <div class='feature-item q4'> < ${parseInt(q4)}W</div><br>
745        <div class='feature-item q5'>Highest 20% (< ${parseInt(q5)}W)</div></span><br>
746        Tap squares to see details.<br>
747        <div> <span class='act-item-count'>2</span> Number of activities that day</div></span>`
748
749    list.appendChild(tipText);
750    app.defaultView();
751    document.getElementById('footer').style.display  = 'none';
752    document.getElementById('list').style.display    = '';
753}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.