chartjs-plugin-streaming-react 2.0.1 → 2.0.3

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.
@@ -0,0 +1,946 @@
1
+ /*!
2
+ * chartjs-plugin-streaming-react v2.0.2
3
+ * https://github.com/NotPunchnox/chartjs-plugin-streaming-react
4
+ * (c) 2017-2026 Akihiko Kusanagi
5
+ * Released under the MIT license
6
+ */
7
+ (function (global, factory) {
8
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js'), require('chart.js/helpers')) :
9
+ typeof define === 'function' && define.amd ? define(['chart.js', 'chart.js/helpers'], factory) :
10
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ChartStreaming = factory(global.Chart, global.Chart.helpers));
11
+ })(this, (function (chart_js, helpers) { 'use strict';
12
+
13
+ function clamp(value, lower, upper) {
14
+ return Math.min(Math.max(value, lower), upper);
15
+ }
16
+ function resolveOption(scale, key) {
17
+ const realtimeOpts = scale.options.realtime;
18
+ const streamingOpts = scale.chart.options.plugins.streaming;
19
+ return helpers.valueOrDefault(realtimeOpts[key], streamingOpts[key]);
20
+ }
21
+ function getAxisMap(element, {x, y}, {xAxisID, yAxisID}) {
22
+ const axisMap = {};
23
+ helpers.each(x, key => {
24
+ axisMap[key] = {axisId: xAxisID};
25
+ });
26
+ helpers.each(y, key => {
27
+ axisMap[key] = {axisId: yAxisID};
28
+ });
29
+ return axisMap;
30
+ }
31
+ const cancelAnimFrame = (function() {
32
+ if (typeof window === 'undefined') {
33
+ return helpers.noop;
34
+ }
35
+ return window.cancelAnimationFrame;
36
+ }());
37
+ function startFrameRefreshTimer(context, func) {
38
+ if (!context.frameRequestID) {
39
+ const refresh = () => {
40
+ const nextRefresh = context.nextRefresh || 0;
41
+ const now = Date.now();
42
+ if (nextRefresh <= now) {
43
+ const newFrameRate = helpers.callback(func);
44
+ const frameDuration = 1000 / (Math.max(newFrameRate, 0) || 30);
45
+ const newNextRefresh = context.nextRefresh + frameDuration || 0;
46
+ context.nextRefresh = newNextRefresh > now ? newNextRefresh : now + frameDuration;
47
+ }
48
+ context.frameRequestID = helpers.requestAnimFrame.call(window, refresh);
49
+ };
50
+ context.frameRequestID = helpers.requestAnimFrame.call(window, refresh);
51
+ }
52
+ }
53
+ function stopFrameRefreshTimer(context) {
54
+ const frameRequestID = context.frameRequestID;
55
+ if (frameRequestID) {
56
+ cancelAnimFrame.call(window, frameRequestID);
57
+ delete context.frameRequestID;
58
+ }
59
+ }
60
+ function stopDataRefreshTimer(context) {
61
+ const refreshTimerID = context.refreshTimerID;
62
+ if (refreshTimerID) {
63
+ clearInterval(refreshTimerID);
64
+ delete context.refreshTimerID;
65
+ delete context.refreshInterval;
66
+ }
67
+ }
68
+ function startDataRefreshTimer(context, func, interval) {
69
+ if (!context.refreshTimerID) {
70
+ context.refreshTimerID = setInterval(() => {
71
+ const newInterval = helpers.callback(func);
72
+ if (context.refreshInterval !== newInterval && !isNaN(newInterval)) {
73
+ stopDataRefreshTimer(context);
74
+ startDataRefreshTimer(context, func, newInterval);
75
+ }
76
+ }, interval || 0);
77
+ context.refreshInterval = interval || 0;
78
+ }
79
+ }
80
+
81
+ function scaleValue(scale, value, fallback) {
82
+ value = typeof value === 'number' ? value : scale.parse(value);
83
+ return helpers.isFinite(value) ?
84
+ {value: scale.getPixelForValue(value), transitionable: true} :
85
+ {value: fallback};
86
+ }
87
+ function updateBoxAnnotation(element, chart, options) {
88
+ const {scales, chartArea} = chart;
89
+ const {xScaleID, yScaleID, xMin, xMax, yMin, yMax} = options;
90
+ const xScale = scales[xScaleID];
91
+ const yScale = scales[yScaleID];
92
+ const {top, left, bottom, right} = chartArea;
93
+ const streaming = element.$streaming = {};
94
+ if (xScale) {
95
+ const min = scaleValue(xScale, xMin, left);
96
+ const max = scaleValue(xScale, xMax, right);
97
+ const reverse = min.value > max.value;
98
+ if (min.transitionable) {
99
+ streaming[reverse ? 'x2' : 'x'] = {axisId: xScaleID};
100
+ }
101
+ if (max.transitionable) {
102
+ streaming[reverse ? 'x' : 'x2'] = {axisId: xScaleID};
103
+ }
104
+ if (min.transitionable !== max.transitionable) {
105
+ streaming.width = {axisId: xScaleID, reverse: min.transitionable};
106
+ }
107
+ }
108
+ if (yScale) {
109
+ const min = scaleValue(yScale, yMin, top);
110
+ const max = scaleValue(yScale, yMax, bottom);
111
+ const reverse = min.value > max.value;
112
+ if (min.transitionable) {
113
+ streaming[reverse ? 'y2' : 'y'] = {axisId: yScaleID};
114
+ }
115
+ if (max.transitionable) {
116
+ streaming[reverse ? 'y' : 'y2'] = {axisId: yScaleID};
117
+ }
118
+ if (min.transitionable !== max.transitionable) {
119
+ streaming.height = {axisId: yScaleID, reverse: min.transitionable};
120
+ }
121
+ }
122
+ }
123
+ function updateLineAnnotation(element, chart, options) {
124
+ const {scales, chartArea} = chart;
125
+ const {scaleID, value} = options;
126
+ const scale = scales[scaleID];
127
+ const {top, left, bottom, right} = chartArea;
128
+ const streaming = element.$streaming = {};
129
+ if (scale) {
130
+ const isHorizontal = scale.isHorizontal();
131
+ const pixel = scaleValue(scale, value);
132
+ if (pixel.transitionable) {
133
+ streaming[isHorizontal ? 'x' : 'y'] = {axisId: scaleID};
134
+ streaming[isHorizontal ? 'x2' : 'y2'] = {axisId: scaleID};
135
+ }
136
+ return isHorizontal ? {top, bottom} : {left, right};
137
+ }
138
+ const {xScaleID, yScaleID, xMin, xMax, yMin, yMax} = options;
139
+ const xScale = scales[xScaleID];
140
+ const yScale = scales[yScaleID];
141
+ const clip = {};
142
+ if (xScale) {
143
+ const min = scaleValue(xScale, xMin);
144
+ const max = scaleValue(xScale, xMax);
145
+ if (min.transitionable) {
146
+ streaming.x = {axisId: xScaleID};
147
+ } else {
148
+ clip.left = left;
149
+ }
150
+ if (max.transitionable) {
151
+ streaming.x2 = {axisId: xScaleID};
152
+ } else {
153
+ clip.right = right;
154
+ }
155
+ }
156
+ if (yScale) {
157
+ const min = scaleValue(yScale, yMin);
158
+ const max = scaleValue(yScale, yMax);
159
+ if (min.transitionable) {
160
+ streaming.y = {axisId: yScaleID};
161
+ } else {
162
+ clip.top = top;
163
+ }
164
+ if (max.transitionable) {
165
+ streaming.y2 = {axisId: yScaleID};
166
+ } else {
167
+ clip.bottom = bottom;
168
+ }
169
+ }
170
+ return clip;
171
+ }
172
+ function updatePointAnnotation(element, chart, options) {
173
+ const scales = chart.scales;
174
+ const {xScaleID, yScaleID, xValue, yValue} = options;
175
+ const xScale = scales[xScaleID];
176
+ const yScale = scales[yScaleID];
177
+ const streaming = element.$streaming = {};
178
+ if (xScale) {
179
+ const x = scaleValue(xScale, xValue);
180
+ if (x.transitionable) {
181
+ streaming.x = {axisId: xScaleID};
182
+ }
183
+ }
184
+ if (yScale) {
185
+ const y = scaleValue(yScale, yValue);
186
+ if (y.transitionable) {
187
+ streaming.y = {axisId: yScaleID};
188
+ }
189
+ }
190
+ }
191
+ function initAnnotationPlugin() {
192
+ const BoxAnnotation = chart_js.registry.getElement('boxAnnotation');
193
+ const LineAnnotation = chart_js.registry.getElement('lineAnnotation');
194
+ const PointAnnotation = chart_js.registry.getElement('pointAnnotation');
195
+ const resolveBoxAnnotationProperties = BoxAnnotation.prototype.resolveElementProperties;
196
+ const resolveLineAnnotationProperties = LineAnnotation.prototype.resolveElementProperties;
197
+ const resolvePointAnnotationProperties = PointAnnotation.prototype.resolveElementProperties;
198
+ BoxAnnotation.prototype.resolveElementProperties = function(chart, options) {
199
+ updateBoxAnnotation(this, chart, options);
200
+ return resolveBoxAnnotationProperties.call(this, chart, options);
201
+ };
202
+ LineAnnotation.prototype.resolveElementProperties = function(chart, options) {
203
+ const chartArea = chart.chartArea;
204
+ chart.chartArea = updateLineAnnotation(this, chart, options);
205
+ const properties = resolveLineAnnotationProperties.call(this, chart, options);
206
+ chart.chartArea = chartArea;
207
+ return properties;
208
+ };
209
+ PointAnnotation.prototype.resolveElementProperties = function(chart, options) {
210
+ updatePointAnnotation(this, chart, options);
211
+ return resolvePointAnnotationProperties.call(this, chart, options);
212
+ };
213
+ }
214
+ function attachChart$1(plugin, chart) {
215
+ const streaming = chart.$streaming;
216
+ if (streaming.annotationPlugin !== plugin) {
217
+ const afterUpdate = plugin.afterUpdate;
218
+ initAnnotationPlugin();
219
+ streaming.annotationPlugin = plugin;
220
+ plugin.afterUpdate = (chart, args, options) => {
221
+ const mode = args.mode;
222
+ const animationOpts = options.animation;
223
+ if (mode === 'quiet') {
224
+ options.animation = false;
225
+ }
226
+ afterUpdate.call(this, chart, args, options);
227
+ if (mode === 'quiet') {
228
+ options.animation = animationOpts;
229
+ }
230
+ };
231
+ }
232
+ }
233
+ function getElements(chart) {
234
+ const plugin = chart.$streaming.annotationPlugin;
235
+ if (plugin) {
236
+ const state = plugin._getState(chart);
237
+ return state && state.elements || [];
238
+ }
239
+ return [];
240
+ }
241
+ function detachChart$1(chart) {
242
+ delete chart.$streaming.annotationPlugin;
243
+ }
244
+
245
+ const transitionKeys$1 = {x: ['x', 'caretX'], y: ['y', 'caretY']};
246
+ function update$1(...args) {
247
+ const me = this;
248
+ const element = me.getActiveElements()[0];
249
+ if (element) {
250
+ const meta = me.chart.getDatasetMeta(element.datasetIndex);
251
+ me.$streaming = getAxisMap(me, transitionKeys$1, meta);
252
+ } else {
253
+ me.$streaming = {};
254
+ }
255
+ me.constructor.prototype.update.call(me, ...args);
256
+ }
257
+
258
+ const chartStates = new WeakMap();
259
+ function getState(chart) {
260
+ let state = chartStates.get(chart);
261
+ if (!state) {
262
+ state = {originalScaleOptions: {}};
263
+ chartStates.set(chart, state);
264
+ }
265
+ return state;
266
+ }
267
+ function removeState(chart) {
268
+ chartStates.delete(chart);
269
+ }
270
+ function storeOriginalScaleOptions(chart) {
271
+ const {originalScaleOptions} = getState(chart);
272
+ const scales = chart.scales;
273
+ helpers.each(scales, scale => {
274
+ const id = scale.id;
275
+ if (!originalScaleOptions[id]) {
276
+ originalScaleOptions[id] = {
277
+ duration: resolveOption(scale, 'duration'),
278
+ delay: resolveOption(scale, 'delay')
279
+ };
280
+ }
281
+ });
282
+ helpers.each(originalScaleOptions, (opt, key) => {
283
+ if (!scales[key]) {
284
+ delete originalScaleOptions[key];
285
+ }
286
+ });
287
+ return originalScaleOptions;
288
+ }
289
+ function zoomRealTimeScale(scale, zoom, center, limits) {
290
+ const {chart, axis} = scale;
291
+ const {minDuration = 0, maxDuration = Infinity, minDelay = -Infinity, maxDelay = Infinity} = limits && limits[axis] || {};
292
+ const realtimeOpts = scale.options.realtime;
293
+ const duration = resolveOption(scale, 'duration');
294
+ const delay = resolveOption(scale, 'delay');
295
+ const newDuration = clamp(duration * (2 - zoom), minDuration, maxDuration);
296
+ let maxPercent, newDelay;
297
+ storeOriginalScaleOptions(chart);
298
+ if (scale.isHorizontal()) {
299
+ maxPercent = (scale.right - center.x) / (scale.right - scale.left);
300
+ } else {
301
+ maxPercent = (scale.bottom - center.y) / (scale.bottom - scale.top);
302
+ }
303
+ newDelay = delay + maxPercent * (duration - newDuration);
304
+ realtimeOpts.duration = newDuration;
305
+ realtimeOpts.delay = clamp(newDelay, minDelay, maxDelay);
306
+ return newDuration !== scale.max - scale.min;
307
+ }
308
+ function panRealTimeScale(scale, delta, limits) {
309
+ const {chart, axis} = scale;
310
+ const {minDelay = -Infinity, maxDelay = Infinity} = limits && limits[axis] || {};
311
+ const delay = resolveOption(scale, 'delay');
312
+ const newDelay = delay + (scale.getValueForPixel(delta) - scale.getValueForPixel(0));
313
+ storeOriginalScaleOptions(chart);
314
+ scale.options.realtime.delay = clamp(newDelay, minDelay, maxDelay);
315
+ return true;
316
+ }
317
+ function resetRealTimeScaleOptions(chart) {
318
+ const originalScaleOptions = storeOriginalScaleOptions(chart);
319
+ helpers.each(chart.scales, scale => {
320
+ const realtimeOptions = scale.options.realtime;
321
+ if (realtimeOptions) {
322
+ const original = originalScaleOptions[scale.id];
323
+ if (original) {
324
+ realtimeOptions.duration = original.duration;
325
+ realtimeOptions.delay = original.delay;
326
+ } else {
327
+ delete realtimeOptions.duration;
328
+ delete realtimeOptions.delay;
329
+ }
330
+ }
331
+ });
332
+ }
333
+ function initZoomPlugin(plugin) {
334
+ plugin.zoomFunctions.realtime = zoomRealTimeScale;
335
+ plugin.panFunctions.realtime = panRealTimeScale;
336
+ }
337
+ function attachChart(plugin, chart) {
338
+ const streaming = chart.$streaming;
339
+ if (streaming.zoomPlugin !== plugin) {
340
+ const resetZoom = streaming.resetZoom = chart.resetZoom;
341
+ initZoomPlugin(plugin);
342
+ chart.resetZoom = transition => {
343
+ resetRealTimeScaleOptions(chart);
344
+ resetZoom(transition);
345
+ };
346
+ streaming.zoomPlugin = plugin;
347
+ }
348
+ }
349
+ function detachChart(chart) {
350
+ const streaming = chart.$streaming;
351
+ if (streaming.zoomPlugin) {
352
+ chart.resetZoom = streaming.resetZoom;
353
+ removeState(chart);
354
+ delete streaming.resetZoom;
355
+ delete streaming.zoomPlugin;
356
+ }
357
+ }
358
+
359
+ const INTERVALS = {
360
+ millisecond: {
361
+ common: true,
362
+ size: 1,
363
+ steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
364
+ },
365
+ second: {
366
+ common: true,
367
+ size: 1000,
368
+ steps: [1, 2, 5, 10, 15, 30]
369
+ },
370
+ minute: {
371
+ common: true,
372
+ size: 60000,
373
+ steps: [1, 2, 5, 10, 15, 30]
374
+ },
375
+ hour: {
376
+ common: true,
377
+ size: 3600000,
378
+ steps: [1, 2, 3, 6, 12]
379
+ },
380
+ day: {
381
+ common: true,
382
+ size: 86400000,
383
+ steps: [1, 2, 5]
384
+ },
385
+ week: {
386
+ common: false,
387
+ size: 604800000,
388
+ steps: [1, 2, 3, 4]
389
+ },
390
+ month: {
391
+ common: true,
392
+ size: 2.628e9,
393
+ steps: [1, 2, 3]
394
+ },
395
+ quarter: {
396
+ common: false,
397
+ size: 7.884e9,
398
+ steps: [1, 2, 3, 4]
399
+ },
400
+ year: {
401
+ common: true,
402
+ size: 3.154e10
403
+ }
404
+ };
405
+ const UNITS = Object.keys(INTERVALS);
406
+ function determineStepSize(min, max, unit, capacity) {
407
+ const range = max - min;
408
+ const {size: milliseconds, steps} = INTERVALS[unit];
409
+ let factor;
410
+ if (!steps) {
411
+ return Math.ceil(range / (capacity * milliseconds));
412
+ }
413
+ for (let i = 0, ilen = steps.length; i < ilen; ++i) {
414
+ factor = steps[i];
415
+ if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
416
+ break;
417
+ }
418
+ }
419
+ return factor;
420
+ }
421
+ function determineUnitForAutoTicks(minUnit, min, max, capacity) {
422
+ const range = max - min;
423
+ const ilen = UNITS.length;
424
+ for (let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
425
+ const {common, size, steps} = INTERVALS[UNITS[i]];
426
+ const factor = steps ? steps[steps.length - 1] : Number.MAX_SAFE_INTEGER;
427
+ if (common && Math.ceil(range / (factor * size)) <= capacity) {
428
+ return UNITS[i];
429
+ }
430
+ }
431
+ return UNITS[ilen - 1];
432
+ }
433
+ function determineMajorUnit(unit) {
434
+ for (let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
435
+ if (INTERVALS[UNITS[i]].common) {
436
+ return UNITS[i];
437
+ }
438
+ }
439
+ }
440
+ function addTick(ticks, time, timestamps) {
441
+ if (!timestamps) {
442
+ ticks[time] = true;
443
+ } else if (timestamps.length) {
444
+ const {lo, hi} = helpers._lookup(timestamps, time);
445
+ const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];
446
+ ticks[timestamp] = true;
447
+ }
448
+ }
449
+ const datasetPropertyKeys = [
450
+ 'pointBackgroundColor',
451
+ 'pointBorderColor',
452
+ 'pointBorderWidth',
453
+ 'pointRadius',
454
+ 'pointRotation',
455
+ 'pointStyle',
456
+ 'pointHitRadius',
457
+ 'pointHoverBackgroundColor',
458
+ 'pointHoverBorderColor',
459
+ 'pointHoverBorderWidth',
460
+ 'pointHoverRadius',
461
+ 'backgroundColor',
462
+ 'borderColor',
463
+ 'borderSkipped',
464
+ 'borderWidth',
465
+ 'hoverBackgroundColor',
466
+ 'hoverBorderColor',
467
+ 'hoverBorderWidth',
468
+ 'hoverRadius',
469
+ 'hitRadius',
470
+ 'radius',
471
+ 'rotation'
472
+ ];
473
+ function clean(scale) {
474
+ const {chart, id, max} = scale;
475
+ const duration = resolveOption(scale, 'duration');
476
+ const delay = resolveOption(scale, 'delay');
477
+ const ttl = resolveOption(scale, 'ttl');
478
+ const pause = resolveOption(scale, 'pause');
479
+ const min = Date.now() - (isNaN(ttl) ? duration + delay : ttl);
480
+ let i, start, count, removalRange;
481
+ helpers.each(chart.data.datasets, (dataset, datasetIndex) => {
482
+ const meta = chart.getDatasetMeta(datasetIndex);
483
+ const axis = id === meta.xAxisID && 'x' || id === meta.yAxisID && 'y';
484
+ if (axis) {
485
+ const controller = meta.controller;
486
+ const data = dataset.data;
487
+ const length = data.length;
488
+ if (pause) {
489
+ for (i = 0; i < length; ++i) {
490
+ const point = controller.getParsed(i);
491
+ if (point && !(point[axis] < max)) {
492
+ break;
493
+ }
494
+ }
495
+ start = i + 2;
496
+ } else {
497
+ start = 0;
498
+ }
499
+ for (i = start; i < length; ++i) {
500
+ const point = controller.getParsed(i);
501
+ if (!point || !(point[axis] <= min)) {
502
+ break;
503
+ }
504
+ }
505
+ count = i - start;
506
+ if (isNaN(ttl)) {
507
+ count = Math.max(count - 2, 0);
508
+ }
509
+ data.splice(start, count);
510
+ helpers.each(datasetPropertyKeys, key => {
511
+ if (helpers.isArray(dataset[key])) {
512
+ dataset[key].splice(start, count);
513
+ }
514
+ });
515
+ helpers.each(dataset.datalabels, value => {
516
+ if (helpers.isArray(value)) {
517
+ value.splice(start, count);
518
+ }
519
+ });
520
+ if (typeof data[0] !== 'object') {
521
+ removalRange = {
522
+ start: start,
523
+ count: count
524
+ };
525
+ }
526
+ helpers.each(chart._active, (item, index) => {
527
+ if (item.datasetIndex === datasetIndex && item.index >= start) {
528
+ if (item.index >= start + count) {
529
+ item.index -= count;
530
+ } else {
531
+ chart._active.splice(index, 1);
532
+ }
533
+ }
534
+ }, null, true);
535
+ }
536
+ });
537
+ if (removalRange) {
538
+ chart.data.labels.splice(removalRange.start, removalRange.count);
539
+ }
540
+ }
541
+ function transition(element, id, translate) {
542
+ const animations = element.$animations || {};
543
+ helpers.each(element.$streaming, (item, key) => {
544
+ if (item.axisId === id) {
545
+ const delta = item.reverse ? -translate : translate;
546
+ const animation = animations[key];
547
+ if (helpers.isFinite(element[key])) {
548
+ element[key] -= delta;
549
+ }
550
+ if (animation) {
551
+ animation._from -= delta;
552
+ animation._to -= delta;
553
+ }
554
+ }
555
+ });
556
+ }
557
+ function scroll(scale) {
558
+ const {chart, id, $realtime: realtime} = scale;
559
+ const duration = resolveOption(scale, 'duration');
560
+ const delay = resolveOption(scale, 'delay');
561
+ const isHorizontal = scale.isHorizontal();
562
+ const length = isHorizontal ? scale.width : scale.height;
563
+ const now = Date.now();
564
+ const tooltip = chart.tooltip;
565
+ const annotations = getElements(chart);
566
+ let offset = length * (now - realtime.head) / duration;
567
+ if (isHorizontal === !!scale.options.reverse) {
568
+ offset = -offset;
569
+ }
570
+ helpers.each(chart.data.datasets, (dataset, datasetIndex) => {
571
+ const meta = chart.getDatasetMeta(datasetIndex);
572
+ const {data: elements = [], dataset: element} = meta;
573
+ for (let i = 0, ilen = elements.length; i < ilen; ++i) {
574
+ transition(elements[i], id, offset);
575
+ }
576
+ if (element) {
577
+ transition(element, id, offset);
578
+ delete element._path;
579
+ }
580
+ });
581
+ for (let i = 0, ilen = annotations.length; i < ilen; ++i) {
582
+ transition(annotations[i], id, offset);
583
+ }
584
+ if (tooltip) {
585
+ transition(tooltip, id, offset);
586
+ }
587
+ scale.max = now - delay;
588
+ scale.min = scale.max - duration;
589
+ realtime.head = now;
590
+ }
591
+ class RealTimeScale extends chart_js.TimeScale {
592
+ constructor(props) {
593
+ super(props);
594
+ this.$realtime = this.$realtime || {};
595
+ }
596
+ init(scaleOpts, opts) {
597
+ const me = this;
598
+ super.init(scaleOpts, opts);
599
+ startDataRefreshTimer(me.$realtime, () => {
600
+ const chart = me.chart;
601
+ const onRefresh = resolveOption(me, 'onRefresh');
602
+ helpers.callback(onRefresh, [chart], me);
603
+ clean(me);
604
+ chart.update('quiet');
605
+ return resolveOption(me, 'refresh');
606
+ });
607
+ }
608
+ update(maxWidth, maxHeight, margins) {
609
+ const me = this;
610
+ const {$realtime: realtime, options} = me;
611
+ const {bounds, offset, ticks: ticksOpts} = options;
612
+ const {autoSkip, source, major: majorTicksOpts} = ticksOpts;
613
+ const majorEnabled = majorTicksOpts.enabled;
614
+ if (resolveOption(me, 'pause')) {
615
+ stopFrameRefreshTimer(realtime);
616
+ } else {
617
+ if (!realtime.frameRequestID) {
618
+ realtime.head = Date.now();
619
+ }
620
+ startFrameRefreshTimer(realtime, () => {
621
+ const chart = me.chart;
622
+ const streaming = chart.$streaming;
623
+ scroll(me);
624
+ if (streaming) {
625
+ helpers.callback(streaming.render, [chart]);
626
+ }
627
+ return resolveOption(me, 'frameRate');
628
+ });
629
+ }
630
+ options.bounds = undefined;
631
+ options.offset = false;
632
+ ticksOpts.autoSkip = false;
633
+ ticksOpts.source = source === 'auto' ? '' : source;
634
+ majorTicksOpts.enabled = true;
635
+ super.update(maxWidth, maxHeight, margins);
636
+ options.bounds = bounds;
637
+ options.offset = offset;
638
+ ticksOpts.autoSkip = autoSkip;
639
+ ticksOpts.source = source;
640
+ majorTicksOpts.enabled = majorEnabled;
641
+ }
642
+ buildTicks() {
643
+ const me = this;
644
+ const duration = resolveOption(me, 'duration');
645
+ const delay = resolveOption(me, 'delay');
646
+ const max = me.$realtime.head - delay;
647
+ const min = max - duration;
648
+ const maxArray = [1e15, max];
649
+ const minArray = [-1e15, min];
650
+ Object.defineProperty(me, 'min', {
651
+ get: () => minArray.shift(),
652
+ set: helpers.noop
653
+ });
654
+ Object.defineProperty(me, 'max', {
655
+ get: () => maxArray.shift(),
656
+ set: helpers.noop
657
+ });
658
+ const ticks = super.buildTicks();
659
+ delete me.min;
660
+ delete me.max;
661
+ me.min = min;
662
+ me.max = max;
663
+ return ticks;
664
+ }
665
+ calculateLabelRotation() {
666
+ const ticksOpts = this.options.ticks;
667
+ const maxRotation = ticksOpts.maxRotation;
668
+ ticksOpts.maxRotation = ticksOpts.minRotation || 0;
669
+ super.calculateLabelRotation();
670
+ ticksOpts.maxRotation = maxRotation;
671
+ }
672
+ fit() {
673
+ const me = this;
674
+ const options = me.options;
675
+ super.fit();
676
+ if (options.ticks.display && options.display && me.isHorizontal()) {
677
+ me.paddingLeft = 3;
678
+ me.paddingRight = 3;
679
+ me._handleMargins();
680
+ }
681
+ }
682
+ draw(chartArea) {
683
+ const me = this;
684
+ const {chart, ctx} = me;
685
+ const area = me.isHorizontal() ?
686
+ {
687
+ left: chartArea.left,
688
+ top: 0,
689
+ right: chartArea.right,
690
+ bottom: chart.height
691
+ } : {
692
+ left: 0,
693
+ top: chartArea.top,
694
+ right: chart.width,
695
+ bottom: chartArea.bottom
696
+ };
697
+ me._gridLineItems = null;
698
+ me._labelItems = null;
699
+ helpers.clipArea(ctx, area);
700
+ super.draw(chartArea);
701
+ helpers.unclipArea(ctx);
702
+ }
703
+ destroy() {
704
+ const realtime = this.$realtime;
705
+ stopFrameRefreshTimer(realtime);
706
+ stopDataRefreshTimer(realtime);
707
+ }
708
+ _generate() {
709
+ const me = this;
710
+ const adapter = me._adapter;
711
+ const duration = resolveOption(me, 'duration');
712
+ const delay = resolveOption(me, 'delay');
713
+ const refresh = resolveOption(me, 'refresh');
714
+ const max = me.$realtime.head - delay;
715
+ const min = max - duration;
716
+ const capacity = me._getLabelCapacity(min);
717
+ const {time: timeOpts, ticks: ticksOpts} = me.options;
718
+ const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
719
+ const major = determineMajorUnit(minor);
720
+ const stepSize = timeOpts.stepSize || determineStepSize(min, max, minor, capacity);
721
+ const weekday = minor === 'week' ? timeOpts.isoWeekday : false;
722
+ const majorTicksEnabled = ticksOpts.major.enabled;
723
+ const hasWeekday = helpers.isNumber(weekday) || weekday === true;
724
+ const interval = INTERVALS[minor];
725
+ const ticks = {};
726
+ let first = min;
727
+ let time, count;
728
+ if (hasWeekday) {
729
+ first = +adapter.startOf(first, 'isoWeek', weekday);
730
+ }
731
+ first = +adapter.startOf(first, hasWeekday ? 'day' : minor);
732
+ if (adapter.diff(max, min, minor) > 100000 * stepSize) {
733
+ throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);
734
+ }
735
+ time = first;
736
+ if (majorTicksEnabled && major && !hasWeekday && !timeOpts.round) {
737
+ time = +adapter.startOf(time, major);
738
+ time = +adapter.add(time, ~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
739
+ }
740
+ const timestamps = ticksOpts.source === 'data' && me.getDataTimestamps();
741
+ for (count = 0; time < max + refresh; time = +adapter.add(time, stepSize, minor), count++) {
742
+ addTick(ticks, time, timestamps);
743
+ }
744
+ if (time === max + refresh || count === 1) {
745
+ addTick(ticks, time, timestamps);
746
+ }
747
+ return Object.keys(ticks).sort((a, b) => a - b).map(x => +x);
748
+ }
749
+ }
750
+ RealTimeScale.id = 'realtime';
751
+ RealTimeScale.defaults = {
752
+ bounds: 'data',
753
+ adapters: {},
754
+ time: {
755
+ parser: false,
756
+ unit: false,
757
+ round: false,
758
+ isoWeekday: false,
759
+ minUnit: 'millisecond',
760
+ displayFormats: {}
761
+ },
762
+ realtime: {},
763
+ ticks: {
764
+ autoSkip: false,
765
+ source: 'auto',
766
+ major: {
767
+ enabled: true
768
+ }
769
+ }
770
+ };
771
+ chart_js.defaults.describe('scale.realtime', {
772
+ _scriptable: name => name !== 'onRefresh'
773
+ });
774
+
775
+ var version = "2.0.2";
776
+
777
+ chart_js.defaults.set('transitions', {
778
+ quiet: {
779
+ animation: {
780
+ duration: 0
781
+ }
782
+ }
783
+ });
784
+ const transitionKeys = {x: ['x', 'cp1x', 'cp2x'], y: ['y', 'cp1y', 'cp2y']};
785
+ function update(mode) {
786
+ const me = this;
787
+ if (mode === 'quiet') {
788
+ helpers.each(me.data.datasets, (dataset, datasetIndex) => {
789
+ me.getDatasetMeta(datasetIndex).controller;
790
+ });
791
+ }
792
+ chart_js.Chart.prototype.update.call(me, mode);
793
+ if (mode === 'quiet') {
794
+ helpers.each(me.data.datasets, (dataset, datasetIndex) => {
795
+ delete me.getDatasetMeta(datasetIndex).controller._setStyle;
796
+ });
797
+ }
798
+ }
799
+ function render(chart) {
800
+ const streaming = chart.$streaming;
801
+ chart.render();
802
+ if (streaming.lastMouseEvent) {
803
+ setTimeout(() => {
804
+ const lastMouseEvent = streaming.lastMouseEvent;
805
+ if (lastMouseEvent) {
806
+ chart._eventHandler(lastMouseEvent);
807
+ }
808
+ }, 0);
809
+ }
810
+ }
811
+ var StreamingPlugin = {
812
+ id: 'streaming',
813
+ version,
814
+ beforeInit(chart) {
815
+ const streaming = chart.$streaming = chart.$streaming || {render};
816
+ const canvas = streaming.canvas = chart.canvas;
817
+ const mouseEventListener = streaming.mouseEventListener = event => {
818
+ const pos = helpers.getRelativePosition(event, chart);
819
+ streaming.lastMouseEvent = {
820
+ type: 'mousemove',
821
+ chart: chart,
822
+ native: event,
823
+ x: pos.x,
824
+ y: pos.y
825
+ };
826
+ };
827
+ canvas.addEventListener('mousedown', mouseEventListener);
828
+ canvas.addEventListener('mouseup', mouseEventListener);
829
+ },
830
+ afterInit(chart) {
831
+ chart.update = update;
832
+ },
833
+ beforeUpdate(chart) {
834
+ const {scales, elements} = chart.options;
835
+ const tooltip = chart.tooltip;
836
+ helpers.each(scales, ({type}) => {
837
+ if (type === 'realtime') {
838
+ elements.line.capBezierPoints = false;
839
+ }
840
+ });
841
+ if (tooltip) {
842
+ tooltip.update = update$1;
843
+ }
844
+ try {
845
+ const plugin = chart_js.registry.getPlugin('annotation');
846
+ attachChart$1(plugin, chart);
847
+ } catch (e) {
848
+ detachChart$1(chart);
849
+ }
850
+ try {
851
+ const plugin = chart_js.registry.getPlugin('zoom');
852
+ attachChart(plugin, chart);
853
+ } catch (e) {
854
+ detachChart(chart);
855
+ }
856
+ },
857
+ beforeDatasetUpdate(chart, args) {
858
+ const {meta, mode} = args;
859
+ if (mode === 'quiet') {
860
+ const {controller, $animations} = meta;
861
+ if ($animations && $animations.visible && $animations.visible._active) {
862
+ controller.updateElement = helpers.noop;
863
+ controller.updateSharedOptions = helpers.noop;
864
+ }
865
+ }
866
+ },
867
+ afterDatasetUpdate(chart, args) {
868
+ const {meta, mode} = args;
869
+ const {data: elements = [], dataset: element, controller} = meta;
870
+ for (let i = 0, ilen = elements.length; i < ilen; ++i) {
871
+ elements[i].$streaming = getAxisMap(elements[i], transitionKeys, meta);
872
+ }
873
+ if (element) {
874
+ element.$streaming = getAxisMap(element, transitionKeys, meta);
875
+ }
876
+ if (mode === 'quiet') {
877
+ delete controller.updateElement;
878
+ delete controller.updateSharedOptions;
879
+ }
880
+ },
881
+ beforeDatasetDraw(chart, args) {
882
+ const {ctx, chartArea, width, height} = chart;
883
+ const {xAxisID, yAxisID, controller} = args.meta;
884
+ const area = {
885
+ left: 0,
886
+ top: 0,
887
+ right: width,
888
+ bottom: height
889
+ };
890
+ if (xAxisID && controller.getScaleForId(xAxisID) instanceof RealTimeScale) {
891
+ area.left = chartArea.left;
892
+ area.right = chartArea.right;
893
+ }
894
+ if (yAxisID && controller.getScaleForId(yAxisID) instanceof RealTimeScale) {
895
+ area.top = chartArea.top;
896
+ area.bottom = chartArea.bottom;
897
+ }
898
+ helpers.clipArea(ctx, area);
899
+ },
900
+ afterDatasetDraw(chart) {
901
+ helpers.unclipArea(chart.ctx);
902
+ },
903
+ beforeEvent(chart, args) {
904
+ const streaming = chart.$streaming;
905
+ const event = args.event;
906
+ if (event.type === 'mousemove') {
907
+ streaming.lastMouseEvent = event;
908
+ } else if (event.type === 'mouseout') {
909
+ delete streaming.lastMouseEvent;
910
+ }
911
+ },
912
+ destroy(chart) {
913
+ const {scales, $streaming: streaming, tooltip} = chart;
914
+ const {canvas, mouseEventListener} = streaming;
915
+ delete chart.update;
916
+ if (tooltip) {
917
+ delete tooltip.update;
918
+ }
919
+ canvas.removeEventListener('mousedown', mouseEventListener);
920
+ canvas.removeEventListener('mouseup', mouseEventListener);
921
+ helpers.each(scales, scale => {
922
+ if (scale instanceof RealTimeScale) {
923
+ scale.destroy();
924
+ }
925
+ });
926
+ },
927
+ defaults: {
928
+ duration: 10000,
929
+ delay: 0,
930
+ frameRate: 30,
931
+ refresh: 1000,
932
+ onRefresh: null,
933
+ pause: false,
934
+ ttl: undefined
935
+ },
936
+ descriptors: {
937
+ _scriptable: name => name !== 'onRefresh'
938
+ }
939
+ };
940
+
941
+ const registerables = [StreamingPlugin, RealTimeScale];
942
+ chart_js.Chart.register(registerables);
943
+
944
+ return registerables;
945
+
946
+ }));