1
2const blockSeconds = 12 * 3600; // must match config.GraphBlockSize.Seconds()
3
4var tileWidth = 1024;
5var tileHeight = 288;
6
7const timeStep = 2 * 3600; // one label every 2 hours
8
9const isMobile = window.matchMedia("(hover: none) and (pointer: coarse)").matches;
10if (isMobile) {
11 tileWidth *= 4;
12 tileHeight *= 4;
13}
14
15const track = document.getElementById("graph-track");
16const scroll = document.getElementById("graph-scroll");
17
18let loaded = new Set();
19
20let centerTime = Math.floor(Date.now()/1000);
21
22// round to block boundary
23function snap(t) {
24 return Math.floor(t / blockSeconds) * blockSeconds;
25}
26
27
28function formatLocalTime(unix) {
29 return new Date(unix * 1000).toLocaleString(undefined, {
30 day: "2-digit",
31 month: "2-digit",
32 hour: "2-digit",
33 minute: "2-digit",
34 hour12: false
35 });
36}
37
38function getBlockTimestamp(block)
39{
40 let img = block.querySelector(".graph-tile");
41
42 return Number(
43 img.src
44 .split("/")
45 .pop()
46 .split(".")[0]
47 );
48}
49
50function addBlock(timestamp, before=false)
51{
52 timestamp = snap(timestamp);
53
54 if(loaded.has(timestamp))
55 return;
56
57 loaded.add(timestamp);
58
59
60 let block = document.createElement("div");
61 block.className = "graph-block";
62
63
64 let img = document.createElement("img");
65
66 img.className = "graph-tile";
67
68 img.width = tileWidth;
69 img.height = tileHeight;
70
71 img.src =
72 `https://caffeine.ontake.dev/graph/${timestamp}.png`;
73
74
75 block.appendChild(img);
76
77
78 // add time labels
79 for(let t = timestamp; t < timestamp + blockSeconds; t += timeStep)
80 {
81 let label = document.createElement("div");
82
83 label.className = "time-label";
84
85 let x =
86 ((t - timestamp) / blockSeconds) * tileWidth;
87
88
89 label.style.left = `${x}px`;
90
91
92 label.textContent = formatLocalTime(t);
93
94
95 block.appendChild(label);
96 }
97
98
99 if(before)
100 track.prepend(block);
101 else
102 track.appendChild(block);
103}
104
105
106function loadAround(t)
107{
108 // Older tiles: add in reverse chronological order
109 for (let i = -1; i >= -3; i--)
110 {
111 addBlock(
112 t + i * blockSeconds,
113 true
114 );
115 }
116
117 // Current + future tiles
118 for (let i = 0; i <= 3; i++)
119 {
120 addBlock(
121 t + i * blockSeconds,
122 false
123 );
124 }
125}
126
127
128loadAround(centerTime);
129
130
131// initial position
132setTimeout(()=>{
133
134 scroll.scrollLeft =
135 scroll.scrollWidth/2 - scroll.clientWidth/2;
136
137},100);
138
139
140
141scroll.addEventListener(
142"scroll",
143()=>{
144
145
146 let left =
147 scroll.scrollLeft;
148
149
150 let max =
151 scroll.scrollWidth -
152 scroll.clientWidth;
153
154
155
156 // near left edge
157 if(left < tileWidth / 2)
158 {
159 let first =
160 track.firstChild;
161
162
163 if(first)
164 {
165 let ts = getBlockTimestamp(first);
166
167 let oldWidth = scroll.scrollWidth;
168
169 addBlock(ts - blockSeconds, true);
170
171 requestAnimationFrame(() => {
172 let newWidth = scroll.scrollWidth;
173 scroll.scrollLeft += newWidth - oldWidth;
174 });
175 }
176 }
177
178
179
180 // near right edge
181 if(max-left < tileWidth / 2)
182 {
183
184 let last =
185 track.lastChild;
186
187
188 if(last)
189 {
190 let ts = getBlockTimestamp(last);
191
192
193 addBlock(
194 ts+blockSeconds
195 );
196 }
197
198 }
199
200
201});