diff --git a/.travis.yml b/.travis.yml
index 90331521284..78b056b8dbc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,5 +26,5 @@ matrix:
include:
- node_js: "10"
<<: *node_js-steps
- - node_js: "node" # Latest Node is not supported, and recommend, but we'll test it to know incompatibility issues
+ - node_js: "12" # Latest Node is not supported, and recommend, but we'll test it to know incompatibility issues
<<: *node_js-steps
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index de4a539a48d..c77a0df1c6a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -257,7 +257,7 @@ Languages with less than 90% coverage will be removed in a future Nightscout ver
| Suomi (`fi`)|[@sulkaharo] |OK|
| Français (`fr`)|Please volunteer|OK|
| עברית (`he`)|Please volunteer|OK|
-| Hrvatski (`hr`)|[@OpossumGit]|Needs attention: 47.8% - committed 100% to dev|
+| Hrvatski (`hr`)|[@OpossumGit]|OK|
| Italiano (`it`)|Please volunteer|OK|
| 日本語 (`ja`)|[@LuminaryXion]|Working on this|
| 한국어 (`ko`)|Please volunteer|Needs attention: 80.6%|
diff --git a/Makefile b/Makefile
index bf87aaed1c1..1ca626ab88c 100644
--- a/Makefile
+++ b/Makefile
@@ -43,7 +43,7 @@ report:
test_onebyone:
python -c 'import os,sys,fcntl; flags = fcntl.fcntl(sys.stdout, fcntl.F_GETFL); fcntl.fcntl(sys.stdout, fcntl.F_SETFL, flags&~os.O_NONBLOCK);'
- $(foreach var,$(wildcard tests/*.js),${MONGO_SETTINGS} ${MOCHA} --timeout 30000 --exit --bail -R tap $(var);)
+ for var in tests/*.js; do ${MONGO_SETTINGS} ${MOCHA} --timeout 30000 --exit --bail -R tap $$var; done | tap-set-exit
test:
${MONGO_SETTINGS} ${MOCHA} --timeout 30000 --exit --bail -R tap ${TESTS}
@@ -52,7 +52,7 @@ travis:
python -c 'import os,sys,fcntl; flags = fcntl.fcntl(sys.stdout, fcntl.F_GETFL); fcntl.fcntl(sys.stdout, fcntl.F_SETFL, flags&~os.O_NONBLOCK);'
# NODE_ENV=test ${MONGO_SETTINGS} \
# ${ISTANBUL} cover ${MOCHA} --report lcovonly -- --timeout 5000 -R tap ${TESTS}
- $(foreach var,$(wildcard tests/*.js),${MONGO_SETTINGS} ${MOCHA} --timeout 30000 --exit --bail -R tap $(var);)
+ for var in tests/*.js; do ${MONGO_SETTINGS} ${MOCHA} --timeout 30000 --exit --bail -R tap $$var; done
docker_release:
# Get the version from the package.json file
diff --git a/README.md b/README.md
index d418480a311..8ad79a39d72 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Nightscout Web Monitor (a.k.a. cgm-remote-monitor)
[![Dependency Status][dependency-img]][dependency-url]
[![Coverage Status][coverage-img]][coverage-url]
[![Codacy Badge][codacy-img]][codacy-url]
-[![Gitter chat][gitter-img]][gitter-url]
+[![Discord chat][discord-img]][discord-url]
[](https://azuredeploy.net/) [![Deploy to Heroku][heroku-img]][heroku-url] [![Update your site][update-img]][update-fork]
@@ -35,8 +35,8 @@ Community maintained fork of the
[coverage-url]: https://coveralls.io/github/nightscout/cgm-remote-monitor?branch=master
[codacy-img]: https://www.codacy.com/project/badge/f79327216860472dad9afda07de39d3b
[codacy-url]: https://www.codacy.com/app/Nightscout/cgm-remote-monitor
-[gitter-img]: https://img.shields.io/badge/Gitter-Join%20Chat%20%E2%86%92-1dce73.svg
-[gitter-url]: https://gitter.im/nightscout/public
+[discord-img]: https://img.shields.io/discord/629952586895851530?label=discord%20chat
+[discord-url]: https://discordapp.com/channels/629952586895851530/629952669967974410
[heroku-img]: https://www.herokucdn.com/deploy/button.png
[heroku-url]: https://heroku.com/deploy
[update-img]: update.png
@@ -311,7 +311,6 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs/ or
* `Clock` - Shows current BG, trend arrow, and time of day. Grey text on a black background.
* `Color` - Shows current BG and trend arrow. White text on a background that changes color to indicate current BG threshold (green = in range; blue = below range; yellow = above range; red = urgent below/above).
* `Simple` - Shows current BG. Grey text on a black background.
- * Optional configuration: set `SHOW_CLOCK_CLOSEBUTTON` to `false` to never show the small X button in clock views. For bookmarking a clock view without the close box but have it appear when navigating to a clock from the Nightscout menu, don't change the settng, but remove the `showClockClosebutton=true` parameter from the clock view URL.
### Plugins
diff --git a/lib/client/chart.js b/lib/client/chart.js
index bb9ef950977..ba35ed52f6a 100644
--- a/lib/client/chart.js
+++ b/lib/client/chart.js
@@ -3,7 +3,20 @@
// var _ = require('lodash');
var times = require('../times');
var d3locales = require('./d3locales');
-var padding = { bottom: 30 };
+var scrolling = false
+ , scrollNow = 0
+ , scrollBrushExtent = null
+ , scrollRange = null
+;
+
+var PADDING_BOTTOM = 30
+ , CONTEXT_MAX = 420
+ , CONTEXT_MIN = 36
+ , FOCUS_MAX = 510
+ , FOCUS_MIN = 30
+;
+
+var loadTime = Date.now();
function init (client, d3, $) {
var chart = { };
@@ -31,20 +44,37 @@ function init (client, d3, $) {
// arrow head
defs.append('marker')
- .attr({
- 'id': 'arrow',
- 'viewBox': '0 -5 10 10',
- 'refX': 5,
- 'refY': 0,
- 'markerWidth': 8,
- 'markerHeight': 8,
- 'orient': 'auto'
- })
+ .attr('id', 'arrow')
+ .attr('viewBox', '0 -5 10 10')
+ .attr('refX', 5)
+ .attr('refY', 0)
+ .attr('markerWidth', 8)
+ .attr('markerHeight', 8)
+ .attr('orient', 'auto')
.append('path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('class', 'arrowHead');
- var localeFormatter = d3.locale(d3locales.locale(client.settings.language));
+ var localeFormatter = d3.timeFormatLocale(d3locales.locale(client.settings.language));
+
+ function beforeBrushStarted ( ) {
+ // go ahead and move the brush because
+ // a single click will not execute the brush event
+ var now = new Date();
+ var dx = chart.xScale2(now) - chart.xScale2(new Date(now.getTime() - client.focusRangeMS));
+
+ var cx = d3.mouse(this)[0];
+ var x0 = cx - dx / 2;
+ var x1 = cx + dx / 2;
+
+ var range = chart.xScale2.range();
+ var X0 = range[0];
+ var X1 = range[1];
+
+ var brush = x0 < X0 ? [X0, X0 + dx] : x1 > X1 ? [X1 - dx, X1] : [x0, x1];
+
+ chart.theBrush.call(chart.brush.move, brush);
+ }
function brushStarted ( ) {
// update the opacity of the context data points to brush extent
@@ -64,15 +94,15 @@ function init (client, d3, $) {
var yScaleType;
if (client.settings.scaleY === 'linear') {
- yScaleType = d3.scale.linear;
+ yScaleType = d3.scaleLinear;
} else {
- yScaleType = d3.scale.log;
+ yScaleType = d3.scaleLog;
}
- var focusYDomain = [utils.scaleMgdl(30), utils.scaleMgdl(510)];
- var contextYDomain = [utils.scaleMgdl(36), utils.scaleMgdl(420)];
+ var focusYDomain = [utils.scaleMgdl(FOCUS_MIN), utils.scaleMgdl(FOCUS_MAX)];
+ var contextYDomain = [utils.scaleMgdl(CONTEXT_MIN), utils.scaleMgdl(CONTEXT_MAX)];
- function dynamicDomain() {
+ function dynamicDomain () {
// allow y-axis to extend all the way to the top of the basal area, but leave room to display highest value
var mult = 1.15
, targetTop = client.settings.thresholds.bgTargetTop
@@ -84,12 +114,12 @@ function init (client, d3, $) {
//, mgdlMax = d3.quantile(client.entries, 0.99, function (d) { return d.mgdl; });
return [
- utils.scaleMgdl(30)
+ utils.scaleMgdl(FOCUS_MIN)
, Math.max(utils.scaleMgdl(mgdlMax * mult), utils.scaleMgdl(targetTop * mult))
];
}
- function dynamicDomainOrElse(defaultDomain) {
+ function dynamicDomainOrElse (defaultDomain) {
if (client.settings.scaleY === 'linear' || client.settings.scaleY === 'log-dynamic') {
return dynamicDomain();
} else {
@@ -98,71 +128,94 @@ function init (client, d3, $) {
}
// define the parts of the axis that aren't dependent on width or height
- var xScale = chart.xScale = d3.time.scale().domain(extent);
+ var xScale = chart.xScale = d3.scaleTime().domain(extent);
+ focusYDomain = dynamicDomainOrElse(focusYDomain);
var yScale = chart.yScale = yScaleType()
- .domain(dynamicDomainOrElse(focusYDomain));
+ .domain(focusYDomain);
- var xScale2 = chart.xScale2 = d3.time.scale().domain(extent);
+ var xScale2 = chart.xScale2 = d3.scaleTime().domain(extent);
+
+ contextYDomain = dynamicDomainOrElse(contextYDomain);
var yScale2 = chart.yScale2 = yScaleType()
- .domain(dynamicDomainOrElse(contextYDomain));
+ .domain(contextYDomain);
- chart.xScaleBasals = d3.time.scale().domain(extent);
+ chart.xScaleBasals = d3.scaleTime().domain(extent);
- chart.yScaleBasals = d3.scale.linear()
+ chart.yScaleBasals = d3.scaleLinear()
.domain([0, 5]);
- var tickFormat = localeFormatter.timeFormat.multi( [
- ['.%L', function(d) { return d.getMilliseconds(); }],
- [':%S', function(d) { return d.getSeconds(); }],
- [client.settings.timeFormat === 24 ? '%H:%M' : '%I:%M', function(d) { return d.getMinutes(); }],
- [client.settings.timeFormat === 24 ? '%H:%M' : '%-I %p', function(d) { return d.getHours(); }],
- ['%a %d', function(d) { return d.getDay() && d.getDate() !== 1; }],
- ['%b %d', function(d) { return d.getDate() !== 1; }],
- ['%B', function(d) { return d.getMonth(); }],
- ['%Y', function() { return true; }]
- ]);
+ var formatMillisecond = localeFormatter.format('.%L'),
+ formatSecond = localeFormatter.format(':%S'),
+ formatMinute = client.settings.timeFormat === 24 ? localeFormatter.format('%H:%M') :
+ localeFormatter.format('%I:%M'),
+ formatHour = client.settings.timeFormat === 24 ? localeFormatter.format('%H:%M') :
+ localeFormatter.format('%-I %p'),
+ formatDay = localeFormatter.format('%a %d'),
+ formatWeek = localeFormatter.format('%b %d'),
+ formatMonth = localeFormatter.format('%B'),
+ formatYear = localeFormatter.format('%Y');
+
+ var tickFormat = function (date) {
+ return (d3.timeSecond(date) < date ? formatMillisecond
+ : d3.timeMinute(date) < date ? formatSecond
+ : d3.timeHour(date) < date ? formatMinute
+ : d3.timeDay(date) < date ? formatHour
+ : d3.timeMonth(date) < date ? (d3.timeWeek(date) < date ? formatDay : formatWeek)
+ : d3.timeYear(date) < date ? formatMonth
+ : formatYear)(date);
+ };
var tickValues = client.ticks(client);
- chart.xAxis = d3.svg.axis()
- .scale(xScale)
+ chart.xAxis = d3.axisBottom(xScale)
+
+ chart.xAxis = d3.axisBottom(xScale)
.tickFormat(tickFormat)
- .ticks(4)
- .orient('bottom');
+ .ticks(6);
- chart.yAxis = d3.svg.axis()
- .scale(yScale)
+ chart.yAxis = d3.axisLeft(yScale)
.tickFormat(d3.format('d'))
- .tickValues(tickValues)
- .orient('left');
+ .tickValues(tickValues);
- chart.xAxis2 = d3.svg.axis()
- .scale(xScale2)
+ chart.xAxis2 = d3.axisBottom(xScale2)
.tickFormat(tickFormat)
- .ticks(6)
- .orient('bottom');
+ .ticks(6);
- chart.yAxis2 = d3.svg.axis()
- .scale(yScale2)
+ chart.yAxis2 = d3.axisRight(yScale2)
.tickFormat(d3.format('d'))
- .tickValues(tickValues)
- .orient('right');
+ .tickValues(tickValues);
+
+ d3.select('tick')
+ .style('z-index', '10000');
// setup a brush
- chart.brush = d3.svg.brush()
- .x(xScale2)
- .on('brushstart', brushStarted)
+ chart.brush = d3.brushX()
+ .on('start', brushStarted)
.on('brush', function brush (time) {
- client.loadRetroIfNeeded();
+ // layouting the graph causes a brushed event
+ // ignore retro data load the first two seconds
+ if (Date.now() - loadTime > 2000) client.loadRetroIfNeeded();
client.brushed(time);
})
- .on('brushend', brushEnded);
+ .on('end', brushEnded);
+
+ chart.theBrush = null;
- chart.futureOpacity = d3.scale.linear( )
- .domain([times.mins(25).msecs, times.mins(60).msecs])
- .range([0.8, 0.1]);
+ chart.futureOpacity = (function () {
+ var scale = d3.scaleLinear( )
+ .domain([times.mins(25).msecs, times.mins(60).msecs])
+ .range([0.8, 0.1]);
+
+ return function (delta) {
+ if (delta < 0) {
+ return null;
+ } else {
+ return scale(delta);
+ }
+ };
+ })();
// create svg and g to contain the chart contents
chart.charts = d3.select('#chartContainer').append('svg')
@@ -176,48 +229,72 @@ function init (client, d3, $) {
// create the x axis container
chart.focus.append('g')
- .attr('class', 'x axis');
-
+ .attr('class', 'x axis')
+ .style("font-size", "16px");
+
// create the y axis container
chart.focus.append('g')
- .attr('class', 'y axis');
+ .attr('class', 'y axis')
+ .style("font-size", "16px");
- chart.context = chart.charts.append('g').attr('class', 'chart-context');
+ chart.context = chart.charts.append('g')
+ .attr('class', 'chart-context');
// create the x axis container
chart.context.append('g')
- .attr('class', 'x axis');
+ .attr('class', 'x axis')
+ .style("font-size", "16px");
// create the y axis container
chart.context.append('g')
- .attr('class', 'y axis');
+ .attr('class', 'y axis')
+ .style("font-size", "16px");
- function createAdjustedRange() {
- var range = chart.brush.extent().slice();
+ chart.createBrushedRange = function () {
+ var brushedRange = chart.theBrush && d3.brushSelection(chart.theBrush.node()) || null;
+ var range = brushedRange && brushedRange.map(chart.xScale2.invert);
+ var dataExtent = client.dataExtent();
- var end = range[1].getTime() + client.forecastTime;
+ if (!brushedRange) {
+ // console.log('No current brushed range. Setting range to last focusRangeMS amount of available data');
+ range = dataExtent;
+
+ range[0] = new Date(range[1].getTime() - client.focusRangeMS);
+ }
+
+ var end = range[1].getTime()
if (!chart.inRetroMode()) {
- var lastSGVMills = client.latestSGV ? client.latestSGV.mills : client.now;
- end += (client.now - lastSGVMills);
+ end = client.now > dataExtent[1].getTime() ? client.now : dataExtent[1].getTime();
}
range[1] = new Date(end);
+ range[0] = new Date(end - client.focusRangeMS);
return range;
}
- chart.inRetroMode = function inRetroMode() {
- if (!chart.brush || !chart.xScale2) {
+ chart.createAdjustedRange = function () {
+ var adjustedRange = chart.createBrushedRange();
+
+ adjustedRange[1] = new Date(adjustedRange[1].getTime() + client.forecastTime);
+
+ return adjustedRange;
+ }
+
+ chart.inRetroMode = function inRetroMode () {
+ var brushedRange = chart.theBrush && d3.brushSelection(chart.theBrush.node()) || null;
+
+ if (!brushedRange || !chart.xScale2) {
return false;
}
- var brushTime = chart.brush.extent()[1].getTime();
var maxTime = chart.xScale2.domain()[1].getTime();
+ var brushTime = chart.xScale2.invert(brushedRange[1]).getTime();
return brushTime < maxTime;
};
// called for initial update and updates for resize
- chart.update = function update(init) {
+ chart.update = function update (init) {
if (client.documentHidden && !init) {
console.info('Document Hidden, not updating - ' + (new Date()));
@@ -235,15 +312,16 @@ function init (client, d3, $) {
var dataRange = client.dataExtent();
var chartContainerRect = chartContainer[0].getBoundingClientRect();
var chartWidth = chartContainerRect.width;
- var chartHeight = chartContainerRect.height - padding.bottom;
+ var chartHeight = chartContainerRect.height - PADDING_BOTTOM;
// get the height of each chart based on its container size ratio
var focusHeight = chart.focusHeight = chartHeight * .7;
- var contextHeight = chart.contextHeight = chartHeight * .2;
+ var contextHeight = chart.contextHeight = chartHeight * .3;
chart.basalsHeight = focusHeight / 4;
// get current brush extent
- var currentBrushExtent = createAdjustedRange();
+ var currentRange = chart.createAdjustedRange();
+ var currentBrushExtent = chart.createBrushedRange();
// only redraw chart if chart size has changed
var widthChanged = (chart.prevChartWidth !== chartWidth);
@@ -259,14 +337,14 @@ function init (client, d3, $) {
//set the width and height of the SVG element
chart.charts.attr('width', chartWidth)
- .attr('height', chartHeight + padding.bottom);
+ .attr('height', chartHeight + PADDING_BOTTOM);
// ranges are based on the width and height available so reset
chart.xScale.range([0, chartWidth]);
chart.xScale2.range([0, chartWidth]);
chart.xScaleBasals.range([0, chartWidth]);
chart.yScale.range([focusHeight, 0]);
- chart.yScale2.range([chartHeight, chartHeight - contextHeight]);
+ chart.yScale2.range([contextHeight, 0]);
chart.yScaleBasals.range([0, focusHeight / 4]);
if (init) {
@@ -281,42 +359,48 @@ function init (client, d3, $) {
.call(chart.yAxis);
// if first run then just display axis with no transition
+ chart.context
+ .attr('transform', 'translate(0,' + focusHeight + ')')
+
chart.context.select('.x')
- .attr('transform', 'translate(0,' + chartHeight + ')')
+ .attr('transform', 'translate(0,' + contextHeight + ')')
.call(chart.xAxis2);
-// chart.basals.select('.y')
-// .attr('transform', 'translate(0,' + 0 + ')')
-// .call(chart.yAxisBasals);
-
- chart.context.append('g')
+ chart.theBrush = chart.context.append('g')
.attr('class', 'x brush')
- .call(d3.svg.brush().x(chart.xScale2).on('brush', client.brushed))
- .selectAll('rect')
- .attr('y', focusHeight)
- .attr('height', chartHeight - focusHeight);
+ .call(chart.brush)
+ .call(g => g.select(".overlay")
+ .datum({type: 'selection'})
+ .on('mousedown touchstart', beforeBrushStarted));
+
+ chart.theBrush.selectAll('rect')
+ .attr('y', 0)
+ .attr('height', contextHeight);
// disable resizing of brush
- d3.select('.x.brush').select('.background').style('cursor', 'move');
- d3.select('.x.brush').select('.resize.e').style('cursor', 'move');
- d3.select('.x.brush').select('.resize.w').style('cursor', 'move');
+ chart.context.select('.x.brush').select('.overlay').style('cursor', 'move');
+ chart.context.select('.x.brush').selectAll('.handle')
+ .style('cursor', 'move');
+
+ chart.context.select('.x.brush').select('.selection')
+ .style('visibility', 'hidden');
// add a line that marks the current time
chart.focus.append('line')
.attr('class', 'now-line')
.attr('x1', chart.xScale(new Date(client.now)))
- .attr('y1', chart.yScale(utils.scaleMgdl(30)))
+ .attr('y1', chart.yScale(focusYDomain[0]))
.attr('x2', chart.xScale(new Date(client.now)))
- .attr('y2', chart.yScale(utils.scaleMgdl(420)))
+ .attr('y2', chart.yScale(focusYDomain[1]))
.style('stroke-dasharray', ('3, 3'))
.attr('stroke', 'grey');
// add a y-axis line that shows the high bg threshold
chart.focus.append('line')
.attr('class', 'high-line')
- .attr('x1', chart.xScale(dataRange[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgHigh)))
- .attr('x2', chart.xScale(dataRange[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgHigh)))
.style('stroke-dasharray', ('1, 6'))
.attr('stroke', '#777');
@@ -324,9 +408,9 @@ function init (client, d3, $) {
// add a y-axis line that shows the high bg threshold
chart.focus.append('line')
.attr('class', 'target-top-line')
- .attr('x1', chart.xScale(dataRange[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetTop)))
- .attr('x2', chart.xScale(dataRange[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetTop)))
.style('stroke-dasharray', ('3, 3'))
.attr('stroke', 'grey');
@@ -334,9 +418,9 @@ function init (client, d3, $) {
// add a y-axis line that shows the low bg threshold
chart.focus.append('line')
.attr('class', 'target-bottom-line')
- .attr('x1', chart.xScale(dataRange[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetBottom)))
- .attr('x2', chart.xScale(dataRange[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetBottom)))
.style('stroke-dasharray', ('3, 3'))
.attr('stroke', 'grey');
@@ -344,9 +428,9 @@ function init (client, d3, $) {
// add a y-axis line that shows the low bg threshold
chart.focus.append('line')
.attr('class', 'low-line')
- .attr('x1', chart.xScale(dataRange[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgLow)))
- .attr('x2', chart.xScale(dataRange[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgLow)))
.style('stroke-dasharray', ('1, 6'))
.attr('stroke', '#777');
@@ -371,9 +455,9 @@ function init (client, d3, $) {
chart.context.append('line')
.attr('class', 'now-line')
.attr('x1', chart.xScale(new Date(client.now)))
- .attr('y1', chart.yScale2(utils.scaleMgdl(36)))
+ .attr('y1', chart.yScale2(contextYDomain[0]))
.attr('x2', chart.xScale(new Date(client.now)))
- .attr('y2', chart.yScale2(utils.scaleMgdl(420)))
+ .attr('y2', chart.yScale2(contextYDomain[1]))
.style('stroke-dasharray', ('3, 3'))
.attr('stroke', 'grey');
@@ -400,7 +484,7 @@ function init (client, d3, $) {
} else {
// for subsequent updates use a transition to animate the axis to the new position
- var focusTransition = chart.focus.transition();
+ var focusTransition = chart.focus;
focusTransition.select('.x')
.attr('transform', 'translate(0,' + focusHeight + ')')
@@ -410,86 +494,75 @@ function init (client, d3, $) {
.attr('transform', 'translate(' + chartWidth + ', 0)')
.call(chart.yAxis);
- var contextTransition = chart.context.transition();
+ var contextTransition = chart.context;
+
+ chart.context
+ .attr('transform', 'translate(0,' + focusHeight + ')')
contextTransition.select('.x')
- .attr('transform', 'translate(0,' + chartHeight + ')')
+ .attr('transform', 'translate(0,' + contextHeight + ')')
.call(chart.xAxis2);
- chart.basals.transition();
-
-// basalsTransition.select('.y')
-// .attr('transform', 'translate(0,' + 0 + ')')
-// .call(chart.yAxisBasals);
+ chart.basals;
// reset brush location
- chart.context.select('.x.brush')
- .selectAll('rect')
- .attr('y', focusHeight)
- .attr('height', chartHeight - focusHeight);
+ chart.theBrush.selectAll('rect')
+ .attr('y', 0)
+ .attr('height', contextHeight);
- // clear current brushs
- d3.select('.brush').call(chart.brush.clear());
+ // console.log('Redrawing old brush with new dimensions: ', currentBrushExtent);
// redraw old brush with new dimensions
- d3.select('.brush').transition().call(chart.brush.extent(currentBrushExtent));
+ chart.theBrush.call(chart.brush.move, currentBrushExtent.map(chart.xScale2));
// transition lines to correct location
chart.focus.select('.high-line')
- .transition()
- .attr('x1', chart.xScale(currentBrushExtent[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgHigh)))
- .attr('x2', chart.xScale(currentBrushExtent[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgHigh)));
chart.focus.select('.target-top-line')
- .transition()
- .attr('x1', chart.xScale(currentBrushExtent[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetTop)))
- .attr('x2', chart.xScale(currentBrushExtent[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetTop)));
chart.focus.select('.target-bottom-line')
- .transition()
- .attr('x1', chart.xScale(currentBrushExtent[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetBottom)))
- .attr('x2', chart.xScale(currentBrushExtent[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgTargetBottom)));
chart.focus.select('.low-line')
- .transition()
- .attr('x1', chart.xScale(currentBrushExtent[0]))
+ .attr('x1', chart.xScale.range()[0])
.attr('y1', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgLow)))
- .attr('x2', chart.xScale(currentBrushExtent[1]))
+ .attr('x2', chart.xScale.range()[1])
.attr('y2', chart.yScale(utils.scaleMgdl(client.settings.thresholds.bgLow)));
// transition open-top line to correct location
chart.context.select('.open-top')
- .transition()
- .attr('x1', chart.xScale2(currentBrushExtent[0]))
- .attr('y1', chart.yScale(utils.scaleMgdl(30)))
- .attr('x2', chart.xScale2(currentBrushExtent[1]))
- .attr('y2', chart.yScale(utils.scaleMgdl(30)));
+ .attr('x1', chart.xScale2(currentRange[0]))
+ .attr('y1', chart.yScale2(utils.scaleMgdl(CONTEXT_MAX)))
+ .attr('x2', chart.xScale2(currentRange[1]))
+ .attr('y2', chart.yScale2(utils.scaleMgdl(CONTEXT_MAX)));
// transition open-left line to correct location
chart.context.select('.open-left')
- .transition()
- .attr('x1', chart.xScale2(currentBrushExtent[0]))
- .attr('y1', focusHeight)
- .attr('x2', chart.xScale2(currentBrushExtent[0]))
- .attr('y2', chartHeight);
+ .attr('x1', chart.xScale2(currentRange[0]))
+ .attr('y1', chart.yScale2(contextYDomain[0]))
+ .attr('x2', chart.xScale2(currentRange[0]))
+ .attr('y2', chart.yScale2(contextYDomain[1]));
// transition open-right line to correct location
chart.context.select('.open-right')
- .transition()
- .attr('x1', chart.xScale2(currentBrushExtent[1]))
- .attr('y1', focusHeight)
- .attr('x2', chart.xScale2(currentBrushExtent[1]))
- .attr('y2', chartHeight);
+ .attr('x1', chart.xScale2(currentRange[1]))
+ .attr('y1', chart.yScale2(contextYDomain[0]))
+ .attr('x2', chart.xScale2(currentRange[1]))
+ .attr('y2', chart.yScale2(contextYDomain[1]));
// transition high line to correct location
chart.context.select('.high-line')
- .transition()
.attr('x1', chart.xScale2(dataRange[0]))
.attr('y1', chart.yScale2(utils.scaleMgdl(client.settings.thresholds.bgTargetTop)))
.attr('x2', chart.xScale2(dataRange[1]))
@@ -497,7 +570,6 @@ function init (client, d3, $) {
// transition low line to correct location
chart.context.select('.low-line')
- .transition()
.attr('x1', chart.xScale2(dataRange[0]))
.attr('y1', chart.yScale2(utils.scaleMgdl(client.settings.thresholds.bgTargetBottom)))
.attr('x2', chart.xScale2(dataRange[1]))
@@ -505,64 +577,83 @@ function init (client, d3, $) {
}
}
- // update domain
- chart.xScale2.domain(dataRange);
+ chart.updateContext(dataRange);
+
chart.xScaleBasals.domain(dataRange);
- var updateBrush = d3.select('.brush').transition();
- updateBrush
- .call(chart.brush.extent([new Date(dataRange[1].getTime() - client.focusRangeMS), dataRange[1]]));
- client.brushed(true);
+ // console.log('Redrawing brush due to update: ', currentBrushExtent);
+
+ chart.theBrush.call(chart.brush.move, currentBrushExtent.map(chart.xScale2));
+ };
+
+ chart.updateContext = function (dataRange_) {
+ if (client.documentHidden) {
+ console.info('Document Hidden, not updating - ' + (new Date()));
+ return;
+ }
+
+ // get current data range
+ var dataRange = dataRange_ || client.dataExtent();
+
+ // update domain
+ chart.xScale2.domain(dataRange);
renderer.addContextCircles();
// update x axis domain
chart.context.select('.x').call(chart.xAxis2);
-
};
- chart.scroll = function scroll (nowDate) {
- chart.xScale.domain(createAdjustedRange());
- chart.yScale.domain(dynamicDomainOrElse(focusYDomain));
- chart.xScaleBasals.domain(createAdjustedRange());
+ function scrollUpdate () {
+ scrolling = false;
+
+ var nowDate = scrollNow;
+
+ var currentBrushExtent = scrollBrushExtent;
+ var currentRange = scrollRange;
+
+ chart.xScale.domain(currentRange);
+
+ focusYDomain = dynamicDomainOrElse(focusYDomain);
+
+ chart.yScale.domain(focusYDomain);
+ chart.xScaleBasals.domain(currentRange);
// remove all insulin/carb treatment bubbles so that they can be redrawn to correct location
d3.selectAll('.path').remove();
// transition open-top line to correct location
chart.context.select('.open-top')
- .attr('x1', chart.xScale2(chart.brush.extent()[0]))
- .attr('y1', chart.yScale(utils.scaleMgdl(30)))
- .attr('x2', chart.xScale2(new Date(chart.brush.extent()[1].getTime() + client.forecastTime)))
- .attr('y2', chart.yScale(utils.scaleMgdl(30)));
+ .attr('x1', chart.xScale2(currentRange[0]))
+ .attr('y1', chart.yScale2(contextYDomain[1]))
+ .attr('x2', chart.xScale2(currentRange[1]))
+ .attr('y2', chart.yScale2(contextYDomain[1]));
// transition open-left line to correct location
chart.context.select('.open-left')
- .attr('x1', chart.xScale2(chart.brush.extent()[0]))
- .attr('y1', chart.focusHeight)
- .attr('x2', chart.xScale2(chart.brush.extent()[0]))
- .attr('y2', chart.prevChartHeight);
+ .attr('x1', chart.xScale2(currentRange[0]))
+ .attr('y1', chart.yScale2(contextYDomain[0]))
+ .attr('x2', chart.xScale2(currentRange[0]))
+ .attr('y2', chart.yScale2(contextYDomain[1]));
// transition open-right line to correct location
chart.context.select('.open-right')
- .attr('x1', chart.xScale2(new Date(chart.brush.extent()[1].getTime() + client.forecastTime)))
- .attr('y1', chart.focusHeight)
- .attr('x2', chart.xScale2(new Date(chart.brush.extent()[1].getTime() + client.forecastTime)))
- .attr('y2', chart.prevChartHeight);
+ .attr('x1', chart.xScale2(currentRange[1]))
+ .attr('y1', chart.yScale2(contextYDomain[0]))
+ .attr('x2', chart.xScale2(currentRange[1]))
+ .attr('y2', chart.yScale2(contextYDomain[1]));
chart.focus.select('.now-line')
- .transition()
.attr('x1', chart.xScale(nowDate))
- .attr('y1', chart.yScale(utils.scaleMgdl(36)))
+ .attr('y1', chart.yScale(focusYDomain[0]))
.attr('x2', chart.xScale(nowDate))
- .attr('y2', chart.yScale(utils.scaleMgdl(420)));
+ .attr('y2', chart.yScale(focusYDomain[1]));
chart.context.select('.now-line')
- .transition()
- .attr('x1', chart.xScale2(chart.brush.extent()[1]))
- .attr('y1', chart.yScale2(utils.scaleMgdl(36)))
- .attr('x2', chart.xScale2(chart.brush.extent()[1]))
- .attr('y2', chart.yScale2(utils.scaleMgdl(420)));
+ .attr('x1', chart.xScale2(currentBrushExtent[1]))
+ .attr('y1', chart.yScale2(contextYDomain[0]))
+ .attr('x2', chart.xScale2(currentBrushExtent[1]))
+ .attr('y2', chart.yScale2(contextYDomain[1]));
// update x,y axis
chart.focus.select('.x.axis').call(chart.xAxis);
@@ -575,6 +666,18 @@ function init (client, d3, $) {
renderer.addTreatmentProfiles(client);
renderer.drawTreatments(client);
+ }
+
+ chart.scroll = function scroll (nowDate) {
+ scrollNow = nowDate;
+ scrollBrushExtent = chart.createBrushedRange();
+ scrollRange = chart.createAdjustedRange();
+
+ if (!scrolling) {
+ requestAnimationFrame(scrollUpdate);
+ }
+
+ scrolling = true;
};
return chart;
diff --git a/lib/client/clock-client.js b/lib/client/clock-client.js
index 18b5116f94d..5ff3a787e28 100644
--- a/lib/client/clock-client.js
+++ b/lib/client/clock-client.js
@@ -98,13 +98,6 @@ client.render = function render (xhr) {
if (m < 10) m = "0" + m;
$('#clock').text(h + ":" + m);
- var queryDict = {};
- location.search.substr(1).split("&").forEach(function(item) { queryDict[item.split("=")[0]] = item.split("=")[1] });
-
- if (!window.serverSettings.settings.showClockClosebutton || !queryDict['showClockClosebutton']) {
- $('#close').css('display', 'none');
- }
-
// defined in the template this is loaded into
// eslint-disable-next-line no-undef
if (clockFace === 'clock-color') {
@@ -122,11 +115,6 @@ client.render = function render (xhr) {
var green = 'rgba(134,207,70,1)';
var blue = 'rgba(78,143,207,1)';
- var darkRed = 'rgba(183,9,21,1)';
- var darkYellow = 'rgba(214,168,0,1)';
- var darkGreen = 'rgba(110,192,70,1)';
- var darkBlue = 'rgba(78,143,187,1)';
-
var elapsedMins = Math.round(((now - last) / 1000) / 60);
// Insert the BG stale time text.
@@ -135,28 +123,18 @@ client.render = function render (xhr) {
// Threshold background coloring.
if (bgNum < bgLow) {
$('body').css('background-color', red);
- $('#close').css('border-color', darkRed);
- $('#close').css('color', darkRed);
}
if ((bgLow <= bgNum) && (bgNum < bgTargetBottom)) {
$('body').css('background-color', blue);
- $('#close').css('border-color', darkBlue);
- $('#close').css('color', darkBlue);
}
if ((bgTargetBottom <= bgNum) && (bgNum < bgTargetTop)) {
$('body').css('background-color', green);
- $('#close').css('border-color', darkGreen);
- $('#close').css('color', darkGreen);
}
if ((bgTargetTop <= bgNum) && (bgNum < bgHigh)) {
$('body').css('background-color', yellow);
- $('#close').css('border-color', darkYellow);
- $('#close').css('color', darkYellow);
}
if (bgNum >= bgHigh) {
$('body').css('background-color', red);
- $('#close').css('border-color', darkRed);
- $('#close').css('color', darkRed);
}
// Restyle body bg, and make the "x minutes ago" visible too.
diff --git a/lib/client/index.js b/lib/client/index.js
index 979348c0cb5..5e9874f2ea6 100644
--- a/lib/client/index.js
+++ b/lib/client/index.js
@@ -102,8 +102,7 @@ client.init = function init (callback) {
client.load = function load (serverSettings, callback) {
- var UPDATE_TRANS_MS = 750 // milliseconds
- , FORMAT_TIME_12 = '%-I:%M %p'
+ var FORMAT_TIME_12 = '%-I:%M %p'
, FORMAT_TIME_12_COMPACT = '%-I:%M'
, FORMAT_TIME_24 = '%H:%M%'
, FORMAT_TIME_12_SCALE = '%-I %p'
@@ -124,7 +123,11 @@ client.load = function load (serverSettings, callback) {
, urgentAlarmSound = 'alarm2.mp3'
, previousNotifyTimestamp;
- client.entryToDate = function entryToDate (entry) { return new Date(entry.mills); };
+ client.entryToDate = function entryToDate (entry) {
+ if (entry.date) return entry.date;
+ entry.date = new Date(entry.mills);
+ return entry.date;
+ };
client.now = Date.now();
client.ddata = require('../data/ddata')();
@@ -261,10 +264,12 @@ client.load = function load (serverSettings, callback) {
//client.ctx.bus.uptime( );
client.dataExtent = function dataExtent () {
- return client.entries.length > 0 ?
- d3.extent(client.entries, client.entryToDate) :
- d3.extent([new Date(client.now - times.hours(history).msecs), new Date(client.now)]);
- };
+ if (client.entries.length > 0) {
+ return[client.entryToDate(client.entries[0]), client.entryToDate(client.entries[client.entries.length-1])];
+ } else {
+ return [new Date(client.now - times.hours(history).msecs), new Date(client.now)];
+ }
+ };
client.bottomOfPills = function bottomOfPills () {
//the offset's might not exist for some tests
@@ -276,7 +281,7 @@ client.load = function load (serverSettings, callback) {
function formatTime (time, compact) {
var timeFormat = getTimeFormat(false, compact);
- time = d3.time.format(timeFormat)(time);
+ time = d3.timeFormat(timeFormat)(time);
if (client.settings.timeFormat !== 24) {
time = time.toLowerCase();
}
@@ -375,14 +380,14 @@ client.load = function load (serverSettings, callback) {
// clears the current user brush and resets to the current real time data
function updateBrushToNow (skipBrushing) {
- // get current time range
- var dataRange = client.dataExtent();
-
// update brush and focus chart with recent data
- d3.select('.brush')
- .transition()
- .duration(UPDATE_TRANS_MS)
- .call(chart.brush.extent([new Date(dataRange[1].getTime() - client.focusRangeMS), dataRange[1]]));
+ var brushExtent = client.dataExtent();
+
+ brushExtent[0] = new Date(brushExtent[1].getTime() - client.focusRangeMS);
+
+ // console.log('Resetting brush in updateBrushToNow: ', brushExtent);
+
+ chart.theBrush && chart.theBrush.call(chart.brush.move, brushExtent.map(chart.xScale2));
if (!skipBrushing) {
brushed();
@@ -398,21 +403,35 @@ client.load = function load (serverSettings, callback) {
}
function brushed () {
+ // Brush not initialized
+ console.log("brushed");
+ if (!chart.theBrush) {
+ return;
+ }
+
+ // default to most recent focus period
+ var brushExtent = client.dataExtent();
+ brushExtent[0] = new Date(brushExtent[1].getTime() - client.focusRangeMS);
+
+ var brushedRange = d3.brushSelection(chart.theBrush.node());
+
+ if (brushedRange) {
+ brushExtent = brushedRange.map(chart.xScale2.invert);
+ }
- var brushExtent = chart.brush.extent();
+ // console.log('Brushed to: ', brushExtent);
- // ensure that brush extent is fixed at 3.5 hours
- if (brushExtent[1].getTime() - brushExtent[0].getTime() !== client.focusRangeMS) {
+ if (!brushedRange || (brushExtent[1].getTime() - brushExtent[0].getTime() !== client.focusRangeMS)) {
// ensure that brush updating is with the time range
if (brushExtent[0].getTime() + client.focusRangeMS > client.dataExtent()[1].getTime()) {
brushExtent[0] = new Date(brushExtent[1].getTime() - client.focusRangeMS);
- d3.select('.brush')
- .call(chart.brush.extent([brushExtent[0], brushExtent[1]]));
} else {
brushExtent[1] = new Date(brushExtent[0].getTime() + client.focusRangeMS);
- d3.select('.brush')
- .call(chart.brush.extent([brushExtent[0], brushExtent[1]]));
}
+
+ // console.log('Updating brushed to: ', brushExtent);
+
+ chart.theBrush.call(chart.brush.move, brushExtent.map(chart.xScale2));
}
function adjustCurrentSGVClasses (value, isCurrent) {
@@ -428,7 +447,6 @@ client.load = function load (serverSettings, callback) {
currentBG.toggleClass('icon-hourglass', value === 9);
currentBG.toggleClass('error-code', value < 39);
currentBG.toggleClass('bg-limit', value === 39 || value > 400);
- container.removeClass('loading');
}
function updateCurrentSGV (entry) {
@@ -458,10 +476,22 @@ client.load = function load (serverSettings, callback) {
client.ddata.inRetroMode = inRetroMode();
client.ddata.profile = profile;
+ // retro data only ever contains device statuses
+ // Cleate a clone of the data for the sandbox given to plugins
+
+ var mergedStatuses = client.ddata.devicestatus;
+
+ if (client.retro.data) {
+ mergedStatuses = _.merge({}, client.retro.data.devicestatus, client.ddata.devicestatus);
+ }
+
+ var clonedData = _.clone(client.ddata);
+ clonedData.devicestatus = mergedStatuses;
+
client.sbx = sandbox.clientInit(
client.ctx
, new Date(time).getTime() //make sure we send a timestamp
- , _.merge({}, client.retro.data || {}, client.ddata)
+ , clonedData
);
//all enabled plugins get a chance to set properties, even if they aren't shown
@@ -546,6 +576,7 @@ client.load = function load (serverSettings, callback) {
var top = (client.bottomOfPills() + 5);
$('#chartContainer').css({ top: top + 'px', height: $(window).height() - top - 10 });
+ container.removeClass('loading');
}
function sgvToColor (sgv) {
@@ -1127,9 +1158,14 @@ client.load = function load (serverSettings, callback) {
point.color = 'transparent';
}
});
+
+ client.entries.sort(function sorter(a,b) {
+ return a.mills - b.mills;
+ });
}
- function dataUpdate (received) {
+ function dataUpdate (received, headless) {
+ console.info('got dataUpdate', new Date(client.now));
var lastUpdated = Date.now();
receiveDData(received, client.ddata, client.settings);
@@ -1162,15 +1198,23 @@ client.load = function load (serverSettings, callback) {
prepareEntries();
updateTitle();
+ // Don't invoke D3 in headless mode
+
if (!isInitialData) {
isInitialData = true;
- chart = client.chart = require('./chart')(client, d3, $);
- brushed();
- chart.update(true);
+ if (!headless) {
+ chart = client.chart = require('./chart')(client, d3, $);
+ chart.update(true);
+ brushed();
+ chart.update(false);
+// brushed();
+ }
} else if (!inRetroMode()) {
- chart.update(false);
+ if (!headless) chart.update(false);
client.plugins.updateVisualisations(client.nowSBX);
- brushed();
+ if (!headless) brushed();
+ } else {
+ if (!headless) chart.updateContext();
}
}
};
diff --git a/lib/client/renderer.js b/lib/client/renderer.js
index 84f8574ed47..c526798e76c 100644
--- a/lib/client/renderer.js
+++ b/lib/client/renderer.js
@@ -6,10 +6,11 @@ var times = require('../times');
var DEFAULT_FOCUS = times.hours(3).msecs
, WIDTH_SMALL_DOTS = 420
, WIDTH_BIG_DOTS = 800
- , TOOLTIP_TRANS_MS = 100 // milliseconds
, TOOLTIP_WIDTH = 150 //min-width + padding
;
+const zeroDate = new Date(0);
+
function init (client, d3) {
var renderer = {};
@@ -17,6 +18,12 @@ function init (client, d3) {
var utils = client.utils;
var translate = client.translate;
+ function getOrAddDate(entry) {
+ if (entry.date) return entry.date;
+ entry.date = new Date(entry.mills);
+ return entry.date;
+ }
+
//chart isn't created till the client gets data, so can grab the var at init
function chart () {
return client.chart;
@@ -40,20 +47,22 @@ function init (client, d3) {
};
function tooltipLeft () {
- var windowWidth = $(client.tooltip).parent().parent().width();
+ var windowWidth = $(client.tooltip.node()).parent().parent().width();
var left = d3.event.pageX + TOOLTIP_WIDTH < windowWidth ? d3.event.pageX : windowWidth - TOOLTIP_WIDTH - 10;
return left + 'px';
}
function hideTooltip () {
- client.tooltip.transition()
- .duration(TOOLTIP_TRANS_MS)
- .style('opacity', 0);
+ client.tooltip.style('opacity', 0);
}
// get the desired opacity for context chart based on the brush extent
renderer.highlightBrushPoints = function highlightBrushPoints (data) {
- if (client.latestSGV && data.mills >= chart().brush.extent()[0].getTime() && data.mills <= chart().brush.extent()[1].getTime()) {
+ var selectedRange = chart().createAdjustedRange();
+ var from = selectedRange[0].getTime();
+ var to = selectedRange[1].getTime();
+
+ if (client.latestSGV && data.mills >= from && data.mills <= to) {
return chart().futureOpacity(data.mills - client.latestSGV.mills);
} else {
return 0.5;
@@ -73,9 +82,17 @@ function init (client, d3) {
var shownForecastPoints = _.filter(client.sbx.pluginBase.forecastPoints, function isShown (point) {
return client.settings.showForecast.indexOf(point.info.type) > -1;
});
- var maxForecastMills = _.max(_.map(shownForecastPoints, function(point) { return point.mills }));
// limit lookahead to the same as lookback
- var focusHoursAheadMills = chart().brush.extent()[1].getTime() + client.focusRangeMS;
+ var selectedRange = chart().createBrushedRange();
+ var to = selectedRange[1].getTime();
+
+ var focusHoursAheadMills = to + client.focusRangeMS;
+ var maxForecastMills = focusHoursAheadMills;
+
+ if (shownForecastPoints.length > 0) {
+ maxForecastMills = _.max(_.map(shownForecastPoints, function(point) { return point.mills }));
+ }
+
maxForecastMills = Math.min(focusHoursAheadMills, maxForecastMills);
client.forecastTime = maxForecastMills > 0 ? maxForecastMills - client.sbx.lastSGVMills() : 0;
focusData = focusData.concat(shownForecastPoints);
@@ -85,17 +102,17 @@ function init (client, d3) {
// selects all our data into data and uses date function to get current max date
var focusCircles = chart().focus.selectAll('circle').data(focusData, client.entryToDate);
- function prepareFocusCircles (sel) {
+ function updateFocusCircles (sel) {
var badData = [];
sel.attr('cx', function(d) {
if (!d) {
console.error('Bad data', d);
- return chart().xScale(new Date(0));
+ return chart().xScale(zeroDate);
} else if (!d.mills) {
console.error('Bad data, no mills', d);
- return chart().xScale(new Date(0));
+ return chart().xScale(zeroDate);
} else {
- return chart().xScale(new Date(d.mills));
+ return chart().xScale(getOrAddDate(d));
}
})
.attr('cy', function(d) {
@@ -107,17 +124,12 @@ function init (client, d3) {
return chart().yScale(scaled);
}
})
- .attr('fill', function(d) {
- return d.type === 'forecast' ? 'none' : d.color;
- })
.attr('opacity', function(d) {
- return d.noFade || !client.latestSGV ? 100 : chart().futureOpacity(d.mills - client.latestSGV.mills);
- })
- .attr('stroke-width', function(d) {
- return d.type === 'mbg' ? 2 : d.type === 'forecast' ? 2 : 0;
- })
- .attr('stroke', function(d) {
- return (d.type === 'mbg' ? 'white' : d.color);
+ if (d.noFade) {
+ return null;
+ } else {
+ return !client.latestSGV ? 1 : chart().futureOpacity(d.mills - client.latestSGV.mills);
+ }
})
.attr('r', function(d) {
return dotRadius(d.type);
@@ -130,6 +142,21 @@ function init (client, d3) {
return sel;
}
+ function prepareFocusCircles (sel) {
+ updateFocusCircles(sel)
+ .attr('fill', function(d) {
+ return d.type === 'forecast' ? 'none' : d.color;
+ })
+ .attr('stroke-width', function(d) {
+ return d.type === 'mbg' ? 2 : d.type === 'forecast' ? 2 : 0;
+ })
+ .attr('stroke', function(d) {
+ return (d.type === 'mbg' ? 'white' : d.color);
+ });
+
+ return sel;
+ }
+
function focusCircleTooltip (d) {
if (d.type !== 'sgv' && d.type !== 'mbg' && d.type !== 'forecast') {
return;
@@ -149,19 +176,19 @@ function init (client, d3) {
var rawbgInfo = getRawbgInfo();
- client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9);
+ client.tooltip.style('opacity', .9);
client.tooltip.html('' + translate('BG') + ': ' + client.sbx.scaleEntry(d) +
(d.type === 'mbg' ? '
' + translate('Device') + ': ' + d.device : '') +
(d.type === 'forecast' && d.forecastType ? '
' + translate('Forecast Type') + ': ' + d.forecastType : '') +
(rawbgInfo.value ? '
' + translate('Raw BG') + ': ' + rawbgInfo.value : '') +
(rawbgInfo.noise ? '
' + translate('Noise') + ': ' + rawbgInfo.noise : '') +
- '
' + translate('Time') + ': ' + client.formatTime(new Date(d.mills)))
+ '
' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d)))
.style('left', tooltipLeft())
.style('top', (d3.event.pageY + 15) + 'px');
}
// if already existing then transition each circle to its new position
- prepareFocusCircles(focusCircles.transition());
+ updateFocusCircles(focusCircles);
// if new circle then just display
prepareFocusCircles(focusCircles.enter().append('circle'))
@@ -215,7 +242,7 @@ function init (client, d3) {
}
}
- return '' + translate('Time') + ': ' + client.formatTime(new Date(d.mills)) + '
' +
+ return '' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d)) + '
' +
(d.eventType ? '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(d.eventType)) + '
' : '') +
(d.reason ? '' + translate('Reason') + ': ' + translate(d.reason) + '
' : '') +
(d.glucose ? '' + translate('BG') + ': ' + d.glucose + (d.glucoseType ? ' (' + translate(d.glucoseType) + ')' : '') + '
' : '') +
@@ -229,7 +256,7 @@ function init (client, d3) {
}
function announcementTooltip (d) {
- return '' + translate('Time') + ': ' + client.formatTime(new Date(d.mills)) + '
' +
+ return '' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d)) + '
' +
(d.eventType ? '' + translate('Announcement') + '
' : '') +
(d.notes && d.notes.length > 1 ? '' + translate('Message') + ': ' + d.notes + '
' : '') +
(d.enteredBy ? '' + translate('Entered By') + ': ' + d.enteredBy + '
' : '');
@@ -240,7 +267,7 @@ function init (client, d3) {
//NOTE: treatments with insulin or carbs are drawn by drawTreatment()
// bind up the focus chart data to an array of circles
- var treatCircles = chart().focus.selectAll('treatment-dot').data(client.ddata.treatments.filter(function(treatment) {
+ var treatCircles = chart().focus.selectAll('.treatment-dot').data(client.ddata.treatments.filter(function(treatment) {
var notCarbsOrInsulin = !treatment.carbs && !treatment.insulin;
var notTempOrProfile = !_.includes(['Temp Basal', 'Profile Switch', 'Combo Bolus', 'Temporary Target'], treatment.eventType);
@@ -253,7 +280,22 @@ function init (client, d3) {
}));
return notCarbsOrInsulin && !treatment.duration && treatment.durationType !== 'indefinite' && notTempOrProfile && notOpenAPSSpam;
- }));
+ }), function (d) { return d._id; });
+
+ function updateTreatCircles (sel) {
+
+ sel.attr('cx', function(d) {
+ return chart().xScale(getOrAddDate(d));
+ })
+ .attr('cy', function(d) {
+ return chart().yScale(client.sbx.scaleEntry(d));
+ })
+ .attr('r', function() {
+ return dotRadius('mbg');
+ });
+
+ return sel;
+ }
function prepareTreatCircles (sel) {
function strokeColor (d) {
@@ -276,15 +318,7 @@ function init (client, d3) {
return color;
}
- sel.attr('cx', function(d) {
- return chart().xScale(new Date(d.mills));
- })
- .attr('cy', function(d) {
- return chart().yScale(client.sbx.scaleEntry(d));
- })
- .attr('r', function() {
- return dotRadius('mbg');
- })
+ updateTreatCircles(sel)
.attr('stroke-width', 2)
.attr('stroke', strokeColor)
.attr('fill', fillColor);
@@ -293,18 +327,21 @@ function init (client, d3) {
}
// if already existing then transition each circle to its new position
- prepareTreatCircles(treatCircles.transition());
+ updateTreatCircles(treatCircles);
// if new circle then just display
prepareTreatCircles(treatCircles.enter().append('circle'))
+ .attr('class', 'treatment-dot')
.on('mouseover', function(d) {
- client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9);
+ client.tooltip.style('opacity', .9);
client.tooltip.html(d.isAnnouncement ? announcementTooltip(d) : treatmentTooltip(d))
.style('left', tooltipLeft())
.style('top', (d3.event.pageY + 15) + 'px');
})
.on('mouseout', hideTooltip);
+ treatCircles.exit().remove();
+
var durationTreatments = client.ddata.treatments.filter(function(treatment) {
return !treatment.carbs && !treatment.insulin && (treatment.duration || treatment.durationType !== undefined) &&
!_.includes(['Temp Basal', 'Profile Switch', 'Combo Bolus', 'Temporary Target'], treatment.eventType);
@@ -342,26 +379,26 @@ function init (client, d3) {
if (d.eventType === 'Temporary Target') {
top = d.targetTop === d.targetBottom ? d.targetTop + rectHeight(d) : d.targetTop;
}
- return 'translate(' + chart().xScale(new Date(d.mills)) + ',' + chart().yScale(utils.scaleMgdl(top)) + ')';
+ return 'translate(' + chart().xScale(getOrAddDate(d)) + ',' + chart().yScale(utils.scaleMgdl(top)) + ')';
}
function treatmentRectWidth (d) {
if (d.durationType === "indefinite") {
- return chart().xScale(chart().xScale.domain()[1].getTime()) - chart().xScale(new Date(d.mills));
+ return chart().xScale(chart().xScale.domain()[1].getTime()) - chart().xScale(getOrAddDate(d));
} else {
- return chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(new Date(d.mills));
+ return chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(getOrAddDate(d));
}
}
function treatmentTextTransform (d) {
if (d.durationType === "indefinite") {
var offset = 0;
- if (chart().xScale(new Date(d.mills)) < chart().xScale(chart().xScale.domain()[0].getTime())) {
- offset = chart().xScale(nowDate) - chart().xScale(new Date(d.mills));
+ if (chart().xScale(getOrAddDate(d)) < chart().xScale(chart().xScale.domain()[0].getTime())) {
+ offset = chart().xScale(nowDate) - chart().xScale(getOrAddDate(d));
}
return 'translate(' + offset + ',' + 10 + ')';
} else {
- return 'translate(' + (chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(new Date(d.mills))) / 2 + ',' + 10 + ')';
+ return 'translate(' + (chart().xScale(new Date(d.mills + times.mins(d.duration).msecs)) - chart().xScale(getOrAddDate(d))) / 2 + ',' + 10 + ')';
}
}
@@ -377,8 +414,8 @@ function init (client, d3) {
}
// if transitioning, update rect text, position, and width
- var rectUpdates = treatRects.transition()
- rectUpdates.attr('transform', rectTranslate)
+ var rectUpdates = treatRects;
+ rectUpdates.attr('transform', rectTranslate);
rectUpdates.select('text')
.text(treatmentText)
@@ -393,7 +430,7 @@ function init (client, d3) {
.attr('class', 'g-duration')
.attr('transform', rectTranslate)
.on('mouseover', function(d) {
- client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9);
+ client.tooltip.style('opacity', .9);
client.tooltip.html(d.isAnnouncement ? announcementTooltip(d) : treatmentTooltip(d))
.style('left', tooltipLeft())
.style('top', (d3.event.pageY + 15) + 'px');
@@ -430,7 +467,7 @@ function init (client, d3) {
function prepareContextCircles (sel) {
var badData = [];
- sel.attr('cx', function(d) { return chart().xScale2(new Date(d.mills)); })
+ sel.attr('cx', function(d) { return chart().xScale2(getOrAddDate(d)); })
.attr('cy', function(d) {
var scaled = client.sbx.scaleEntry(d);
if (isNaN(scaled)) {
@@ -441,7 +478,7 @@ function init (client, d3) {
}
})
.attr('fill', function(d) { return d.color; })
- .style('opacity', function(d) { return renderer.highlightBrushPoints(d) })
+ //.style('opacity', function(d) { return renderer.highlightBrushPoints(d) })
.attr('stroke-width', function(d) { return d.type === 'mbg' ? 2 : 0; })
.attr('stroke', function() { return 'white'; })
.attr('r', function(d) { return d.type === 'mbg' ? 4 : 2; });
@@ -454,7 +491,7 @@ function init (client, d3) {
}
// if already existing then transition each circle to its new position
- prepareContextCircles(contextCircles.transition());
+ prepareContextCircles(contextCircles);
// if new circle then just display
prepareContextCircles(contextCircles.enter().append('circle'));
@@ -538,7 +575,7 @@ function init (client, d3) {
arc_data[4].element = translate(treatment.status);
}
- var arc = d3.svg.arc()
+ var arc = d3.arc()
.innerRadius(function(d) {
return 5 * d.inner;
})
@@ -595,8 +632,8 @@ function init (client, d3) {
glucose = Math.round(glucose * decimals) / decimals;
}
- client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9);
- client.tooltip.html('' + translate('Time') + ': ' + client.formatTime(new Date(treatment.mills)) + '
' + '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(treatment.eventType)) + '
' +
+ client.tooltip.style('opacity', .9);
+ client.tooltip.html('' + translate('Time') + ': ' + client.formatTime(getOrAddDate(treatment)) + '
' + '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(treatment.eventType)) + '
' +
(treatment.carbs ? '' + translate('Carbs') + ': ' + treatment.carbs + '
' : '') +
(treatment.protein ? '' + translate('Protein') + ': ' + treatment.protein + '
' : '') +
(treatment.fat ? '' + translate('Fat') + ': ' + treatment.fat + '
' : '') +
@@ -617,12 +654,12 @@ function init (client, d3) {
var insulinRect = { x: 0, y: 0, width: 0, height: 0 };
var carbsRect = { x: 0, y: 0, width: 0, height: 0 };
var operation;
- renderer.drag = d3.behavior.drag()
- .on('dragstart', function() {
+ renderer.drag = d3.drag()
+ .on('start', function() {
//console.log(treatment);
- var windowWidth = $(client.tooltip).parent().parent().width();
+ var windowWidth = $(client.tooltip.node()).parent().parent().width();
var left = d3.event.x + TOOLTIP_WIDTH < windowWidth ? d3.event.x : windowWidth - TOOLTIP_WIDTH - 10;
- client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9)
+ client.tooltip.style('opacity', .9)
.style('left', left + 'px')
.style('top', (d3.event.pageY ? d3.event.pageY + 15 : 40) + 'px');
@@ -633,29 +670,25 @@ function init (client, d3) {
, height: chart().yScale(chart().yScale.domain()[0])
};
chart().drag.append('rect')
- .attr({
- class: 'drag-droparea'
- , x: deleteRect.x
- , y: deleteRect.y
- , width: deleteRect.width
- , height: deleteRect.height
- , fill: 'red'
- , opacity: 0.4
- , rx: 10
- , ry: 10
- });
+ .attr('class', 'drag-droparea')
+ .attr('x', deleteRect.x)
+ .attr('y', deleteRect.y)
+ .attr('width', deleteRect.width)
+ .attr('height', deleteRect.height)
+ .attr('fill', 'red')
+ .attr('opacity', 0.4)
+ .attr('rx', 10)
+ .attr('ry', 10);
chart().drag.append('text')
- .attr({
- class: 'drag-droparea'
- , x: deleteRect.x + deleteRect.width / 2
- , y: deleteRect.y + deleteRect.height / 2
- , 'font-size': 15
- , 'font-weight': 'bold'
- , fill: 'red'
- , 'text-anchor': 'middle'
- , dy: '.35em'
- , transform: 'rotate(-90 ' + (deleteRect.x + deleteRect.width / 2) + ',' + (deleteRect.y + deleteRect.height / 2) + ')'
- })
+ .attr('class', 'drag-droparea')
+ .attr('x', deleteRect.x + deleteRect.width / 2)
+ .attr('y', deleteRect.y + deleteRect.height / 2)
+ .attr('font-size', 15)
+ .attr('font-weight', 'bold')
+ .attr('fill', 'red')
+ .attr('text-anchor', 'middle')
+ .attr('dy', '.35em')
+ .attr('transform', 'rotate(-90 ' + (deleteRect.x + deleteRect.width / 2) + ',' + (deleteRect.y + deleteRect.height / 2) + ')')
.text(translate('Remove'));
if (treatment.insulin && treatment.carbs) {
@@ -672,52 +705,44 @@ function init (client, d3) {
, height: 50
};
chart().drag.append('rect')
- .attr({
- class: 'drag-droparea'
- , x: carbsRect.x
- , y: carbsRect.y
- , width: carbsRect.width
- , height: carbsRect.height
- , fill: 'white'
- , opacity: 0.4
- , rx: 10
- , ry: 10
- });
+ .attr('class', 'drag-droparea')
+ .attr('x', carbsRect.x)
+ .attr('y', carbsRect.y)
+ .attr('width', carbsRect.width)
+ .attr('height', carbsRect.height)
+ .attr('fill', 'white')
+ .attr('opacitys', 0.4)
+ .attr('rx', 10)
+ .attr('ry', 10);
chart().drag.append('text')
- .attr({
- class: 'drag-droparea'
- , x: carbsRect.x + carbsRect.width / 2
- , y: carbsRect.y + carbsRect.height / 2
- , 'font-size': 15
- , 'font-weight': 'bold'
- , fill: 'white'
- , 'text-anchor': 'middle'
- , dy: '.35em'
- })
+ .attr('class', 'drag-droparea')
+ .attr('x', carbsRect.x + carbsRect.width / 2)
+ .attr('y', carbsRect.y + carbsRect.height / 2)
+ .attr('font-size', 15)
+ .attr('font-weight', 'bold')
+ .attr('fill', 'white')
+ .attr('text-anchor', 'middle')
+ .attr('dy', '.35em')
.text(translate('Move carbs'));
chart().drag.append('rect')
- .attr({
- class: 'drag-droparea'
- , x: insulinRect.x
- , y: insulinRect.y
- , width: insulinRect.width
- , height: insulinRect.height
- , fill: '#0099ff'
- , opacity: 0.4
- , rx: 10
- , ry: 10
- });
+ .attr('class', 'drag-droparea')
+ .attr('x', insulinRect.x)
+ .attr('y', insulinRect.y)
+ .attr('width', insulinRect.width)
+ .attr('height', insulinRect.height)
+ .attr('fill', '#0099ff')
+ .attr('opacity', 0.4)
+ .attr('rx', 10)
+ .attr('ry', 10);
chart().drag.append('text')
- .attr({
- class: 'drag-droparea'
- , x: insulinRect.x + insulinRect.width / 2
- , y: insulinRect.y + insulinRect.height / 2
- , 'font-size': 15
- , 'font-weight': 'bold'
- , fill: '#0099ff'
- , 'text-anchor': 'middle'
- , dy: '.35em'
- })
+ .attr('class', 'drag-droparea')
+ .attr('x', insulinRect.x + insulinRect.width / 2)
+ .attr('y', insulinRect.y + insulinRect.height / 2)
+ .attr('font-size', 15)
+ .attr('font-weight', 'bold')
+ .attr('fill', '#0099ff')
+ .attr('text-anchor', 'middle')
+ .attr('dy', '.35em')
.text(translate('Move insulin'));
}
@@ -727,7 +752,7 @@ function init (client, d3) {
})
.on('drag', function() {
//console.log(d3.event);
- client.tooltip.transition().style('opacity', .9);
+ client.tooltip.style('opacity', .9);
var x = Math.min(Math.max(0, d3.event.x), chart().charts.attr('width'));
var y = Math.min(Math.max(0, d3.event.y), chart().focusHeight);
@@ -754,19 +779,16 @@ function init (client, d3) {
chart().drag.selectAll('.arrow').remove();
chart().drag.append('line')
- .attr({
- 'class': 'arrow'
- , 'marker-end': 'url(#arrow)'
- , 'x1': chart().xScale(new Date(treatment.mills))
- , 'y1': chart().yScale(client.sbx.scaleEntry(treatment))
- , 'x2': x
- , 'y2': y
- , 'stroke-width': 2
- , 'stroke': 'white'
- });
-
+ .attr('class', 'arrow')
+ .attr('marker-end', 'url(#arrow)')
+ .attr('x1', chart().xScale(getOrAddDate(treatment)))
+ .attr('y1', chart().yScale(client.sbx.scaleEntry(treatment)))
+ .attr('x2', x)
+ .attr('y2', y)
+ .attr('stroke-width', 2)
+ .attr('stroke', 'white');
})
- .on('dragend', function() {
+ .on('end', function() {
var newTreatment;
chart().drag.selectAll('.drag-droparea').remove();
hideTooltip();
@@ -781,7 +803,7 @@ function init (client, d3) {
}
, function callback (result) {
console.log(result);
- chart().drag.selectAll('.arrow').transition().duration(5000).style('opacity', 0).remove();
+ chart().drag.selectAll('.arrow').style('opacity', 0).remove();
}
);
} else {
@@ -798,7 +820,7 @@ function init (client, d3) {
}
, function callback (result) {
console.log(result);
- chart().drag.selectAll('.arrow').transition().duration(5000).style('opacity', 0).remove();
+ chart().drag.selectAll('.arrow').style('opacity', 0).remove();
}
);
} else {
@@ -815,7 +837,7 @@ function init (client, d3) {
}
, function callback (result) {
console.log(result);
- chart().drag.selectAll('.arrow').transition().duration(5000).style('opacity', 0).remove();
+ chart().drag.selectAll('.arrow').style('opacity', 0).remove();
}
);
} else {
@@ -831,7 +853,7 @@ function init (client, d3) {
}
, function callback (result) {
console.log(result);
- chart().drag.selectAll('.arrow').transition().duration(5000).style('opacity', 0).remove();
+ chart().drag.selectAll('.arrow').style('opacity', 0).remove();
}
);
} else {
@@ -859,7 +881,7 @@ function init (client, d3) {
}
, function callback (result) {
console.log(result);
- chart().drag.selectAll('.arrow').transition().duration(5000).style('opacity', 0).remove();
+ chart().drag.selectAll('.arrow').style('opacity', 0).remove();
}
);
} else {
@@ -887,7 +909,7 @@ function init (client, d3) {
}
, function callback (result) {
console.log(result);
- chart().drag.selectAll('.arrow').transition().duration(5000).style('opacity', 0).remove();
+ chart().drag.selectAll('.arrow').style('opacity', 0).remove();
}
);
} else {
@@ -903,7 +925,7 @@ function init (client, d3) {
.enter()
.append('g')
.attr('class', 'draggable-treatment')
- .attr('transform', 'translate(' + chart().xScale(new Date(treatment.mills)) + ', ' + chart().yScale(client.sbx.scaleEntry(treatment)) + ')')
+ .attr('transform', 'translate(' + chart().xScale(getOrAddDate(treatment)) + ', ' + chart().yScale(client.sbx.scaleEntry(treatment)) + ')')
.on('mouseover', treatmentTooltip)
.on('mouseout', hideTooltip);
if (client.editMode) {
@@ -985,7 +1007,7 @@ function init (client, d3) {
//when the tests are run window isn't available
var innerWidth = window && window.innerWidth || -1;
// don't render the treatment if it's not visible
- if (Math.abs(chart().xScale(new Date(treatment.mills))) > innerWidth) {
+ if (Math.abs(chart().xScale(getOrAddDate(treatment))) > innerWidth) {
return;
}
@@ -1009,8 +1031,9 @@ function init (client, d3) {
var basalareadata = [];
var tempbasalareadata = [];
var comboareadata = [];
- var from = chart().brush.extent()[0].getTime();
- var to = Math.max(chart().brush.extent()[1].getTime(), client.sbx.time) + client.forecastTime;
+ var selectedRange = chart().createAdjustedRange();
+ var from = selectedRange[0].getTime();
+ var to = selectedRange[1].getTime();
var date = from;
var lastbasal = 0;
@@ -1069,16 +1092,16 @@ function init (client, d3) {
chart().basals.selectAll('.tempbasalarea').remove().data(tempbasalareadata);
chart().basals.selectAll('.comboarea').remove().data(comboareadata);
- var valueline = d3.svg.line()
- .interpolate('step-after')
+ var valueline = d3.line()
.x(function(d) { return chart().xScaleBasals(d.d); })
- .y(function(d) { return chart().yScaleBasals(d.b); });
+ .y(function(d) { return chart().yScaleBasals(d.b); })
+ .curve(d3.curveStepAfter);
- var area = d3.svg.area()
- .interpolate('step-after')
+ var area = d3.area()
.x(function(d) { return chart().xScaleBasals(d.d); })
.y0(chart().yScaleBasals(0))
- .y1(function(d) { return chart().yScaleBasals(d.b); });
+ .y1(function(d) { return chart().yScaleBasals(d.b); })
+ .curve(d3.curveStepAfter);
var g = chart().basals.append('g');
@@ -1149,7 +1172,7 @@ function init (client, d3) {
}
function profileTooltip (d) {
- return '' + translate('Time') + ': ' + client.formatTime(new Date(d.mills)) + '
' +
+ return '' + translate('Time') + ': ' + client.formatTime(getOrAddDate(d)) + '
' +
(d.eventType ? '' + translate('Treatment type') + ': ' + translate(client.careportal.resolveEventName(d.eventType)) + '
' : '') +
(d.endprofile ? '' + translate('End of profile') + ': ' + d.endprofile + '
' : '') +
(d.profile ? '' + translate('Profile') + ': ' + d.profile + '
' : '') +
@@ -1159,8 +1182,9 @@ function init (client, d3) {
}
// calculate position of profile on left side
- var from = chart().brush.extent()[0].getTime();
- var to = chart().brush.extent()[1].getTime();
+ var selectedRange = chart().createAdjustedRange();
+ var from = selectedRange[0].getTime();
+ var to = selectedRange[1].getTime();
var mult = (to - from) / times.hours(24).msecs;
from += times.mins(20 * mult).msecs;
@@ -1199,8 +1223,7 @@ function init (client, d3) {
return ret;
};
- treatProfiles.transition().duration(0)
- .attr('transform', function(t) {
+ treatProfiles.attr('transform', function(t) {
// change text of record on left side
return 'rotate(-90,' + chart().xScale(t.mills) + ',' + chart().yScaleBasals(topOfText) + ') ' +
'translate(' + chart().xScale(t.mills) + ',' + chart().yScaleBasals(topOfText) + ')';
@@ -1220,7 +1243,7 @@ function init (client, d3) {
})
.text(generateText)
.on('mouseover', function(d) {
- client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9);
+ client.tooltip.style('opacity', .9);
client.tooltip.html(profileTooltip(d))
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY + 15) + 'px');
diff --git a/lib/data/ddata.js b/lib/data/ddata.js
index 1912ca4a6ae..0251ec17d99 100644
--- a/lib/data/ddata.js
+++ b/lib/data/ddata.js
@@ -32,39 +32,11 @@ function init () {
});
};
- ddata.splitRecent = function splitRecent (time, cutoff, max, treatmentsToo) {
- var result = {
- first: {}
- , rest: {}
- };
-
- function recent (item) {
- return item.mills >= time - cutoff;
- }
-
- function filterMax (item) {
- return item.mills >= time - max;
- }
-
- function partition (field, filter) {
- var data;
- if (filter) {
- data = ddata[field].filter(filterMax);
- } else {
- data = ddata[field];
- }
-
- var parts = _.partition(data, recent);
- result.first[field] = parts[0];
- result.rest[field] = parts[1];
- }
-
- partition('treatments', treatmentsToo ? filterMax : false);
-
- result.first.devicestatus = ddata.recentDeviceStatus(time);
-
- result.first.sgvs = ddata.sgvs.filter(filterMax);
- result.first.cals = ddata.cals;
+ ddata.dataWithRecentStatuses = function dataWithRecentStatuses() {
+ var results = {};
+ results.devicestatus = ddata.recentDeviceStatus(Date.now());
+ results.sgvs = ddata.sgvs;
+ results.cals = ddata.cals;
var profiles = _.cloneDeep(ddata.profiles);
if (profiles && profiles[0]) {
@@ -74,17 +46,14 @@ function init () {
}
})
}
- result.first.profiles = profiles;
-
- result.rest.mbgs = ddata.mbgs.filter(filterMax);
- result.rest.food = ddata.food;
- result.rest.activity = ddata.activity;
+ results.profiles = profiles;
+ results.mbgs = ddata.mbgs;
+ results.food = ddata.food;
+ results.treatments = ddata.treatments;
- console.log('results.first size', JSON.stringify(result.first).length, 'bytes');
- console.log('results.rest size', JSON.stringify(result.rest).length, 'bytes');
+ return results;
- return result;
- };
+ }
ddata.recentDeviceStatus = function recentDeviceStatus (time) {
diff --git a/lib/language.js b/lib/language.js
index bd84df3acd0..cb56d3f500f 100644
--- a/lib/language.js
+++ b/lib/language.js
@@ -14048,31 +14048,37 @@ function init() {
'Protein': {
fi: 'Proteiini'
, de: 'Protein'
+ ,hr: 'Proteini'
},
'Fat': {
fi: 'Rasva'
, de: 'Fett'
+ ,hr: 'Masti'
},
'Protein average': {
fi: 'Proteiini keskiarvo'
, de: 'Proteine Durchschnitt'
+ ,hr: 'Prosjek proteina'
},
'Fat average': {
fi: 'Rasva keskiarvo'
, de: 'Fett Durchschnitt'
-
+ ,hr: 'Prosjek masti'
},
'Total carbs': {
fi: 'Hiilihydraatit yhteensä'
, de: 'Kohlenhydrate gesamt'
+ ,hr: 'Ukupno ugh'
},
'Total protein': {
fi: 'Proteiini yhteensä'
, de: 'Protein gesamt'
+ ,hr: 'Ukupno proteini'
},
'Total fat': {
fi: 'Rasva yhteensä'
, de: 'Fett gesamt'
+ ,hr: 'Ukupno masti'
}
};
diff --git a/lib/plugins/cob.js b/lib/plugins/cob.js
index bc769197c2b..5020f2e6458 100644
--- a/lib/plugins/cob.js
+++ b/lib/plugins/cob.js
@@ -41,14 +41,16 @@ function init (ctx) {
}
var devicestatusCOB = cob.lastCOBDeviceStatus(devicestatus, time);
+ var result = devicestatusCOB;
- var treatmentCOB = (treatments !== undefined && treatments.length) ? cob.fromTreatments(treatments, devicestatus, profile, time, spec_profile) : {};
+ const TEN_MINUTES = 10 * 60 * 1000;
+
+ if (_.isEmpty(result) || _.isNil(result.cob) || (Date.now() - result.mills) > TEN_MINUTES) {
+
+ var treatmentCOB = (treatments !== undefined && treatments.length) ? cob.fromTreatments(treatments, devicestatus, profile, time, spec_profile) : {};
- var result = devicestatusCOB;
- if (_.isEmpty(result)) {
result = treatmentCOB;
result.source = 'Care Portal';
- } else if (treatmentCOB) {
result.treatmentCOB = treatmentCOB;
}
diff --git a/lib/plugins/pluginbase.js b/lib/plugins/pluginbase.js
index a6f0e77e455..87102d1831d 100644
--- a/lib/plugins/pluginbase.js
+++ b/lib/plugins/pluginbase.js
@@ -84,9 +84,9 @@ function init (majorPills, minorPills, statusPills, bgStatus, tooltip) {
}).join('
\n');
pill.mouseover(function pillMouseover (event) {
- tooltip.transition().duration(200).style('opacity', .9);
+ tooltip.style('opacity', .9);
- var windowWidth = $(tooltip).parent().parent().width();
+ var windowWidth = $(tooltip.node()).parent().parent().width();
var left = event.pageX + TOOLTIP_WIDTH < windowWidth ? event.pageX : windowWidth - TOOLTIP_WIDTH - 10;
tooltip.html(html)
.style('left', left + 'px')
@@ -94,9 +94,7 @@ function init (majorPills, minorPills, statusPills, bgStatus, tooltip) {
});
pill.mouseout(function pillMouseout ( ) {
- tooltip.transition()
- .duration(200)
- .style('opacity', 0);
+ tooltip.style('opacity', 0);
});
} else {
pill.off('mouseover');
diff --git a/lib/profilefunctions.js b/lib/profilefunctions.js
index 3c7d1337282..5826e6108b4 100644
--- a/lib/profilefunctions.js
+++ b/lib/profilefunctions.js
@@ -4,16 +4,20 @@ var _ = require('lodash');
var moment = require('moment-timezone');
var c = require('memory-cache');
var times = require('./times');
-var crypto = require('crypto');
-
-var cacheTTL = 600;
+var cacheTTL = 5000;
var prevBasalTreatment = null;
+var cache = new c.Cache();
function init (profileData) {
var profile = {};
- var cache = new c.Cache();
+
+ profile.clear = function clear() {
+ cache.clear();
+ profile.data = null;
+ prevBasalTreatment = null;
+ }
profile.loadData = function loadData (profileData) {
if (profileData && profileData.length) {
@@ -71,6 +75,15 @@ function init (profileData) {
profile.getValueByTime = function getValueByTime (time, valueType, spec_profile) {
if (!time) { time = Date.now(); }
+ //round to the minute for better caching
+ var minuteTime = Math.round(time / 60000) * 60000;
+ var cacheKey = (minuteTime + valueType + spec_profile);
+ var returnValue = cache.get(cacheKey);
+
+ if (returnValue) {
+ return returnValue;
+ }
+
// CircadianPercentageProfile support
var timeshift = 0;
var percentage = 100;
@@ -83,16 +96,6 @@ function init (profileData) {
var offset = timeshift % 24;
time = time + offset * times.hours(offset).msecs;
- //round to the minute for better caching
- var minuteTime = Math.round(time / 60000) * 60000;
-
- var cacheKey = (minuteTime + valueType + spec_profile + profile.profiletreatments_hash);
- var returnValue = cache.get(cacheKey);
-
- if (returnValue) {
- return returnValue;
- }
-
var valueContainer = profile.getCurrentProfile(time, spec_profile)[valueType];
// Assumes the timestamps are in UTC
@@ -139,10 +142,22 @@ function init (profileData) {
};
profile.getCurrentProfile = function getCurrentProfile (time, spec_profile) {
- time = time || new Date().getTime();
+
+ time = time || Date.now();
+ var minuteTime = Math.round(time / 60000) * 60000;
+ var cacheKey = ("profile" + minuteTime + spec_profile);
+ var returnValue = cache.get(cacheKey);
+
+ if (returnValue) {
+ return returnValue;
+ }
+
var data = profile.hasData() ? profile.data[0] : null;
var timeprofile = spec_profile || profile.activeProfileToTime(time);
- return data && data.store[timeprofile] ? data.store[timeprofile] : {};
+ returnValue = data && data.store[timeprofile] ? data.store[timeprofile] : {};
+
+ cache.put(cacheKey, returnValue, cacheTTL);
+ return returnValue;
};
profile.getUnits = function getUnits (spec_profile) {
@@ -204,9 +219,8 @@ function init (profileData) {
});
profile.combobolustreatments = combobolustreatments || [];
- profile.profiletreatments_hash = crypto.createHash('sha1').update(JSON.stringify(profile.profiletreatments)).digest('hex');
- profile.tempbasaltreatments_hash = crypto.createHash('sha1').update(JSON.stringify(profile.tempbasaltreatments)).digest('hex');
- profile.combobolustreatments_hash = crypto.createHash('sha1').update(JSON.stringify(profile.combobolustreatments)).digest('hex');
+
+ cache.clear();
};
profile.activeProfileToTime = function activeProfileToTime (time) {
@@ -223,9 +237,10 @@ function init (profileData) {
};
profile.activeProfileTreatmentToTime = function activeProfileTreatmentToTime (time) {
- var cacheKey = 'profile' + time + profile.profiletreatments_hash;
- //var returnValue = profile.timeValueCache[cacheKey];
- var returnValue;
+
+ var minuteTime = Math.round(time / 60000) * 60000;
+ var cacheKey = 'profileCache' + minuteTime;
+ var returnValue = cache.get(cacheKey);
if (returnValue) {
return returnValue;
@@ -312,7 +327,8 @@ function init (profileData) {
profile.getTempBasal = function getTempBasal (time, spec_profile) {
- var cacheKey = 'basal' + time + profile.tempbasaltreatments_hash + profile.combobolustreatments_hash + profile.profiletreatments_hash + spec_profile;
+ var minuteTime = Math.round(time / 60000) * 60000;
+ var cacheKey = 'basalCache' + minuteTime + spec_profile;
var returnValue = cache.get(cacheKey);
if (returnValue) {
diff --git a/lib/report_plugins/calibrations.js b/lib/report_plugins/calibrations.js
index 958dd06c906..baf16a47a27 100644
--- a/lib/report_plugins/calibrations.js
+++ b/lib/report_plugins/calibrations.js
@@ -146,20 +146,16 @@ calibrations.report = function report_calibrations (datastorage, sorteddaystosho
calibration_context = charts.append('g');
// define the parts of the axis that aren't dependent on width or height
- xScale2 = d3.scale.linear()
+ xScale2 = d3.scaleLinear()
.domain([0, maxBG]);
- yScale2 = d3.scale.linear()
+ yScale2 = d3.scaleLinear()
.domain([0, 400000]);
- var xAxis2 = d3.svg.axis()
- .scale(xScale2)
- .ticks(10)
- .orient('bottom');
+ var xAxis2 = d3.axisBottom(xScale2)
+ .ticks(10);
- var yAxis2 = d3.svg.axis()
- .scale(yScale2)
- .orient('left');
+ var yAxis2 = d3.axisLeft(yScale2);
// get current data range
var dataRange = [0, maxBG];
diff --git a/lib/report_plugins/daytoday.js b/lib/report_plugins/daytoday.js
index b4b1d65167b..4fde5658b19 100644
--- a/lib/report_plugins/daytoday.js
+++ b/lib/report_plugins/daytoday.js
@@ -82,8 +82,6 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
var report_plugins = Nightscout.report_plugins;
var scaledTreatmentBG = report_plugins.utils.scaledTreatmentBG;
- var TOOLTIP_TRANS_MS = 300;
-
var padding = { top: 15, right: 22, bottom: 30, left: 35 };
var tddSum = 0;
@@ -170,37 +168,33 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
context = charts.append('g');
// define the parts of the axis that aren't dependent on width or height
- xScale2 = d3.time.scale()
+ xScale2 = d3.scaleTime()
.domain(d3.extent(data.sgv, dateFn));
if (options.scale === report_plugins.consts.SCALE_LOG) {
- yScale2 = d3.scale.log()
+ yScale2 = d3.scaleLog()
.domain([client.utils.scaleMgdl(options.basal ? 30 : 36), client.utils.scaleMgdl(420)]);
} else {
- yScale2 = d3.scale.linear()
+ yScale2 = d3.scaleLinear()
.domain([client.utils.scaleMgdl(options.basal ? -40 : 36), client.utils.scaleMgdl(420)]);
}
// allow insulin to be negative (when plotting negative IOB)
- yInsulinScale = d3.scale.linear()
+ yInsulinScale = d3.scaleLinear()
.domain([-2 * options.maxInsulinValue, 2 * options.maxInsulinValue]);
- yCarbsScale = d3.scale.linear()
+ yCarbsScale = d3.scaleLinear()
.domain([0, options.maxCarbsValue * 1.25]);
- yScaleBasals = d3.scale.linear();
+ yScaleBasals = d3.scaleLinear();
- xAxis2 = d3.svg.axis()
- .scale(xScale2)
+ xAxis2 = d3.axisBottom(xScale2)
.tickFormat(timeTicks)
- .ticks(24)
- .orient('bottom');
+ .ticks(24);
- yAxis2 = d3.svg.axis()
- .scale(yScale2)
+ yAxis2 = d3.axisLeft(yScale2)
.tickFormat(d3.format('d'))
- .tickValues(tickValues)
- .orient('left');
+ .tickValues(tickValues);
// get current data range
var dataRange = d3.extent(data.sgv, dateFn);
@@ -294,7 +288,7 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
})
.on('mouseover', function(d) {
if (options.openAps && d.openaps) {
- client.tooltip.transition().duration(TOOLTIP_TRANS_MS).style('opacity', .9);
+ client.tooltip.style('opacity', .9);
var text = 'BG: ' + d.openaps.suggested.bg +
', ' + d.openaps.suggested.reason +
(d.openaps.suggested.mealAssist ? ' Meal Assist: ' + d.openaps.suggested.mealAssist : '');
@@ -602,13 +596,13 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
yScaleBasals.domain([basalMax, 0]);
- var valueline = d3.svg.line()
- .interpolate('step-after')
+ var valueline = d3.line()
+ .curve(d3.curveStepAfter)
.x(function(d) { return xScale2(d.d) + padding.left; })
.y(function(d) { return yScaleBasals(d.b) + padding.top; });
- var area = d3.svg.area()
- .interpolate('step-after')
+ var area = d3.area()
+ .curve(d3.curveStepAfter)
.x(function(d) { return xScale2(d.d) + padding.left; })
.y0(yScaleBasals(0) + padding.top)
.y1(function(d) { return yScaleBasals(d.b) + padding.top; });
@@ -931,9 +925,9 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
var height = 120;
var radius = Math.min(width, height) / 2;
- var color = d3.scale.ordinal().range([basalcolor, boluscolor]);
+ var color = d3.scaleOrdinal().range([basalcolor, boluscolor]);
- var labelArc = d3.svg.arc()
+ var labelArc = d3.arc()
.outerRadius(radius / 2)
.innerRadius(radius / 2);
@@ -945,10 +939,11 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
- var arc = d3.svg.arc()
+ var arc = d3.arc()
+ .innerRadius(0)
.outerRadius(radius);
- var pie = d3.layout.pie()
+ var pie = d3.pie()
.value(function(d) {
return d.count;
})
@@ -980,7 +975,7 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
// Carbs pie chart
- var carbscolor = d3.scale.ordinal().range(['red']);
+ var carbscolor = d3.scaleOrdinal().range(['red']);
var carbsData = [
{ label: translate('Carbs'), count: data.dailyCarbs }
@@ -994,10 +989,10 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
- var carbsarc = d3.svg.arc()
+ var carbsarc = d3.arc()
.outerRadius(radius * data.dailyCarbs / options.maxDailyCarbsValue);
- var carbspie = d3.layout.pie()
+ var carbspie = d3.pie()
.value(function(d) {
return d.count;
})
@@ -1066,8 +1061,6 @@ daytoday.report = function report_daytoday (datastorage, sorteddaystoshow, optio
}
function hideTooltip () {
- client.tooltip.transition()
- .duration(TOOLTIP_TRANS_MS)
- .style('opacity', 0);
+ client.tooltip.style('opacity', 0);
}
};
diff --git a/lib/report_plugins/weektoweek.js b/lib/report_plugins/weektoweek.js
index 3ff84a78639..8719af81542 100644
--- a/lib/report_plugins/weektoweek.js
+++ b/lib/report_plugins/weektoweek.js
@@ -79,8 +79,6 @@ weektoweek.report = function report_weektoweek(datastorage, sorteddaystoshow, op
var client = Nightscout.client;
var report_plugins = Nightscout.report_plugins;
- var TOOLTIP_TRANS_MS = 300;
-
var padding = { top: 15, right: 22, bottom: 30, left: 35 };
var weekstoshow = [ ];
@@ -196,28 +194,24 @@ weektoweek.report = function report_weektoweek(datastorage, sorteddaystoshow, op
context = charts.append('g');
// define the parts of the axis that aren't dependent on width or height
- xScale2 = d3.time.scale()
+ xScale2 = d3.scaleTime()
.domain(d3.extent(sgvData, dateFn));
if (options.weekscale === report_plugins.consts.SCALE_LOG) {
- yScale2 = d3.scale.log()
+ yScale2 = d3.scaleLog()
.domain([client.utils.scaleMgdl(36), client.utils.scaleMgdl(420)]);
} else {
- yScale2 = d3.scale.linear()
+ yScale2 = d3.scaleLinear()
.domain([client.utils.scaleMgdl(36), client.utils.scaleMgdl(420)]);
}
- xAxis2 = d3.svg.axis()
- .scale(xScale2)
+ xAxis2 = d3.axisBottom(xScale2)
.tickFormat(timeTicks)
- .ticks(24)
- .orient('bottom');
+ .ticks(24);
- yAxis2 = d3.svg.axis()
- .scale(yScale2)
+ yAxis2 = d3.axisLeft(yScale2)
.tickFormat(d3.format('d'))
- .tickValues(tickValues)
- .orient('left');
+ .tickValues(tickValues);
// get current data range
var dataRange = d3.extent(sgvData, dateFn);
@@ -326,8 +320,6 @@ weektoweek.report = function report_weektoweek(datastorage, sorteddaystoshow, op
}
function hideTooltip ( ) {
- client.tooltip.transition()
- .duration(TOOLTIP_TRANS_MS)
- .style('opacity', 0);
+ client.tooltip.style('opacity', 0);
}
};
diff --git a/lib/server/websocket.js b/lib/server/websocket.js
index 3946c129a5d..76276edffb2 100644
--- a/lib/server/websocket.js
+++ b/lib/server/websocket.js
@@ -447,20 +447,15 @@ function init (env, ctx, server) {
filterTreatments = true;
msecHistory = Math.min(new Date().getTime() - from, msecHistory);
}
- // send all data upon new connection
- if (lastData && lastData.splitRecent) {
- var split = lastData.splitRecent(Date.now(), times.hours(3).msecs, msecHistory, filterTreatments);
+
+ if (lastData && lastData.dataWithRecentStatuses) {
+ let data = lastData.dataWithRecentStatuses();
+
if (message.status) {
- split.first.status = status(split.first.profiles);
+ data.status = status(data.profiles);
}
- //send out first chunk
- socket.emit('dataUpdate', split.first);
-
- //then send out the rest
- setTimeout(function sendTheRest() {
- split.rest.delta = true;
- socket.emit('dataUpdate', split.rest);
- }, 500);
+
+ socket.emit('dataUpdate', data);
}
}
console.log(LOG_WS + 'Authetication ID: ', socket.client.id, ' client: ', clientType, ' history: ' + history);
diff --git a/lib/settings.js b/lib/settings.js
index 2d16abd0f2d..cd2aa3dc928 100644
--- a/lib/settings.js
+++ b/lib/settings.js
@@ -47,7 +47,6 @@ function init () {
, secureHstsHeaderIncludeSubdomains: false
, secureHstsHeaderPreload: false
, secureCsp: false
- , showClockClosebutton: true
, deNormalizeDates: false
};
@@ -69,7 +68,6 @@ function init () {
, insecureUseHttp: mapTruthy
, secureHstsHeader: mapTruthy
, secureCsp: mapTruthy
- , showClockClosebutton: mapTruthy
, deNormalizeDates: mapTruthy
};
diff --git a/lib/utils.js b/lib/utils.js
index fe1778f8120..083c4284846 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -39,7 +39,8 @@ function init(ctx) {
return '0';
}
var mult = Math.pow(10,digits);
- var fixed = Math.sign(value) * Math.round(Math.abs(value)*mult) / mult
+ var fixed = Math.sign(value) * Math.round(Math.abs(value)*mult) / mult;
+ if (isNaN(fixed)) return '0';
return String(fixed);
};
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 46108450a9c..554719ebdd2 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -3185,9 +3185,268 @@
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
},
"d3": {
- "version": "3.5.17",
- "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
- "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g="
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-5.12.0.tgz",
+ "integrity": "sha512-flYVMoVuhPFHd9zVCe2BxIszUWqBcd5fvQGMNRmSiBrgdnh6Vlruh60RJQTouAK9xPbOB0plxMvBm4MoyODXNg==",
+ "requires": {
+ "d3-array": "1",
+ "d3-axis": "1",
+ "d3-brush": "1",
+ "d3-chord": "1",
+ "d3-collection": "1",
+ "d3-color": "1",
+ "d3-contour": "1",
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-dsv": "1",
+ "d3-ease": "1",
+ "d3-fetch": "1",
+ "d3-force": "1",
+ "d3-format": "1",
+ "d3-geo": "1",
+ "d3-hierarchy": "1",
+ "d3-interpolate": "1",
+ "d3-path": "1",
+ "d3-polygon": "1",
+ "d3-quadtree": "1",
+ "d3-random": "1",
+ "d3-scale": "2",
+ "d3-scale-chromatic": "1",
+ "d3-selection": "1",
+ "d3-shape": "1",
+ "d3-time": "1",
+ "d3-time-format": "2",
+ "d3-timer": "1",
+ "d3-transition": "1",
+ "d3-voronoi": "1",
+ "d3-zoom": "1"
+ }
+ },
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ },
+ "d3-axis": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
+ "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
+ },
+ "d3-brush": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.3.tgz",
+ "integrity": "sha512-v8bbYyCFKjyCzFk/tdWqXwDykY8YWqhXYjcYxfILIit085VZOpj4XJKOMccTsvWxgzSLMJQg5SiqHjslsipEDg==",
+ "requires": {
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "1",
+ "d3-transition": "1"
+ }
+ },
+ "d3-chord": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
+ "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
+ "requires": {
+ "d3-array": "1",
+ "d3-path": "1"
+ }
+ },
+ "d3-collection": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
+ "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
+ },
+ "d3-color": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.0.tgz",
+ "integrity": "sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg=="
+ },
+ "d3-contour": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
+ "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
+ "requires": {
+ "d3-array": "^1.1.1"
+ }
+ },
+ "d3-dispatch": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz",
+ "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g=="
+ },
+ "d3-drag": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.4.tgz",
+ "integrity": "sha512-ICPurDETFAelF1CTHdIyiUM4PsyZLaM+7oIBhmyP+cuVjze5vDZ8V//LdOFjg0jGnFIZD/Sfmk0r95PSiu78rw==",
+ "requires": {
+ "d3-dispatch": "1",
+ "d3-selection": "1"
+ }
+ },
+ "d3-dsv": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz",
+ "integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==",
+ "requires": {
+ "commander": "2",
+ "iconv-lite": "0.4",
+ "rw": "1"
+ }
+ },
+ "d3-ease": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz",
+ "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ=="
+ },
+ "d3-fetch": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz",
+ "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==",
+ "requires": {
+ "d3-dsv": "1"
+ }
+ },
+ "d3-force": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz",
+ "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==",
+ "requires": {
+ "d3-collection": "1",
+ "d3-dispatch": "1",
+ "d3-quadtree": "1",
+ "d3-timer": "1"
+ }
+ },
+ "d3-format": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.1.tgz",
+ "integrity": "sha512-TUswGe6hfguUX1CtKxyG2nymO+1lyThbkS1ifLX0Sr+dOQtAD5gkrffpHnx+yHNKUZ0Bmg5T4AjUQwugPDrm0g=="
+ },
+ "d3-geo": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.6.tgz",
+ "integrity": "sha512-z0J8InXR9e9wcgNtmVnPTj0TU8nhYT6lD/ak9may2PdKqXIeHUr8UbFLoCtrPYNsjv6YaLvSDQVl578k6nm7GA==",
+ "requires": {
+ "d3-array": "1"
+ }
+ },
+ "d3-hierarchy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz",
+ "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w=="
+ },
+ "d3-interpolate": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz",
+ "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==",
+ "requires": {
+ "d3-color": "1"
+ }
+ },
+ "d3-path": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz",
+ "integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg=="
+ },
+ "d3-polygon": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz",
+ "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w=="
+ },
+ "d3-quadtree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz",
+ "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA=="
+ },
+ "d3-random": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
+ "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
+ },
+ "d3-scale": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
+ "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
+ "requires": {
+ "d3-array": "^1.2.0",
+ "d3-collection": "1",
+ "d3-format": "1",
+ "d3-interpolate": "1",
+ "d3-time": "1",
+ "d3-time-format": "2"
+ }
+ },
+ "d3-scale-chromatic": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz",
+ "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==",
+ "requires": {
+ "d3-color": "1",
+ "d3-interpolate": "1"
+ }
+ },
+ "d3-selection": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz",
+ "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg=="
+ },
+ "d3-shape": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz",
+ "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==",
+ "requires": {
+ "d3-path": "1"
+ }
+ },
+ "d3-time": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
+ "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
+ },
+ "d3-time-format": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz",
+ "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==",
+ "requires": {
+ "d3-time": "1"
+ }
+ },
+ "d3-timer": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz",
+ "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg=="
+ },
+ "d3-transition": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz",
+ "integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==",
+ "requires": {
+ "d3-color": "1",
+ "d3-dispatch": "1",
+ "d3-ease": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "^1.1.0",
+ "d3-timer": "1"
+ }
+ },
+ "d3-voronoi": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
+ "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
+ },
+ "d3-zoom": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz",
+ "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==",
+ "requires": {
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "1",
+ "d3-transition": "1"
+ }
},
"dashdash": {
"version": "1.14.1",
@@ -9509,6 +9768,11 @@
"aproba": "^1.1.1"
}
},
+ "rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
+ },
"rxjs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz",
diff --git a/package.json b/package.json
index 8b4fdb16723..bf3c4c7f802 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,7 @@
"compression": "^1.7.4",
"css-loader": "^1.0.1",
"cssmin": "^0.4.3",
- "d3": "^3.5.17",
+ "d3": "^5.12.0",
"ejs": "^2.6.2",
"errorhandler": "^1.5.1",
"event-stream": "3.3.4",
diff --git a/tests/admintools.test.js b/tests/admintools.test.js
index bbede54156a..9f867a543c3 100644
--- a/tests/admintools.test.js
+++ b/tests/admintools.test.js
@@ -153,7 +153,9 @@ describe('admintools', function ( ) {
var d3 = require('d3');
//disable all d3 transitions so most of the other code can run with jsdom
- d3.timer = function mockTimer() { };
+ //d3.timer = function mockTimer() { };
+ let timer = d3.timer(function mockTimer() { });
+ timer.stop();
var cookieStorageType = self.localStorage._type
diff --git a/tests/careportal.test.js b/tests/careportal.test.js
index 782bc4fa566..36f48d3a5a4 100644
--- a/tests/careportal.test.js
+++ b/tests/careportal.test.js
@@ -49,7 +49,7 @@ describe('client', function ( ) {
client.init();
- client.dataUpdate(nowData);
+ client.dataUpdate(nowData, true);
client.careportal.prepareEvents();
diff --git a/tests/client.renderer.test.js b/tests/client.renderer.test.js
index 569691cd717..ca81e7d99e8 100644
--- a/tests/client.renderer.test.js
+++ b/tests/client.renderer.test.js
@@ -54,6 +54,10 @@ describe('renderer', () => {
}
}
, futureOpacity: (millsDifference) => { return 1; }
+ , createAdjustedRange: () => { return [
+ { getTime: () => { return extent.times[0]}},
+ { getTime: () => { return extent.times[1]}}
+ ] }
}
, latestSGV: { mills: 120 }
};
diff --git a/tests/ddata.test.js b/tests/ddata.test.js
index f3757348c53..ceb163b7c4f 100644
--- a/tests/ddata.test.js
+++ b/tests/ddata.test.js
@@ -41,19 +41,6 @@ describe('ddata', function ( ) {
done( );
});
- it('has #split( )', function (done) {
- var date = new Date( );
- var time = date.getTime( );
- var cutoff = 1000 * 60 * 5;
- var max = 1000 * 60 * 60 * 24 * 2;
- var pieces = ctx.ddata.splitRecent(time, cutoff, max);
- should.exist(pieces);
- should.exist(pieces.first);
- should.exist(pieces.rest);
-
- done( );
- });
-
// TODO: ensure partition function gets called via:
// Properties
// * ddata.devicestatus
diff --git a/tests/profile.test.js b/tests/profile.test.js
index 8171f459e3d..373f0479d9d 100644
--- a/tests/profile.test.js
+++ b/tests/profile.test.js
@@ -5,6 +5,10 @@ describe('Profile', function ( ) {
var profile_empty = require('../lib/profilefunctions')();
+ beforeEach(function() {
+ profile_empty.clear();
+ });
+
it('should say it does not have data before it has data', function() {
var hasData = profile_empty.hasData();
hasData.should.equal(false);
@@ -30,8 +34,6 @@ describe('Profile', function ( ) {
};
var profile = require('../lib/profilefunctions')([profileData]);
-// console.log(profile);
-
var now = Date.now();
it('should know what the DIA is with old style profiles', function() {
diff --git a/tests/reports.test.js b/tests/reports.test.js
index 3c79e3b096a..7d5a0eb7009 100644
--- a/tests/reports.test.js
+++ b/tests/reports.test.js
@@ -261,10 +261,12 @@ describe('reports', function ( ) {
var result = $('body').html();
//var filesys = require('fs');
//var logfile = filesys.createWriteStream('out.txt', { flags: 'a'} )
- //logfile.write($('body').html());
-
+ //logfile.write(result);
+ //console.log('RESULT', result);
+
result.indexOf('Milk now').should.be.greaterThan(-1); // daytoday
- result.indexOf('50 g (1.67U)').should.be.greaterThan(-1); // daytoday
+ result.indexOf('50 g').should.be.greaterThan(-1); // daytoday
+ result.indexOf('TDD average: 2.9U').should.be.greaterThan(-1); // daytoday
result.indexOf('