bamtigraph 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,3161 @@
1
+ /*! BamtiGraph 0.1.0 | BSD-3-Clause | See LICENSE */
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ Chart: () => Chart,
25
+ LIMITS: () => LIMITS,
26
+ RenderResult: () => RenderResult,
27
+ VERSION: () => VERSION,
28
+ aggregate: () => aggregate,
29
+ color: () => color,
30
+ compareImages: () => compareImages,
31
+ counterRate: () => counterRate,
32
+ daily: () => daily,
33
+ dashboard: () => dashboard,
34
+ decodeImage: () => decodeImage,
35
+ default: () => index_default,
36
+ defaults: () => defaults,
37
+ differenceImage: () => differenceImage,
38
+ downloadBytes: () => downloadBytes,
39
+ drawImageToCanvas: () => drawImageToCanvas,
40
+ encodePNG: () => encodePNG,
41
+ epoch: () => epoch,
42
+ formatTime: () => formatTime,
43
+ formatValue: () => formatValue,
44
+ monthly: () => monthly,
45
+ parseCSV: () => parseCSV,
46
+ readCanvas: () => readCanvas,
47
+ regularSeries: () => regularSeries,
48
+ render: () => render,
49
+ series: () => series,
50
+ toCSV: () => toCSV,
51
+ traffic: () => traffic,
52
+ weekly: () => weekly,
53
+ yearly: () => yearly
54
+ });
55
+ module.exports = __toCommonJS(index_exports);
56
+ var VERSION = "0.1.0";
57
+ var LIMITS = Object.freeze({
58
+ pixels: 16e6,
59
+ layerPixels: 4e7,
60
+ ticks: 5e3,
61
+ series: 128,
62
+ samples: 2e6,
63
+ text: 4096
64
+ });
65
+ var finite = (v) => Number.isFinite(v);
66
+ var round = (v) => Math.floor(v + 0.5);
67
+ var clamp = (v, a, b) => Math.max(a, Math.min(b, v));
68
+ var own = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
69
+ var missing = (v) => v === null || v === void 0 || typeof v === "number" && Number.isNaN(v);
70
+ function check(ok, message) {
71
+ if (!ok) throw new RangeError(message);
72
+ }
73
+ function text(s, name) {
74
+ check(
75
+ typeof s === "string" && s.length <= LIMITS.text && !/[\r\n\u0000]/.test(s),
76
+ name + " must be a single-line string."
77
+ );
78
+ return s;
79
+ }
80
+ function number(v, name, lo = -Infinity, hi = Infinity) {
81
+ check(
82
+ finite(v) && v >= lo && v <= hi,
83
+ name + " is outside its finite range."
84
+ );
85
+ return v;
86
+ }
87
+ function integer(v, name, lo, hi) {
88
+ check(Number.isInteger(v), name + " must be an integer.");
89
+ return number(v, name, lo, hi);
90
+ }
91
+ function record(o) {
92
+ return o !== null && typeof o === "object" && (Object.getPrototypeOf(o) === Object.prototype || Object.getPrototypeOf(o) === null);
93
+ }
94
+ function clone(v) {
95
+ if (Array.isArray(v) || ArrayBuffer.isView(v))
96
+ return Array.from(v, (value) => clone(value));
97
+ if (v instanceof Date) return new Date(v.getTime());
98
+ if (record(v)) {
99
+ const r = {};
100
+ for (const k of Object.keys(v)) {
101
+ check(
102
+ !["__proto__", "prototype", "constructor"].includes(k),
103
+ "Unsafe property name."
104
+ );
105
+ r[k] = clone(v[k]);
106
+ }
107
+ return r;
108
+ }
109
+ return v;
110
+ }
111
+ function merge(a, b) {
112
+ check(record(b), "Options must be a plain object.");
113
+ const r = clone(a);
114
+ for (const k of Object.keys(b)) {
115
+ check(
116
+ !["__proto__", "prototype", "constructor"].includes(k),
117
+ "Unsafe property name."
118
+ );
119
+ const previous = r[k], next = b[k];
120
+ r[k] = record(previous) && record(next) ? merge(previous, next) : clone(next);
121
+ }
122
+ return r;
123
+ }
124
+ function freeze(o) {
125
+ if (o && typeof o === "object") {
126
+ for (const v of Object.values(o)) freeze(v);
127
+ Object.freeze(o);
128
+ }
129
+ return o;
130
+ }
131
+ function color(value) {
132
+ if (Array.isArray(value) || ArrayBuffer.isView(value)) {
133
+ check(
134
+ value.length === 3 || value.length === 4,
135
+ "Color needs 3 or 4 channels."
136
+ );
137
+ const c = Array.from(value);
138
+ c.forEach((v) => integer(v, "Color channel", 0, 255));
139
+ if (c.length === 3) c.push(255);
140
+ return c;
141
+ }
142
+ const named = {
143
+ black: "#000000",
144
+ white: "#ffffff",
145
+ red: "#ff0000",
146
+ green: "#008000",
147
+ blue: "#0000ff",
148
+ transparent: "#00000000"
149
+ };
150
+ check(
151
+ typeof value === "string",
152
+ "Color must be hexadecimal or an RGB(A) array."
153
+ );
154
+ let h = named[value.toLowerCase()] || value;
155
+ check(
156
+ /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(h),
157
+ "Invalid color: " + value
158
+ );
159
+ h = h.slice(1);
160
+ if (h.length <= 4) h = [...h].map((c) => c + c).join("");
161
+ if (h.length === 6) h += "ff";
162
+ return [0, 2, 4, 6].map((i) => parseInt(h.slice(i, i + 2), 16));
163
+ }
164
+ function epoch(value) {
165
+ if (value instanceof Date) value = value.getTime() / 1e3;
166
+ if (typeof value === "string") {
167
+ const v = value.trim();
168
+ if (/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(v)) value = Number(v);
169
+ else {
170
+ const m = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?(Z|[+-]\d\d:\d\d)$/i.exec(
171
+ v
172
+ );
173
+ check(
174
+ m,
175
+ "Datetime strings require ISO 8601 with an explicit offset; numeric timestamps are seconds."
176
+ );
177
+ const [y, mo, d, h, mi, s] = [
178
+ m[1],
179
+ m[2],
180
+ m[3],
181
+ m[4],
182
+ m[5],
183
+ m[6] || "0"
184
+ ].map(Number);
185
+ check(
186
+ y >= 1 && mo >= 1 && mo <= 12 && d >= 1 && h < 24 && mi < 60 && s < 60,
187
+ "Invalid calendar date."
188
+ );
189
+ const test = /* @__PURE__ */ new Date(0);
190
+ test.setUTCFullYear(y, mo - 1, d);
191
+ test.setUTCHours(h, mi, s, 0);
192
+ check(
193
+ test.getUTCMonth() === mo - 1 && test.getUTCDate() === d,
194
+ "Invalid calendar date."
195
+ );
196
+ if (m[8].toUpperCase() !== "Z")
197
+ check(
198
+ Number(m[8].slice(1, 3)) <= 23 && Number(m[8].slice(4)) <= 59,
199
+ "Invalid timezone offset."
200
+ );
201
+ value = Date.parse(v) / 1e3;
202
+ }
203
+ }
204
+ return number(value, "Timestamp", -62135596800, 253402300799999e-3);
205
+ }
206
+ function samples(timestamps, values) {
207
+ check(
208
+ (Array.isArray(timestamps) || ArrayBuffer.isView(timestamps)) && (Array.isArray(values) || ArrayBuffer.isView(values)),
209
+ "Timestamps and values must be arrays."
210
+ );
211
+ check(
212
+ timestamps.length === values.length && timestamps.length <= LIMITS.samples,
213
+ "Sample lengths must match and stay within the sample limit."
214
+ );
215
+ const ts = Array.from(timestamps, epoch), vs = Array.from(
216
+ values,
217
+ (v, i) => missing(v) ? NaN : number(v, "Value at " + i)
218
+ );
219
+ for (let i = 1; i < ts.length; i++)
220
+ check(
221
+ ts[i] > ts[i - 1],
222
+ "Timestamps must be strictly increasing (index " + i + ")."
223
+ );
224
+ return { timestamps: ts, values: vs };
225
+ }
226
+ var DEFAULTS = freeze({
227
+ title: "",
228
+ verticalLabel: "",
229
+ watermark: "",
230
+ series: [],
231
+ timeAxis: {
232
+ start: null,
233
+ end: null,
234
+ mode: "auto",
235
+ timezone: "UTC",
236
+ minorSeconds: null,
237
+ majorSeconds: null,
238
+ labelSeconds: null,
239
+ labelFormat: null,
240
+ ticks: null,
241
+ majorTicks: null,
242
+ minorTicks: null,
243
+ labelOffsetSeconds: 0
244
+ },
245
+ yAxis: {
246
+ minimum: 0,
247
+ maximum: null,
248
+ majorStep: null,
249
+ minorDivisions: 5,
250
+ base: 1e3,
251
+ scaleFactor: null,
252
+ suffix: null,
253
+ decimals: null,
254
+ legendDecimals: 2,
255
+ showZeroSuffix: false
256
+ },
257
+ layout: {
258
+ width: 595,
259
+ plotHeight: 122,
260
+ left: 64,
261
+ right: 31,
262
+ top: 34,
263
+ titleY: 8,
264
+ titleOffsetX: 27,
265
+ unitX: 5,
266
+ xLabelGap: 5,
267
+ yLabelGap: 6,
268
+ legendGap: 20,
269
+ legendRowHeight: 14,
270
+ legendBottom: 7,
271
+ legend: "reference",
272
+ antialias: 4,
273
+ pixelScale: 1,
274
+ legendLayout: {
275
+ nameX: 30,
276
+ swatchX: 15,
277
+ swatchWidth: 9,
278
+ swatchHeight: 10,
279
+ referenceWidth: 595,
280
+ autoScaleColumns: true,
281
+ compact: [
282
+ [102, 228],
283
+ [244, 370],
284
+ [386, 512]
285
+ ],
286
+ expanded: [
287
+ [124, 250],
288
+ [289, 415],
289
+ [454, 580]
290
+ ],
291
+ aligned: [
292
+ [102, 250],
293
+ [267, 415],
294
+ [432, 580]
295
+ ]
296
+ }
297
+ },
298
+ theme: {
299
+ background: "#f3f3f3",
300
+ canvas: "#ffffff",
301
+ shadeLight: "#cfcfcf",
302
+ shadeDark: "#9e9e9e",
303
+ text: "#000000",
304
+ minorGrid: "#8f8f8f3c",
305
+ majorGrid: "#df4f4f3c",
306
+ axis: "#777777",
307
+ arrow: "#7f1f1f",
308
+ watermark: "#aaaaaa",
309
+ frame: "#000000",
310
+ gridFront: true,
311
+ gridDash: [1, 1],
312
+ titleSize: 14,
313
+ axisSize: 11,
314
+ unitSize: 10,
315
+ legendSize: 11,
316
+ watermarkSize: 8,
317
+ captionSize: 11,
318
+ titleAdvance: 8,
319
+ axisAdvance: 6,
320
+ legendAdvance: 7
321
+ },
322
+ fonts: {
323
+ mode: "system",
324
+ family: '"DejaVu Sans Mono", "Liberation Mono", Consolas, monospace',
325
+ titleFamily: null,
326
+ unitFamily: null,
327
+ captionFamily: "Arial, sans-serif",
328
+ strictGlyphs: true
329
+ },
330
+ legendLabels: ["Current:", "Average:", "Maximum:"],
331
+ missingLabel: "NaN",
332
+ hRules: [],
333
+ vRules: []
334
+ });
335
+ function series(name, timestamps, values, options = {}) {
336
+ const s = merge(
337
+ {
338
+ name,
339
+ timestamps,
340
+ values,
341
+ kind: "line",
342
+ color: "#0000cc",
343
+ lineWidth: 0.7,
344
+ outline: null,
345
+ baseline: 0,
346
+ interpolation: "linear",
347
+ gapAfter: 0,
348
+ legendValues: null
349
+ },
350
+ options
351
+ );
352
+ text(s.name, "Series name");
353
+ const data = samples(s.timestamps, s.values);
354
+ s.timestamps = data.timestamps;
355
+ s.values = data.values;
356
+ check(["line", "area"].includes(s.kind), "Series kind must be line or area.");
357
+ check(
358
+ ["linear", "step-post"].includes(s.interpolation),
359
+ "Interpolation must be linear or step-post."
360
+ );
361
+ number(s.lineWidth, "Line width", 0.01, 128);
362
+ number(s.baseline, "Baseline");
363
+ number(s.gapAfter, "Gap threshold", 0);
364
+ s.color = color(s.color);
365
+ if (s.outline !== null) s.outline = color(s.outline);
366
+ if (s.legendValues !== null) {
367
+ check(record(s.legendValues), "Legend overrides must be an object.");
368
+ for (const k of ["current", "average", "maximum"])
369
+ s.legendValues[k] = missing(s.legendValues[k]) ? null : number(s.legendValues[k], "Legend override");
370
+ }
371
+ return freeze(s);
372
+ }
373
+ function regularSeries(name, values, start, step = 300, options = {}) {
374
+ start = epoch(start);
375
+ number(step, "Step", Number.MIN_VALUE);
376
+ const first = start;
377
+ return series(
378
+ name,
379
+ Array.from(values, (_, i) => first + i * step),
380
+ values,
381
+ options
382
+ );
383
+ }
384
+ function modeAxis(mode, options = {}) {
385
+ return merge(
386
+ DEFAULTS.timeAxis,
387
+ merge(
388
+ { mode },
389
+ typeof options === "string" ? { timezone: options } : options
390
+ )
391
+ );
392
+ }
393
+ function daily(options = {}) {
394
+ return modeAxis("daily", options);
395
+ }
396
+ function weekly(options = {}) {
397
+ return modeAxis("weekly", options);
398
+ }
399
+ function monthly(options = {}) {
400
+ return modeAxis("monthly", options);
401
+ }
402
+ function yearly(options = {}) {
403
+ return modeAxis("yearly", options);
404
+ }
405
+ function dimensions(c) {
406
+ const l = c.layout;
407
+ return [
408
+ l.width,
409
+ l.top + l.plotHeight + (l.legend === "none" ? 18 : l.legendGap + Math.max(1, c.series.length) * l.legendRowHeight + l.legendBottom)
410
+ ];
411
+ }
412
+ function dash(d) {
413
+ if (d === null) return;
414
+ check(Array.isArray(d) && d.length === 2, "Dash must be null or [on, off].");
415
+ d.forEach((v) => integer(v, "Dash interval", 1, 16384));
416
+ }
417
+ function normalize(options) {
418
+ const c = merge(DEFAULTS, options);
419
+ check(
420
+ Array.isArray(c.series) && c.series.length <= LIMITS.series,
421
+ "Invalid series count."
422
+ );
423
+ c.series = c.series.map((s) => series(s.name, s.timestamps, s.values, s));
424
+ for (const k of [
425
+ "title",
426
+ "verticalLabel",
427
+ "watermark",
428
+ "missingLabel"
429
+ ])
430
+ text(c[k], k);
431
+ check(
432
+ Array.isArray(c.legendLabels) && c.legendLabels.length === 3,
433
+ "Three statistic labels are required."
434
+ );
435
+ c.legendLabels.forEach((v) => text(v, "Statistic label"));
436
+ const a = c.timeAxis, l = c.layout, y = c.yAxis, t = c.theme, f = c.fonts;
437
+ check(
438
+ ["auto", "daily", "weekly", "monthly", "yearly", "custom"].includes(a.mode),
439
+ "Unknown time axis mode."
440
+ );
441
+ text(a.timezone, "Timezone");
442
+ zoneFormatter(a.timezone);
443
+ for (const k of ["start", "end"])
444
+ if (a[k] !== null) a[k] = epoch(a[k]);
445
+ if (a.start !== null && a.end !== null)
446
+ check(
447
+ a.end > a.start,
448
+ "Time end must exceed start."
449
+ );
450
+ for (const k of ["minorSeconds", "majorSeconds", "labelSeconds"])
451
+ if (a[k] !== null) number(a[k], k, 1e-3);
452
+ number(a.labelOffsetSeconds, "Label offset", -31622400, 31622400);
453
+ if (a.labelFormat !== null) {
454
+ text(a.labelFormat, "Label format");
455
+ formatTime(0, "UTC", a.labelFormat);
456
+ }
457
+ if (a.ticks !== null) {
458
+ check(
459
+ Array.isArray(a.ticks) && a.ticks.length <= LIMITS.ticks,
460
+ "Too many or invalid ticks."
461
+ );
462
+ const ticks = a.ticks.map((v) => ({
463
+ time: epoch(v.time),
464
+ label: text(v.label, "Tick label")
465
+ }));
466
+ for (let i = 1; i < ticks.length; i++)
467
+ check(
468
+ ticks[i].time > ticks[i - 1].time,
469
+ "Explicit ticks must be strictly increasing."
470
+ );
471
+ a.ticks = ticks;
472
+ }
473
+ for (const k of ["minorTicks", "majorTicks"])
474
+ if (a[k] !== null) {
475
+ const input = a[k];
476
+ check(
477
+ Array.isArray(input) && input.length <= LIMITS.ticks,
478
+ "Too many or invalid ticks."
479
+ );
480
+ const ticks = input.map(epoch);
481
+ for (let i = 1; i < ticks.length; i++)
482
+ check(
483
+ ticks[i] > ticks[i - 1],
484
+ "Explicit ticks must be strictly increasing."
485
+ );
486
+ a[k] = ticks;
487
+ }
488
+ for (const k of ["minimum", "maximum", "majorStep", "scaleFactor"])
489
+ if (y[k] !== null) number(y[k], k);
490
+ for (const k of ["majorStep", "scaleFactor"])
491
+ if (y[k] !== null) check(y[k] > 0, k + " must be positive.");
492
+ if (y.minimum !== null && y.maximum !== null)
493
+ check(y.maximum > y.minimum, "Y maximum must exceed minimum.");
494
+ check(y.base === 1e3 || y.base === 1024, "Unit base must be 1000 or 1024.");
495
+ integer(y.minorDivisions, "Minor divisions", 1, 100);
496
+ integer(y.legendDecimals, "Legend decimals", 0, 12);
497
+ if (y.decimals !== null) integer(y.decimals, "Decimals", 0, 12);
498
+ if (y.suffix !== null) text(y.suffix, "Suffix");
499
+ integer(l.width, "Width", 400, 8192);
500
+ integer(l.plotHeight, "Plot height", 30, 4096);
501
+ for (const k of [
502
+ "left",
503
+ "right",
504
+ "top",
505
+ "titleY",
506
+ "unitX",
507
+ "xLabelGap",
508
+ "yLabelGap",
509
+ "legendGap",
510
+ "legendRowHeight",
511
+ "legendBottom"
512
+ ])
513
+ integer(l[k], k, 0, 16384);
514
+ check(
515
+ l.left >= 20 && l.right >= 12 && l.top >= 12 && l.width - l.left - l.right >= 100,
516
+ "Insufficient plot margins."
517
+ );
518
+ number(l.titleOffsetX, "Title offset", -8192, 8192);
519
+ check(
520
+ ["reference", "aligned", "none"].includes(l.legend),
521
+ "Invalid legend mode."
522
+ );
523
+ if (l.legend !== "none")
524
+ check(
525
+ l.legendGap >= 14 && l.legendRowHeight >= 10,
526
+ "Legend spacing is insufficient."
527
+ );
528
+ integer(l.antialias, "Antialias", 1, 8);
529
+ integer(l.pixelScale, "Pixel scale", 1, 8);
530
+ const ll = l.legendLayout;
531
+ for (const k of [
532
+ "nameX",
533
+ "swatchX",
534
+ "swatchWidth",
535
+ "swatchHeight",
536
+ "referenceWidth"
537
+ ])
538
+ integer(ll[k], k, 0, 16384);
539
+ check(
540
+ ll.swatchWidth >= 3 && ll.swatchHeight >= 3 && ll.referenceWidth > ll.nameX,
541
+ "Invalid legend geometry."
542
+ );
543
+ for (const key of ["compact", "expanded", "aligned"]) {
544
+ const cols = ll[key];
545
+ check(Array.isArray(cols) && cols.length === 3, "Invalid legend columns.");
546
+ let last = ll.nameX;
547
+ for (const p of cols) {
548
+ check(
549
+ Array.isArray(p) && p.length === 2 && finite(p[0]) && finite(p[1]) && last < p[0] && p[0] < p[1] && p[1] < ll.referenceWidth,
550
+ "Legend columns must not overlap."
551
+ );
552
+ last = p[1];
553
+ }
554
+ }
555
+ for (const k of [
556
+ "background",
557
+ "canvas",
558
+ "shadeLight",
559
+ "shadeDark",
560
+ "text",
561
+ "minorGrid",
562
+ "majorGrid",
563
+ "axis",
564
+ "arrow",
565
+ "watermark",
566
+ "frame"
567
+ ])
568
+ t[k] = color(t[k]);
569
+ check(
570
+ t.background[3] === 255 && t.canvas[3] === 255,
571
+ "Background and canvas must be opaque."
572
+ );
573
+ dash(t.gridDash);
574
+ check(t.gridDash !== null, "Grid dash is required.");
575
+ for (const k of [
576
+ "titleSize",
577
+ "axisSize",
578
+ "unitSize",
579
+ "legendSize",
580
+ "watermarkSize",
581
+ "captionSize",
582
+ "titleAdvance",
583
+ "axisAdvance",
584
+ "legendAdvance"
585
+ ])
586
+ number(t[k], k, 1, 128);
587
+ check(
588
+ ["system", "bitmap"].includes(f.mode),
589
+ "Font mode must be system or bitmap."
590
+ );
591
+ text(f.family, "Font family");
592
+ for (const k of ["titleFamily", "unitFamily", "captionFamily"])
593
+ if (f[k] !== null) text(f[k], k);
594
+ function rule(input, field) {
595
+ const r = merge(
596
+ { color: "#990000", width: 1, dash: [3, 2] },
597
+ input
598
+ );
599
+ if (field === "time") r.time = epoch(r.time);
600
+ else r.value = number(r.value, "Rule value");
601
+ number(r.width, "Rule width", 0.1, 128);
602
+ dash(r.dash);
603
+ r.color = color(r.color);
604
+ return r;
605
+ }
606
+ check(
607
+ Array.isArray(c.hRules) && c.hRules.length <= 1e3,
608
+ "Invalid rule count."
609
+ );
610
+ c.hRules = c.hRules.map((r) => rule(r, "value"));
611
+ check(
612
+ Array.isArray(c.vRules) && c.vRules.length <= 1e3,
613
+ "Invalid rule count."
614
+ );
615
+ c.vRules = c.vRules.map((r) => rule(r, "time"));
616
+ const [w, h] = dimensions(c);
617
+ check(
618
+ w * h * l.pixelScale ** 2 <= LIMITS.pixels,
619
+ "Output allocation exceeds the pixel limit."
620
+ );
621
+ check(
622
+ (l.width - l.left - l.right + 1) * (l.plotHeight + 1) * l.antialias ** 2 <= LIMITS.layerPixels,
623
+ "Supersampled layer exceeds the pixel limit."
624
+ );
625
+ return freeze(c);
626
+ }
627
+ var Surface = class _Surface {
628
+ width;
629
+ height;
630
+ data;
631
+ constructor(width, height, fill = null) {
632
+ integer(width, "Image width", 1, 65536);
633
+ integer(height, "Image height", 1, 65536);
634
+ check(width * height <= LIMITS.layerPixels, "Image is too large.");
635
+ this.width = width;
636
+ this.height = height;
637
+ this.data = new Uint8ClampedArray(width * height * 4);
638
+ if (fill) this.rect(0, 0, width, height, fill);
639
+ }
640
+ pixel(x, y, c, blend = false) {
641
+ if (x < 0 || y < 0 || x >= this.width || y >= this.height) return;
642
+ const i = (y * this.width + x) * 4, d = this.data;
643
+ if (!blend || c[3] === 255) {
644
+ d[i] = c[0];
645
+ d[i + 1] = c[1];
646
+ d[i + 2] = c[2];
647
+ d[i + 3] = c[3];
648
+ return;
649
+ }
650
+ if (c[3] === 0) return;
651
+ const sa = c[3], da = d[i + 3], alpha = sa * 255 + da * (255 - sa);
652
+ for (let k = 0; k < 3; k++)
653
+ d[i + k] = Math.floor(
654
+ (c[k] * sa * 255 + d[i + k] * da * (255 - sa) + alpha / 2) / alpha
655
+ );
656
+ d[i + 3] = Math.floor((alpha + 127) / 255);
657
+ }
658
+ rect(x0, y0, x1, y1, c) {
659
+ x0 = Math.max(0, Math.ceil(x0));
660
+ y0 = Math.max(0, Math.ceil(y0));
661
+ x1 = Math.min(this.width, Math.ceil(x1));
662
+ y1 = Math.min(this.height, Math.ceil(y1));
663
+ for (let y = y0; y < y1; y++)
664
+ for (let x = x0, i = (y * this.width + x0) * 4; x < x1; x++, i += 4) {
665
+ this.data[i] = c[0];
666
+ this.data[i + 1] = c[1];
667
+ this.data[i + 2] = c[2];
668
+ this.data[i + 3] = c[3];
669
+ }
670
+ }
671
+ over(src, dx = 0, dy = 0) {
672
+ const p = [0, 0, 0, 0];
673
+ for (let y = Math.max(0, -dy); y < Math.min(src.height, this.height - dy); y++)
674
+ for (let x = Math.max(0, -dx); x < Math.min(src.width, this.width - dx); x++) {
675
+ const i = (y * src.width + x) * 4;
676
+ if (src.data[i + 3]) {
677
+ p[0] = src.data[i];
678
+ p[1] = src.data[i + 1];
679
+ p[2] = src.data[i + 2];
680
+ p[3] = src.data[i + 3];
681
+ this.pixel(x + dx, y + dy, p, true);
682
+ }
683
+ }
684
+ }
685
+ polygon(points, c) {
686
+ if (points.length < 3) return;
687
+ let lo = Infinity, hi = -Infinity;
688
+ for (const p of points) {
689
+ lo = Math.min(lo, p[1]);
690
+ hi = Math.max(hi, p[1]);
691
+ }
692
+ for (let y = Math.max(0, Math.ceil(lo)); y <= Math.min(this.height - 1, Math.floor(hi)); y++) {
693
+ const xs = [];
694
+ let a = points[points.length - 1];
695
+ for (const b of points) {
696
+ if (a[1] === b[1]) {
697
+ if (y === a[1])
698
+ this.rect(
699
+ Math.ceil(Math.min(a[0], b[0])),
700
+ y,
701
+ Math.floor(Math.max(a[0], b[0])) + 1,
702
+ y + 1,
703
+ c
704
+ );
705
+ } else if (a[1] <= y && y < b[1] || b[1] <= y && y < a[1])
706
+ xs.push(a[0] + (y - a[1]) / (b[1] - a[1]) * (b[0] - a[0]));
707
+ a = b;
708
+ }
709
+ xs.sort((a2, b) => a2 - b);
710
+ for (let i = 0; i + 1 < xs.length; i += 2)
711
+ this.rect(
712
+ Math.ceil(xs[i] - 1e-9),
713
+ y,
714
+ Math.floor(xs[i + 1] + 1e-9) + 1,
715
+ y + 1,
716
+ c
717
+ );
718
+ }
719
+ }
720
+ line(a, b, c, width = 1) {
721
+ let x = round(a[0]), y = round(a[1]), x1 = round(b[0]), y1 = round(b[1]);
722
+ width = Math.max(1, round(width));
723
+ if (width > 1) {
724
+ const r = (width - 1) / 2;
725
+ if (x === x1) {
726
+ this.rect(
727
+ x - Math.floor(width / 2),
728
+ Math.min(y, y1),
729
+ x + Math.floor((width - 1) / 2) + 1,
730
+ Math.max(y, y1) + 1,
731
+ c
732
+ );
733
+ return;
734
+ }
735
+ if (y === y1) {
736
+ this.rect(
737
+ Math.min(x, x1),
738
+ y - Math.floor(width / 2),
739
+ Math.max(x, x1) + 1,
740
+ y + Math.floor((width - 1) / 2) + 1,
741
+ c
742
+ );
743
+ return;
744
+ }
745
+ const len = Math.hypot(x1 - x, y1 - y), ox = -(y1 - y) / len * r, oy = (x1 - x) / len * r;
746
+ this.polygon(
747
+ [
748
+ [x + ox, y + oy],
749
+ [x1 + ox, y1 + oy],
750
+ [x1 - ox, y1 - oy],
751
+ [x - ox, y - oy]
752
+ ].map((p) => [round(p[0]), round(p[1])]),
753
+ c
754
+ );
755
+ return;
756
+ }
757
+ const dx = Math.abs(x1 - x), dy = -Math.abs(y1 - y), sx = x < x1 ? 1 : -1, sy = y < y1 ? 1 : -1;
758
+ let err = dx + dy;
759
+ for (; ; ) {
760
+ this.pixel(x, y, c);
761
+ if (x === x1 && y === y1) break;
762
+ const e = 2 * err;
763
+ if (e >= dy) {
764
+ err += dy;
765
+ x += sx;
766
+ }
767
+ if (e <= dx) {
768
+ err += dx;
769
+ y += sy;
770
+ }
771
+ }
772
+ }
773
+ dashed(a, b, c, pattern = [1, 1], width = 1) {
774
+ if (pattern === null) {
775
+ this.line(a, b, c, width);
776
+ return;
777
+ }
778
+ const len = Math.hypot(b[0] - a[0], b[1] - a[1]);
779
+ if (!len) {
780
+ this.pixel(round(a[0]), round(a[1]), c);
781
+ return;
782
+ }
783
+ const dx = (b[0] - a[0]) / len, dy = (b[1] - a[1]) / len;
784
+ for (let s = 0; s <= Math.ceil(len); s += pattern[0] + pattern[1]) {
785
+ const e = Math.min(len, s + pattern[0] - 1);
786
+ this.line(
787
+ [a[0] + dx * s, a[1] + dy * s],
788
+ [a[0] + dx * e, a[1] + dy * e],
789
+ c,
790
+ width
791
+ );
792
+ }
793
+ }
794
+ circle(x, y, r, c) {
795
+ for (let yy = Math.max(0, Math.floor(y - r)); yy <= Math.min(this.height - 1, Math.ceil(y + r)); yy++)
796
+ for (let xx = Math.max(0, Math.floor(x - r)); xx <= Math.min(this.width - 1, Math.ceil(x + r)); xx++)
797
+ if ((xx - x) ** 2 + (yy - y) ** 2 <= r * r) this.pixel(xx, yy, c);
798
+ }
799
+ down(scale) {
800
+ if (scale === 1) return this;
801
+ const out = new _Surface(this.width / scale, this.height / scale), p = [0, 0, 0, 0], n = scale * scale;
802
+ for (let y = 0; y < out.height; y++)
803
+ for (let x = 0; x < out.width; x++) {
804
+ let a = 0, r = 0, g = 0, b = 0;
805
+ for (let sy = 0; sy < scale; sy++)
806
+ for (let sx = 0; sx < scale; sx++) {
807
+ const i = ((y * scale + sy) * this.width + x * scale + sx) * 4, ca = this.data[i + 3];
808
+ a += ca;
809
+ r += this.data[i] * ca;
810
+ g += this.data[i + 1] * ca;
811
+ b += this.data[i + 2] * ca;
812
+ }
813
+ if (a) {
814
+ p[0] = Math.floor(r / a + 0.5);
815
+ p[1] = Math.floor(g / a + 0.5);
816
+ p[2] = Math.floor(b / a + 0.5);
817
+ p[3] = Math.floor(a / n + 0.5);
818
+ out.pixel(x, y, p);
819
+ }
820
+ }
821
+ return out;
822
+ }
823
+ scale(n) {
824
+ if (n === 1) return this;
825
+ const out = new _Surface(this.width * n, this.height * n);
826
+ for (let y = 0; y < out.height; y++)
827
+ for (let x = 0; x < out.width; x++) {
828
+ const s = (Math.floor(y / n) * this.width + Math.floor(x / n)) * 4, i = (y * out.width + x) * 4;
829
+ out.data[i] = this.data[s];
830
+ out.data[i + 1] = this.data[s + 1];
831
+ out.data[i + 2] = this.data[s + 2];
832
+ out.data[i + 3] = this.data[s + 3];
833
+ }
834
+ return out;
835
+ }
836
+ };
837
+ function clipLine(a, b, w, h) {
838
+ let lo = 0, hi = 1;
839
+ const dx = b[0] - a[0], dy = b[1] - a[1];
840
+ for (const [p, q] of [
841
+ [-dx, a[0]],
842
+ [dx, w - a[0]],
843
+ [-dy, a[1]],
844
+ [dy, h - a[1]]
845
+ ]) {
846
+ if (p === 0) {
847
+ if (q < 0) return null;
848
+ } else {
849
+ const u = q / p;
850
+ if (p < 0) lo = Math.max(lo, u);
851
+ else hi = Math.min(hi, u);
852
+ if (lo > hi) return null;
853
+ }
854
+ }
855
+ return [
856
+ [a[0] + lo * dx, a[1] + lo * dy],
857
+ [a[0] + hi * dx, a[1] + hi * dy]
858
+ ];
859
+ }
860
+ function clipPolygon(points, w, h) {
861
+ let ps = points;
862
+ for (const [axis, bound, greater] of [
863
+ [0, 0, true],
864
+ [0, w, false],
865
+ [1, 0, true],
866
+ [1, h, false]
867
+ ]) {
868
+ if (!ps.length) break;
869
+ const out = [], inside = (p) => greater ? p[axis] >= bound : p[axis] <= bound;
870
+ let a = ps[ps.length - 1], ai = inside(a);
871
+ for (const b of ps) {
872
+ const bi = inside(b);
873
+ if (ai !== bi) {
874
+ const q = (bound - a[axis]) / (b[axis] - a[axis]);
875
+ const p = [a[0] + q * (b[0] - a[0]), a[1] + q * (b[1] - a[1])];
876
+ p[axis] = bound;
877
+ out.push(p);
878
+ }
879
+ if (bi) out.push(b);
880
+ a = b;
881
+ ai = bi;
882
+ }
883
+ ps = out;
884
+ }
885
+ return ps;
886
+ }
887
+ function lowerBound(a, x) {
888
+ let l = 0, r = a.length;
889
+ while (l < r) {
890
+ const m = l + r >>> 1;
891
+ if (a[m] < x) l = m + 1;
892
+ else r = m;
893
+ }
894
+ return l;
895
+ }
896
+ function upperBound(a, x) {
897
+ let l = 0, r = a.length;
898
+ while (l < r) {
899
+ const m = l + r >>> 1;
900
+ if (a[m] <= x) l = m + 1;
901
+ else r = m;
902
+ }
903
+ return l;
904
+ }
905
+ function visibleRuns(s, start, end) {
906
+ const ts = s.timestamps, vs = s.values, result = [];
907
+ let run = [];
908
+ const push = () => {
909
+ if (run.length) {
910
+ result.push(run);
911
+ run = [];
912
+ }
913
+ };
914
+ const left = Math.max(0, lowerBound(ts, start) - 1), right = Math.min(ts.length, upperBound(ts, end) + 1);
915
+ for (let i = left; i < right; i++) {
916
+ if (!finite(vs[i])) {
917
+ push();
918
+ continue;
919
+ }
920
+ if (i > left && s.gapAfter > 0 && ts[i] - ts[i - 1] > s.gapAfter) push();
921
+ if (run.length && s.interpolation === "step-post")
922
+ run.push([ts[i], run[run.length - 1][1]]);
923
+ run.push([ts[i], vs[i]]);
924
+ }
925
+ push();
926
+ const clipped = [];
927
+ for (const r of result) {
928
+ const out = [];
929
+ if (r.length === 1) {
930
+ if (r[0][0] >= start && r[0][0] <= end) out.push(r[0]);
931
+ }
932
+ for (let i = 1; i < r.length; i++) {
933
+ const a = r[i - 1], b = r[i];
934
+ if (b[0] < start || a[0] > end) continue;
935
+ const interp = (x) => a[1] * (1 - (x - a[0]) / (b[0] - a[0])) + b[1] * ((x - a[0]) / (b[0] - a[0]));
936
+ const p = a[0] < start ? [start, interp(start)] : a, q = b[0] > end ? [end, interp(end)] : b;
937
+ if (!out.length || out[out.length - 1][0] !== p[0] || out[out.length - 1][1] !== p[1])
938
+ out.push(p);
939
+ out.push(q);
940
+ }
941
+ if (out.length) clipped.push(out);
942
+ }
943
+ return clipped;
944
+ }
945
+ function decimate(ps, start, end, width) {
946
+ if (ps.length <= width * 4) return ps;
947
+ const out = [];
948
+ let pos = 0;
949
+ while (pos < ps.length) {
950
+ const col = Math.floor((ps[pos][0] - start) / (end - start) * width), first = pos;
951
+ let mn = pos, mx = pos;
952
+ while (pos + 1 < ps.length && Math.floor((ps[pos + 1][0] - start) / (end - start) * width) === col) {
953
+ pos++;
954
+ if (ps[pos][1] < ps[mn][1]) mn = pos;
955
+ if (ps[pos][1] > ps[mx][1]) mx = pos;
956
+ }
957
+ for (const i of [.../* @__PURE__ */ new Set([first, mn, mx, pos])].sort((a, b) => a - b))
958
+ out.push(ps[i]);
959
+ pos++;
960
+ }
961
+ return out;
962
+ }
963
+ function stableMean(values) {
964
+ if (!values.length) return null;
965
+ let max = 0;
966
+ for (const v of values) max = Math.max(max, Math.abs(v));
967
+ if (max === 0) return 0;
968
+ let sum = 0, c = 0;
969
+ for (const v of values) {
970
+ const a = v / max - c, t = sum + a;
971
+ c = t - sum - a;
972
+ sum = t;
973
+ }
974
+ return clamp(sum / values.length, -1, 1) * max;
975
+ }
976
+ function statistics(s, start, end) {
977
+ const a = lowerBound(s.timestamps, start), b = upperBound(s.timestamps, end), values = [];
978
+ let mn = Infinity, mx = -Infinity, miss = 0;
979
+ for (let i = a; i < b; i++) {
980
+ const v = s.values[i];
981
+ if (!finite(v)) miss++;
982
+ else {
983
+ values.push(v);
984
+ mn = Math.min(mn, v);
985
+ mx = Math.max(mx, v);
986
+ }
987
+ }
988
+ return {
989
+ name: s.name,
990
+ current: b > a && finite(s.values[b - 1]) ? s.values[b - 1] : null,
991
+ average: stableMean(values),
992
+ maximum: values.length ? mx : null,
993
+ minimum: values.length ? mn : null,
994
+ count: values.length,
995
+ missing: miss,
996
+ displayOverride: s.legendValues
997
+ };
998
+ }
999
+ function multiples(lo, hi, step) {
1000
+ check(
1001
+ finite(step) && step > 0 && finite((hi - lo) / step) && (hi - lo) / step <= LIMITS.ticks,
1002
+ "Too many ticks or invalid step."
1003
+ );
1004
+ const a = Math.ceil(lo / step - 1e-10), b = Math.floor(hi / step + 1e-10);
1005
+ check(
1006
+ Math.abs(a) < 9e15 && Math.abs(b) < 9e15 && b - a <= LIMITS.ticks,
1007
+ "Tick precision limit exceeded."
1008
+ );
1009
+ return Array.from(
1010
+ { length: Math.max(0, b - a + 1) },
1011
+ (_, i) => (a + i) * step || 0
1012
+ );
1013
+ }
1014
+ function nice(v) {
1015
+ check(finite(v) && v >= 1e-300, "Unsupported numeric axis span.");
1016
+ const p = 10 ** Math.floor(Math.log10(v));
1017
+ for (const m of [1, 2, 5, 10]) if (v <= m * p * (1 + 1e-12)) return m * p;
1018
+ return 10 * p;
1019
+ }
1020
+ function unitFor(v, base) {
1021
+ if (!v) return { factor: 1, suffix: "" };
1022
+ let i = Math.floor(Math.log(Math.abs(v)) / Math.log(base) + 1e-12);
1023
+ i = clamp(i, base === 1024 ? 0 : -8, 8);
1024
+ return {
1025
+ factor: base ** i,
1026
+ suffix: (base === 1024 ? ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"] : [
1027
+ "y",
1028
+ "z",
1029
+ "a",
1030
+ "f",
1031
+ "p",
1032
+ "n",
1033
+ "u",
1034
+ "m",
1035
+ "",
1036
+ "k",
1037
+ "M",
1038
+ "G",
1039
+ "T",
1040
+ "P",
1041
+ "E",
1042
+ "Z",
1043
+ "Y"
1044
+ ])[base === 1024 ? i : i + 8]
1045
+ };
1046
+ }
1047
+ function resolveY(a, loData, hiData) {
1048
+ let lo = a.minimum === null ? Math.min(0, loData) : a.minimum, hi = a.maximum === null ? Math.max(0, hiData) : a.maximum;
1049
+ if (a.maximum === null && hi <= lo)
1050
+ hi = lo + Math.max(Math.abs(lo) * 0.05, 1);
1051
+ if (a.minimum === null && lo >= hi)
1052
+ lo = hi - Math.max(Math.abs(hi) * 0.05, 1);
1053
+ const span = hi - lo;
1054
+ check(finite(span) && span > 0, "Invalid Y span.");
1055
+ const af = a.scaleFactor === null ? unitFor(Math.max(Math.abs(lo), Math.abs(hi)), a.base).factor : a.scaleFactor;
1056
+ const step = a.majorStep === null ? nice(span / af / 5) * af : a.majorStep, q = step / a.minorDivisions;
1057
+ check(finite(q) && q > 0, "Invalid Y quantum.");
1058
+ if (a.minimum === null && lo < 0) lo = Math.floor((lo - span * 0.02) / q) * q;
1059
+ if (a.maximum === null) hi = Math.ceil((hi + span * 0.02) / q) * q;
1060
+ check(finite(hi - lo) && hi > lo, "Degenerate Y range.");
1061
+ const major = multiples(lo, hi, step), minor = multiples(lo, hi, q).filter(
1062
+ (v) => Math.abs(v / step - round(v / step)) > 1e-8
1063
+ );
1064
+ const unit = unitFor(Math.max(Math.abs(lo), Math.abs(hi)), a.base);
1065
+ if (a.scaleFactor !== null) {
1066
+ unit.factor = a.scaleFactor;
1067
+ if (a.suffix === null) unit.suffix = "";
1068
+ }
1069
+ if (a.suffix !== null) unit.suffix = a.suffix;
1070
+ let decimals = a.decimals;
1071
+ if (decimals === null) {
1072
+ const s = step / unit.factor;
1073
+ decimals = 9;
1074
+ for (let d = 0; d < 10; d++)
1075
+ if (Math.abs(s - round(s * 10 ** d) / 10 ** d) <= Math.max(1e-10, Math.abs(s) * 1e-9)) {
1076
+ decimals = d;
1077
+ break;
1078
+ }
1079
+ }
1080
+ return { minimum: lo, maximum: hi, step, major, minor, ...unit, decimals };
1081
+ }
1082
+ function formatValue(value, unit, decimals = 2, missingText = "NaN") {
1083
+ if (!finite(value)) return missingText;
1084
+ let v = value / unit.factor;
1085
+ if (Math.abs(v) < 0.5 * 10 ** -decimals) v = 0;
1086
+ return v.toFixed(decimals) + (unit.suffix ? " " + unit.suffix : "");
1087
+ }
1088
+ var formatterCache = /* @__PURE__ */ new Map();
1089
+ function zoneFormatter(zone) {
1090
+ if (formatterCache.has(zone)) return formatterCache.get(zone);
1091
+ let f;
1092
+ try {
1093
+ f = new Intl.DateTimeFormat("en-GB-u-ca-gregory-nu-latn", {
1094
+ timeZone: zone,
1095
+ year: "numeric",
1096
+ month: "2-digit",
1097
+ day: "2-digit",
1098
+ hour: "2-digit",
1099
+ minute: "2-digit",
1100
+ second: "2-digit",
1101
+ hourCycle: "h23"
1102
+ });
1103
+ } catch (e) {
1104
+ throw new RangeError("Unsupported time zone: " + zone);
1105
+ }
1106
+ if (formatterCache.size >= 32)
1107
+ formatterCache.delete(formatterCache.keys().next().value);
1108
+ formatterCache.set(zone, f);
1109
+ return f;
1110
+ }
1111
+ function utcFromParts(y, m, d, h = 0, mi = 0, s = 0) {
1112
+ const t = /* @__PURE__ */ new Date(0);
1113
+ t.setUTCFullYear(y, m - 1, d);
1114
+ t.setUTCHours(h, mi, s, 0);
1115
+ return t.getTime() / 1e3;
1116
+ }
1117
+ function wallParts(t, zone) {
1118
+ if (zone === "UTC") {
1119
+ const d = new Date(t * 1e3);
1120
+ return {
1121
+ year: d.getUTCFullYear(),
1122
+ month: d.getUTCMonth() + 1,
1123
+ day: d.getUTCDate(),
1124
+ hour: d.getUTCHours(),
1125
+ minute: d.getUTCMinutes(),
1126
+ second: d.getUTCSeconds()
1127
+ };
1128
+ }
1129
+ const r = {};
1130
+ for (const p of zoneFormatter(zone).formatToParts(new Date(t * 1e3)))
1131
+ if (p.type !== "literal") r[p.type] = Number(p.value);
1132
+ if (r.hour === 24) r.hour = 0;
1133
+ return r;
1134
+ }
1135
+ function wallEpoch(t, zone) {
1136
+ const p = wallParts(t, zone);
1137
+ return utcFromParts(p.year, p.month, p.day, p.hour, p.minute, p.second) + (t - Math.floor(t));
1138
+ }
1139
+ function offsetAt(t, zone) {
1140
+ return Math.round(wallEpoch(t, zone) - t);
1141
+ }
1142
+ function localCandidates(w, zone) {
1143
+ if (zone === "UTC") return [w];
1144
+ const offsets = new Set(
1145
+ [-172800, -86400, 0, 86400, 172800].map((d) => offsetAt(w + d, zone))
1146
+ ), out = [];
1147
+ for (const off of offsets) {
1148
+ const t = w - off;
1149
+ if (Math.abs(wallEpoch(t, zone) - w) < 1e-3) out.push(t);
1150
+ }
1151
+ return out.sort((a, b) => a - b);
1152
+ }
1153
+ function wallTicks(start, end, step, zone) {
1154
+ if (zone === "UTC") return multiples(start, end, step);
1155
+ const sa = wallEpoch(start, zone), sb = wallEpoch(end, zone), a = Math.floor(Math.min(sa, sb) / step) - 2, b = Math.ceil(Math.max(sa, sb) / step) + 2;
1156
+ check(
1157
+ finite(b - a) && b - a <= LIMITS.ticks && Math.abs(a) < 9e15 && Math.abs(b) < 9e15,
1158
+ "Too many time ticks; increase the interval."
1159
+ );
1160
+ const offsets = /* @__PURE__ */ new Set();
1161
+ const probe = Math.max(43200, (end - start) / 4096);
1162
+ for (let t = start - 172800; t <= end + 172800; t += probe)
1163
+ offsets.add(offsetAt(t, zone));
1164
+ offsets.add(offsetAt(end, zone));
1165
+ const out = /* @__PURE__ */ new Set();
1166
+ for (let i = 0; i <= b - a; i++) {
1167
+ const w = (a + i) * step;
1168
+ for (const off of offsets) {
1169
+ const t = w - off;
1170
+ if (t >= start && t <= end && Math.abs(wallEpoch(t, zone) - w) < 1e-3)
1171
+ out.add(t);
1172
+ }
1173
+ }
1174
+ return [...out].sort((a2, b2) => a2 - b2);
1175
+ }
1176
+ function monthTicks(start, end, zone, stride = 1) {
1177
+ const a = wallParts(start, zone), b = wallParts(end, zone), first = Math.floor((a.year * 12 + a.month - 1) / stride) * stride, last = b.year * 12 + b.month - 1 + stride;
1178
+ check((last - first) / stride <= LIMITS.ticks, "Too many calendar ticks.");
1179
+ const out = [];
1180
+ for (let i = first; i <= last; i += stride) {
1181
+ const y = Math.floor(i / 12), m = i % 12 + 1;
1182
+ if (y < 1 || y > 9999) continue;
1183
+ for (const t of localCandidates(utcFromParts(y, m, 1), zone))
1184
+ if (t >= start && t <= end) out.push(t);
1185
+ }
1186
+ return out.sort((a2, b2) => a2 - b2);
1187
+ }
1188
+ var MONTHS = [
1189
+ "Jan",
1190
+ "Feb",
1191
+ "Mar",
1192
+ "Apr",
1193
+ "May",
1194
+ "Jun",
1195
+ "Jul",
1196
+ "Aug",
1197
+ "Sep",
1198
+ "Oct",
1199
+ "Nov",
1200
+ "Dec"
1201
+ ];
1202
+ var DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1203
+ var pad = (v, n = 2) => String(v).padStart(n, "0");
1204
+ function formatTime(t, zone = "UTC", format = "%H:%M") {
1205
+ t = epoch(t);
1206
+ const p = wallParts(t, zone), d = new Date(utcFromParts(p.year, p.month, p.day) * 1e3), off = offsetAt(t, zone), day = d.getUTCDay();
1207
+ const fields = {
1208
+ H: pad(p.hour),
1209
+ I: pad(p.hour % 12 || 12),
1210
+ M: pad(p.minute),
1211
+ S: pad(p.second),
1212
+ d: pad(p.day),
1213
+ e: String(p.day).padStart(2, " "),
1214
+ m: pad(p.month),
1215
+ Y: pad(p.year, 4),
1216
+ y: pad(p.year % 100),
1217
+ a: DAYS[day],
1218
+ A: [
1219
+ "Sunday",
1220
+ "Monday",
1221
+ "Tuesday",
1222
+ "Wednesday",
1223
+ "Thursday",
1224
+ "Friday",
1225
+ "Saturday"
1226
+ ][day],
1227
+ b: MONTHS[p.month - 1],
1228
+ h: MONTHS[p.month - 1],
1229
+ B: [
1230
+ "January",
1231
+ "February",
1232
+ "March",
1233
+ "April",
1234
+ "May",
1235
+ "June",
1236
+ "July",
1237
+ "August",
1238
+ "September",
1239
+ "October",
1240
+ "November",
1241
+ "December"
1242
+ ][p.month - 1],
1243
+ p: p.hour < 12 ? "AM" : "PM",
1244
+ w: String(day),
1245
+ u: String(day || 7),
1246
+ j: pad(
1247
+ Math.floor((d.getTime() / 1e3 - utcFromParts(p.year, 1, 1)) / 86400) + 1,
1248
+ 3
1249
+ ),
1250
+ z: (off < 0 ? "-" : "+") + pad(Math.floor(Math.abs(off) / 3600)) + pad(Math.floor(Math.abs(off) % 3600 / 60)),
1251
+ Z: zone,
1252
+ "%": "%"
1253
+ };
1254
+ fields.F = fields.Y + "-" + fields.m + "-" + fields.d;
1255
+ fields.T = fields.H + ":" + fields.M + ":" + fields.S;
1256
+ fields.R = fields.H + ":" + fields.M;
1257
+ let out = "";
1258
+ for (let i = 0; i < format.length; i++) {
1259
+ if (format[i] !== "%") {
1260
+ out += format[i];
1261
+ continue;
1262
+ }
1263
+ const k = format[++i];
1264
+ check(own(fields, k), "Unsupported date directive: %" + (k || ""));
1265
+ out += fields[k];
1266
+ }
1267
+ return out;
1268
+ }
1269
+ function resolveX(a, start, end, width) {
1270
+ const span = end - start, zone = a.timezone, mode = a.mode === "auto" ? span <= 172800 ? "daily" : span <= 864e3 ? "weekly" : span <= 5356800 ? "monthly" : "yearly" : a.mode;
1271
+ let minor = [], major = [], labelTimes = [], format = "%H:%M";
1272
+ if (mode === "yearly") {
1273
+ const stride = span > 550 * 86400 ? Math.max(1, Math.ceil(span / (365.25 * 86400))) : 1;
1274
+ format = stride > 1 ? "%b %Y" : "%b";
1275
+ if (a.majorTicks === null || a.ticks === null) {
1276
+ major = monthTicks(start, end, zone, stride);
1277
+ labelTimes = major;
1278
+ }
1279
+ if (a.minorTicks === null) minor = monthTicks(start, end, zone, 1);
1280
+ if (a.majorTicks === null && a.majorSeconds !== null)
1281
+ major = wallTicks(start, end, a.majorSeconds, zone);
1282
+ if (a.minorTicks === null && a.minorSeconds !== null)
1283
+ minor = wallTicks(start, end, a.minorSeconds, zone);
1284
+ if (a.ticks === null && a.labelSeconds !== null)
1285
+ labelTimes = wallTicks(start, end, a.labelSeconds, zone);
1286
+ } else {
1287
+ let mi = 1800, ma = 7200, ls = 7200;
1288
+ if (mode === "weekly") {
1289
+ mi = 21600;
1290
+ ma = ls = 86400;
1291
+ format = "%d";
1292
+ }
1293
+ if (mode === "monthly") {
1294
+ mi = 86400;
1295
+ ma = ls = 604800;
1296
+ format = "%d %b";
1297
+ }
1298
+ if (a.mode === "auto" && span < 43200) {
1299
+ const target = span / Math.max(2, Math.floor(width / 48));
1300
+ ls = [1, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200].find(
1301
+ (v) => v >= target
1302
+ ) || 7200;
1303
+ mi = Math.max(1, ls / 4);
1304
+ ma = ls;
1305
+ if (ls < 60) format = "%H:%M:%S";
1306
+ }
1307
+ mi = Math.max(mi, span / 2e3);
1308
+ if (a.minorSeconds !== null) mi = a.minorSeconds;
1309
+ if (a.majorSeconds !== null) ma = a.majorSeconds;
1310
+ if (a.labelSeconds !== null) ls = a.labelSeconds;
1311
+ if (a.minorTicks === null) minor = wallTicks(start, end, mi, zone);
1312
+ if (a.majorTicks === null) major = wallTicks(start, end, ma, zone);
1313
+ if (a.ticks === null) labelTimes = wallTicks(start, end, ls, zone);
1314
+ }
1315
+ if (a.minorTicks !== null)
1316
+ minor = a.minorTicks.filter((v) => v >= start && v <= end);
1317
+ if (a.majorTicks !== null)
1318
+ major = a.majorTicks.filter((v) => v >= start && v <= end);
1319
+ if (a.labelFormat !== null) format = a.labelFormat;
1320
+ let labels = [];
1321
+ if (a.ticks !== null)
1322
+ labels = a.ticks.filter((v) => v.time >= start && v.time <= end);
1323
+ else if (mode === "weekly" && a.labelSeconds === null && a.labelOffsetSeconds === 0) {
1324
+ for (const t of wallTicks(start - 172800, end, 86400, zone)) {
1325
+ const w = wallEpoch(t, zone), next = localCandidates(w + 86400, zone);
1326
+ if (t < start || !next.length || next[next.length - 1] > end) continue;
1327
+ for (const noon of localCandidates(w + 43200, zone))
1328
+ if (noon >= start && noon <= end)
1329
+ labels.push({ time: noon, label: formatTime(t, zone, format) });
1330
+ }
1331
+ } else
1332
+ for (const t of labelTimes) {
1333
+ const pos = t + a.labelOffsetSeconds;
1334
+ if (pos >= start && pos <= end)
1335
+ labels.push({ time: pos, label: formatTime(t, zone, format) });
1336
+ }
1337
+ labels.sort((a2, b) => a2.time - b.time);
1338
+ if (a.ticks === null && mode === "daily" && span <= 95040) {
1339
+ const seen = /* @__PURE__ */ new Map();
1340
+ for (const t of labels) {
1341
+ if (!seen.has(t.label)) seen.set(t.label, /* @__PURE__ */ new Set());
1342
+ seen.get(t.label).add(offsetAt(t.time, zone));
1343
+ }
1344
+ labels = labels.map(
1345
+ (t) => seen.get(t.label).size > 1 ? {
1346
+ time: t.time,
1347
+ label: t.label + " " + formatTime(t.time, zone, "%z")
1348
+ } : t
1349
+ );
1350
+ }
1351
+ const majorSet = new Set(major);
1352
+ minor = minor.filter((v) => !majorSet.has(v));
1353
+ return { minor, major, labels, mode };
1354
+ }
1355
+ function timeRange(c) {
1356
+ let start = Infinity, end = -Infinity;
1357
+ for (const s of c.series)
1358
+ if (s.timestamps.length) {
1359
+ start = Math.min(start, s.timestamps[0]);
1360
+ end = Math.max(end, s.timestamps[s.timestamps.length - 1]);
1361
+ }
1362
+ if (c.timeAxis.start !== null) start = c.timeAxis.start;
1363
+ if (c.timeAxis.end !== null) end = c.timeAxis.end;
1364
+ if (start === end && c.timeAxis.start === null && c.timeAxis.end === null) {
1365
+ start -= 150;
1366
+ end += 150;
1367
+ }
1368
+ epoch(start);
1369
+ epoch(end);
1370
+ check(
1371
+ end > start,
1372
+ "Empty data needs explicit start and end; range must be nonzero."
1373
+ );
1374
+ return [start, end];
1375
+ }
1376
+ var PIXEL_GLYPHS = {
1377
+ " ": ["00000", "00000", "00000", "00000", "00000", "00000", "00000"],
1378
+ "0": ["01110", "10001", "10011", "10101", "11001", "10001", "01110"],
1379
+ "1": ["00100", "01100", "00100", "00100", "00100", "00100", "01110"],
1380
+ "2": ["01110", "10001", "00001", "00010", "00100", "01000", "11111"],
1381
+ "3": ["11110", "00001", "00001", "01110", "00001", "00001", "11110"],
1382
+ "4": ["00010", "00110", "01010", "10010", "11111", "00010", "00010"],
1383
+ "5": ["11111", "10000", "10000", "11110", "00001", "00001", "11110"],
1384
+ "6": ["01110", "10000", "10000", "11110", "10001", "10001", "01110"],
1385
+ "7": ["11111", "00001", "00010", "00100", "01000", "01000", "01000"],
1386
+ "8": ["01110", "10001", "10001", "01110", "10001", "10001", "01110"],
1387
+ "9": ["01110", "10001", "10001", "01111", "00001", "00001", "01110"],
1388
+ A: ["01110", "10001", "10001", "11111", "10001", "10001", "10001"],
1389
+ B: ["11110", "10001", "10001", "11110", "10001", "10001", "11110"],
1390
+ C: ["01111", "10000", "10000", "10000", "10000", "10000", "01111"],
1391
+ D: ["11110", "10001", "10001", "10001", "10001", "10001", "11110"],
1392
+ E: ["11111", "10000", "10000", "11110", "10000", "10000", "11111"],
1393
+ F: ["11111", "10000", "10000", "11110", "10000", "10000", "10000"],
1394
+ G: ["01110", "10001", "10000", "10111", "10001", "10001", "01111"],
1395
+ H: ["10001", "10001", "10001", "11111", "10001", "10001", "10001"],
1396
+ I: ["01110", "00100", "00100", "00100", "00100", "00100", "01110"],
1397
+ J: ["00111", "00010", "00010", "00010", "00010", "10010", "01100"],
1398
+ K: ["10001", "10010", "10100", "11000", "10100", "10010", "10001"],
1399
+ L: ["10000", "10000", "10000", "10000", "10000", "10000", "11111"],
1400
+ M: ["10001", "11011", "10101", "10101", "10001", "10001", "10001"],
1401
+ N: ["10001", "11001", "10101", "10011", "10001", "10001", "10001"],
1402
+ O: ["01110", "10001", "10001", "10001", "10001", "10001", "01110"],
1403
+ P: ["11110", "10001", "10001", "11110", "10000", "10000", "10000"],
1404
+ Q: ["01110", "10001", "10001", "10001", "10101", "10010", "01101"],
1405
+ R: ["11110", "10001", "10001", "11110", "10100", "10010", "10001"],
1406
+ S: ["01111", "10000", "10000", "01110", "00001", "00001", "11110"],
1407
+ T: ["11111", "00100", "00100", "00100", "00100", "00100", "00100"],
1408
+ U: ["10001", "10001", "10001", "10001", "10001", "10001", "01110"],
1409
+ V: ["10001", "10001", "10001", "10001", "10001", "01010", "00100"],
1410
+ W: ["10001", "10001", "10001", "10101", "10101", "11011", "10001"],
1411
+ X: ["10001", "10001", "01010", "00100", "01010", "10001", "10001"],
1412
+ Y: ["10001", "10001", "01010", "00100", "00100", "00100", "00100"],
1413
+ Z: ["11111", "00001", "00010", "00100", "01000", "10000", "11111"],
1414
+ a: ["00000", "00000", "01110", "00001", "01111", "10001", "01111"],
1415
+ b: ["10000", "10000", "10110", "11001", "10001", "10001", "11110"],
1416
+ c: ["00000", "00000", "01111", "10000", "10000", "10000", "01111"],
1417
+ d: ["00001", "00001", "01101", "10011", "10001", "10001", "01111"],
1418
+ e: ["00000", "00000", "01110", "10001", "11111", "10000", "01110"],
1419
+ f: ["00110", "01001", "01000", "11100", "01000", "01000", "01000"],
1420
+ g: ["00000", "01111", "10001", "10001", "01111", "00001", "01110"],
1421
+ h: ["10000", "10000", "10110", "11001", "10001", "10001", "10001"],
1422
+ i: ["00100", "00000", "01100", "00100", "00100", "00100", "01110"],
1423
+ j: ["00010", "00000", "00110", "00010", "00010", "10010", "01100"],
1424
+ k: ["10000", "10000", "10010", "10100", "11000", "10100", "10010"],
1425
+ l: ["01100", "00100", "00100", "00100", "00100", "00100", "01110"],
1426
+ m: ["00000", "00000", "11010", "10101", "10101", "10101", "10101"],
1427
+ n: ["00000", "00000", "10110", "11001", "10001", "10001", "10001"],
1428
+ o: ["00000", "00000", "01110", "10001", "10001", "10001", "01110"],
1429
+ p: ["00000", "00000", "11110", "10001", "11110", "10000", "10000"],
1430
+ q: ["00000", "00000", "01111", "10001", "01111", "00001", "00001"],
1431
+ r: ["00000", "00000", "10111", "11000", "10000", "10000", "10000"],
1432
+ s: ["00000", "00000", "01111", "10000", "01110", "00001", "11110"],
1433
+ t: ["01000", "01000", "11100", "01000", "01000", "01001", "00110"],
1434
+ u: ["00000", "00000", "10001", "10001", "10001", "10011", "01101"],
1435
+ v: ["00000", "00000", "10001", "10001", "10001", "01010", "00100"],
1436
+ w: ["00000", "00000", "10001", "10001", "10101", "10101", "01010"],
1437
+ x: ["00000", "00000", "10001", "01010", "00100", "01010", "10001"],
1438
+ y: ["00000", "00000", "10001", "10001", "01111", "00001", "01110"],
1439
+ z: ["00000", "00000", "11111", "00010", "00100", "01000", "11111"],
1440
+ "-": ["00000", "00000", "00000", "11111", "00000", "00000", "00000"],
1441
+ _: ["00000", "00000", "00000", "00000", "00000", "00000", "11111"],
1442
+ ":": ["00000", "00100", "00100", "00000", "00100", "00100", "00000"],
1443
+ ".": ["00000", "00000", "00000", "00000", "00000", "00100", "00100"],
1444
+ ",": ["00000", "00000", "00000", "00000", "00100", "00100", "01000"],
1445
+ "/": ["00001", "00010", "00010", "00100", "01000", "01000", "10000"],
1446
+ "\\": ["10000", "01000", "01000", "00100", "00010", "00010", "00001"],
1447
+ "(": ["00010", "00100", "01000", "01000", "01000", "00100", "00010"],
1448
+ ")": ["01000", "00100", "00010", "00010", "00010", "00100", "01000"],
1449
+ "[": ["01110", "01000", "01000", "01000", "01000", "01000", "01110"],
1450
+ "]": ["01110", "00010", "00010", "00010", "00010", "00010", "01110"],
1451
+ "+": ["00000", "00100", "00100", "11111", "00100", "00100", "00000"],
1452
+ "=": ["00000", "00000", "11111", "00000", "11111", "00000", "00000"],
1453
+ "%": ["11001", "11010", "00010", "00100", "01000", "01011", "10011"],
1454
+ "?": ["01110", "10001", "00001", "00010", "00100", "00000", "00100"],
1455
+ "!": ["00100", "00100", "00100", "00100", "00100", "00000", "00100"],
1456
+ "#": ["01010", "01010", "11111", "01010", "11111", "01010", "01010"],
1457
+ "*": ["00000", "10101", "01110", "11111", "01110", "10101", "00000"],
1458
+ "<": ["00010", "00100", "01000", "10000", "01000", "00100", "00010"],
1459
+ ">": ["01000", "00100", "00010", "00001", "00010", "00100", "01000"],
1460
+ "|": ["00100", "00100", "00100", "00100", "00100", "00100", "00100"],
1461
+ '"': ["01010", "01010", "00000", "00000", "00000", "00000", "00000"],
1462
+ "'": ["00100", "00100", "00000", "00000", "00000", "00000", "00000"],
1463
+ ";": ["00000", "00100", "00100", "00000", "00100", "00100", "01000"],
1464
+ "@": ["01110", "10001", "10111", "10101", "10111", "10000", "01111"],
1465
+ $: ["00100", "01111", "10100", "01110", "00101", "11110", "00100"],
1466
+ "&": ["01100", "10010", "10100", "01000", "10101", "10010", "01101"],
1467
+ "^": ["00100", "01010", "10001", "00000", "00000", "00000", "00000"],
1468
+ "`": ["01000", "00100", "00000", "00000", "00000", "00000", "00000"],
1469
+ "~": ["00000", "00000", "01001", "10110", "00000", "00000", "00000"],
1470
+ "{": ["00011", "00100", "00100", "01000", "00100", "00100", "00011"],
1471
+ "}": ["11000", "00100", "00100", "00010", "00100", "00100", "11000"]
1472
+ };
1473
+ function createCanvas(w, h) {
1474
+ let canvas;
1475
+ if (typeof document !== "undefined" && document.createElement)
1476
+ canvas = document.createElement("canvas");
1477
+ else if (typeof OffscreenCanvas !== "undefined")
1478
+ canvas = new OffscreenCanvas(w, h);
1479
+ else
1480
+ throw new Error(
1481
+ 'System fonts need Canvas 2D. Use fonts: { mode: "bitmap" } in a DOM-free runtime.'
1482
+ );
1483
+ canvas.width = w;
1484
+ canvas.height = h;
1485
+ return canvas;
1486
+ }
1487
+ function wide(ch) {
1488
+ const n = ch.codePointAt(0);
1489
+ return n >= 4352 && (n <= 4447 || n >= 11904 && n <= 42191 || n >= 44032 && n <= 55203 || n >= 63744 && n <= 64255 || n >= 65280 && n <= 65376 || n >= 127744);
1490
+ }
1491
+ var Fonts = class {
1492
+ config;
1493
+ theme;
1494
+ cache;
1495
+ canvas;
1496
+ ctx;
1497
+ constructor(config, theme) {
1498
+ this.config = config;
1499
+ this.theme = theme;
1500
+ this.cache = /* @__PURE__ */ new Map();
1501
+ if (config.mode === "system") {
1502
+ this.canvas = createCanvas(8, 8);
1503
+ const ctx = this.canvas.getContext("2d", {
1504
+ willReadFrequently: true
1505
+ });
1506
+ check(ctx, "Canvas 2D is unavailable.");
1507
+ this.ctx = ctx;
1508
+ }
1509
+ }
1510
+ size(role, scale = 1) {
1511
+ return this.theme[`${role}Size`] * scale;
1512
+ }
1513
+ family(role) {
1514
+ return (role === "title" || role === "unit" || role === "caption" ? this.config[`${role}Family`] : null) || this.config.family;
1515
+ }
1516
+ setup(role, size) {
1517
+ this.ctx.font = (role === "caption" ? "bold " : "") + size + "px " + this.family(role);
1518
+ this.ctx.textBaseline = "alphabetic";
1519
+ this.ctx.fillStyle = "#000000";
1520
+ if ("fontKerning" in this.ctx) this.ctx.fontKerning = "none";
1521
+ }
1522
+ width(value, role, advance = 0, scale = 1) {
1523
+ if (advance > 0)
1524
+ return [...value].reduce((n, ch) => n + advance * (wide(ch) ? 2 : 1), 0);
1525
+ if (this.config.mode === "bitmap")
1526
+ return [...value].length * this.size(role, scale) * 0.6;
1527
+ this.setup(role, this.size(role, scale));
1528
+ return this.ctx.measureText(value).width;
1529
+ }
1530
+ fit(value, maxWidth, role, advance, scale = 1) {
1531
+ if (this.width(value, role, advance, scale) <= maxWidth) return value;
1532
+ const end = "...";
1533
+ if (this.width(end, role, advance, scale) > maxWidth) return "";
1534
+ const a = [...value];
1535
+ while (a.length && this.width(a.join("") + end, role, advance, scale) > maxWidth)
1536
+ a.pop();
1537
+ return a.join("") + end;
1538
+ }
1539
+ raster(value, role, advance = 0, scale = 1) {
1540
+ const key = JSON.stringify([value, role, advance, scale]);
1541
+ if (this.cache.has(key)) return this.cache.get(key);
1542
+ const size = this.size(role, scale), w = Math.max(1, Math.ceil(this.width(value, role, advance, scale)) + 4);
1543
+ let out;
1544
+ check(
1545
+ w <= 32768 && w * size < LIMITS.pixels,
1546
+ "Text allocation limit exceeded."
1547
+ );
1548
+ if (this.config.mode === "bitmap") {
1549
+ const h = Math.max(1, round(size * 0.72));
1550
+ out = new Surface(w, h + 2);
1551
+ let x = 1;
1552
+ for (const ch of value) {
1553
+ let rows = PIXEL_GLYPHS[ch];
1554
+ if (!rows) {
1555
+ check(
1556
+ !this.config.strictGlyphs,
1557
+ "Bitmap text supports printable ASCII only: " + ch
1558
+ );
1559
+ rows = PIXEL_GLYPHS["?"];
1560
+ }
1561
+ const cell = advance > 0 ? advance : size * 0.6, gw = Math.max(1, Math.min(round(size * 0.5), Math.floor(cell) - 1));
1562
+ for (let yy = 0; yy < h; yy++)
1563
+ for (let xx = 0; xx < gw; xx++)
1564
+ if (rows[Math.min(6, Math.floor(yy / h * 7))][Math.min(4, Math.floor(xx / gw * 5))] === "1")
1565
+ out.pixel(round(x) + xx, yy + 1, [0, 0, 0, 255]);
1566
+ x += cell * (wide(ch) ? 2 : 1);
1567
+ }
1568
+ } else {
1569
+ this.setup(role, size);
1570
+ const metric = this.ctx.measureText(value || "0");
1571
+ const ascent = Math.ceil(metric.actualBoundingBoxAscent || size * 0.8), descent = Math.ceil(metric.actualBoundingBoxDescent || 0), h = Math.max(1, ascent + descent) + 2;
1572
+ this.canvas.width = w;
1573
+ this.canvas.height = h;
1574
+ this.setup(role, size);
1575
+ let x = 1;
1576
+ for (const ch of value) {
1577
+ this.ctx.fillText(ch, round(x), 1 + ascent);
1578
+ x += advance > 0 ? advance * (wide(ch) ? 2 : 1) : this.ctx.measureText(ch).width;
1579
+ }
1580
+ const d = this.ctx.getImageData(0, 0, w, h);
1581
+ out = new Surface(w, h);
1582
+ out.data.set(d.data);
1583
+ }
1584
+ if (this.cache.size >= 512)
1585
+ this.cache.delete(this.cache.keys().next().value);
1586
+ this.cache.set(key, out);
1587
+ return out;
1588
+ }
1589
+ draw(im, x, y, value, role, fill, advance = 0, align = "left", centerY = false, scale = 1) {
1590
+ if (!value) return;
1591
+ const r = this.raster(value, role, advance, scale), width = this.width(value, role, advance, scale);
1592
+ if (align === "center") x -= width / 2;
1593
+ else if (align === "right") x -= width;
1594
+ if (centerY) y -= (r.height - 2) / 2;
1595
+ const xx = round(x) - 1, yy = round(y) - 1, p = [fill[0], fill[1], fill[2], 0];
1596
+ for (let sy = 0; sy < r.height; sy++)
1597
+ for (let sx = 0; sx < r.width; sx++) {
1598
+ const a = r.data[(sy * r.width + sx) * 4 + 3];
1599
+ if (a) {
1600
+ p[3] = round(a * fill[3] / 255);
1601
+ im.pixel(xx + sx, yy + sy, p, true);
1602
+ }
1603
+ }
1604
+ }
1605
+ rotated(value, role, fill, clockwise = false) {
1606
+ const r = this.raster(value, role), out = new Surface(r.height, r.width), p = [fill[0], fill[1], fill[2], 0];
1607
+ for (let y = 0; y < r.height; y++)
1608
+ for (let x = 0; x < r.width; x++) {
1609
+ p[3] = round(r.data[(y * r.width + x) * 4 + 3] * fill[3] / 255);
1610
+ if (p[3])
1611
+ out.pixel(
1612
+ clockwise ? r.height - 1 - y : y,
1613
+ clockwise ? x : r.width - 1 - x,
1614
+ p
1615
+ );
1616
+ }
1617
+ return out;
1618
+ }
1619
+ };
1620
+ function renderChart(c) {
1621
+ const l = c.layout, t = c.theme, [width, height] = dimensions(c), left = l.left, top = l.top, right = l.width - l.right, bottom = l.top + l.plotHeight, pw = right - left, ph = bottom - top;
1622
+ const [start, end] = timeRange(c);
1623
+ const runs = c.series.map((s) => visibleRuns(s, start, end));
1624
+ let loData = Infinity, hiData = -Infinity;
1625
+ for (let i = 0; i < runs.length; i++) {
1626
+ for (const run of runs[i])
1627
+ for (const p of run) {
1628
+ check(finite(p[1]), "Interpolated value overflow.");
1629
+ loData = Math.min(loData, p[1]);
1630
+ hiData = Math.max(hiData, p[1]);
1631
+ }
1632
+ if (c.series[i].kind === "area") {
1633
+ loData = Math.min(loData, c.series[i].baseline);
1634
+ hiData = Math.max(hiData, c.series[i].baseline);
1635
+ }
1636
+ }
1637
+ if (loData === Infinity) loData = hiData = 0;
1638
+ const ys = resolveY(c.yAxis, loData, hiData), xs = resolveX(c.timeAxis, start, end, pw), fonts = new Fonts(c.fonts, t), im = new Surface(width, height, t.background);
1639
+ im.rect(left, top, right + 1, bottom + 1, t.canvas);
1640
+ const xx = (v) => (v - start) / (end - start) * pw, yy = (v) => ph * (1 - (v / (ys.maximum - ys.minimum) - ys.minimum / (ys.maximum - ys.minimum)));
1641
+ const projected = runs.map(
1642
+ (rr) => rr.map(
1643
+ (r) => decimate(r, start, end, pw).map((p) => {
1644
+ const x = xx(p[0]), y = yy(p[1]);
1645
+ check(
1646
+ finite(x) && finite(y) && Math.abs(y) < 1e15,
1647
+ "Data magnitude is too large relative to the Y axis."
1648
+ );
1649
+ return [x, y];
1650
+ })
1651
+ )
1652
+ );
1653
+ function grid() {
1654
+ const lay = new Surface(pw + 1, ph + 1);
1655
+ for (const [values, col] of [
1656
+ [ys.minor, t.minorGrid],
1657
+ [ys.major, t.majorGrid]
1658
+ ])
1659
+ for (const v of values) {
1660
+ const y = round(yy(v));
1661
+ if (y >= 0 && y <= ph) lay.dashed([0, y], [pw, y], col, t.gridDash);
1662
+ }
1663
+ for (const [values, col] of [
1664
+ [xs.minor, t.minorGrid],
1665
+ [xs.major, t.majorGrid]
1666
+ ])
1667
+ for (const v of values) {
1668
+ const x = round(xx(v));
1669
+ lay.dashed([x, 0], [x, ph], col, t.gridDash);
1670
+ }
1671
+ im.over(lay, left, top);
1672
+ }
1673
+ const aa = l.antialias, layerWidth = (pw + 1) * aa, layerHeight = (ph + 1) * aa;
1674
+ if (!t.gridFront) grid();
1675
+ for (let i = 0; i < c.series.length; i++) {
1676
+ const s = c.series[i];
1677
+ if (s.kind !== "area") continue;
1678
+ const base = yy(s.baseline);
1679
+ check(
1680
+ finite(base) && Math.abs(base) < 1e15,
1681
+ "Baseline magnitude is too large."
1682
+ );
1683
+ const layer = new Surface(layerWidth, layerHeight);
1684
+ for (const ps of projected[i])
1685
+ if (ps.length > 1) {
1686
+ const poly = clipPolygon(
1687
+ [[ps[0][0], base], ...ps, [ps[ps.length - 1][0], base]],
1688
+ pw,
1689
+ ph
1690
+ ).map((p) => [round(p[0] * aa), round(p[1] * aa)]);
1691
+ layer.polygon(poly, s.color);
1692
+ }
1693
+ im.over(layer.down(aa), left, top);
1694
+ }
1695
+ if (t.gridFront) grid();
1696
+ for (let i = 0; i < c.series.length; i++) {
1697
+ const s = c.series[i], col = s.kind === "line" ? s.color : s.outline;
1698
+ if (col === null) continue;
1699
+ const layer = new Surface(layerWidth, layerHeight), lw = Math.max(1, round(s.lineWidth * aa));
1700
+ for (const ps of projected[i]) {
1701
+ if (ps.length === 1) {
1702
+ const p = ps[0];
1703
+ if (p[0] >= 0 && p[0] <= pw && p[1] >= 0 && p[1] <= ph)
1704
+ layer.circle(p[0] * aa, p[1] * aa, Math.max(aa / 2, lw / 2), col);
1705
+ }
1706
+ for (let k = 1; k < ps.length; k++) {
1707
+ const seg = clipLine(ps[k - 1], ps[k], pw, ph);
1708
+ if (seg)
1709
+ layer.line(
1710
+ [round(seg[0][0] * aa), round(seg[0][1] * aa)],
1711
+ [round(seg[1][0] * aa), round(seg[1][1] * aa)],
1712
+ col,
1713
+ lw
1714
+ );
1715
+ }
1716
+ }
1717
+ im.over(layer.down(aa), left, top);
1718
+ }
1719
+ const rules = new Surface(pw + 1, ph + 1);
1720
+ for (const r of c.hRules)
1721
+ if (r.value >= ys.minimum && r.value <= ys.maximum)
1722
+ rules.dashed(
1723
+ [0, round(yy(r.value))],
1724
+ [pw, round(yy(r.value))],
1725
+ r.color,
1726
+ r.dash,
1727
+ Math.max(1, round(r.width))
1728
+ );
1729
+ for (const r of c.vRules)
1730
+ if (r.time >= start && r.time <= end)
1731
+ rules.dashed(
1732
+ [round(xx(r.time)), 0],
1733
+ [round(xx(r.time)), ph],
1734
+ r.color,
1735
+ r.dash,
1736
+ Math.max(1, round(r.width))
1737
+ );
1738
+ im.over(rules, left, top);
1739
+ im.line([left, top - 3], [left, bottom + 4], t.axis);
1740
+ im.line([left - 4, bottom], [right + 4, bottom], t.axis);
1741
+ im.polygon(
1742
+ [
1743
+ [left, top - 5],
1744
+ [left - 3, top],
1745
+ [left + 3, top]
1746
+ ],
1747
+ t.arrow
1748
+ );
1749
+ im.polygon(
1750
+ [
1751
+ [right + 7, bottom],
1752
+ [right + 2, bottom - 3],
1753
+ [right + 2, bottom + 3]
1754
+ ],
1755
+ t.arrow
1756
+ );
1757
+ for (const v of ys.major) {
1758
+ const y = top + yy(v), unit = v === 0 && !c.yAxis.showZeroSuffix ? { factor: ys.factor, suffix: "" } : ys, label = formatValue(v, unit, ys.decimals);
1759
+ check(
1760
+ fonts.width(label, "axis", t.axisAdvance) <= left - l.yLabelGap - 20,
1761
+ "Y labels overlap the vertical label. Increase layout.left or adjust units."
1762
+ );
1763
+ im.line([left - 3, round(y)], [left, round(y)], t.axis);
1764
+ fonts.draw(
1765
+ im,
1766
+ left - l.yLabelGap,
1767
+ y,
1768
+ label,
1769
+ "axis",
1770
+ t.text,
1771
+ t.axisAdvance,
1772
+ "right",
1773
+ true
1774
+ );
1775
+ }
1776
+ const xLabels = [];
1777
+ let lastRight = -Infinity;
1778
+ for (const tick of xs.labels) {
1779
+ const x = left + xx(tick.time), tw = fonts.width(tick.label, "axis", t.axisAdvance), a = x - tw / 2, b = x + tw / 2;
1780
+ if (c.timeAxis.ticks === null && a < lastRight + 3) continue;
1781
+ if (a < 2 || b > width - 3) continue;
1782
+ im.line(
1783
+ [round(x), bottom],
1784
+ [round(x), bottom + 3],
1785
+ [t.majorGrid[0], t.majorGrid[1], t.majorGrid[2], 255]
1786
+ );
1787
+ fonts.draw(
1788
+ im,
1789
+ x,
1790
+ bottom + l.xLabelGap,
1791
+ tick.label,
1792
+ "axis",
1793
+ t.text,
1794
+ t.axisAdvance,
1795
+ "center"
1796
+ );
1797
+ lastRight = b;
1798
+ xLabels.push({ ...tick, x });
1799
+ }
1800
+ const titleX = (left + right) / 2 + l.titleOffsetX, title = fonts.fit(
1801
+ c.title,
1802
+ 2 * Math.min(titleX - 5, width - 14 - titleX),
1803
+ "title",
1804
+ t.titleAdvance
1805
+ );
1806
+ fonts.draw(
1807
+ im,
1808
+ titleX,
1809
+ l.titleY,
1810
+ title,
1811
+ "title",
1812
+ t.text,
1813
+ t.titleAdvance,
1814
+ "center"
1815
+ );
1816
+ if (c.verticalLabel) {
1817
+ const unit = fonts.rotated(c.verticalLabel, "unit", t.text);
1818
+ check(
1819
+ unit.height <= ph + 18 && l.unitX + unit.width <= left - l.yLabelGap,
1820
+ "Vertical label does not fit."
1821
+ );
1822
+ im.over(unit, l.unitX, round((top + bottom - unit.height) / 2));
1823
+ }
1824
+ if (c.watermark) {
1825
+ const mark = fonts.rotated(c.watermark, "watermark", t.watermark, true);
1826
+ check(
1827
+ mark.height <= height - 8 && mark.width <= l.right - 9,
1828
+ "Watermark does not fit."
1829
+ );
1830
+ im.over(mark, width - mark.width - 4, 4);
1831
+ }
1832
+ const stats = c.series.map((s) => statistics(s, start, end));
1833
+ if (l.legend !== "none") {
1834
+ const scale = Math.min(1, (width - 40) / 555), advance = t.legendAdvance * scale, ll = l.legendLayout, factor = ll.autoScaleColumns ? (width - ll.nameX) / (ll.referenceWidth - ll.nameX) : 1, anchor = (x) => ll.nameX + (x - ll.nameX) * factor;
1835
+ c.series.forEach((s, i) => {
1836
+ const pairs = l.legend === "aligned" ? ll.aligned : l.legend === "reference" && i === c.series.length - 1 && i > 0 ? ll.expanded : ll.compact, y = bottom + l.legendGap + i * l.legendRowHeight;
1837
+ check(
1838
+ ll.swatchHeight <= l.legendRowHeight && ll.swatchX + ll.swatchWidth < width && y + ll.swatchHeight <= height - 2,
1839
+ "Legend swatch does not fit."
1840
+ );
1841
+ im.rect(
1842
+ ll.swatchX,
1843
+ y,
1844
+ ll.swatchX + ll.swatchWidth,
1845
+ y + ll.swatchHeight,
1846
+ t.frame
1847
+ );
1848
+ const swatch = new Surface(
1849
+ ll.swatchWidth - 2,
1850
+ ll.swatchHeight - 2,
1851
+ s.color
1852
+ );
1853
+ im.over(swatch, ll.swatchX + 1, y + 1);
1854
+ const name = fonts.fit(
1855
+ s.name,
1856
+ anchor(pairs[0][0]) - ll.nameX - 12,
1857
+ "legend",
1858
+ advance,
1859
+ scale
1860
+ );
1861
+ fonts.draw(
1862
+ im,
1863
+ ll.nameX,
1864
+ y,
1865
+ name,
1866
+ "legend",
1867
+ t.text,
1868
+ advance,
1869
+ "left",
1870
+ false,
1871
+ scale
1872
+ );
1873
+ const display = s.legendValues || stats[i];
1874
+ ["current", "average", "maximum"].forEach((key, j) => {
1875
+ const label = c.legendLabels[j], value = formatValue(
1876
+ display[key],
1877
+ ys,
1878
+ c.yAxis.legendDecimals,
1879
+ c.missingLabel
1880
+ ), a = anchor(pairs[j][0]), b = anchor(pairs[j][1]);
1881
+ check(b < width - 3, "Legend column is outside the panel.");
1882
+ check(
1883
+ fonts.width(label, "legend", advance, scale) + fonts.width(value, "legend", advance, scale) + 7 * scale <= b - a + 1,
1884
+ "Legend statistic does not fit. Increase width, adjust anchors or reduce legendDecimals."
1885
+ );
1886
+ fonts.draw(
1887
+ im,
1888
+ a,
1889
+ y,
1890
+ label,
1891
+ "legend",
1892
+ t.text,
1893
+ advance,
1894
+ "left",
1895
+ false,
1896
+ scale
1897
+ );
1898
+ fonts.draw(
1899
+ im,
1900
+ b,
1901
+ y,
1902
+ value,
1903
+ "legend",
1904
+ t.text,
1905
+ advance,
1906
+ "right",
1907
+ false,
1908
+ scale
1909
+ );
1910
+ });
1911
+ });
1912
+ }
1913
+ for (const [a, b] of [
1914
+ [
1915
+ [0, 0],
1916
+ [width - 1, 0]
1917
+ ],
1918
+ [
1919
+ [1, 1],
1920
+ [width - 2, 1]
1921
+ ],
1922
+ [
1923
+ [0, 0],
1924
+ [0, height - 1]
1925
+ ],
1926
+ [
1927
+ [1, 1],
1928
+ [1, height - 2]
1929
+ ]
1930
+ ])
1931
+ im.line(a, b, t.shadeLight);
1932
+ for (const [a, b] of [
1933
+ [
1934
+ [width - 2, 1],
1935
+ [width - 2, height - 1]
1936
+ ],
1937
+ [
1938
+ [width - 1, 0],
1939
+ [width - 1, height - 1]
1940
+ ],
1941
+ [
1942
+ [1, height - 2],
1943
+ [width - 1, height - 2]
1944
+ ],
1945
+ [
1946
+ [0, height - 1],
1947
+ [width - 1, height - 1]
1948
+ ]
1949
+ ])
1950
+ im.line(a, b, t.shadeDark);
1951
+ for (let i = 3; i < im.data.length; i += 4) im.data[i] = 255;
1952
+ const out = im.scale(l.pixelScale);
1953
+ const metadata = {
1954
+ version: VERSION,
1955
+ imageSize: [out.width, out.height],
1956
+ logicalSize: [width, height],
1957
+ plotBox: [left, top, right, bottom],
1958
+ pixelScale: l.pixelScale,
1959
+ title: c.title,
1960
+ verticalLabel: c.verticalLabel,
1961
+ watermark: c.watermark,
1962
+ timeRange: [start, end],
1963
+ timezone: c.timeAxis.timezone,
1964
+ timeMode: xs.mode,
1965
+ yRange: [ys.minimum, ys.maximum],
1966
+ yStep: ys.step,
1967
+ yUnit: { factor: ys.factor, suffix: ys.suffix },
1968
+ xLabels,
1969
+ statistics: stats,
1970
+ statisticsPolicy: "inclusive viewport; original sample arithmetic mean; current includes final missing sample",
1971
+ font: {
1972
+ mode: c.fonts.mode,
1973
+ family: c.fonts.mode === "system" ? c.fonts.family : null,
1974
+ pixelAlphabet: c.fonts.mode === "bitmap" ? "ascii-5x7-v1" : null
1975
+ },
1976
+ layout: clone(l),
1977
+ theme: clone(t),
1978
+ warnings: c.fonts.mode === "system" ? ["System font selection and glyph rasterization depend on the host."] : []
1979
+ };
1980
+ return new RenderResult(out, metadata);
1981
+ }
1982
+ var CRC_TABLE = (() => {
1983
+ const t = new Uint32Array(256);
1984
+ for (let i = 0; i < 256; i++) {
1985
+ let c = i;
1986
+ for (let j = 0; j < 8; j++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
1987
+ t[i] = c >>> 0;
1988
+ }
1989
+ return t;
1990
+ })();
1991
+ function crc32(bytes) {
1992
+ let c = 4294967295;
1993
+ for (const b of bytes) c = CRC_TABLE[(c ^ b) & 255] ^ c >>> 8;
1994
+ return (c ^ 4294967295) >>> 0;
1995
+ }
1996
+ function adler32(bytes) {
1997
+ let a = 1, b = 0;
1998
+ for (let i = 0; i < bytes.length; ) {
1999
+ const end = Math.min(i + 5552, bytes.length);
2000
+ for (; i < end; i++) {
2001
+ a += bytes[i];
2002
+ b += a;
2003
+ }
2004
+ a %= 65521;
2005
+ b %= 65521;
2006
+ }
2007
+ return (b << 16 | a) >>> 0;
2008
+ }
2009
+ function concat(parts) {
2010
+ const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
2011
+ let at = 0;
2012
+ for (const p of parts) {
2013
+ out.set(p, at);
2014
+ at += p.length;
2015
+ }
2016
+ return out;
2017
+ }
2018
+ function u32(n) {
2019
+ return new Uint8Array([
2020
+ n >>> 24 & 255,
2021
+ n >>> 16 & 255,
2022
+ n >>> 8 & 255,
2023
+ n & 255
2024
+ ]);
2025
+ }
2026
+ function chunk(type, data) {
2027
+ const code = Uint8Array.from(type, (c) => c.charCodeAt(0)), body = concat([code, data]);
2028
+ return concat([u32(data.length), body, u32(crc32(body))]);
2029
+ }
2030
+ function reverseBits(n, bits) {
2031
+ let out = 0;
2032
+ for (let i = 0; i < bits; i++) {
2033
+ out = out << 1 | n & 1;
2034
+ n >>>= 1;
2035
+ }
2036
+ return out;
2037
+ }
2038
+ var FIXED = (() => {
2039
+ const out = [];
2040
+ for (let i = 0; i < 288; i++) {
2041
+ let code, bits;
2042
+ if (i <= 143) {
2043
+ bits = 8;
2044
+ code = 48 + i;
2045
+ } else if (i <= 255) {
2046
+ bits = 9;
2047
+ code = 400 + i - 144;
2048
+ } else if (i <= 279) {
2049
+ bits = 7;
2050
+ code = i - 256;
2051
+ } else {
2052
+ bits = 8;
2053
+ code = 192 + i - 280;
2054
+ }
2055
+ out.push([reverseBits(code, bits), bits]);
2056
+ }
2057
+ return out;
2058
+ })();
2059
+ var LEN_BASE = [
2060
+ 3,
2061
+ 4,
2062
+ 5,
2063
+ 6,
2064
+ 7,
2065
+ 8,
2066
+ 9,
2067
+ 10,
2068
+ 11,
2069
+ 13,
2070
+ 15,
2071
+ 17,
2072
+ 19,
2073
+ 23,
2074
+ 27,
2075
+ 31,
2076
+ 35,
2077
+ 43,
2078
+ 51,
2079
+ 59,
2080
+ 67,
2081
+ 83,
2082
+ 99,
2083
+ 115,
2084
+ 131,
2085
+ 163,
2086
+ 195,
2087
+ 227,
2088
+ 258
2089
+ ];
2090
+ var LEN_EXTRA = [
2091
+ 0,
2092
+ 0,
2093
+ 0,
2094
+ 0,
2095
+ 0,
2096
+ 0,
2097
+ 0,
2098
+ 0,
2099
+ 1,
2100
+ 1,
2101
+ 1,
2102
+ 1,
2103
+ 2,
2104
+ 2,
2105
+ 2,
2106
+ 2,
2107
+ 3,
2108
+ 3,
2109
+ 3,
2110
+ 3,
2111
+ 4,
2112
+ 4,
2113
+ 4,
2114
+ 4,
2115
+ 5,
2116
+ 5,
2117
+ 5,
2118
+ 5,
2119
+ 0
2120
+ ];
2121
+ var DIST_BASE = [
2122
+ 1,
2123
+ 2,
2124
+ 3,
2125
+ 4,
2126
+ 5,
2127
+ 7,
2128
+ 9,
2129
+ 13,
2130
+ 17,
2131
+ 25,
2132
+ 33,
2133
+ 49,
2134
+ 65,
2135
+ 97,
2136
+ 129,
2137
+ 193,
2138
+ 257,
2139
+ 385,
2140
+ 513,
2141
+ 769,
2142
+ 1025,
2143
+ 1537,
2144
+ 2049,
2145
+ 3073,
2146
+ 4097,
2147
+ 6145,
2148
+ 8193,
2149
+ 12289,
2150
+ 16385,
2151
+ 24577
2152
+ ];
2153
+ var DIST_EXTRA = [
2154
+ 0,
2155
+ 0,
2156
+ 0,
2157
+ 0,
2158
+ 1,
2159
+ 1,
2160
+ 2,
2161
+ 2,
2162
+ 3,
2163
+ 3,
2164
+ 4,
2165
+ 4,
2166
+ 5,
2167
+ 5,
2168
+ 6,
2169
+ 6,
2170
+ 7,
2171
+ 7,
2172
+ 8,
2173
+ 8,
2174
+ 9,
2175
+ 9,
2176
+ 10,
2177
+ 10,
2178
+ 11,
2179
+ 11,
2180
+ 12,
2181
+ 12,
2182
+ 13,
2183
+ 13
2184
+ ];
2185
+ function deflate(data) {
2186
+ let bytes = new Uint8Array(Math.max(128, data.length + 64)), len = 0, buffer = 0, count = 0;
2187
+ function byte(b) {
2188
+ if (len === bytes.length) {
2189
+ const next = new Uint8Array(bytes.length * 2);
2190
+ next.set(bytes);
2191
+ bytes = next;
2192
+ }
2193
+ bytes[len++] = b;
2194
+ }
2195
+ function bits(value, n) {
2196
+ buffer |= value << count;
2197
+ count += n;
2198
+ while (count >= 8) {
2199
+ byte(buffer & 255);
2200
+ buffer >>>= 8;
2201
+ count -= 8;
2202
+ }
2203
+ }
2204
+ const sym = (v) => bits(FIXED[v][0], FIXED[v][1]);
2205
+ byte(120);
2206
+ byte(1);
2207
+ bits(3, 3);
2208
+ const head = new Int32Array(65536).fill(-1), prev = new Int32Array(32768).fill(-1);
2209
+ const hash = (i) => (data[i] * 251 + data[i + 1]) * 251 + data[i + 2] & 65535;
2210
+ function insert(i) {
2211
+ if (i + 2 >= data.length) return;
2212
+ const h = hash(i);
2213
+ prev[i & 32767] = head[h];
2214
+ head[h] = i;
2215
+ }
2216
+ for (let i = 0; i < data.length; ) {
2217
+ let best = 0, dist = 0;
2218
+ if (i + 2 < data.length) {
2219
+ let candidate = head[hash(i)], tries = 64;
2220
+ const max = Math.min(258, data.length - i);
2221
+ while (candidate >= 0 && i - candidate <= 32768 && candidate < i && tries--) {
2222
+ if (data[candidate] === data[i] && data[candidate + best] === data[i + best]) {
2223
+ let n = 0;
2224
+ while (n < max && data[candidate + n] === data[i + n]) n++;
2225
+ if (n > best && n >= 3) {
2226
+ best = n;
2227
+ dist = i - candidate;
2228
+ if (n === max) break;
2229
+ }
2230
+ }
2231
+ const next = prev[candidate & 32767];
2232
+ if (next >= candidate) break;
2233
+ candidate = next;
2234
+ }
2235
+ }
2236
+ if (best >= 3) {
2237
+ let lc = 0;
2238
+ while (lc < 28 && LEN_BASE[lc + 1] <= best) lc++;
2239
+ sym(257 + lc);
2240
+ bits(best - LEN_BASE[lc], LEN_EXTRA[lc]);
2241
+ let dc = 0;
2242
+ while (dc < 29 && DIST_BASE[dc + 1] <= dist) dc++;
2243
+ bits(reverseBits(dc, 5), 5);
2244
+ bits(dist - DIST_BASE[dc], DIST_EXTRA[dc]);
2245
+ for (let j = 0; j < best; j++) insert(i + j);
2246
+ i += best;
2247
+ } else {
2248
+ sym(data[i]);
2249
+ insert(i);
2250
+ i++;
2251
+ }
2252
+ }
2253
+ sym(256);
2254
+ if (count) byte(buffer & 255);
2255
+ const sum = adler32(data);
2256
+ for (const b of u32(sum)) byte(b);
2257
+ return bytes.slice(0, len);
2258
+ }
2259
+ function paeth(a, b, c) {
2260
+ const p = a + b - c, pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c);
2261
+ return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
2262
+ }
2263
+ function encodePNG(image, metadata = null) {
2264
+ validateImage(image);
2265
+ check(
2266
+ image.width * image.height <= LIMITS.pixels,
2267
+ "PNG image exceeds output limit."
2268
+ );
2269
+ const w = image.width, h = image.height, stride = w * 4, raw = new Uint8Array((stride + 1) * h), candidates = Array.from({ length: 5 }, () => new Uint8Array(stride));
2270
+ for (let y = 0; y < h; y++) {
2271
+ const scores = [0, 0, 0, 0, 0], off = y * stride;
2272
+ for (let x = 0; x < stride; x++) {
2273
+ const v = image.data[off + x], a = x >= 4 ? image.data[off + x - 4] : 0, b = y ? image.data[off + x - stride] : 0, c = y && x >= 4 ? image.data[off + x - stride - 4] : 0, predict = [0, a, b, Math.floor((a + b) / 2), paeth(a, b, c)];
2274
+ for (let f = 0; f < 5; f++) {
2275
+ const n = v - predict[f] & 255;
2276
+ candidates[f][x] = n;
2277
+ scores[f] += Math.min(n, 256 - n);
2278
+ }
2279
+ }
2280
+ let best = 0;
2281
+ for (let f = 1; f < 5; f++) if (scores[f] < scores[best]) best = f;
2282
+ raw[y * (stride + 1)] = best;
2283
+ raw.set(candidates[best], y * (stride + 1) + 1);
2284
+ }
2285
+ const ihdr = concat([u32(w), u32(h), new Uint8Array([8, 6, 0, 0, 0])]), parts = [
2286
+ new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]),
2287
+ chunk("IHDR", ihdr),
2288
+ chunk("sRGB", new Uint8Array([0]))
2289
+ ];
2290
+ if (metadata !== null) {
2291
+ const json = JSON.stringify(metadata);
2292
+ check(json.length <= 1048576, "PNG metadata is too large.");
2293
+ parts.push(
2294
+ chunk(
2295
+ "iTXt",
2296
+ concat([
2297
+ new TextEncoder().encode("chart"),
2298
+ new Uint8Array([0, 0, 0, 0, 0]),
2299
+ new TextEncoder().encode(json)
2300
+ ])
2301
+ )
2302
+ );
2303
+ }
2304
+ parts.push(chunk("IDAT", deflate(raw)), chunk("IEND", new Uint8Array(0)));
2305
+ return concat(parts);
2306
+ }
2307
+ function validateImage(im) {
2308
+ check(
2309
+ im && Number.isInteger(im.width) && Number.isInteger(im.height) && im.width > 0 && im.height > 0 && im.width * im.height <= LIMITS.layerPixels && im.data && im.data.length === im.width * im.height * 4,
2310
+ "Expected an RGBA image with matching dimensions."
2311
+ );
2312
+ }
2313
+ function drawImageToCanvas(image, canvas) {
2314
+ validateImage(image);
2315
+ check(
2316
+ canvas && typeof canvas.getContext === "function",
2317
+ "Expected a canvas element."
2318
+ );
2319
+ const ctx = canvas.getContext("2d");
2320
+ check(ctx, "Canvas 2D context is unavailable.");
2321
+ canvas.width = image.width;
2322
+ canvas.height = image.height;
2323
+ const data = ctx.createImageData(image.width, image.height);
2324
+ data.data.set(image.data);
2325
+ ctx.putImageData(data, 0, 0);
2326
+ return canvas;
2327
+ }
2328
+ function downloadBytes(bytes, name, type = "application/octet-stream") {
2329
+ check(
2330
+ typeof document !== "undefined",
2331
+ "Downloads require a browser document."
2332
+ );
2333
+ text(name, "Filename");
2334
+ check(
2335
+ !/[\\/\x00-\x1f]/.test(name),
2336
+ "Filename must not contain path separators or controls."
2337
+ );
2338
+ const data = bytes.buffer instanceof ArrayBuffer ? bytes : new Uint8Array(bytes);
2339
+ const blob = new Blob([data], { type }), url = URL.createObjectURL(blob), a = document.createElement("a");
2340
+ a.href = url;
2341
+ a.download = name;
2342
+ a.hidden = true;
2343
+ document.body.appendChild(a);
2344
+ a.click();
2345
+ a.remove();
2346
+ setTimeout(() => URL.revokeObjectURL(url), 3e4);
2347
+ }
2348
+ var RenderResult = class {
2349
+ /** Pixels intentionally remain mutable; exports observe subsequent edits. */
2350
+ image;
2351
+ /** Immutable metadata captured at rendering time. */
2352
+ metadata;
2353
+ constructor(image, metadata) {
2354
+ this.image = image;
2355
+ this.metadata = freeze(metadata);
2356
+ }
2357
+ get width() {
2358
+ return this.image.width;
2359
+ }
2360
+ get height() {
2361
+ return this.image.height;
2362
+ }
2363
+ get data() {
2364
+ return this.image.data;
2365
+ }
2366
+ draw(canvas) {
2367
+ return drawImageToCanvas(this.image, canvas);
2368
+ }
2369
+ toPNG(options = {}) {
2370
+ return encodePNG(
2371
+ this.image,
2372
+ options.metadata === false ? null : this.metadata
2373
+ );
2374
+ }
2375
+ toBlob(options = {}) {
2376
+ return new Blob([this.toPNG(options)], { type: "image/png" });
2377
+ }
2378
+ download(filename = "chart.png", options = {}) {
2379
+ downloadBytes(this.toPNG(options), filename, "image/png");
2380
+ }
2381
+ };
2382
+ var Chart = class _Chart {
2383
+ /** Fully normalized, deeply frozen options used by every render. */
2384
+ config;
2385
+ constructor(options = {}) {
2386
+ this.config = normalize(options);
2387
+ Object.freeze(this);
2388
+ }
2389
+ with(patch) {
2390
+ return new _Chart(merge(this.config, patch));
2391
+ }
2392
+ render() {
2393
+ return renderChart(this.config);
2394
+ }
2395
+ draw(canvas) {
2396
+ const r = this.render();
2397
+ r.draw(canvas);
2398
+ return r;
2399
+ }
2400
+ toPNG(options = {}) {
2401
+ return this.render().toPNG(options);
2402
+ }
2403
+ mount(canvas, options = {}) {
2404
+ return new BrowserController(this, canvas, options);
2405
+ }
2406
+ nearest(input) {
2407
+ const time = epoch(input);
2408
+ const [start, end] = timeRange(this.config);
2409
+ return this.config.series.map((s) => {
2410
+ const lo = lowerBound(s.timestamps, start), hi = upperBound(s.timestamps, end);
2411
+ if (lo === hi)
2412
+ return { name: s.name, index: null, time: null, value: null };
2413
+ let i = clamp(lowerBound(s.timestamps, time), lo, hi - 1);
2414
+ if (i > lo && Math.abs(s.timestamps[i - 1] - time) <= Math.abs(s.timestamps[i] - time))
2415
+ i--;
2416
+ return {
2417
+ name: s.name,
2418
+ index: i,
2419
+ time: s.timestamps[i],
2420
+ value: finite(s.values[i]) ? s.values[i] : null
2421
+ };
2422
+ });
2423
+ }
2424
+ };
2425
+ function traffic(timestamps, inbound, outbound, options = {}) {
2426
+ const gap = options.gapAfter === void 0 ? 0 : options.gapAfter, opts = { ...options };
2427
+ delete opts.gapAfter;
2428
+ return new Chart(
2429
+ merge(
2430
+ {
2431
+ title: "Traffic - ether1",
2432
+ verticalLabel: "bits per second",
2433
+ series: [
2434
+ series("Inbound", timestamps, inbound, {
2435
+ kind: "area",
2436
+ color: "#00cc00",
2437
+ outline: "#003000",
2438
+ gapAfter: gap
2439
+ }),
2440
+ series("Outbound", timestamps, outbound, {
2441
+ kind: "line",
2442
+ color: "#0000cc",
2443
+ gapAfter: gap
2444
+ })
2445
+ ]
2446
+ },
2447
+ opts
2448
+ )
2449
+ );
2450
+ }
2451
+ function dashboard(panels, options = {}) {
2452
+ check(
2453
+ Array.isArray(panels) && panels.length > 0 && panels.length <= 128,
2454
+ "Dashboard requires 1..128 panels."
2455
+ );
2456
+ const o = merge(
2457
+ { gap: 24, padding: [4, 3, 6, 6], background: "#f3f3f3", cropHeight: null },
2458
+ options
2459
+ );
2460
+ integer(o.gap, "Panel gap", 0, 4096);
2461
+ check(
2462
+ Array.isArray(o.padding) && o.padding.length === 4,
2463
+ "Padding is [left, top, right, bottom]."
2464
+ );
2465
+ o.padding.forEach((v) => integer(v, "Padding", 0, 4096));
2466
+ if (o.cropHeight !== null) integer(o.cropHeight, "Crop height", 1, 65536);
2467
+ const pp = panels.map(
2468
+ (p) => p instanceof Chart ? { chart: p, caption: "" } : p
2469
+ );
2470
+ for (const p of pp) {
2471
+ check(p.chart instanceof Chart, "Panel needs a Chart.");
2472
+ text(p.caption || "", "Caption");
2473
+ }
2474
+ const scale = pp[0].chart.config.layout.pixelScale;
2475
+ check(
2476
+ pp.every((p) => p.chart.config.layout.pixelScale === scale),
2477
+ "Dashboard panels must have equal pixelScale."
2478
+ );
2479
+ const rendered = pp.map((p) => p.chart.render()), left = o.padding[0] * scale, top = o.padding[1] * scale, gap = o.gap * scale;
2480
+ const width = Math.max(...rendered.map((r) => r.width)) + (o.padding[0] + o.padding[2]) * scale;
2481
+ let height = rendered.reduce((n, r) => n + r.height, 0) + (o.padding[1] + o.padding[3]) * scale + gap * (pp.length - 1 + (pp[pp.length - 1].caption ? 1 : 0));
2482
+ if (o.cropHeight !== null) height = Math.min(height, o.cropHeight * scale);
2483
+ check(width * height <= LIMITS.pixels, "Dashboard is too large.");
2484
+ const out = new Surface(width, height, color(o.background));
2485
+ let y = top;
2486
+ const meta = [];
2487
+ for (let i = 0; i < pp.length; i++) {
2488
+ const r = rendered[i], p = pp[i];
2489
+ out.over(r.image, left, y);
2490
+ meta.push({
2491
+ position: [left, y],
2492
+ caption: p.caption || "",
2493
+ chart: r.metadata
2494
+ });
2495
+ y += r.height;
2496
+ if (p.caption) {
2497
+ const f = new Fonts(p.chart.config.fonts, p.chart.config.theme), w = f.width(p.caption, "caption"), h = p.chart.config.theme.captionSize;
2498
+ check(
2499
+ w <= width / scale - 8 && h + 4 <= o.gap,
2500
+ "Caption does not fit the dashboard gap."
2501
+ );
2502
+ const line = new Surface(width / scale, o.gap);
2503
+ f.draw(
2504
+ line,
2505
+ width / scale / 2,
2506
+ 4,
2507
+ p.caption,
2508
+ "caption",
2509
+ p.chart.config.theme.text,
2510
+ 0,
2511
+ "center"
2512
+ );
2513
+ out.over(line.scale(scale), 0, y);
2514
+ }
2515
+ y += gap;
2516
+ }
2517
+ return new RenderResult(out, {
2518
+ version: VERSION,
2519
+ imageSize: [width, height],
2520
+ pixelScale: scale,
2521
+ panels: meta
2522
+ });
2523
+ }
2524
+ function counterRate(timestamps, counters, options = {}) {
2525
+ const o = merge(
2526
+ { factor: 1, onDecrease: "gap", counterBits: null, maxRate: null },
2527
+ options
2528
+ );
2529
+ number(o.factor, "Rate factor", Number.MIN_VALUE);
2530
+ check(
2531
+ ["gap", "wrap"].includes(o.onDecrease),
2532
+ "Decrease policy must be gap or wrap."
2533
+ );
2534
+ if (o.counterBits !== null) integer(o.counterBits, "Counter bits", 1, 128);
2535
+ if (o.maxRate !== null) number(o.maxRate, "Maximum rate", 0);
2536
+ check(
2537
+ o.onDecrease !== "wrap" || o.counterBits !== null,
2538
+ "Wrapping requires an explicit counterBits."
2539
+ );
2540
+ check(
2541
+ (Array.isArray(counters) || ArrayBuffer.isView(counters)) && counters.length === timestamps.length,
2542
+ "Counter and timestamp lengths must match."
2543
+ );
2544
+ const s = samples(
2545
+ timestamps,
2546
+ Array.from(counters, () => 0)
2547
+ ), max = (1n << BigInt(o.counterBits || 128)) - 1n;
2548
+ const cs = Array.from(counters, (v, i) => {
2549
+ if (missing(v)) return null;
2550
+ if (typeof v === "number") {
2551
+ check(
2552
+ Number.isSafeInteger(v) && v >= 0,
2553
+ "Counter " + i + " must be a safe nonnegative integer; use BigInt for large counters."
2554
+ );
2555
+ v = BigInt(v);
2556
+ }
2557
+ check(
2558
+ typeof v === "bigint" && v >= 0n && v <= max,
2559
+ "Counter must fit the configured unsigned bit width."
2560
+ );
2561
+ return v;
2562
+ });
2563
+ s.values.fill(NaN);
2564
+ for (let i = 1; i < cs.length; i++) {
2565
+ const current = cs[i], previous = cs[i - 1];
2566
+ if (current === null || previous === null) continue;
2567
+ let d = current - previous;
2568
+ if (d < 0n) {
2569
+ if (o.onDecrease === "gap") continue;
2570
+ d += max + 1n;
2571
+ }
2572
+ const v = Number(d) * o.factor / (s.timestamps[i] - s.timestamps[i - 1]);
2573
+ check(finite(v), "Counter rate overflow.");
2574
+ if (o.maxRate === null || v <= o.maxRate) s.values[i] = v;
2575
+ }
2576
+ return s;
2577
+ }
2578
+ function aggregate(timestamps, values, options = {}) {
2579
+ const o = merge(
2580
+ {
2581
+ interval: 300,
2582
+ method: "mean",
2583
+ origin: 0,
2584
+ minCoverage: 0,
2585
+ expectedStep: null,
2586
+ maxBuckets: 1e6
2587
+ },
2588
+ options
2589
+ );
2590
+ number(o.interval, "Aggregation interval", 1e-3);
2591
+ number(o.origin, "Aggregation origin");
2592
+ number(o.minCoverage, "Minimum coverage", 0, 1);
2593
+ if (o.expectedStep !== null)
2594
+ number(o.expectedStep, "Expected step", Number.MIN_VALUE);
2595
+ integer(o.maxBuckets, "Maximum buckets", 1, 1e6);
2596
+ check(
2597
+ ["mean", "min", "max", "last", "sum"].includes(o.method),
2598
+ "Unknown aggregation method."
2599
+ );
2600
+ const s = samples(timestamps, values);
2601
+ if (!s.timestamps.length) return s;
2602
+ const bucket = (t) => Math.floor((t - o.origin) / o.interval), a = bucket(s.timestamps[0]), b = bucket(s.timestamps[s.timestamps.length - 1]);
2603
+ check(
2604
+ Number.isSafeInteger(a) && Number.isSafeInteger(b) && b - a + 1 <= o.maxBuckets,
2605
+ "Aggregation bucket limit or precision exceeded."
2606
+ );
2607
+ const out = { timestamps: [], values: [] };
2608
+ let i = 0;
2609
+ for (let k = a; k <= b; k++) {
2610
+ const ts = o.origin + k * o.interval;
2611
+ epoch(ts);
2612
+ out.timestamps.push(ts);
2613
+ let total = 0, last = NaN, mn = Infinity, mx = -Infinity;
2614
+ const finiteValues = [];
2615
+ while (i < s.timestamps.length && bucket(s.timestamps[i]) === k) {
2616
+ const v = s.values[i++];
2617
+ total++;
2618
+ last = v;
2619
+ if (finite(v)) {
2620
+ finiteValues.push(v);
2621
+ mn = Math.min(mn, v);
2622
+ mx = Math.max(mx, v);
2623
+ }
2624
+ }
2625
+ const denominator = Math.max(
2626
+ total,
2627
+ o.expectedStep === null ? 0 : o.interval / o.expectedStep
2628
+ );
2629
+ if (!finiteValues.length || finiteValues.length / denominator < o.minCoverage) {
2630
+ out.values.push(NaN);
2631
+ continue;
2632
+ }
2633
+ let value;
2634
+ if (o.method === "mean") value = stableMean(finiteValues);
2635
+ else if (o.method === "min") value = mn;
2636
+ else if (o.method === "max") value = mx;
2637
+ else if (o.method === "last") value = last;
2638
+ else {
2639
+ value = stableMean(finiteValues) * finiteValues.length;
2640
+ check(finite(value), "Aggregation sum overflow.");
2641
+ }
2642
+ out.values.push(value);
2643
+ }
2644
+ return out;
2645
+ }
2646
+ function parseCSV(input, options = {}) {
2647
+ const o = merge(
2648
+ {
2649
+ timestampColumn: "timestamp",
2650
+ columns: [
2651
+ {
2652
+ column: "inbound",
2653
+ name: "Inbound",
2654
+ kind: "area",
2655
+ color: "#00cc00",
2656
+ outline: "#003000"
2657
+ },
2658
+ {
2659
+ column: "outbound",
2660
+ name: "Outbound",
2661
+ kind: "line",
2662
+ color: "#0000cc"
2663
+ }
2664
+ ],
2665
+ maxRows: 1e6,
2666
+ maxBytes: 16777216
2667
+ },
2668
+ options
2669
+ );
2670
+ check(typeof input === "string", "CSV must be a string.");
2671
+ integer(o.maxRows, "Maximum CSV rows", 1, 2e6);
2672
+ integer(o.maxBytes, "Maximum CSV bytes", 1, 67108864);
2673
+ check(
2674
+ input.length <= o.maxBytes && new TextEncoder().encode(input).length <= o.maxBytes,
2675
+ "CSV exceeds the configured byte limit."
2676
+ );
2677
+ check(
2678
+ Array.isArray(o.columns) && o.columns.length > 0 && o.columns.length <= 128,
2679
+ "CSV needs at least one column mapping."
2680
+ );
2681
+ if (input.charCodeAt(0) === 65279) input = input.slice(1);
2682
+ const rows = [];
2683
+ let row = [], cell = "", quoted = false, closed = false;
2684
+ function endCell() {
2685
+ row.push(cell);
2686
+ cell = "";
2687
+ closed = false;
2688
+ }
2689
+ function endRow() {
2690
+ endCell();
2691
+ if (row.some((v) => v !== "")) rows.push(row);
2692
+ row = [];
2693
+ check(rows.length <= o.maxRows + 1, "CSV row limit exceeded.");
2694
+ }
2695
+ for (let i = 0; i < input.length; i++) {
2696
+ const ch = input[i];
2697
+ if (quoted) {
2698
+ if (ch === '"') {
2699
+ if (input[i + 1] === '"') {
2700
+ cell += '"';
2701
+ i++;
2702
+ } else {
2703
+ quoted = false;
2704
+ closed = true;
2705
+ }
2706
+ } else cell += ch;
2707
+ continue;
2708
+ }
2709
+ if (closed) {
2710
+ check(
2711
+ ch === "," || ch === "\r" || ch === "\n",
2712
+ "Unexpected text after a closing CSV quote."
2713
+ );
2714
+ }
2715
+ if (ch === '"') {
2716
+ check(
2717
+ cell === "" && !closed,
2718
+ "Unexpected quote in an unquoted CSV field."
2719
+ );
2720
+ quoted = true;
2721
+ } else if (ch === ",") endCell();
2722
+ else if (ch === "\n" || ch === "\r") {
2723
+ if (ch === "\r" && input[i + 1] === "\n") i++;
2724
+ endRow();
2725
+ } else cell += ch;
2726
+ }
2727
+ check(!quoted, "Unterminated quoted CSV field.");
2728
+ if (cell !== "" || row.length || closed) endRow();
2729
+ check(rows.length >= 1, "CSV header is missing.");
2730
+ const header = rows.shift().map((v) => v.trim());
2731
+ check(
2732
+ header.every(Boolean) && new Set(header).size === header.length,
2733
+ "CSV headers must be nonempty and unique."
2734
+ );
2735
+ const ti = header.indexOf(o.timestampColumn);
2736
+ check(ti >= 0, "Timestamp column not found: " + o.timestampColumn);
2737
+ const ids = o.columns.map((v) => {
2738
+ const i = header.indexOf(v.column);
2739
+ check(i >= 0, "Column not found: " + v.column);
2740
+ return i;
2741
+ });
2742
+ const ts = [], vv = o.columns.map(() => []);
2743
+ for (let i = 0; i < rows.length; i++) {
2744
+ const r = rows[i];
2745
+ check(
2746
+ r.length === header.length,
2747
+ "CSV row " + (i + 2) + " has the wrong number of columns."
2748
+ );
2749
+ try {
2750
+ ts.push(epoch(r[ti]));
2751
+ } catch (e) {
2752
+ throw new RangeError(
2753
+ "CSV row " + (i + 2) + ": " + (e instanceof Error ? e.message : String(e))
2754
+ );
2755
+ }
2756
+ ids.forEach((id, j) => {
2757
+ const v = r[id].trim();
2758
+ if (/^(?:nan|none|null)?$/i.test(v)) {
2759
+ vv[j].push(NaN);
2760
+ return;
2761
+ }
2762
+ check(
2763
+ /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(v),
2764
+ "Invalid numeric CSV value at row " + (i + 2) + "."
2765
+ );
2766
+ vv[j].push(number(Number(v), "CSV value"));
2767
+ });
2768
+ }
2769
+ return o.columns.map((v, j) => series(v.name || v.column, ts, vv[j], v));
2770
+ }
2771
+ function toCSV(list, options = {}) {
2772
+ check(
2773
+ Array.isArray(list) && list.length <= 128,
2774
+ "Expected an array of series."
2775
+ );
2776
+ const escaped = options.escapeFormulas !== false;
2777
+ const ss = list.map((s) => series(s.name, s.timestamps, s.values, s));
2778
+ let total = 0;
2779
+ for (const s of ss) total += s.timestamps.length;
2780
+ check(
2781
+ total <= LIMITS.samples,
2782
+ "CSV export exceeds the combined sample limit."
2783
+ );
2784
+ const times = [...new Set(ss.flatMap((s) => s.timestamps))].sort(
2785
+ (a, b) => a - b
2786
+ );
2787
+ check(
2788
+ new Set(ss.map((s) => s.name)).size === ss.length && !ss.some((s) => s.name === "timestamp"),
2789
+ "CSV series names must be unique and not timestamp."
2790
+ );
2791
+ const quote = (v) => {
2792
+ v = String(v);
2793
+ if (escaped && /^[=+\-@\t\r]/.test(v)) v = "'" + v;
2794
+ return /[,"\r\n]/.test(v) ? '"' + v.replace(/"/g, '""') + '"' : v;
2795
+ };
2796
+ const lines = [["timestamp", ...ss.map((s) => s.name)].map(quote).join(",")], indexes = ss.map(() => 0);
2797
+ for (const t of times) {
2798
+ const cells = [String(t)];
2799
+ ss.forEach((s, j) => {
2800
+ while (indexes[j] < s.timestamps.length && s.timestamps[indexes[j]] < t)
2801
+ indexes[j]++;
2802
+ const i = indexes[j];
2803
+ cells.push(
2804
+ i < s.timestamps.length && s.timestamps[i] === t && finite(s.values[i]) ? String(s.values[i]) : ""
2805
+ );
2806
+ });
2807
+ lines.push(cells.join(","));
2808
+ }
2809
+ return lines.join("\r\n") + "\r\n";
2810
+ }
2811
+ function compareImages(reference, actual, options = {}) {
2812
+ const a = reference instanceof RenderResult ? reference.image : reference, b = actual instanceof RenderResult ? actual.image : actual;
2813
+ validateImage(a);
2814
+ validateImage(b);
2815
+ check(
2816
+ a.width === b.width && a.height === b.height,
2817
+ "Image sizes must match; comparison never aligns or resizes."
2818
+ );
2819
+ const tolerance = options.tolerance === void 0 ? 0 : options.tolerance;
2820
+ integer(tolerance, "Tolerance", 0, 255);
2821
+ const box = options.box || [0, 0, a.width, a.height];
2822
+ check(
2823
+ Array.isArray(box) && box.length === 4,
2824
+ "Comparison box needs four coordinates."
2825
+ );
2826
+ box.forEach((v) => integer(v, "Comparison coordinate", 0, 65536));
2827
+ const [x0, y0, x1, y1] = box;
2828
+ check(
2829
+ x1 > x0 && y1 > y0 && x1 <= a.width && y1 <= a.height,
2830
+ "Comparison box is out of bounds."
2831
+ );
2832
+ let exact = 0, within = 0, sum = 0, square = 0, maxError = 0, loX = Infinity, loY = Infinity, hiX = -Infinity, hiY = -Infinity;
2833
+ for (let y = y0; y < y1; y++)
2834
+ for (let x = x0; x < x1; x++) {
2835
+ const i = (y * a.width + x) * 4;
2836
+ let err = 0;
2837
+ for (let k = 0; k < 3; k++) {
2838
+ const d = Math.abs(a.data[i + k] - b.data[i + k]);
2839
+ sum += d;
2840
+ square += d * d;
2841
+ err = Math.max(err, d);
2842
+ }
2843
+ maxError = Math.max(maxError, err);
2844
+ if (err === 0) exact++;
2845
+ else {
2846
+ loX = Math.min(loX, x - x0);
2847
+ loY = Math.min(loY, y - y0);
2848
+ hiX = Math.max(hiX, x - x0);
2849
+ hiY = Math.max(hiY, y - y0);
2850
+ }
2851
+ if (err <= tolerance) within++;
2852
+ }
2853
+ const count = (x1 - x0) * (y1 - y0);
2854
+ return {
2855
+ pixels: count,
2856
+ exactPixels: exact,
2857
+ exactRatio: exact / count,
2858
+ toleranceRatio: within / count,
2859
+ meanAbsoluteError: sum / (count * 3),
2860
+ rootMeanSquareError: Math.sqrt(square / (count * 3)),
2861
+ maxError,
2862
+ differenceBox: loX === Infinity ? null : [loX, loY, hiX + 1, hiY + 1]
2863
+ };
2864
+ }
2865
+ function differenceImage(reference, actual, amplify = 4) {
2866
+ number(amplify, "Difference amplification", 0, 255);
2867
+ const a = reference instanceof RenderResult ? reference.image : reference, b = actual instanceof RenderResult ? actual.image : actual;
2868
+ compareImages(a, b);
2869
+ const out = new Surface(a.width, a.height);
2870
+ for (let i = 0; i < a.data.length; i += 4) {
2871
+ for (let k = 0; k < 3; k++)
2872
+ out.data[i + k] = Math.min(
2873
+ 255,
2874
+ round(Math.abs(a.data[i + k] - b.data[i + k]) * amplify)
2875
+ );
2876
+ out.data[i + 3] = 255;
2877
+ }
2878
+ return new RenderResult(out, { imageSize: [out.width, out.height], amplify });
2879
+ }
2880
+ function readCanvas(canvas) {
2881
+ check(
2882
+ canvas && typeof canvas.getContext === "function",
2883
+ "Expected a canvas."
2884
+ );
2885
+ const c = canvas.getContext("2d");
2886
+ check(c, "Canvas 2D is unavailable.");
2887
+ const data = c.getImageData(0, 0, canvas.width, canvas.height);
2888
+ const out = new Surface(data.width, data.height);
2889
+ out.data.set(data.data);
2890
+ return out;
2891
+ }
2892
+ async function decodeImage(blob) {
2893
+ check(
2894
+ typeof Blob !== "undefined" && blob instanceof Blob,
2895
+ "decodeImage expects a Blob or File."
2896
+ );
2897
+ check(blob.size <= 67108864, "Image file exceeds 64 MiB.");
2898
+ const head = new Uint8Array(await blob.slice(0, 24).arrayBuffer());
2899
+ check(
2900
+ head.length >= 24 && [137, 80, 78, 71, 13, 10, 26, 10].every((v, i) => head[i] === v) && head[12] === 73 && head[13] === 72 && head[14] === 68 && head[15] === 82,
2901
+ "decodeImage accepts PNG files only."
2902
+ );
2903
+ const headerView = new DataView(head.buffer), width = headerView.getUint32(16), height = headerView.getUint32(20);
2904
+ check(
2905
+ width > 0 && height > 0 && width * height <= LIMITS.pixels,
2906
+ "Decoded PNG exceeds the pixel limit."
2907
+ );
2908
+ check(
2909
+ typeof createImageBitmap === "function",
2910
+ "This browser does not expose createImageBitmap."
2911
+ );
2912
+ const bitmap = await createImageBitmap(blob);
2913
+ try {
2914
+ check(
2915
+ bitmap.width * bitmap.height <= LIMITS.pixels,
2916
+ "Decoded image exceeds the pixel limit."
2917
+ );
2918
+ const canvas = createCanvas(bitmap.width, bitmap.height);
2919
+ const ctx = canvas.getContext("2d");
2920
+ check(ctx, "Canvas 2D is unavailable.");
2921
+ ctx.drawImage(bitmap, 0, 0);
2922
+ return readCanvas(canvas);
2923
+ } finally {
2924
+ bitmap.close();
2925
+ }
2926
+ }
2927
+ var BrowserController = class {
2928
+ chart;
2929
+ canvas;
2930
+ options;
2931
+ destroyed;
2932
+ result;
2933
+ handlers;
2934
+ cursorTime;
2935
+ saved;
2936
+ marker;
2937
+ wrap;
2938
+ overlay;
2939
+ tip;
2940
+ status;
2941
+ constructor(chart, canvas, options = {}) {
2942
+ check(
2943
+ typeof document !== "undefined" && canvas && canvas.ownerDocument,
2944
+ "mount requires an HTML canvas in a document."
2945
+ );
2946
+ check(canvas.parentNode, "Attach the canvas to the document before mount.");
2947
+ check(
2948
+ !canvas.__bamtiGraphController,
2949
+ "A controller is already mounted on this canvas."
2950
+ );
2951
+ this.chart = chart;
2952
+ this.canvas = canvas;
2953
+ this.options = { interactive: true, ...options };
2954
+ this.destroyed = false;
2955
+ this.handlers = [];
2956
+ this.cursorTime = null;
2957
+ this.result = chart.render();
2958
+ this.saved = {
2959
+ style: canvas.getAttribute("style"),
2960
+ role: canvas.getAttribute("role"),
2961
+ label: canvas.getAttribute("aria-label"),
2962
+ tab: canvas.getAttribute("tabindex")
2963
+ };
2964
+ this.marker = document.createComment("canvas position");
2965
+ canvas.parentNode.insertBefore(this.marker, canvas);
2966
+ this.wrap = document.createElement("span");
2967
+ this.wrap.style.cssText = "position:relative;display:inline-block;vertical-align:top;line-height:0;max-width:none;";
2968
+ canvas.parentNode.insertBefore(this.wrap, canvas);
2969
+ this.wrap.appendChild(canvas);
2970
+ canvas.style.display = "block";
2971
+ canvas.style.maxWidth = "none";
2972
+ canvas.setAttribute("role", "img");
2973
+ this.overlay = document.createElement("canvas");
2974
+ this.overlay.setAttribute("aria-hidden", "true");
2975
+ this.overlay.style.cssText = "position:absolute;left:0;top:0;pointer-events:none;";
2976
+ this.wrap.appendChild(this.overlay);
2977
+ this.tip = document.createElement("div");
2978
+ this.tip.hidden = true;
2979
+ this.tip.setAttribute("role", "status");
2980
+ this.tip.style.cssText = "position:absolute;z-index:5;pointer-events:none;background:#111827;color:#fff;border:1px solid #4b5563;padding:9px 11px;border-radius:5px;font:11px/1.65 ui-monospace,monospace;white-space:pre;box-shadow:0 4px 18px #0003;text-align:left;";
2981
+ this.wrap.appendChild(this.tip);
2982
+ this.status = document.createElement("span");
2983
+ this.status.setAttribute("aria-live", "polite");
2984
+ this.status.style.cssText = "position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;";
2985
+ this.wrap.appendChild(this.status);
2986
+ this.apply(this.result);
2987
+ canvas.__bamtiGraphController = this;
2988
+ if (this.options.interactive) {
2989
+ canvas.tabIndex = 0;
2990
+ this.on(canvas, "pointermove", (e) => {
2991
+ const rect = canvas.getBoundingClientRect(), x = (e.clientX - rect.left) * canvas.width / rect.width, y = (e.clientY - rect.top) * canvas.height / rect.height, m = this.result.metadata, s = m.pixelScale, [l, t, r, b] = m.plotBox;
2992
+ if (x < l * s || x > r * s || y < t * s || y > b * s) {
2993
+ this.clear();
2994
+ return;
2995
+ }
2996
+ const time = m.timeRange[0] + (x / s - l) / (r - l) * (m.timeRange[1] - m.timeRange[0]);
2997
+ this.show(time, false);
2998
+ });
2999
+ this.on(canvas, "pointerleave", () => this.clear());
3000
+ this.on(canvas, "blur", () => this.clear());
3001
+ this.on(canvas, "keydown", (e) => {
3002
+ if (!["ArrowLeft", "ArrowRight", "Home", "End", "Escape"].includes(e.key))
3003
+ return;
3004
+ e.preventDefault();
3005
+ if (e.key === "Escape") {
3006
+ this.clear();
3007
+ return;
3008
+ }
3009
+ const [a, b] = this.result.metadata.timeRange, s = this.chart.config.series.find((s2) => s2.timestamps.length), visible = s ? s.timestamps.slice(
3010
+ lowerBound(s.timestamps, a),
3011
+ upperBound(s.timestamps, b)
3012
+ ) : [];
3013
+ let time;
3014
+ if (e.key === "Home") time = visible[0] === void 0 ? a : visible[0];
3015
+ else if (e.key === "End")
3016
+ time = visible.length ? visible[visible.length - 1] : b;
3017
+ else if (visible.length) {
3018
+ let i = this.cursorTime === null ? e.key === "ArrowRight" ? -1 : visible.length : lowerBound(visible, this.cursorTime);
3019
+ i = clamp(
3020
+ i + (e.key === "ArrowRight" ? 1 : -1) * (e.shiftKey ? 10 : 1),
3021
+ 0,
3022
+ visible.length - 1
3023
+ );
3024
+ time = visible[i];
3025
+ } else
3026
+ time = clamp(
3027
+ (this.cursorTime === null ? a : this.cursorTime) + (e.key === "ArrowRight" ? 1 : -1) * (b - a) / 100,
3028
+ a,
3029
+ b
3030
+ );
3031
+ this.show(time, true);
3032
+ });
3033
+ }
3034
+ }
3035
+ on(target, type, fn) {
3036
+ target.addEventListener(type, fn);
3037
+ this.handlers.push([target, type, fn]);
3038
+ }
3039
+ apply(r) {
3040
+ this.result = r;
3041
+ r.draw(this.canvas);
3042
+ this.overlay.width = r.width;
3043
+ this.overlay.height = r.height;
3044
+ const m = r.metadata;
3045
+ this.canvas.setAttribute(
3046
+ "aria-label",
3047
+ this.options.ariaLabel || [
3048
+ m.title || "Time-series chart",
3049
+ m.verticalLabel,
3050
+ ...m.statistics.map(
3051
+ (s) => s.name + ": current " + (s.current === null ? "missing" : formatValue(s.current, m.yUnit, 2))
3052
+ )
3053
+ ].join(". ")
3054
+ );
3055
+ this.wrap.style.width = r.width + "px";
3056
+ this.wrap.style.height = r.height + "px";
3057
+ this.clear();
3058
+ }
3059
+ show(time, announce) {
3060
+ if (this.destroyed) return;
3061
+ const m = this.result.metadata, s = m.pixelScale, [left, top, right, bottom] = m.plotBox, ctx = this.overlay.getContext("2d"), x = (left + (time - m.timeRange[0]) / (m.timeRange[1] - m.timeRange[0]) * (right - left)) * s;
3062
+ ctx.clearRect(0, 0, this.overlay.width, this.overlay.height);
3063
+ ctx.beginPath();
3064
+ ctx.strokeStyle = "#555";
3065
+ ctx.lineWidth = 1;
3066
+ ctx.setLineDash([2, 2]);
3067
+ ctx.moveTo(round(x) + 0.5, top * s);
3068
+ ctx.lineTo(round(x) + 0.5, bottom * s);
3069
+ ctx.stroke();
3070
+ const nearest = this.chart.nearest(time), rows = ["Nearest samples"];
3071
+ for (const p of nearest)
3072
+ rows.push(
3073
+ p.name + ": " + formatValue(p.value, m.yUnit, 2, this.chart.config.missingLabel) + (p.time !== null ? " " + formatTime(p.time, m.timezone, "%H:%M:%S") : " no observation")
3074
+ );
3075
+ this.tip.textContent = rows.join("\n");
3076
+ this.tip.hidden = false;
3077
+ this.tip.style.left = Math.max(
3078
+ 4,
3079
+ Math.min(this.result.width - this.tip.offsetWidth - 4, x + 12)
3080
+ ) + "px";
3081
+ this.tip.style.top = top * s + 8 + "px";
3082
+ this.cursorTime = time;
3083
+ if (announce) this.status.textContent = rows.join(". ");
3084
+ if (typeof this.options.onHover === "function")
3085
+ this.options.onHover({ time, samples: nearest });
3086
+ }
3087
+ clear() {
3088
+ if (this.overlay)
3089
+ this.overlay.getContext("2d").clearRect(0, 0, this.overlay.width, this.overlay.height);
3090
+ if (this.tip) this.tip.hidden = true;
3091
+ this.cursorTime = null;
3092
+ }
3093
+ update(patch) {
3094
+ check(!this.destroyed, "Controller has been destroyed.");
3095
+ const next = patch instanceof Chart ? patch : this.chart.with(patch), result = next.render();
3096
+ this.chart = next;
3097
+ this.apply(result);
3098
+ return result;
3099
+ }
3100
+ destroy() {
3101
+ if (this.destroyed) return;
3102
+ this.destroyed = true;
3103
+ for (const [el, type, fn] of this.handlers)
3104
+ el.removeEventListener(type, fn);
3105
+ this.handlers.length = 0;
3106
+ if (this.marker.parentNode) {
3107
+ this.marker.parentNode.insertBefore(this.canvas, this.marker);
3108
+ this.marker.remove();
3109
+ } else this.wrap.removeChild(this.canvas);
3110
+ this.wrap.remove();
3111
+ for (const [key, attr] of [
3112
+ ["style", "style"],
3113
+ ["role", "role"],
3114
+ ["label", "aria-label"],
3115
+ ["tab", "tabindex"]
3116
+ ]) {
3117
+ if (this.saved[key] === null) this.canvas.removeAttribute(attr);
3118
+ else this.canvas.setAttribute(attr, this.saved[key]);
3119
+ }
3120
+ delete this.canvas.__bamtiGraphController;
3121
+ }
3122
+ };
3123
+ function defaults() {
3124
+ return clone(DEFAULTS);
3125
+ }
3126
+ function render(options = {}) {
3127
+ return new Chart(options).render();
3128
+ }
3129
+ var BamtiGraph = Object.freeze({
3130
+ VERSION,
3131
+ LIMITS,
3132
+ Chart,
3133
+ RenderResult,
3134
+ series,
3135
+ regularSeries,
3136
+ traffic,
3137
+ daily,
3138
+ weekly,
3139
+ monthly,
3140
+ yearly,
3141
+ dashboard,
3142
+ counterRate,
3143
+ aggregate,
3144
+ parseCSV,
3145
+ toCSV,
3146
+ compareImages,
3147
+ differenceImage,
3148
+ encodePNG,
3149
+ decodeImage,
3150
+ readCanvas,
3151
+ drawImageToCanvas,
3152
+ downloadBytes,
3153
+ formatTime,
3154
+ formatValue,
3155
+ epoch,
3156
+ color,
3157
+ defaults,
3158
+ render
3159
+ });
3160
+ var index_default = BamtiGraph;
3161
+ //# sourceMappingURL=index.cjs.map