diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..1b91dcd --- /dev/null +++ b/bower.json @@ -0,0 +1,15 @@ +{ + "name": "chartjs-plugin-streaming", + "description": "Chart.js plugin for live streaming data", + "homepage": "https://nagix.github.io/chartjs-plugin-streaming", + "license": "MIT", + "version": "1.2.0", + "main": "./dist/chartjs-plugin-streaming.js", + "ignore": [ + ".codeclimate.yml", + ".gitignore", + ".npmignore", + ".travis.yml", + "scripts" + ] +} \ No newline at end of file diff --git a/dist/chartjs-plugin-streaming.js b/dist/chartjs-plugin-streaming.js new file mode 100644 index 0000000..5c7bb44 --- /dev/null +++ b/dist/chartjs-plugin-streaming.js @@ -0,0 +1,707 @@ +/* + * @license + * chartjs-plugin-streaming + * http://github.com/nagix/chartjs-plugin-streaming/ + * Version: 1.2.0 + * + * Copyright 2017 Akihiko Kusanagi + * Released under the MIT license + * https://github.com/nagix/chartjs-plugin-streaming/blob/master/LICENSE.md + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js'), require('moment')) : + typeof define === 'function' && define.amd ? define(['chart.js', 'moment'], factory) : + (global['chartjs-plugin-streaming'] = factory(global.Chart,global.moment)); +}(this, (function (Chart,moment) { 'use strict'; + +Chart = Chart && Chart.hasOwnProperty('default') ? Chart['default'] : Chart; +moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment; + +var realTimeScale = function(Chart$$1, moment$$1) { + + var helpers = Chart$$1.helpers; + + var realTimeScaleDefaultConfig = { + position: 'bottom', + + time: { + parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment + format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ + unit: false, // false == automatic or override with week, month, year, etc. + round: false, // none, or override with week, month, year, etc. + displayFormat: false, // DEPRECATED + isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ + minUnit: 'millisecond', + + // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ + displayFormats: { + millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, + second: 'h:mm:ss a', // 11:20:01 AM + minute: 'h:mm a', // 11:20 AM + hour: 'hA', // 5PM + day: 'MMM D', // Sep 4 + week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? + month: 'MMM YYYY', // Sept 2015 + quarter: '[Q]Q - YYYY', // Q3 + year: 'YYYY' // 2015 + }, + }, + realtime: { + duration: 10000, + refresh: 1000, + delay: 0, + onRefresh: null + }, + ticks: { + autoSkip: false + } + }; + + // Backported from Chart.js 4c763bf. No need for 2.7.0 or later. + function isObject(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; + } + + // Backported from Chart.js 4c763bf. No need for 2.7.0 or later. + function valueOrDefault(value, defaultValue) { + return typeof value === 'undefined' ? defaultValue : value; + } + + // Backported from Chart.js 4c763bf. No need for 2.7.0 or later. + function clone(source) { + if (helpers.isArray(source)) { + return source.map(clone); + } + + if (isObject(source)) { + var target = {}; + var keys = Object.keys(source); + var klen = keys.length; + var k = 0; + + for (; k < klen; ++k) { + target[keys[k]] = clone(source[keys[k]]); + } + + return target; + } + + return source; + } + + // Backported from Chart.js 4c763bf. No need for 2.7.0 or later. + function _merger(key, target, source, options) { + var tval = target[key]; + var sval = source[key]; + + if (isObject(tval) && isObject(sval)) { + helpers.merge(tval, sval, options); + } else { + target[key] = clone(sval); + } + } + + // Backported from Chart.js 4c763bf. No need for 2.7.0 or later. + helpers.merge = function(target, source, options) { + var sources = helpers.isArray(source) ? source : [source]; + var ilen = sources.length; + var merge, i, keys, klen, k; + + if (!isObject(target)) { + return target; + } + + options = options || {}; + merge = options.merger || _merger; + + for (i = 0; i < ilen; ++i) { + source = sources[i]; + if (!isObject(source)) { + continue; + } + + keys = Object.keys(source); + for (k = 0, klen = keys.length; k < klen; ++k) { + merge(keys[k], target, source, options); + } + } + + return target; + }; + + // Backported from Chart.js 9ef5cc7. No need for 2.7.0 or later. + helpers.scaleMerge = function(/* objects ... */) { + return helpers.merge(clone(arguments[0]), [].slice.call(arguments, 1), { + merger: function(key, target, source, options) { + if (key === 'xAxes' || key === 'yAxes') { + var slen = source[key].length; + var i, type, scale; + + if (!target[key]) { + target[key] = []; + } + + for (i = 0; i < slen; ++i) { + scale = source[key][i]; + type = valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); + + if (i >= target[key].length) { + target[key].push({}); + } + + if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { + // new/untyped scale or type changed: let's apply the new defaults + // then merge source scale to correctly overwrite the defaults. + helpers.merge(target[key][i], [Chart$$1.scaleService.getScaleDefaults(type), scale]); + } else { + // scales type are the same + helpers.merge(target[key][i], scale); + } + } + } else { + _merger(key, target, source, options); + } + } + }); + }; + + // Backported from Chart.js 7f15beb. No need for 2.7.0 or later. + var interval = { + millisecond: { + size: 1, + steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] + }, + second: { + size: 1000, + steps: [1, 2, 5, 10, 30] + }, + minute: { + size: 60000, + steps: [1, 2, 5, 10, 30] + }, + hour: { + size: 3600000, + steps: [1, 2, 3, 6, 12] + }, + day: { + size: 86400000, + steps: [1, 2, 5] + }, + week: { + size: 604800000, + maxStep: 4 + }, + month: { + size: 2.628e9, + maxStep: 3 + }, + quarter: { + size: 7.884e9, + maxStep: 4 + }, + year: { + size: 3.154e10, + maxStep: false + } + }; + + // Backported from Chart.js 7f15beb. No need for 2.7.0 or later. + function generateTicksNiceRange(options, dataRange, niceRange) { + var ticks = []; + if (options.maxTicks) { + var stepSize = options.stepSize; + var startTick = options.min !== undefined ? options.min : niceRange.min; + var majorUnit = options.majorUnit; + var majorUnitStart = majorUnit ? moment$$1(startTick).add(1, majorUnit).startOf(majorUnit) : startTick; + var startRange = majorUnitStart.valueOf() - startTick; + var stepValue = interval[options.unit].size * stepSize; + var startFraction = startRange % stepValue; + var alignedTick = startTick; + if (startFraction && majorUnit && !options.timeOpts.round && !options.timeOpts.isoWeekday) { + alignedTick += startFraction - stepValue; + ticks.push(alignedTick); + } else { + ticks.push(startTick); + } + var cur = moment$$1(alignedTick); + var realMax = options.max || niceRange.max; + while (cur.add(stepSize, options.unit).valueOf() < realMax) { + ticks.push(cur.valueOf()); + } + ticks.push(cur.valueOf()); + } + return ticks; + } + + // Backported from Chart.js 7f15beb. No need for 2.7.0 or later. + function determineUnit(minUnit, min, max, maxTicks) { + var units = Object.keys(interval); + var unit; + var numUnits = units.length; + + for (var i = units.indexOf(minUnit); i < numUnits; i++) { + unit = units[i]; + var unitDetails = interval[unit]; + var steps = (unitDetails.steps && unitDetails.steps[unitDetails.steps.length - 1]) || unitDetails.maxStep; + if (steps === undefined || Math.ceil((max - min) / (steps * unitDetails.size)) <= maxTicks) { + break; + } + } + + return unit; + } + + // Backported from Chart.js 7f15beb. No need for 2.7.0 or later. + function determineMajorUnit(unit) { + var units = Object.keys(interval); + var unitIndex = units.indexOf(unit); + while (unitIndex < units.length) { + var majorUnit = units[++unitIndex]; + // exclude 'week' and 'quarter' units + if (majorUnit !== 'week' && majorUnit !== 'quarter') { + return majorUnit; + } + } + + return null; + } + + // Backported from Chart.js 7f15beb. No need for 2.7.0 or later. + function determineStepSize(min, max, unit, maxTicks) { + // Using our unit, figure out what we need to scale as + var unitDefinition = interval[unit]; + var unitSizeInMilliSeconds = unitDefinition.size; + var sizeInUnits = Math.ceil((max - min) / unitSizeInMilliSeconds); + var multiplier = 1; + var range = max - min; + + if (unitDefinition.steps) { + // Have an array of steps + var numSteps = unitDefinition.steps.length; + for (var i = 0; i < numSteps && sizeInUnits > maxTicks; i++) { + multiplier = unitDefinition.steps[i]; + sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier)); + } + } else { + while (sizeInUnits > maxTicks && maxTicks > 0) { + ++multiplier; + sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier)); + } + } + + return multiplier; + } + + // Backported from Chart.js 7f15beb. No need for 2.7.0 or later. + function generateTicks(options, dataRange) { + var niceMin; + var niceMax; + var isoWeekday = options.timeOpts.isoWeekday; + if (options.unit === 'week' && isoWeekday !== false) { + niceMin = moment$$1(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf(); + niceMax = moment$$1(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday); + if (dataRange.max - niceMax > 0) { + niceMax.add(1, 'week'); + } + niceMax = niceMax.valueOf(); + } else { + niceMin = moment$$1(dataRange.min).startOf(options.unit).valueOf(); + niceMax = moment$$1(dataRange.max).startOf(options.unit); + if (dataRange.max - niceMax > 0) { + niceMax.add(1, options.unit); + } + niceMax = niceMax.valueOf(); + } + return generateTicksNiceRange(options, dataRange, { + min: niceMin, + max: niceMax + }); + } + + var transitionKeys = { + x: { + data: ['x', 'controlPointPreviousX', 'controlPointNextX'], + dataset: ['x'], + tooltip: ['x', 'caretX'] + }, + y: { + data: ['y', 'controlPointPreviousY', 'controlPointNextY'], + dataset: ['y'], + tooltip: ['y', 'caretY'] + } + }; + + function transition(element, keys, translate) { + var start = element._start || {}; + var view = element._view || {}; + var model = element._model || {}; + var i, ilen; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + var key = keys[i]; + if (start.hasOwnProperty(key)) { + start[key] -= translate; + } + if (view.hasOwnProperty(key) && view !== start) { + view[key] -= translate; + } + if (model.hasOwnProperty(key) && model !== view) { + model[key] -= translate; + } + } + } + + var TimeScale = Chart$$1.scaleService.getScaleConstructor('time'); + + Chart$$1.scaleService.getScaleConstructor = function(type) { + // For backwards compatibility + if (type === 'time') { + type = 'realtime'; + } + return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; + }; + + var RealTimeScale = TimeScale.extend({ + initialize: function() { + TimeScale.prototype.initialize.call(this); + + var me = this; + var options = me.options; + + // For backwards compatibility + if (options.type === 'time' && !me.chart.options.plugins.streaming) { + return; + } + + var nextRefresh = Date.now(); + var prev = Date.now(); + + var frameRefresh = function() { + var chart = me.chart; + var realtimeOpts = options.realtime; + var duration = realtimeOpts.duration; + var refresh = realtimeOpts.refresh; + var keys, length; + + if (me.isHorizontal()) { + length = me.width; + keys = transitionKeys.x; + } else { + length = me.height; + keys = transitionKeys.y; + } + + var now = Date.now(); + var offset = length * (now - prev) / duration; + + // Shift all the elements leftward or upward + helpers.each(chart.data.datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + var elements = meta.data || []; + var i, ilen; + + for (i = 0, ilen = elements.length; i < ilen; ++i) { + transition(elements[i], keys.data, offset); + } + + if (meta.dataset) { + transition(meta.dataset, keys.dataset, offset); + } + }); + + transition(chart.tooltip, keys.tooltip, offset); + + if (now >= nextRefresh) { + nextRefresh = now + refresh + (now - nextRefresh) % refresh; + if (realtimeOpts.onRefresh) { + realtimeOpts.onRefresh.call(chart, me); + } + + chart.update(); + + } else { + // Update min/max + me.max = now - realtimeOpts.delay; + me.min = me.max - duration; + + // Draw only when animation is inactive + if (!chart.animating) { + chart.draw(); + } + } + + prev = now; + + helpers.requestAnimFrame.call(window, frameRefresh); + }; + helpers.requestAnimFrame.call(window, frameRefresh); + }, + + buildTicks: function() { + var me = this; + var options = me.options; + + // For backwards compatibility + if (options.type === 'time' && !me.chart.options.plugins.streaming) { + TimeScale.prototype.buildTicks.call(this); + return; + } + + var timeOpts = options.time; + var realtimeOpts = options.realtime; + + var maxTimestamp = Date.now() - realtimeOpts.delay; + var minTimestamp = maxTimestamp - realtimeOpts.duration; + var maxTicks = me.getLabelCapacity(minTimestamp); + + var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, minTimestamp, maxTimestamp, maxTicks); + var majorUnit = determineMajorUnit(unit); + + me.displayFormat = timeOpts.displayFormats[unit]; + me.majorDisplayFormat = timeOpts.displayFormats[majorUnit]; + me.unit = unit; + me.majorUnit = majorUnit; + + var optionStepSize = valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); + var stepSize = optionStepSize || determineStepSize(minTimestamp, maxTimestamp, unit, maxTicks); + me.ticks = generateTicks({ + maxTicks: maxTicks, + min: minTimestamp, + // Add refresh interval for scroll margin + max: maxTimestamp + realtimeOpts.refresh, + stepSize: stepSize, + majorUnit: majorUnit, + unit: unit, + timeOpts: timeOpts + }, { + min: me.dataMin, + max: me.dataMax + }); + + // max and min are set based on the duration and delay settings + me.max = maxTimestamp; + me.min = minTimestamp; + }, + + draw: function(chartArea) { + var me = this; + var chart = me.chart; + + // For backwards compatibility + if (me.options.type === 'time' && !chart.options.plugins.streaming) { + return TimeScale.prototype.draw.call(this, chartArea); + } + + var context = me.ctx; + var clipArea = me.isHorizontal() ? { + left: chartArea.left, + top: 0, + right: chartArea.right, + bottom: chart.height + } : { + left: 0, + top: chartArea.top, + right: chart.width, + bottom: chartArea.bottom + }; + + // Clip and draw the scale + helpers.canvas.clipArea(context, clipArea); + TimeScale.prototype.draw.call(this, chartArea); + helpers.canvas.unclipArea(context); + }, + + getPixelForOffset: function(offset) { + var me = this; + + // For backwards compatibility + if (me.options.type === 'time' && !me.chart.options.plugins.streaming) { + return TimeScale.prototype.getPixelForOffset.call(this, offset); + } + + var epochWidth = me.max - me.min; + var decimal = epochWidth ? (offset - me.min) / epochWidth : 0; + + // For smooth scroll, don't round the offset + if (me.isHorizontal()) { + return me.left + me.width * decimal; + } + + return me.top + me.height * decimal; + } + }); + + Chart$$1.scaleService.registerScaleType('realtime', RealTimeScale, realTimeScaleDefaultConfig); +}; + +var streamingPlugin = function(Chart$$1) { + + Chart$$1.defaults.global.plugins.streaming = { + duration: 10000, + refresh: 1000, + delay: 0, + onRefresh: null + }; + + function onRefresh(scale) { + var me = this; + var streamingOpts = me.options.plugins.streaming; + var start = scale.isHorizontal() ? scale.left : scale.top; + var data, i, ilen, howMany; + + if (streamingOpts.onRefresh) { + streamingOpts.onRefresh(me); + } + + // Remove old data + me.data.datasets.forEach(function(dataset, datasetIndex) { + data = dataset.data; + for (i = 2, ilen = data.length; i < ilen; ++i) { + if (!(scale.getPixelForValue(null, i, datasetIndex) <= start)) { + break; + } + } + // Keep the last two data points outside the range not to affect the existing bezier curve + data.splice(0, i - 2); + if (typeof data[0] !== 'object') { + howMany = i - 2; + } + }); + if (howMany) { + me.data.labels.splice(0, howMany); + } + } + + return { + id: 'streaming', + + beforeUpdate: function(chart, options) { + var chartOpts = chart.options; + var scalesOpts = chartOpts.scales; + var realtimeOpts; + + chartOpts.elements.line.capBezierPoints = false; + + scalesOpts.xAxes.concat(scalesOpts.yAxes).forEach(function(scaleOpts) { + if (scaleOpts.type === 'realtime' || scaleOpts.type === 'time') { + realtimeOpts = scaleOpts.realtime; + + // For backwards compatibility + if (!realtimeOpts) { + realtimeOpts = scaleOpts.realtime = {}; + } + + // Copy plugin options to scale options + realtimeOpts.duration = options.duration; + realtimeOpts.refresh = options.refresh; + realtimeOpts.delay = options.delay; + realtimeOpts.onRefresh = onRefresh; + } + }); + }, + + beforeDraw: function(chart) { + // Dispach mouse event for scroll + var event = chart.lastMouseMoveEvent; + if (event) { + chart.canvas.dispatchEvent(event.native); + } + return true; + }, + + beforeEvent: function(chart, event) { + if (event.type === 'mousemove') { + // Save mousemove event for reuse + chart.lastMouseMoveEvent = event; + } else if (event.type === 'mouseout') { + // Remove mousemove event + delete chart.lastMouseMoveEvent; + } + return true; + } + }; +}; + +realTimeScale(Chart, moment); + +// Workaround for Chart.js issue #4450. No need for 2.7.0 or later. +Chart.plugins.getAll().forEach(function(plugin) { + if (plugin.id === 'filler') { + var beforeDatasetDraw = plugin.beforeDatasetDraw; + plugin.beforeDatasetDraw = function(chart, args) { + Chart.helpers.canvas.clipArea(chart.ctx, chart.chartArea); + beforeDatasetDraw(chart, args); + Chart.helpers.canvas.unclipArea(chart.ctx); + }; + } +}); + +// Backported from Chart.js 5d57de4. No need for 2.7.0 or later. +Chart.controllers.bar.prototype.calculateBarValuePixels = function(datasetIndex, index) { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var scale = me.getValueScale(); + var datasets = chart.data.datasets; + var value = scale.getRightValue(datasets[datasetIndex].data[index]); + var stacked = scale.options.stacked; + var stack = meta.stack; + var start = 0; + var i, imeta, ivalue, base, head, size; + + if (stacked || (stacked === undefined && stack !== undefined)) { + for (i = 0; i < datasetIndex; ++i) { + imeta = chart.getDatasetMeta(i); + + if (imeta.bar && + imeta.stack === stack && + imeta.controller.getValueScaleId() === scale.id && + chart.isDatasetVisible(i)) { + + ivalue = scale.getRightValue(datasets[i].data[index]); + if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { + start += ivalue; + } + } + } + } + + base = scale.getPixelForValue(start); + head = scale.getPixelForValue(start + value); + size = (head - base) / 2; + + return { + size: size, + base: base, + head: head, + center: head + size / 2 + }; +}; + +// Backported from Chart.js 5d57de4. No need for 2.7.0 or later. +Chart.controllers.bar.prototype.draw = function() { + var me = this; + var chart = me.chart; + var scale = me.getValueScale(); + var rects = me.getMeta().data; + var dataset = me.getDataset(); + var ilen = rects.length; + var i = 0; + + Chart.helpers.canvas.clipArea(chart.ctx, chart.chartArea); + + for (; i < ilen; ++i) { + if (!isNaN(scale.getRightValue(dataset.data[i]))) { + rects[i].draw(); + } + } + + Chart.helpers.canvas.unclipArea(chart.ctx); +}; + +var plugin = streamingPlugin(Chart); +Chart.plugins.register(plugin); + +return plugin; + +}))); diff --git a/dist/chartjs-plugin-streaming.min.js b/dist/chartjs-plugin-streaming.min.js new file mode 100644 index 0000000..0678eef --- /dev/null +++ b/dist/chartjs-plugin-streaming.min.js @@ -0,0 +1,11 @@ +/* + * @license + * chartjs-plugin-streaming + * http://github.com/nagix/chartjs-plugin-streaming/ + * Version: 1.2.0 + * + * Copyright 2017 Akihiko Kusanagi + * Released under the MIT license + * https://github.com/nagix/chartjs-plugin-streaming/blob/master/LICENSE.md + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("chart.js"),require("moment")):"function"==typeof define&&define.amd?define(["chart.js","moment"],t):e["chartjs-plugin-streaming"]=t(e.Chart,e.moment)}(this,function(e,t){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e,t=t&&t.hasOwnProperty("default")?t.default:t;!function(e,t){function a(e){return null!==e&&"[object Object]"===Object.prototype.toString.call(e)}function r(e,t){return void 0===e?t:e}function i(e){if(f.isArray(e))return e.map(i);if(a(e)){for(var t={},r=Object.keys(e),n=r.length,o=0;or;c++)s=i.steps[c],o=Math.ceil(l/(n*s));else for(;o>r&&r>0;)++s,o=Math.ceil(l/(n*s));return s}function c(e,a){var r,i,n=e.timeOpts.isoWeekday;return"week"===e.unit&&!1!==n?(r=t(a.min).startOf("isoWeek").isoWeekday(n).valueOf(),i=t(a.max).startOf("isoWeek").isoWeekday(n),a.max-i>0&&i.add(1,"week"),i=i.valueOf()):(r=t(a.min).startOf(e.unit).valueOf(),i=t(a.max).startOf(e.unit),a.max-i>0&&i.add(1,e.unit),i=i.valueOf()),o(e,a,{min:r,max:i})}function p(e,t,a){var r,i,n=e._start||{},o=e._view||{},s=e._model||{};for(r=0,i=t.length;r=a[t].length&&a[t].push({}),!a[t][s].type||u.type&&u.type!==a[t][s].type?f.merge(a[t][s],[e.scaleService.getScaleDefaults(l),u]):f.merge(a[t][s],u)}else n(t,a,i,o)}})};var d={millisecond:{size:1,steps:[1,2,5,10,20,50,100,250,500]},second:{size:1e3,steps:[1,2,5,10,30]},minute:{size:6e4,steps:[1,2,5,10,30]},hour:{size:36e5,steps:[1,2,3,6,12]},day:{size:864e5,steps:[1,2,5]},week:{size:6048e5,maxStep:4},month:{size:2628e6,maxStep:3},quarter:{size:7884e6,maxStep:4},year:{size:3154e7,maxStep:!1}},h={x:{data:["x","controlPointPreviousX","controlPointNextX"],dataset:["x"],tooltip:["x","caretX"]},y:{data:["y","controlPointPreviousY","controlPointNextY"],dataset:["y"],tooltip:["y","caretY"]}},v=e.scaleService.getScaleConstructor("time");e.scaleService.getScaleConstructor=function(e){return"time"===e&&(e="realtime"),this.constructors.hasOwnProperty(e)?this.constructors[e]:void 0};var g=v.extend({initialize:function(){v.prototype.initialize.call(this);var e=this,t=e.options;if("time"!==t.type||e.chart.options.plugins.streaming){var a=Date.now(),r=Date.now(),i=function(){var n,o,s=e.chart,l=t.realtime,u=l.duration,c=l.refresh;e.isHorizontal()?(o=e.width,n=h.x):(o=e.height,n=h.y);var m=Date.now(),d=o*(m-r)/u;f.each(s.data.datasets,function(e,t){var a,r,i=s.getDatasetMeta(t),o=i.data||[];for(a=0,r=o.length;a=a?(a=m+c+(m-a)%c,l.onRefresh&&l.onRefresh.call(s,e),s.update()):(e.max=m-l.delay,e.min=e.max-u,s.animating||s.draw()),r=m,f.requestAnimFrame.call(window,i)};f.requestAnimFrame.call(window,i)}},buildTicks:function(){var e=this,t=e.options;if("time"!==t.type||e.chart.options.plugins.streaming){var a=t.time,i=t.realtime,n=Date.now()-i.delay,o=n-i.duration,p=e.getLabelCapacity(o),f=a.unit||s(a.minUnit,o,n,p),m=l(f);e.displayFormat=a.displayFormats[f],e.majorDisplayFormat=a.displayFormats[m],e.unit=f,e.majorUnit=m;var d=r(a.stepSize,a.unitStepSize)||u(o,n,f,p);e.ticks=c({maxTicks:p,min:o,max:n+i.refresh,stepSize:d,majorUnit:m,unit:f,timeOpts:a},{min:e.dataMin,max:e.dataMax}),e.max=n,e.min=o}else v.prototype.buildTicks.call(this)},draw:function(e){var t=this,a=t.chart;if("time"===t.options.type&&!a.options.plugins.streaming)return v.prototype.draw.call(this,e);var r=t.ctx,i=t.isHorizontal()?{left:e.left,top:0,right:e.right,bottom:a.height}:{left:0,top:e.top,right:a.width,bottom:e.bottom};f.canvas.clipArea(r,i),v.prototype.draw.call(this,e),f.canvas.unclipArea(r)},getPixelForOffset:function(e){var t=this;if("time"===t.options.type&&!t.chart.options.plugins.streaming)return v.prototype.getPixelForOffset.call(this,e);var a=t.max-t.min,r=a?(e-t.min)/a:0;return t.isHorizontal()?t.left+t.width*r:t.top+t.height*r}});e.scaleService.registerScaleType("realtime",g,m)}(e,t),e.plugins.getAll().forEach(function(t){if("filler"===t.id){var a=t.beforeDatasetDraw;t.beforeDatasetDraw=function(t,r){e.helpers.canvas.clipArea(t.ctx,t.chartArea),a(t,r),e.helpers.canvas.unclipArea(t.ctx)}}}),e.controllers.bar.prototype.calculateBarValuePixels=function(e,t){var a,r,i,n,o,s,l=this,u=l.chart,c=l.getMeta(),p=l.getValueScale(),f=u.data.datasets,m=p.getRightValue(f[e].data[t]),d=p.options.stacked,h=c.stack,v=0;if(d||void 0===d&&void 0!==h)for(a=0;a=0&&i>0)&&(v+=i));return n=p.getPixelForValue(v),o=p.getPixelForValue(v+m),s=(o-n)/2,{size:s,base:n,head:o,center:o+s/2}},e.controllers.bar.prototype.draw=function(){var t=this,a=t.chart,r=t.getValueScale(),i=t.getMeta().data,n=t.getDataset(),o=i.length,s=0;for(e.helpers.canvas.clipArea(a.ctx,a.chartArea);s