From b310b6025037f461644d34c82ec0627e7e7a51d1 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sat, 17 Oct 2015 00:43:54 -0700 Subject: [PATCH 001/752] inital work on a timeago plugin --- lib/plugins/index.js | 1 + lib/plugins/timeago.js | 136 +++++++++++++++++++++++++++++++++++++++++ lib/times.js | 18 +++++- tests/timeago.test.js | 119 ++++++++++++++++++++++++++++++++++++ 4 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 lib/plugins/timeago.js create mode 100644 tests/timeago.test.js diff --git a/lib/plugins/index.js b/lib/plugins/index.js index 23c6eaa823a..b41fcecca3e 100644 --- a/lib/plugins/index.js +++ b/lib/plugins/index.js @@ -43,6 +43,7 @@ function init() { , require('./boluswizardpreview')() , require('./cannulaage')() , require('./treatmentnotify')() + , require('./timeago')() ]; plugins.registerServerDefaults = function registerServerDefaults() { diff --git a/lib/plugins/timeago.js b/lib/plugins/timeago.js new file mode 100644 index 00000000000..f095aa17904 --- /dev/null +++ b/lib/plugins/timeago.js @@ -0,0 +1,136 @@ +'use strict'; + +var _ = require('lodash'); +var levels = require('../levels'); +var times = require('../times'); + +function init ( ) { + + var timeago = { + name: 'timeago' + , label: 'Timeago' + , pluginType: 'notification' + }; + + timeago.checkNotifications = function checkNotifications (sbx) { + var lastSGVEntry = sbx.lastSGVEntry() + , warn = 'off' !== sbx.extendedSettings.warn + , warnMins = sbx.extendedSettings.warnMins || 15 + , urgent = 'off' !== sbx.extendedSettings.urgent + , urgentMins = sbx.extendedSettings.urgentMins || 30 + ; + + if (!lastSGVEntry || lastSGVEntry.mills >= sbx.time) { + return; + } + + function isStale (mins) { + return sbx.time - lastSGVEntry.mills > times.mins(mins).msecs + } + + function buildMessage(agoDisplay) { + var lines = sbx.prepareDefaultLines(); + lines.unshift('Last received: ' + [agoDisplay.value, agoDisplay.label].join(' ')); + return lines.join('\n'); + } + + function sendAlarm (opts) { + var agoDisplay = timeago.calcDisplay(lastSGVEntry, sbx.time); + + sbx.notifications.requestNotify({ + level: opts.level + , title: 'Stale data, check rig?' + , message: buildMessage(agoDisplay) + , eventName: timeago.name + , plugin: timeago + , pushoverSound: opts.pushoverSound + , debug: agoDisplay + }); + } + + if (urgent && isStale(urgentMins)) { + sendAlarm({ + level: levels.URGENT + , pushoverSound: 'siren' + }) + } else if (warn && isStale(warnMins)) { + sendAlarm({ + level: levels.WARN + , pushoverSound: 'siren' + }) + } + + }; + + timeago.isMissing = function isMissing (opts) { + if (!opts || !opts.entry || isNaN(opts.entry.mills) || isNaN(opts.time) || isNaN(opts.timeSince)) { + return { + label: 'time ago' + }; + } + }; + + timeago.inTheFuture = function inTheFuture (opts) { + if (opts.entry.mills - times.mins(5).msecs > opts.time) { + return { + label: 'in the future' + }; + } + }; + + timeago.almostInTheFuture = function almostInTheFuture (opts) { + if (opts.entry.mills > opts.time) { + return { + value: 1 + , label: 'min ago' + }; + } + }; + + timeago.isLessThan = function isLessThan (limit, divisor, label) { + return function checkIsLessThan (opts) { + if (opts.timeSince < limit) { + return { + value: Math.max(1, Math.round(Math.abs(opts.timeSince / divisor))) + , label: label + }; + } + } + }; + + timeago.resolvers = [ + timeago.isMissing + , timeago.inTheFuture + , timeago.almostInTheFuture + , timeago.isLessThan(times.mins(2).msecs, times.min().msecs, 'min ago') + , timeago.isLessThan(times.hour().msecs, times.min().msecs, 'mins ago') + , timeago.isLessThan(times.hours(2).msecs, times.hour().msecs, 'hour ago') + , timeago.isLessThan(times.day().msecs, times.hour().msecs, 'hours ago') + , timeago.isLessThan(times.days(2).msecs, times.day().msecs, 'day ago') + , timeago.isLessThan(times.week().msecs, times.day().msecs, 'days ago') + , function ( ) { return { label: 'long ago' } } + ]; + + timeago.calcDisplay = function calcDisplay (entry, time) { + var opts = { + time: time + , entry: entry + }; + + if (time && entry && entry.mills) { + opts.timeSince = time - entry.mills; + } + + for (var i = 0; i < timeago.resolvers.length; i++) { + var value = timeago.resolvers[i](opts); + if (value) { + return value; + } + } + }; + + return timeago; + +} + +module.exports = init; \ No newline at end of file diff --git a/lib/times.js b/lib/times.js index 4acf8fa0d1a..0e48e6596e8 100644 --- a/lib/times.js +++ b/lib/times.js @@ -3,7 +3,17 @@ var cache = { }; var factories = { - hours: function hours(value) { + weeks: function weeks(value) { + return { + mins: value * 7 * 24 * 60, secs: value * 7 * 24 * 60 * 60, msecs: value * 7 * 24 * 60 * 60 * 1000 + }; + } + , days: function days(value) { + return { + mins: value * 24 * 60, secs: value * 24 * 60 * 60, msecs: value * 24 * 60 * 60 * 1000 + }; + } + , hours: function hours(value) { return { mins: value * 60, secs: value * 60 * 60, msecs: value * 60 * 60 * 1000 }; @@ -33,7 +43,11 @@ function getOrCreate (types) { } var times = { - hour: function ( ) { return getOrCreate('hours')(1); } + week: function ( ) { return getOrCreate('weeks')(1); } + , weeks: function (value) { return getOrCreate('weeks')(value); } + , day: function ( ) { return getOrCreate('days')(1); } + , days: function (value) { return getOrCreate('days')(value); } + , hour: function ( ) { return getOrCreate('hours')(1); } , hours: function (value) { return getOrCreate('hours')(value); } , min: function ( ) { return getOrCreate('mins')(1); } , mins: function (value) { return getOrCreate('mins')(value); } diff --git a/tests/timeago.test.js b/tests/timeago.test.js new file mode 100644 index 00000000000..91006acb32f --- /dev/null +++ b/tests/timeago.test.js @@ -0,0 +1,119 @@ +var should = require('should'); +var levels = require('../lib/levels'); +var times = require('../lib/times'); + +describe('timeago', function ( ) { + + var timeago = require('../lib/plugins/timeago')(); + var delta = require('../lib/plugins/delta')(); + + var env = require('../env')(); + var ctx = {}; + ctx.data = require('../lib/data')(env, ctx); + ctx.notifications = require('../lib/notifications')(env, ctx); + + var now = Date.now(); + + + it('Not trigger an alarm when data is current', function (done) { + ctx.notifications.initRequests(); + ctx.data.sgvs = [{mills: now, mgdl: 100, type: 'sgv'}]; + + var sbx = require('../lib/sandbox')().serverInit(env, ctx); + timeago.checkNotifications(sbx); + should.not.exist(ctx.notifications.findHighestAlarm()); + + done(); + }); + + it('Not trigger an alarm with future data', function (done) { + ctx.notifications.initRequests(); + ctx.data.sgvs = [{mills: now + times.mins(15).msecs, mgdl: 100, type: 'sgv'}]; + + var sbx = require('../lib/sandbox')().serverInit(env, ctx); + timeago.checkNotifications(sbx); + should.not.exist(ctx.notifications.findHighestAlarm()); + + done(); + }); + + it('should trigger a warning when data older than 15m', function (done) { + ctx.notifications.initRequests(); + ctx.data.sgvs = [{mills: now - times.mins(15).msecs, mgdl: 100, type: 'sgv'}]; + + var sbx = require('../lib/sandbox')().serverInit(env, ctx); + timeago.checkNotifications(sbx); + var highest = ctx.notifications.findHighestAlarm(); + highest.level.should.equal(levels.WARN); + highest.message.should.equal('Last received: 15 mins ago\nBG Now: 100 mg/dl'); + done(); + }); + + it('should trigger an urgent alarm when data older than 30m', function (done) { + ctx.notifications.initRequests(); + ctx.data.sgvs = [{mills: now - times.mins(30).msecs, mgdl: 100, type: 'sgv'}]; + + var sbx = require('../lib/sandbox')().serverInit(env, ctx); + timeago.checkNotifications(sbx); + var highest = ctx.notifications.findHighestAlarm(); + highest.level.should.equal(levels.URGENT); + highest.message.should.equal('Last received: 30 mins ago\nBG Now: 100 mg/dl'); + done(); + }); + + it('calc timeago displays', function() { + + should.deepEqual( + timeago.calcDisplay({ mills: now + times.mins(15).msecs }, now) + , {label: 'in the future'} + ); + + //TODO: current behavior, we can do better + //just a little in the future, pretend it's ok + should.deepEqual( + timeago.calcDisplay({ mills: now + times.mins(4).msecs }, now) + , {value: 1, label: 'min ago'} + ); + + should.deepEqual( + timeago.calcDisplay(null, now) + , {label: 'time ago'} + ); + + should.deepEqual( + timeago.calcDisplay({ mills: now }, now) + , {value: 1, label: 'min ago'} + ); + + should.deepEqual( + timeago.calcDisplay({ mills: now - 1 }, now) + , {value: 1, label: 'min ago'} + ); + + should.deepEqual( + timeago.calcDisplay({ mills: now - times.sec(30).msecs }, now) + , {value: 1, label: 'min ago'} + ); + + should.deepEqual( + timeago.calcDisplay({ mills: now - times.mins(30).msecs }, now) + , {value: 30, label: 'mins ago'} + ); + + should.deepEqual( + timeago.calcDisplay({ mills: now - times.hours(5).msecs }, now) + , {value: 5, label: 'hours ago'} + ); + + should.deepEqual( + timeago.calcDisplay({ mills: now - times.days(5).msecs }, now) + , {value: 5, label: 'days ago'} + ); + + should.deepEqual( + timeago.calcDisplay({ mills: now - times.days(10).msecs }, now) + , {label: 'long ago'} + ); + }) + +}); \ No newline at end of file From 3c70b7fd173a164321621ea47799e74e1e648996 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sat, 17 Oct 2015 01:15:14 -0700 Subject: [PATCH 002/752] get rid of extra Math.abs --- lib/plugins/timeago.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/timeago.js b/lib/plugins/timeago.js index f095aa17904..3b806168441 100644 --- a/lib/plugins/timeago.js +++ b/lib/plugins/timeago.js @@ -91,7 +91,7 @@ function init ( ) { return function checkIsLessThan (opts) { if (opts.timeSince < limit) { return { - value: Math.max(1, Math.round(Math.abs(opts.timeSince / divisor))) + value: Math.max(1, Math.round(opts.timeSince / divisor)) , label: label }; } From ded91d6f873f31c8036d43f17221b3a50953b456 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sat, 17 Oct 2015 01:27:06 -0700 Subject: [PATCH 003/752] fix some codacy issues --- lib/plugins/timeago.js | 9 ++++----- tests/timeago.test.js | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/plugins/timeago.js b/lib/plugins/timeago.js index 3b806168441..2930c899964 100644 --- a/lib/plugins/timeago.js +++ b/lib/plugins/timeago.js @@ -1,6 +1,5 @@ 'use strict'; -var _ = require('lodash'); var levels = require('../levels'); var times = require('../times'); @@ -25,7 +24,7 @@ function init ( ) { } function isStale (mins) { - return sbx.time - lastSGVEntry.mills > times.mins(mins).msecs + return sbx.time - lastSGVEntry.mills > times.mins(mins).msecs; } function buildMessage(agoDisplay) { @@ -52,12 +51,12 @@ function init ( ) { sendAlarm({ level: levels.URGENT , pushoverSound: 'siren' - }) + }); } else if (warn && isStale(warnMins)) { sendAlarm({ level: levels.WARN - , pushoverSound: 'siren' - }) + , pushoverSound: 'echo' + }); } }; diff --git a/tests/timeago.test.js b/tests/timeago.test.js index 91006acb32f..85fbc4c55f1 100644 --- a/tests/timeago.test.js +++ b/tests/timeago.test.js @@ -5,7 +5,6 @@ var times = require('../lib/times'); describe('timeago', function ( ) { var timeago = require('../lib/plugins/timeago')(); - var delta = require('../lib/plugins/delta')(); var env = require('../env')(); var ctx = {}; @@ -114,6 +113,6 @@ describe('timeago', function ( ) { timeago.calcDisplay({ mills: now - times.days(10).msecs }, now) , {label: 'long ago'} ); - }) + }); }); \ No newline at end of file From 4699bbe9cee9b7c41c7cae1fde299a3e389b96f1 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Sat, 17 Oct 2015 14:41:53 -0700 Subject: [PATCH 004/752] fix tests, maybe? --- tests/timeago.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/timeago.test.js b/tests/timeago.test.js index 85fbc4c55f1..0f509cb87db 100644 --- a/tests/timeago.test.js +++ b/tests/timeago.test.js @@ -38,25 +38,25 @@ describe('timeago', function ( ) { it('should trigger a warning when data older than 15m', function (done) { ctx.notifications.initRequests(); - ctx.data.sgvs = [{mills: now - times.mins(15).msecs, mgdl: 100, type: 'sgv'}]; + ctx.data.sgvs = [{mills: now - times.mins(15.8).msecs, mgdl: 100, type: 'sgv'}]; var sbx = require('../lib/sandbox')().serverInit(env, ctx); timeago.checkNotifications(sbx); var highest = ctx.notifications.findHighestAlarm(); highest.level.should.equal(levels.WARN); - highest.message.should.equal('Last received: 15 mins ago\nBG Now: 100 mg/dl'); + highest.message.should.equal('Last received: 16 mins ago\nBG Now: 100 mg/dl'); done(); }); it('should trigger an urgent alarm when data older than 30m', function (done) { ctx.notifications.initRequests(); - ctx.data.sgvs = [{mills: now - times.mins(30).msecs, mgdl: 100, type: 'sgv'}]; + ctx.data.sgvs = [{mills: now - times.mins(30.8).msecs, mgdl: 100, type: 'sgv'}]; var sbx = require('../lib/sandbox')().serverInit(env, ctx); timeago.checkNotifications(sbx); var highest = ctx.notifications.findHighestAlarm(); highest.level.should.equal(levels.URGENT); - highest.message.should.equal('Last received: 30 mins ago\nBG Now: 100 mg/dl'); + highest.message.should.equal('Last received: 31 mins ago\nBG Now: 100 mg/dl'); done(); }); From 2d52b2ab8c8b8e95af8ef4b0a0d43b182f11bfe7 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Wed, 21 Oct 2015 00:15:02 -0700 Subject: [PATCH 005/752] use timeago plugin for the pill; remove the olg and ugly utils.timeAgo; still lots left --- lib/client/index.js | 64 +++++++++++++----------------------------- lib/plugins/index.js | 3 +- lib/plugins/timeago.js | 58 +++++++++++++++++++++++++++++--------- lib/settings.js | 2 +- lib/utils.js | 40 -------------------------- static/css/main.css | 9 +++--- static/index.html | 1 - tests/utils.test.js | 7 ----- 8 files changed, 73 insertions(+), 111 deletions(-) diff --git a/lib/client/index.js b/lib/client/index.js index 3121f92a62d..5b2247c30f0 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -53,6 +53,7 @@ client.init = function init(serverSettings, plugins) { client.sbx = sandbox.clientInit(client.settings, client.now); client.rawbg = plugins('rawbg'); client.delta = plugins('delta'); + client.timeago = plugins('timeago'); client.direction = plugins('direction'); client.errorcodes = plugins('errorcodes'); @@ -74,8 +75,6 @@ client.init = function init(serverSettings, plugins) { client.renderer = require('./renderer')(client, d3, $); client.careportal = require('./careportal')(client, $); - var timeAgo = client.utils.timeAgo; - var container = $('.container') , bgStatus = $('.bgStatus') , currentBG = $('.bgStatus .currentBG') @@ -131,10 +130,10 @@ client.init = function init(serverSettings, plugins) { var title = ''; - var time = client.latestSGV ? client.latestSGV.mills : (prevSGV ? prevSGV.mills : -1) - , ago = timeAgo(time); + var status = client.timeago.checkStatus(client.sbx); - if (ago && ago.status !== 'current') { + if (status !== 'current') { + var ago = client.timeago.calcDisplay(client.sbx.lastSGVEntry(), client.sbx.time); title = s(ago.value) + s(ago.label, ' - ') + title; } else if (client.latestSGV) { var currentMgdl = client.latestSGV.mgdl; @@ -253,8 +252,7 @@ client.init = function init(serverSettings, plugins) { function updateCurrentSGV (entry) { var value = entry.mgdl - , ago = timeAgo(entry.mills) - , isCurrent = ago.status === 'current'; + , isCurrent = 'current' === client.timeago.checkStatus(client.sbx); if (value === 9) { currentBG.text(''); @@ -499,66 +497,44 @@ client.init = function init(serverSettings, plugins) { return alarmType === 'warnTimeAgo' || alarmType === 'urgentTimeAgo'; } - function isStale (ago) { - return client.settings.alarmTimeagoWarn && ago.status === 'warn' - || client.settings.alarmTimeagoUrgent && ago.status === 'urgent'; + function isStale (status) { + return client.settings.alarmTimeagoWarn && status === 'warn' + || client.settings.alarmTimeagoUrgent && status === 'urgent'; } function notAcked (alarm) { return Date.now() >= (alarm.lastAckTime || 0) + (alarm.silenceTime || 0); } - function checkTimeAgoAlarm(ago) { - var level = ago.status - , alarm = getClientAlarm(level + 'TimeAgo'); + function checkTimeAgoAlarm (status) { + var alarm = getClientAlarm(status + 'TimeAgo'); - if (isStale(ago) && notAcked(alarm)) { + if (isStale(status) && notAcked(alarm)) { currentAlarmType = alarm.type; console.info('generating timeAgoAlarm', alarm.type); container.addClass('alarming-timeago'); + var display = client.timeago.calcDisplay(client.sbx.lastSGVEntry(), client.sbx.time); var notify = { - title: 'Last data received ' + [ago.value, ago.label].join(' ') - , level: level === 'urgent' ? 2 : 1 + title: 'Last data received ' + [display.value, display.label].join(' ') + , level: status === 'urgent' ? 2 : 1 }; - var sound = level === 'warn' ? alarmSound : urgentAlarmSound; + var sound = status === 'warn' ? alarmSound : urgentAlarmSound; generateAlarm(sound, notify); } - container.toggleClass('alarming-timeago', ago.status !== 'current'); + container.toggleClass('alarming-timeago', status !== 'current'); - if (alarmingNow() && ago.status === 'current' && isTimeAgoAlarmType(currentAlarmType)) { + if (alarmingNow() && status === 'current' && isTimeAgoAlarmType(currentAlarmType)) { stopAlarm(true, times.min().msecs); } } function updateTimeAgo() { - var lastEntry = $('#lastEntry') - , time = client.latestSGV ? client.latestSGV.mills : -1 - , ago = timeAgo(time) - , retroMode = inRetroMode(); - - function updateTimeAgoPill() { - if (retroMode || !ago.value) { - lastEntry.find('em').hide(); - } else { - lastEntry.find('em').show().text(ago.value); - } - if (retroMode || ago.label) { - lastEntry.find('label').show().text(retroMode ? 'RETRO' : ago.label); - } else { - lastEntry.find('label').hide(); - } - } - - lastEntry.removeClass('current warn urgent'); - lastEntry.addClass(ago.status); - - if (ago.status !== 'current') { + var status = client.timeago.checkStatus(client.sbx) + if (status !== 'current') { updateTitle(); } - checkTimeAgoAlarm(ago); - - updateTimeAgoPill(); + checkTimeAgoAlarm(status); } function updateTimeAgoSoon() { diff --git a/lib/plugins/index.js b/lib/plugins/index.js index b41fcecca3e..5b1a4396ec2 100644 --- a/lib/plugins/index.js +++ b/lib/plugins/index.js @@ -28,6 +28,7 @@ function init() { , require('./boluswizardpreview')() , require('./cannulaage')() , require('./basalprofile')() + , require('./timeago')() , require('./upbat')() ]; @@ -87,7 +88,7 @@ function init() { }; //these plugins are either always on or have custom settings - plugins.specialPlugins = 'ar2 delta direction upbat rawbg errorcodes'; + plugins.specialPlugins = 'ar2 delta direction timeago upbat rawbg errorcodes'; plugins.shownPlugins = function(sbx) { return _.filter(enabledPlugins, function filterPlugins(plugin) { diff --git a/lib/plugins/timeago.js b/lib/plugins/timeago.js index 2930c899964..25b69478573 100644 --- a/lib/plugins/timeago.js +++ b/lib/plugins/timeago.js @@ -8,25 +8,17 @@ function init ( ) { var timeago = { name: 'timeago' , label: 'Timeago' - , pluginType: 'notification' + , pluginType: 'pill-status' + , pillFlip: true }; timeago.checkNotifications = function checkNotifications (sbx) { - var lastSGVEntry = sbx.lastSGVEntry() - , warn = 'off' !== sbx.extendedSettings.warn - , warnMins = sbx.extendedSettings.warnMins || 15 - , urgent = 'off' !== sbx.extendedSettings.urgent - , urgentMins = sbx.extendedSettings.urgentMins || 30 - ; + var lastSGVEntry = sbx.lastSGVEntry(); if (!lastSGVEntry || lastSGVEntry.mills >= sbx.time) { return; } - function isStale (mins) { - return sbx.time - lastSGVEntry.mills > times.mins(mins).msecs; - } - function buildMessage(agoDisplay) { var lines = sbx.prepareDefaultLines(); lines.unshift('Last received: ' + [agoDisplay.value, agoDisplay.label].join(' ')); @@ -47,12 +39,13 @@ function init ( ) { }); } - if (urgent && isStale(urgentMins)) { + var status = timeago.checkStatus(sbx); + if (status === 'urgent') { sendAlarm({ level: levels.URGENT , pushoverSound: 'siren' }); - } else if (warn && isStale(warnMins)) { + } else if (status === 'warn') { sendAlarm({ level: levels.WARN , pushoverSound: 'echo' @@ -61,6 +54,33 @@ function init ( ) { }; + timeago.checkStatus = function checkStatus (sbx) { + + var lastSGVEntry = sbx.lastSGVEntry() + , warn = 'off' !== sbx.settings.alarmTimeagoWarn + , warnMins = sbx.settings.alarmTimeagoWarnMins || 15 + , urgent = 'off' !== sbx.settings.alarmTimeagoUrgent + , urgentMins = sbx.settings.alarmTimeagoUrgentMins || 30 + ; + + function isStale(mins) { + return sbx.time - lastSGVEntry.mills > times.mins(mins).msecs; + } + + var status = 'current'; + + if (!lastSGVEntry) { + //assume current + } else if (urgent && isStale(urgentMins)) { + status = 'urgent'; + } else if (warn && isStale(warnMins)) { + status = 'warn'; + } + + return status; + + }; + timeago.isMissing = function isMissing (opts) { if (!opts || !opts.entry || isNaN(opts.entry.mills) || isNaN(opts.time) || isNaN(opts.timeSince)) { return { @@ -128,6 +148,18 @@ function init ( ) { } }; + timeago.updateVisualisation = function updateVisualisation (sbx) { + var agoDisplay = timeago.calcDisplay(sbx.lastSGVEntry(), sbx.time); + var inRetroMode = sbx.data.inRetroMode; + var status = timeago.checkStatus(sbx); + + sbx.pluginBase.updatePillText(timeago, { + value: inRetroMode ? null : agoDisplay.value + , label: inRetroMode ? 'RETRO' : agoDisplay.label + , pillClass: status + }); + }; + return timeago; } diff --git a/lib/settings.js b/lib/settings.js index 7939790a851..6cb45db8393 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -76,7 +76,7 @@ function init ( ) { } //TODO: getting sent in status.json, shouldn't be - settings.DEFAULT_FEATURES = ['delta', 'direction', 'upbat', 'errorcodes']; + settings.DEFAULT_FEATURES = ['delta', 'direction', 'timeago', 'upbat', 'errorcodes']; var wasSet = []; diff --git a/lib/utils.js b/lib/utils.js index 8d7a51397e0..53317fcc817 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -34,46 +34,6 @@ function init(settings) { } }; - utils.timeAgo = function timeAgo(time) { - - var now = Date.now() - , offset = time === -1 ? -1 : (now - time) / 1000 - , parts = {}; - - if (offset < MINUTE_IN_SECS * -5) { - parts = { value: 'in the future' }; - } else if (offset === -1) { - parts = { label: 'time ago' }; - } else if (offset <= MINUTE_IN_SECS * 2) { - parts = { value: 1, label: 'min ago' }; - } else if (offset < (MINUTE_IN_SECS * 60)) { - parts = { value: Math.round(Math.abs(offset / MINUTE_IN_SECS)), label: 'mins ago' }; - } else if (offset < (HOUR_IN_SECS * 2)) { - parts = { value: 1, label: 'hr ago' }; - } else if (offset < (HOUR_IN_SECS * 24)) { - parts = { value: Math.round(Math.abs(offset / HOUR_IN_SECS)), label: 'hrs ago' }; - } else if (offset < DAY_IN_SECS) { - parts = { value: 1, label: 'day ago' }; - } else if (offset <= (DAY_IN_SECS * 7)) { - parts = { value: Math.round(Math.abs(offset / DAY_IN_SECS)), label: 'day ago' }; - } else { - parts = { value: 'long ago' }; - } - - if (offset > DAY_IN_SECS * 7) { - parts.status = 'warn'; - } else if (offset < MINUTE_IN_SECS * -5 || offset > (MINUTE_IN_SECS * settings.alarmTimeagoUrgentMins)) { - parts.status = 'urgent'; - } else if (offset > (MINUTE_IN_SECS * settings.alarmTimeagoWarnMins)) { - parts.status = 'warn'; - } else { - parts.status = 'current'; - } - - return parts; - - }; - // some helpers for input "date" utils.mergeInputTime = function mergeInputTime(timestring, datestring) { return moment(datestring + ' ' + timestring, 'YYYY-MM-D HH:mm'); diff --git a/static/css/main.css b/static/css/main.css index 7852c2ec954..99d08196107 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -138,17 +138,17 @@ body { color: red; } -.alarming-timeago #lastEntry.pill.warn { +.alarming-timeago .timeago.pill.warn { border-color: yellow; } -.alarming-timeago #lastEntry.pill.warn label { +.alarming-timeago .timeago.pill.warn label { background: yellow; } -.alarming-timeago #lastEntry.pill.urgent { +.alarming-timeago .timeago.pill.urgent { border-color: red; } -.alarming-timeago #lastEntry.pill.urgent label { +.alarming-timeago .timeago.pill.urgent label { background: red; } @@ -180,6 +180,7 @@ body { .statusPills .pill { background: #808080; border-color: #808080; + margin-right: 5px; } .statusPills .pill em { diff --git a/static/index.html b/static/index.html index c59d686d586..d14eb6b4078 100644 --- a/static/index.html +++ b/static/index.html @@ -61,7 +61,6 @@
---
-
diff --git a/tests/utils.test.js b/tests/utils.test.js index d107622070b..86ea7ea12a0 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -14,13 +14,6 @@ describe('utils', function ( ) { utils.toFixed(5.499999999).should.equal('5.50'); }); - it('show format recent times to 1 minute', function () { - var result = utils.timeAgo(Date.now() - 30000); - result.value.should.equal(1); - result.label.should.equal('min ago'); - result.status.should.equal('current'); - }); - it('merge date and time', function () { var result = utils.mergeInputTime('22:35', '2015-07-14'); result.hours().should.equal(22); From c099e5c3db91e41633977d4a6539de03b14b5973 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Wed, 21 Oct 2015 00:19:37 -0700 Subject: [PATCH 006/752] fix some missing ;'s --- lib/client/index.js | 2 +- lib/plugins/timeago.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/index.js b/lib/client/index.js index 5b2247c30f0..afee88b9c3d 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -530,7 +530,7 @@ client.init = function init(serverSettings, plugins) { } function updateTimeAgo() { - var status = client.timeago.checkStatus(client.sbx) + var status = client.timeago.checkStatus(client.sbx); if (status !== 'current') { updateTitle(); } diff --git a/lib/plugins/timeago.js b/lib/plugins/timeago.js index 25b69478573..9fdc5e536c3 100644 --- a/lib/plugins/timeago.js +++ b/lib/plugins/timeago.js @@ -114,7 +114,7 @@ function init ( ) { , label: label }; } - } + }; }; timeago.resolvers = [ From eb128d122ed80a32bdb4c1c814060d2c40959b89 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Wed, 21 Oct 2015 00:27:51 -0700 Subject: [PATCH 007/752] clean up --- lib/utils.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 53317fcc817..509208ddfc6 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -6,12 +6,7 @@ var units = require('./units')(); function init(settings) { - var utils = { - }; - - var MINUTE_IN_SECS = 60 - , HOUR_IN_SECS = 3600 - , DAY_IN_SECS = 86400; + var utils = { }; utils.scaleMgdl = function scaleMgdl (mgdl) { if (settings.units === 'mmol' && mgdl) { From b65d800619699d0e9a22cc601c1d94ea50f65e29 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Thu, 22 Oct 2015 21:00:17 +0200 Subject: [PATCH 008/752] profile editor, onthe fly profile conversion --- lib/api/profile/index.js | 8 +- lib/client/index.js | 2 + lib/client/renderer.js | 2 +- lib/language.js | 5 +- lib/profile.js | 9 + lib/profilefunctions.js | 89 +++++- static/css/profile.css | 4 + static/index.html | 2 +- static/profile/index.html | 159 ++++++---- static/profile/js/profileeditor.js | 469 +++++++++++++++++++---------- 10 files changed, 522 insertions(+), 227 deletions(-) diff --git a/lib/api/profile/index.js b/lib/api/profile/index.js index 99295b05a57..38b5f57d7c4 100644 --- a/lib/api/profile/index.js +++ b/lib/api/profile/index.js @@ -40,7 +40,7 @@ function configure (app, wares, ctx) { console.log('Error creating profile'); console.log(err); } else { - res.json(created); + res.json(created.ops); console.log('Profile created', created); } @@ -62,6 +62,12 @@ function configure (app, wares, ctx) { }); }); + + api.delete('/profile/:_id', wares.verifyAuthorization, function(req, res) { + ctx.profile.remove(req.params._id, function ( ) { + res.json({ }); + }); + }); } if (app.enabled('api')) { diff --git a/lib/client/index.js b/lib/client/index.js index 9d6fbb977f1..fc3de3eb8cf 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -75,6 +75,8 @@ client.init = function init(serverSettings, plugins) { client.renderer = require('./renderer')(client, d3, $); client.careportal = require('./careportal')(client, $); + client.profilefunctions = profile; + var timeAgo = client.utils.timeAgo; var container = $('.container') diff --git a/lib/client/renderer.js b/lib/client/renderer.js index 0a2e499939c..c88a27c0679 100644 --- a/lib/client/renderer.js +++ b/lib/client/renderer.js @@ -505,7 +505,7 @@ function init (client, d3) { var basalareadata = []; var tempbasalareadata = []; var from = chart().brush.extent()[0].getTime(); - var to = Math.max(chart().brush.extent()[1].getTime(), new Date().getTime()) + client.forecastTime; + var to = Math.max(chart().brush.extent()[1].getTime(), client.sbx.time) + client.forecastTime; var date = from; var lastbasal = 0; diff --git a/lib/language.js b/lib/language.js index 1c8bb9b9cf6..dd520b45930 100644 --- a/lib/language.js +++ b/lib/language.js @@ -4365,7 +4365,10 @@ function init() { cs: 'Nahrávám dočasné bazály' ,it: 'Caricamento basale temporanea' } - + ,'Save current record before changing to new?' : { + cs: 'Uložit současný záznam před změnou na nový?' + } + }; language.translate = function translate(text, options) { diff --git a/lib/profile.js b/lib/profile.js index 31d2e75e57c..99c60322e88 100644 --- a/lib/profile.js +++ b/lib/profile.js @@ -8,6 +8,7 @@ function storage (collection, ctx) { api().insert(obj, function (err, doc) { fn(null, doc); }); + ctx.bus.emit('data-received'); } function save (obj, fn) { @@ -19,6 +20,7 @@ function storage (collection, ctx) { //id should be added for new docs fn(err, obj); }); + ctx.bus.emit('data-received'); } function list (fn) { @@ -29,6 +31,12 @@ function storage (collection, ctx) { return api().find().sort({startDate: -1}).limit(1).toArray(fn); } + function remove (_id, fn) { + api( ).remove({ '_id': new ObjectID(_id) }, fn); + + ctx.bus.emit('data-received'); + } + function api () { return ctx.store.db.collection(collection); } @@ -36,6 +44,7 @@ function storage (collection, ctx) { api.list = list; api.create = create; api.save = save; + api.remove = remove; api.last = last; api.indexedFields = ['startDate']; return api; diff --git a/lib/profilefunctions.js b/lib/profilefunctions.js index b9a0ecfa0dd..87e7f98b0f7 100644 --- a/lib/profilefunctions.js +++ b/lib/profilefunctions.js @@ -13,10 +13,79 @@ function init(profileData) { profile.loadData = function loadData(profileData) { if (profileData && profileData.length) { - profile.data = _.cloneDeep(profileData); - profile.preprocessProfileOnLoad(profile.data[0]); + profile.data = profile.convertToProfileStore(profileData); + _.each(profile.data, function eachProfileRecord (record) { + _.each(record.store, profile.preprocessProfileOnLoad); + }); } }; + + // since 0.8.2 profile data will be converted following way + // + // source (result from api call) - only [0] used now + // [ { XXX, startDate: xxx }, { YYY, startDate: yyy } ] + // + // converted data + // [ + // { + // defaultProfile: 'Default' + // , store: { + // 'Default' : { XXX } + // } + // , startDate: xxx + // } + // , { + // defaultProfile: 'Default' + // , store: { + // 'Default' : { YYY } + // } + // , startDate: yyy + // } + // ] + + // example of one new profile + // + // { + // defaultProfile: '2-Weekend' + // , store: { + // '1-Weekday' : { AAA } + // '2-Weekend' : { BBB } + // '3-Exercise' : { CCC } + // } + // , startDate: xxx + // } + // + // for basals currently used profile will be determined by + // last treatment record + // { + // eventType: 'Profile Change' + // , profile: '2-Weekend' + // } + // + // for boluscalc profile used for calculation will be specified by key `profile` + // + + profile.convertToProfileStore = function convertToProfileStore (dataArray) { + var convertedProfiles = []; + _.each(dataArray, function (profile) { + if (!profile.defaultProfile) { + var newObject = {}; + newObject.defaultProfile = 'Default'; + newObject.store = {}; + newObject.startDate = profile.startDate; + newObject._id = profile._id; + delete profile.startDate; + delete profile._id; + delete profile.created_at; + newObject.store['Default'] = profile; + convertedProfiles.push(newObject); + console.log('Profile not updated yet. Converted profile:', newObject); + } else { + convertedProfiles.push(profile); + } + }); + return convertedProfiles; + }; profile.timeStringToSeconds = function timeStringToSeconds(time) { var split = time.split(':'); @@ -36,7 +105,6 @@ function init(profileData) { } }); }; - profile.getValueByTime = function getValueByTime (time, valueType) { if (!time) { time = Date.now(); } @@ -83,7 +151,8 @@ function init(profileData) { }; profile.getCurrentProfile = function getCurrentProfile() { - return profile.hasData() ? profile.data[0] : {}; + var data = profile.hasData() ? profile.data[0] : null; + return data && data.store[data.defaultProfile] ? data.store[data.defaultProfile] : {}; }; profile.getUnits = function getUnits() { @@ -138,6 +207,14 @@ function init(profileData) { }; profile.getTempBasal = function getTempBasal(time, basaltreatments) { + + var cacheKey = time + JSON.stringify(basaltreatments); + var returnValue = profile.timeValueCache[cacheKey]; + + if (returnValue) { + return returnValue; + } + var basal = profile.getValueByTime(time,'basal'); var tempbasal = basal; var treatment = profile.tempBasalTreatment(time, basaltreatments); @@ -147,11 +224,13 @@ function init(profileData) { if (treatment && treatment.absolute) { tempbasal = treatment.absolute; } - return { + returnValue = { basal: basal , treatment: treatment , tempbasal: tempbasal }; + profile.timeValueCache[cacheKey] = returnValue; + return returnValue; }; if (profileData) { profile.loadData(profileData); } diff --git a/static/css/profile.css b/static/css/profile.css index e5567da3bff..c3bbf42f888 100644 --- a/static/css/profile.css +++ b/static/css/profile.css @@ -16,4 +16,8 @@ border: 2px solid #aaa; box-shadow: 2px 2px 0 #eee; margin: 20px 0; +} + +.pe_stored_profile { + background-color: #222; } \ No newline at end of file diff --git a/static/index.html b/static/index.html index 6483c16f6df..e606835cd77 100644 --- a/static/index.html +++ b/static/index.html @@ -219,7 +219,7 @@ ' + '
' + + ' ' + + '
' + ' ' + '
' + ' +
- General profile settings + General profile settings
- Title: + Title:
- Units: + Units:
- Time format: + Date format:
- Database records: + Database records:   - +   - +   - +  
- Record valid from: + Record valid from:
- Stored profiles: + Stored profiles: - +   - +   - +  
- Name: + Name:
- Timezone: + Timezone:
- Duration of Insulin Activity (DIA) [hours]: + Duration of Insulin Activity (DIA) [hours]:
- Insulin to carb ratio (IC) [g]: + Insulin to carb ratio (I:C) [g]:
- Insulin Sensitivity Factor (ISF) [mg/dL,mmol/L]: + Insulin Sensitivity Factor (ISF) [mg/dL/U,mmol/L/U]:
- Carbs + Carbs @@ -128,13 +128,13 @@

Profile Editor

- Carbs activity / absorption rate: [g/hour]
+ Carbs activity / absorption rate: [g/hour]
- Carbs activity / absorption rate: [g/hour]
+ Carbs activity / absorption rate: [g/hour]
High: Medium: Low: @@ -149,12 +149,12 @@

Profile Editor

- Basal rates [unit/hour] + Basal rates [unit/hour]
- Target BG range [mg/dL,mmol/L] + Target BG range [mg/dL,mmol/L]
@@ -168,7 +168,7 @@

Profile Editor

- Status: Not loaded + Status: Not loaded

From 2ca0d26ccdd04a4673a10a31d2f55869dcb9052f Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Wed, 28 Oct 2015 10:06:04 +0100 Subject: [PATCH 018/752] some test fixies --- lib/profilefunctions.js | 32 ++++++++++++++++++-------------- static/profile/index.html | 2 +- tests/profileeditor.test.js | 6 ++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/profilefunctions.js b/lib/profilefunctions.js index 2084b6f265b..ba9c44e25a8 100644 --- a/lib/profilefunctions.js +++ b/lib/profilefunctions.js @@ -21,7 +21,7 @@ function init(profileData) { } }; - // since 0.8.2 profile data will be converted following way + // since 0.9.0 profile data will be converted following way // // source (result from api call) - only [0] used now // [ { XXX, startDate: xxx }, { YYY, startDate: yyy } ] @@ -206,13 +206,16 @@ function init(profileData) { } profile.activeProfileToTime = function activeProfileToTime (time) { - var timeprofile = profile.data[0].defaultProfile; - time = time || new Date().getTime(); - var treatment = profile.activeProfileTreatmentToTime(time); - if (treatment) { - timeprofile = treatment.profile; + if (profile.hasData()) { + var timeprofile = profile.data[0].defaultProfile; + time = time || new Date().getTime(); + var treatment = profile.activeProfileTreatmentToTime(time); + if (treatment) { + timeprofile = treatment.profile; + } + return timeprofile; } - return timeprofile; + return null; }; profile.activeProfileTreatmentToTime = function activeProfileTreatmentToTime(time) { @@ -274,14 +277,15 @@ function init(profileData) { }; profile.listBasalProfiles = function listBasalProfiles () { - var current = profile.activeProfileToTime(); var profiles = []; - - profiles.push(current); - - for (var key in profile.data[0].store) { - if (profile.data[0].store.hasOwnProperty(key) && key != current) { - profiles.push(key); + if (profile.hasData()) { + var current = profile.activeProfileToTime(); + profiles.push(current); + + for (var key in profile.data[0].store) { + if (profile.data[0].store.hasOwnProperty(key) && key != current) { + profiles.push(key); + } } } return profiles; diff --git a/static/profile/index.html b/static/profile/index.html index 166a11991e6..bc20ca4e60a 100644 --- a/static/profile/index.html +++ b/static/profile/index.html @@ -74,7 +74,7 @@

Profile Editor


- Record valid from: + Record valid from:
diff --git a/tests/profileeditor.test.js b/tests/profileeditor.test.js index b85b3664007..a193f27287e 100644 --- a/tests/profileeditor.test.js +++ b/tests/profileeditor.test.js @@ -82,10 +82,8 @@ describe('profile editor', function ( ) { self.$.ajax = function mockAjax (url, opts) { return { done: function mockDone (fn) { - if (opts && opts.success && opts.success.call) { - opts.success([exampleProfile]); - } - fn(); + exampleProfile._id = 'abcdef'; + fn(exampleProfile,'success'); return { fail: function () {} }; From 8d7f17e95766d14f5c4e438352e7e1a71281eed3 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Wed, 28 Oct 2015 11:09:56 +0100 Subject: [PATCH 019/752] Basic profileeditor test --- tests/profileeditor.test.js | 182 ++++++++++++++++++++++++------------ 1 file changed, 120 insertions(+), 62 deletions(-) diff --git a/tests/profileeditor.test.js b/tests/profileeditor.test.js index a193f27287e..f53384e41ad 100644 --- a/tests/profileeditor.test.js +++ b/tests/profileeditor.test.js @@ -1,6 +1,7 @@ 'use strict'; require('should'); +var _ = require('lodash'); var benv = require('benv'); var read = require('fs').readFileSync; var serverSettings = require('./fixtures/default-server-settings'); @@ -9,64 +10,70 @@ var nowData = { sgvs: [ { mgdl: 100, mills: Date.now(), direction: 'Flat', type: 'sgv' } ] - , treatments: [] }; - var exampleProfile = { - //General values - 'dia':3, - - // Simple style values, 'from' are in minutes from midnight - 'carbratio': [ - { - 'time': '00:00', - 'value': 30 - }], - 'carbs_hr':30, - 'delay': 20, - 'sens': [ - { - 'time': '00:00', - 'value': 100 - } - , { - 'time': '8:00', - 'value': 80 - }], - 'startDate': new Date(), - 'timezone': 'UTC', - - //perGIvalues style values - 'perGIvalues': false, - 'carbs_hr_high': 30, - 'carbs_hr_medium': 30, - 'carbs_hr_low': 30, - 'delay_high': 15, - 'delay_medium': 20, - 'delay_low': 20, - - 'basal':[ - { - 'time': '00:00', - 'value': 0.1 - }], - 'target_low':[ - { - 'time': '00:00', - 'value': 0 - }], - 'target_high':[ - { - 'time': '00:00', - 'value': 0 - }] + defaultProfile : 'Default' + , store: { + 'Default' : { + //General values + 'dia':3, + + // Simple style values, 'from' are in minutes from midnight + 'carbratio': [ + { + 'time': '00:00', + 'value': 30 + }], + 'carbs_hr':30, + 'delay': 20, + 'sens': [ + { + 'time': '00:00', + 'value': 100 + } + , { + 'time': '8:00', + 'value': 80 + }], + 'startDate': new Date(), + 'timezone': 'UTC', + + //perGIvalues style values + 'perGIvalues': false, + 'carbs_hr_high': 30, + 'carbs_hr_medium': 30, + 'carbs_hr_low': 30, + 'delay_high': 15, + 'delay_medium': 20, + 'delay_low': 20, + + 'basal':[ + { + 'time': '00:00', + 'value': 0.1 + }], + 'target_low':[ + { + 'time': '00:00', + 'value': 0 + }], + 'target_high':[ + { + 'time': '00:00', + 'value': 0 + }] + } + } }; -exampleProfile.startDate.setSeconds(0); -exampleProfile.startDate.setMilliseconds(0); -describe('profile editor', function ( ) { +var someData = { + '/api/v1/profile.json': [exampleProfile] + }; + + +describe('Profile editor', function ( ) { var self = this; before(function (done) { @@ -76,18 +83,55 @@ describe('profile editor', function ( ) { self.$.fn.tipsy = function mockTipsy ( ) { }; + self.$.fn.dialog = function mockDialog (opts) { + function maybeCall (name, obj) { + if (obj[name] && obj[name].call) { + obj[name](); + } + + } + maybeCall('open', opts); + + _.forEach(opts.buttons, function (button) { + maybeCall('click', button); + }); + }; + var indexHtml = read(__dirname + '/../static/profile/index.html', 'utf8'); self.$('body').html(indexHtml); + //var filesys = require('fs'); + //var logfile = filesys.createWriteStream('out.txt', { flags: 'a'} ) + self.$.ajax = function mockAjax (url, opts) { + //logfile.write(url+'\n'); + //console.log(url,opts); + if (opts && opts.success && opts.success.call) { + return { + done: function mockDone (fn) { + if (someData[url]) { + console.log('+++++Data for ' + url + ' sent'); + opts.success(someData[url]); + } else { + console.log('-----Data for ' + url + ' missing'); + opts.success([]); + } + fn(); + return self.$.ajax(); + }, + fail: function mockFail () { + return self.$.ajax(); + } + }; + } return { done: function mockDone (fn) { - exampleProfile._id = 'abcdef'; - fn(exampleProfile,'success'); - return { - fail: function () {} - }; - } + fn({message: 'OK'}); + return self.$.ajax(); + }, + fail: function mockFail () { + return self.$.ajax(); + } }; }; @@ -121,7 +165,7 @@ describe('profile editor', function ( ) { done(); }); - it ('don\'t blow up', function (done) { + it ('should produce some html', function (done) { var plugins = require('../lib/plugins/')().registerClientDefaults(); var client = require('../lib/client'); @@ -132,12 +176,26 @@ describe('profile editor', function ( ) { next(true); }; + window.confirm = function mockConfirm (text) { + console.log('Confirm:', text); + return true; + }; + + window.alert = function mockAlert () { + return true; + }; client.init(serverSettings, plugins); client.dataUpdate(nowData); - - $('#pe_form').find('button').click(); - + + //var result = $('body').html(); + //var filesys = require('fs'); + //var logfile = filesys.createWriteStream('out.txt', { flags: 'a'} ) + //logfile.write($('body').html()); + + //console.log(result); + + $('#pe_submit').click(); done(); }); From 51363c6ab07b74fd9be3215f5b479aeb3932b1cf Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Wed, 12 Aug 2015 18:14:05 +0200 Subject: [PATCH 020/752] extracted needed files --- README.md | 2 + env.js | 1 + lib/api/food/index.js | 84 ++++++ lib/api/index.js | 2 +- lib/bootevent.js | 2 + lib/food.js | 55 ++++ static/food/index.html | 103 +++++++ static/js/food.js | 630 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 878 insertions(+), 1 deletion(-) create mode 100644 lib/api/food/index.js create mode 100644 lib/food.js create mode 100644 static/food/index.html create mode 100644 static/js/food.js diff --git a/README.md b/README.md index 4a8a0d65328..1b3c5cfbecf 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,8 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs.htm * `MONGO_COLLECTION` (`entries`) - The collection used to store SGV, MBG, and CAL records from your CGM device * `MONGO_TREATMENTS_COLLECTION` (`treatments`) -The collection used to store treatments entered in the Care Portal, see the `ENABLE` env var above * `MONGO_DEVICESTATUS_COLLECTION`(`devicestatus`) - The collection used to store device status information such as uploader battery + * `MONGO_PROFILE_COLLECTION`(`profile`) - The collection used to store your profiles + * `MONGO_FOOD_COLLECTION`(`food`) - The collection used to store your food database * `PORT` (`1337`) - The port that the node.js application will listen on. * `SSL_KEY` - Path to your ssl key file, so that ssl(https) can be enabled directly in node.js * `SSL_CERT` - Path to your ssl cert file, so that ssl(https) can be enabled directly in node.js diff --git a/env.js b/env.js index 9d1388b6435..03b53f84094 100644 --- a/env.js +++ b/env.js @@ -100,6 +100,7 @@ function setMongo() { env.treatments_collection = readENV('MONGO_TREATMENTS_COLLECTION', 'treatments'); env.profile_collection = readENV('MONGO_PROFILE_COLLECTION', 'profile'); env.devicestatus_collection = readENV('MONGO_DEVICESTATUS_COLLECTION', 'devicestatus'); + env.food_collection = readENV('MONGO_FOOD_COLLECTION', 'food'); // TODO: clean up a bit // Some people prefer to use a json configuration file instead. diff --git a/lib/api/food/index.js b/lib/api/food/index.js new file mode 100644 index 00000000000..2fef129cab5 --- /dev/null +++ b/lib/api/food/index.js @@ -0,0 +1,84 @@ +'use strict'; + +var consts = require('../../constants'); + +function configure (app, wares, ctx) { + var express = require('express'), + api = express.Router( ); + + // invoke common middleware + api.use(wares.sendJSONStatus); + // text body types get handled as raw buffer stream + api.use(wares.bodyParser.raw( )); + // json body types get handled as parsed json + api.use(wares.bodyParser.json( )); + // also support url-encoded content-type + api.use(wares.bodyParser.urlencoded({ extended: true })); + + // List foods available + api.get('/food/', function(req, res) { + ctx.food.list(function (err, attribute) { + return res.json(attribute); + }); + }); + + api.get('/food/quickpicks', function(req, res) { + ctx.food.listquickpicks(function (err, attribute) { + return res.json(attribute); + }); + }); + + api.get('/food/regular', function(req, res) { + ctx.food.listregular(function (err, attribute) { + return res.json(attribute); + }); + }); + + function config_authed (app, api, wares, ctx) { + + // create new record + api.post('/food/', wares.verifyAuthorization, function(req, res) { + var data = req.body; + ctx.food.create(data, function (err, created) { + if (err) { + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + console.log('Error creating food'); + console.log(err); + } else { + res.json(created); + console.log('food created'); + } + }); + }); + + // update record + api.put('/food/', wares.verifyAuthorization, function(req, res) { + var data = req.body; + ctx.food.save(data, function (err, created) { + if (err) { + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + console.log('Error saving food'); + console.log(err); + } else { + res.json(created); + console.log('food saved'); + } + }); + }); + // delete record + api.delete('/food/:_id', wares.verifyAuthorization, function(req, res) { + ctx.food.remove(req.params._id, function ( ) { + res.json({ }); + }); + }); + } + + if (app.enabled('api')) { + config_authed(app, api, wares, ctx); + } + + return api; +} + +module.exports = configure; + diff --git a/lib/api/index.js b/lib/api/index.js index a409801161a..29798b1550d 100644 --- a/lib/api/index.js +++ b/lib/api/index.js @@ -49,7 +49,7 @@ function create (env, ctx) { app.use('/', require('./devicestatus/')(app, wares, ctx)); app.use('/', require('./notifications-api')(app, wares, ctx)); app.use('/', require('./verifyauth')(app, env)); - + app.use('/', require('./food/')(app, wares, ctx)); // Status app.use('/', require('./status')(app, wares, env)); return app; diff --git a/lib/bootevent.js b/lib/bootevent.js index 2e2665dfa33..3d88910e567 100644 --- a/lib/bootevent.js +++ b/lib/bootevent.js @@ -30,6 +30,7 @@ function boot (env) { ctx.treatments = require('./treatments')(env, ctx); ctx.devicestatus = require('./devicestatus')(env.devicestatus_collection, ctx); ctx.profile = require('./profile')(env.profile_collection, ctx); + ctx.food = require('./food')(env, ctx); ctx.pebble = require('./pebble')(env, ctx); ctx.bus = require('./bus')(env, ctx); ctx.data = require('./data')(env, ctx); @@ -44,6 +45,7 @@ function boot (env) { ctx.store.ensureIndexes(ctx.treatments( ), ctx.treatments.indexedFields); ctx.store.ensureIndexes(ctx.devicestatus( ), ctx.devicestatus.indexedFields); ctx.store.ensureIndexes(ctx.profile( ), ctx.profile.indexedFields); + ctx.store.ensureIndexes(ctx.food( ), ctx.food.indexedFields); next( ); } diff --git a/lib/food.js b/lib/food.js new file mode 100644 index 00000000000..50b0bb1f35f --- /dev/null +++ b/lib/food.js @@ -0,0 +1,55 @@ +'use strict'; + +function storage (env, ctx) { + var ObjectID = require('mongodb').ObjectID; + + function create (obj, fn) { + obj.created_at = (new Date( )).toISOString( ); + api().insert(obj, function (err, doc) { + fn(null, doc); + }); + } + + function save (obj, fn) { + obj._id = new ObjectID(obj._id); + obj.created_at = (new Date( )).toISOString( ); + api().save(obj, function (err, doc) { + fn(err, doc); + }); + } + + function list (fn) { + return api( ).find({ }).toArray(fn); + } + + function listquickpicks (fn) { + return api( ).find({ $and: [ { "type": "quickpick"} , { "hidden" : false } ] }).sort({"position": 1}).toArray(fn); + } + + function listregular (fn) { + return api( ).find( { "type": "food"} ).toArray(fn); + } + + function remove (_id, fn) { + return api( ).remove({ "_id": new ObjectID(_id) }, fn); + } + + + + function api ( ) { + return ctx.store.db.collection(env.food_collection); + } + + api.list = list; + api.listquickpicks = listquickpicks; + api.listregular = listregular; + api.create = create; + api.save = save; + api.remove = remove; + api.indexedFields = indexedFields; + return api; +} + +var indexedFields = ['type','position','hidden']; + +module.exports = storage; diff --git a/static/food/index.html b/static/food/index.html new file mode 100644 index 00000000000..a2e337745de --- /dev/null +++ b/static/food/index.html @@ -0,0 +1,103 @@ + + + + + Nightscout food editor + + + + + + + + +
+
+ Status: Not loaded +
+

Nightscout

+
+

Food editor

+
+
+ +
+
+
+ Your database +
+ Filter + Category: + Subcategory: + Name: +
+
+
+
+
+ Record (ID: ) + + + + + + + + + + +
+ Name:
+
+ Portion:
+
+ Unit:
+
+ Carbs: [g]
+
+ GI:
+
+ Category: +
+ +
+ Subcategory: +
+ +
+ + +
+
+ + Quick picks + | + + | + Show hidden + + | + + +
+
+ Authentication status: + +
+ + + + + + + + + + + + diff --git a/static/js/food.js b/static/js/food.js new file mode 100644 index 00000000000..1069cb61c91 --- /dev/null +++ b/static/js/food.js @@ -0,0 +1,630 @@ +/* Code by Milos Kozak + +*/ + var foodrec_template = { + _id: '' + , type: 'food' + , category: '' + , subcategory: '' + , name: '' + , portion: 0 + , carbs: 0 + , gi: 2 + , unit: '' + }; + + var foodunits = ['g', 'ml', 'pcs']; + + var foodrec = clone(foodrec_template); + + var HIDDEN = 99999; + var quickpickrec_template = { + _id: '' + , type: 'quickpick' + , name: '' + , foods: [] + , carbs: 0 + , hideafteruse: true + , hidden: false + , position: HIDDEN + }; + + var filter = { + category: '' + , subcategory: '' + , name: '' + }; + + var GuiToVal = [ + // API secret + { "html":"fe_filter_category", "type":"dropdownval" , "settings":"filter.category" }, + { "html":"fe_filter_subcategory", "type":"dropdownval" , "settings":"filter.subcategory" }, + { "html":"fe_filter_name", "type":"text" , "settings":"filter.name" }, + + { "html":"fe_id", "type":"text" , "settings":"foodrec._id" }, + { "html":"fe_category", "type":"text" , "settings":"foodrec.category" }, + { "html":"fe_subcategory", "type":"text" , "settings":"foodrec.subcategory" }, + { "html":"fe_name", "type":"text" , "settings":"foodrec.name" }, + { "html":"fe_portion", "type":"int" , "settings":"foodrec.portion" }, + { "html":"fe_unit", "type":"dropdownval" , "settings":"foodrec.unit" }, + { "html":"fe_carbs", "type":"int" , "settings":"foodrec.carbs" }, + { "html":"fe_gi", "type":"int" , "settings":"foodrec.gi" }, + + { "html":"fe_quickpick_showhidden", "type":"checkbox" , "settings":"showhidden" } + + ]; + + //https://www.iconfinder.com/iconsets/musthave + var icon_add = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABa0lEQVQ4T6WTzysEYRjHP+/Mrv2hHOTuqJRNOfgPSORHokg4OClHcnVzVygHF6WIcuHMnyCHVRyI3ZYxa23vzOzs7LzamaI0e5i89fTWt/f5vPV5n1cQsXLbHepvfLv5JaLORoZNwMbyFo5vYfsWB0c7xAasLa5T/vCg45Oj48P4gJWFVYxCA63L5PzkND5gfm4Jo+Chd5W5OrtsDYgS1pQ1OTuNUfTQO8tcX9xE+QugYnS/X81MzGP7MpTWkEFVZY1KxcVPV3h27zAtA+oCagIcDfWUCgEje31qfHwK06gHjaF5iXQcHCV5lHmqqgQCNEAI0IsavCVDwNBurxoeGwmaAkDDwvYsqtIh//6AJUoklP97s62BbJYeAqIcpJNZsoM+r2aVbKKOekiBL8An3BuAEiGg1SSKAYnttpFxPdR9Jv4zipxFTUuQKqsfYbFGWfTYuO06yRfxIyweoLuG+iMsFuBfvzFy7FqE33vs2BFqlfN5AAAAAElFTkSuQmCC"; + var icon_remove = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACrElEQVQ4T42Ty2sTQRzHv5tmk2yyjRNtpfZhL8V6s2KoUNC2XqwgaCsVQcGiFqpHi0c9iRdR/ANE9KR40FIQX4cueKoPaKFoLdSYNtE0abKT1+5s9iW7aUMiHtzTzO7v85md+c6PA4DrHbsPCKIgOWO1pA7dT6YXnXH949SE/F63pqwZtRrO+SCKgjQ5NUV+azpmHj2krMwaJC4c8Erj+/eRyloMMwWFKgbn1nC3ervlK1evkXBLGBZT8SOewotnTylTNLdgeg/pDgZDC2cPHSR8bB22DVC9hFe0SG/H0xFXcHlykjRHRDBWgJcZSCY38Xx2lhqMnRYE34Px/sN9vlQWeoHBAx2yXsRruVAVuFsIBaSJ8+eJGPaBqQV4NROJjTzez89jLBoFn6FgybQL54wS3uTyVDFQ3cL2IYpBv3RhdJSIIQ80tQyv7gEqJvS8AmUlBs7UXPhtjtZgh3UFNYngk86NHCfNAg9dMwHVBPu+CpsVkTXKeJeVG+AGgTOZ3tt6MSKKjy+NjEBjFrR4ElZmA4pdxstMFsyyJu6tZZ7Ux9vwB6EAL50ZGiRECEPPUOixVTRxHlicgSVWxEdZpuZWfNuS2hk48NjwMIkIYZglBnV5Cbqtws/5IaAJmsfCglrEl2y2QeKmEBJ80tixKmxrFpSVr0gV0viQoxho2YUuPohmeFD22PiklLC4ma5JuBvdrfLJI0dJd0s7bM0ES8aR/BXDXGaTskqlL+D3Lwy0tZEePoAd4EA5YF4tYymdonfjmQh3s6dTPjU4SHYGwjAKecSXFyGlM1TdytntE56T+ts7SC/vhw3gm6njc2Kd3vm5Ub1IwQAvnYhGiZpYw1wiWYPrIw7wnBTt7CLOOwdmut14kQQvqt24tfK/utGR6LaF+iRqMf4N/O/8D28HiiCRYqzAAAAAAElFTkSuQmCC"; + var icon_edit = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABEUlEQVQ4jZ3MMUsCYQDG8ee8IySQbNCLyyEKG/RLNAXicqvQcAeNLrcFLlE0+xHuNpt8wy04rrYm8Q4HQRE56BSC3lSqU1BwCoxM39dnffj9BWyxXvVeEzvtctBwHyRebNu2Nk2lzMlrgJB+qBEeTByiKYpihl+fIO8jTI9PDJEVF1+K2iw+M6PhDuyag4NkQi/c3FkCK5Z3ZbM76qLltpCbn+vXxq0FABsDy9hzPdBvqvtXvvXzrw1swmsDLPjfACteGeDBfwK8+FdgGwwAIgC0ncsjxGRSH/eiPBgAJADY2z8sJ4JBfNBsDqlADVYMANIzKalv/bHaefKsTH9iPFb8ISsGAJym0+Qinz3jQktbAHcxvx3559eSAAAAAElFTkSuQmCC"; + var icon_up = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAH3SURBVDhPlZK/a1NRFMe/9/1KotLcwbYiipo2NqmDpTpoiJiguPgPCC7iYAcbYp0LxUGkxVr6Y6lgu6iLm4uCQszi4JS6FHQRFMRBDOJrcvPyEs+97+V3A+2Bw7nvcr6f973nXoZ+8RQTqCGn1hrSuIOCWneF5tfO8MWZ2Ax/NPmYB1kop/Z2iV6AL56O3eNj4TP4Lf7iVnSKm7ByWOuFdAJ88d14lp8Oj+P9jw/Y/vMFBgK4GbnNLUaQpU5Iawbt4gEpzqPiVmirprqi/BRKjo0XW5vFSkmkMevNxAM0bPvidyQuuwJuvUrbdTBWh6ZpGOOjEM4Onn/aKIoyQR6gwBriTHzGs/09j5Jbxo5rw67aEDWhfuOiCkM3cO7wWdQcB68+viyKfyKt4zq2s/H7/OJQEj9Lv6BrOkbCJ/H62xvYBHFAx2Aurh5LITJwAgfMIA6GDiEyPBL8/LVwg2GdPHbF/PllLGwtI2CYlBYsqteOp7H6dhFkxEvHqxqmyGB70szkeU2ya+l0eSQOmh5ECR9SzzzlE8oVsN53IKm0LQGmQRCTXBBAfitAV/QBMBLoTRfyKMqBtN0VfQFymAogXZB4Xw6YD9DJheGDDFrvHUDlytHLSB1J4tJwAonBCwSk1l0ArafciDm6Vtko07+qZrqUz9o1wH8prahHZgYc2gAAAABJRU5ErkJggg=="; + + var foodlist = []; + var foodquickpick = []; + var foodquickpicktodelete = []; + var showhidden = false; + var categories = {}; + + + // Fetch data from mongo + $('#fe_status').hide().text('Loading food database ...').fadeIn("slow"); + $.ajax('/api/v1/food.json', { + success: function (records) { + records.forEach(function (r) { + if (r.type == 'food') { + foodlist.push(r); + if (r.category && !categories[r.category]) categories[r.category] = {}; + if (r.category && r.subcategory) categories[r.category][r.subcategory] = true; + } else if (r.type == 'quickpick') calculateCarbs(foodquickpick.push(r)-1); + else console.log('Unknown food database record'); + }); + $('#fe_status').hide().text(translate('Database loaded')).fadeIn("slow"); + foodquickpick.sort(function (a,b) { return cmp(parseInt(a.position),parseInt(b.position)) }); + }, + error: function () { + $('#fe_status').hide().text(translate('Error: Database failed to load')).fadeIn("slow"); + } + }).done(initeditor); + + function initeditor() { + // Add handlers for gui + $('#fe_editcreate').click(foodSubmit); + $('#fe_clear').click(clearRec); + $('#fe_id').change(updateSaveButton); + $('#fe_quickpick_add').click(quickpickCreateRecord); + $('#fe_quickpick_save').click(quickpickSave); + $('#fe_filter_category').change(fillSubcategories); + $('#fe_filter_subcategory').change(doFilter); + $('#fe_quickpick_showhidden').change(showHidden); + $('#fe_filter_name').on('input',doFilter); + $('#fe_category_list').change(function(event) { $('#fe_category').val($('#fe_category_list').val()); fillSubcategories(event,true) }); + $('#fe_subcategory_list').change(function(event) { $('#fe_subcategory').val($('#fe_subcategory_list').val())}); + $('#fe_unit').empty(); + for (var u=0; u'+ + ''+translate('Name')+''+ + ''+translate('Portion')+''+ + ''+translate('Unit')+''+ + ''+translate('Carbs')+' [g]'+ + ''+translate('GI')+' [1-3]'+ + ''+translate('Category')+''+ + ''+translate('Subcategory')+'' + ); + for (var i=0; i'; + html += ''; + html += ' '; + html += ''; + html += ''; + html += ''+foodlist[i].name+''; + html += ''+foodlist[i].portion+''; + html += ''+foodlist[i].unit+''; + html += ''+foodlist[i].carbs+''; + html += ''+foodlist[i].gi+''; + html += ''+foodlist[i].category+''; + html += ''+foodlist[i].subcategory+''; + html += ''; + } + + html += ''; + $('#fe_data').html(html); + + $('.draggablefood').draggable({ + helper: function(){ + return $(this).clone().width($(this).width()).height($(this).height()); + }, +// start: function () { g_actions_dragorigin = 'draggablefood'; }, + scope: 'foodlist', + revert: "invalid" + }); + + if (event) { + event.preventDefault(); + } + } + + function deleteQuickpickRecord(index) { + foodquickpicktodelete.push(foodquickpick[index]._id); + foodquickpick.splice(index,1); + drawQuickpick(); + return false; + } + + function deleteFoodRecord(index) { + deleteRecord(foodlist[index]._id); + foodlist.splice(index,1); + fillForm(); + return false; + } + + function quickpickFindById(_id) { + for (var i=0; i'; + html += ''; + html += ''; + html += ' | '; + html += ' | '+translate('Name')+': '; + html += ' '+translate('Hidden'); + html += ' '+translate('Hide after use'); + html += ' | '+translate('Carbs')+': '+q.carbs.toFixed(0) + ' g'; + html += ''; +// html += '
'; + + if (q.foods.length) { + html += + ''+ + ''+translate('Name')+''+ + ''+translate('Portion')+' [g,ml]'+ + ''+translate('Carbs')+' [g]'+ + '
'; + } else { + html += '-> Drag&drop food here'; + } + + for (var j=0;j'; + html += ''; + html += ''; + html += ''; + html += ''+r.name+''; + html += ''+r.portion+''; + html += ''+r.carbs+''; + html += ''+translate('Portions')+': '; + html += ''; + } + html += '
'; + } + + $('#fe_picklist').html(html); + $('#fe_quickpick_hiddencount').text(hiddentotal ? (' ('+hiddentotal+')') : ''); + + $('.fq_name').change(function (event) { + var index = $(this).attr('index'); + foodquickpick[index].name = $(this).val(); + }); + $('.fq_hidden').change(function (event) { + var index = $(this).attr('index'); + foodquickpick[index].hidden = this.checked; + if (!this.checked) + foodquickpick.splice(0, 0, foodquickpick.splice(index, 1)[0]); + drawQuickpick(); + }); + + $('.fq_hideafteruse').change(function (event) { + var index = $(this).attr('index'); + foodquickpick[index].hideafteruse = this.checked; + }); + + $('.sortablequickpick').droppable({ + hoverClass: "ui-state-hover", + drop: dropFood, + scope: 'foodlist', + greedy: true + }); + $('#fe_picklist').sortable({ + revert: true + , axis: 'y' + , placeholder: "highlight" + , update: resortArray + }); + } + + function savePortions(i,j,val) { + foodquickpick[i].foods[j].portions=val.replace(/\,/g,'.'); + calculateCarbs(i); + drawQuickpick(); + return false; + } + + function deleteQuickpickFood(index,findex) { + foodquickpick[index].foods.splice(findex,1); + calculateCarbs(index); + drawQuickpick(); + return false; + } + + function dropFood(event,ui) { + + var item = ui.draggable; + var fi = foodlist[item.attr('index')]; + + var qi = $(this).attr('index'); + var q = foodquickpick[qi]; + + fi.portions = 1; + q.foods.push(fi); + calculateCarbs(qi); + + drawQuickpick(); + } + + function calculateCarbs(index) { + var qp = foodquickpick[index]; + qp.carbs = 0; + for (var j=0;j-1) { + + // remove _id when creating new record + delete foodrec._id; + + var dataJson = JSON.stringify(foodrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + var newrec = JSON.parse(xhr.responseText)[0]; + foodlist.push(newrec); + if (foodrec.category && !categories[foodrec.category]) categories[foodrec.category] = {}; + if (foodrec.category && foodrec.subcategory) categories[foodrec.category][foodrec.subcategory] = true; + clearRec(); + fillForm(); + } + } + xhr.send(dataJson); + } else { + // Update record + var dataJson = JSON.stringify(foodrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('PUT', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + updateFoodArray(foodrec); + clearRec(); + fillForm(); + } + } + xhr.send(dataJson); + } + + if (event) event.preventDefault(); + return false; + } + + function deleteRecord(_id) { + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + var dataJson = JSON.stringify(_id, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('DELETE', '/api/v1/food/'+_id, true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + } + } + xhr.send(null); + + return false; + + } + + function updateRecord(foodrec) { + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + var dataJson = JSON.stringify(foodrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('PUT', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.send(dataJson); + } + + function quickpickCreateRecord(event) { + try { + var newrec = clone(quickpickrec_template); + + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + // remove _id when creating new record + delete newrec._id; + + var dataJson = JSON.stringify(newrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + var newrec = JSON.parse(xhr.responseText)[0]; + foodquickpick.push(newrec); + drawQuickpick(); + } + } + xhr.send(dataJson); + + if (event) event.preventDefault(); + return false; + + } catch (e) { alert(e.message); return false; } + } + + function quickpickSave(event) { + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + for (var i=0; iv2?1:0)); + } + From c2fd6e2af2df77785812aeadf8d4b7fae4cb8d19 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Wed, 12 Aug 2015 20:10:55 +0200 Subject: [PATCH 021/752] 1st pass of refactoring --- static/food/index.html | 189 ++++++------- static/food/js/food.js | 580 +++++++++++++++++++++++++++++++++++++ static/js/food.js | 630 ----------------------------------------- 3 files changed, 673 insertions(+), 726 deletions(-) create mode 100644 static/food/js/food.js delete mode 100644 static/js/food.js diff --git a/static/food/index.html b/static/food/index.html index a2e337745de..51360ceddd7 100644 --- a/static/food/index.html +++ b/static/food/index.html @@ -1,103 +1,100 @@ - - Nightscout food editor - - - - - - + + Nightscout food editor + + + + + -
-
- Status: Not loaded -
-

Nightscout

-
-

Food editor

-
+
+
+ Status: Not loaded
- -
-
-
- Your database -
- Filter - Category: - Subcategory: - Name: -
-
-
-
-
- Record (ID: ) - - - - - - - - - - -
- Name:
-
- Portion:
-
- Unit:
-
- Carbs: [g]
-
- GI:
-
- Category: -
- -
- Subcategory: -
- -
- - -
-
- - Quick picks - | - - | - Show hidden - - | - - -
-
- Authentication status: - -
- - - - - - - - - - - +

Nightscout

+
+

Food editor

+
+
+ +
+
+
+ Your database +
+ Filter + Category: + Subcategory: + Name: +
+
+
+
+
+ Record (ID: ) + + + + + + + + + + +
+ Name:
+
+ Portion:
+
+ Unit:
+
+ Carbs: [g]
+
+ GI:
+
+ Category: +
+ +
+ Subcategory: +
+ +
+ + +
+
+ + Quick picks + | + + | + Show hidden + + | + + +
+
+ Authentication status: + +
+ + + + + + + + + diff --git a/static/food/js/food.js b/static/food/js/food.js new file mode 100644 index 00000000000..b5d82e4a7a9 --- /dev/null +++ b/static/food/js/food.js @@ -0,0 +1,580 @@ +'use strict'; + +(function () { + + if (serverSettings === undefined) { + console.error('server settings were not loaded, will not call init'); + } else { + window.Nightscout.client.init(serverSettings, Nightscout.plugins); + } + + var translate = Nightscout.client.translate; + + var foodrec_template = { + _id: '' + , type: 'food' + , category: '' + , subcategory: '' + , name: '' + , portion: 0 + , carbs: 0 + , gi: 2 + , unit: '' + }; + + var foodunits = ['g', 'ml', 'pcs']; + + var foodrec = _.cloneDeep(foodrec_template); + + var HIDDEN = 99999; + var quickpickrec_template = { + _id: '' + , type: 'quickpick' + , name: '' + , foods: [] + , carbs: 0 + , hideafteruse: true + , hidden: false + , position: HIDDEN + }; + + var filter = { + category: '' + , subcategory: '' + , name: '' + }; + + //https://www.iconfinder.com/iconsets/musthave + var icon_add = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABa0lEQVQ4T6WTzysEYRjHP+/Mrv2hHOTuqJRNOfgPSORHokg4OClHcnVzVygHF6WIcuHMnyCHVRyI3ZYxa23vzOzs7LzamaI0e5i89fTWt/f5vPV5n1cQsXLbHepvfLv5JaLORoZNwMbyFo5vYfsWB0c7xAasLa5T/vCg45Oj48P4gJWFVYxCA63L5PzkND5gfm4Jo+Chd5W5OrtsDYgS1pQ1OTuNUfTQO8tcX9xE+QugYnS/X81MzGP7MpTWkEFVZY1KxcVPV3h27zAtA+oCagIcDfWUCgEje31qfHwK06gHjaF5iXQcHCV5lHmqqgQCNEAI0IsavCVDwNBurxoeGwmaAkDDwvYsqtIh//6AJUoklP97s62BbJYeAqIcpJNZsoM+r2aVbKKOekiBL8An3BuAEiGg1SSKAYnttpFxPdR9Jv4zipxFTUuQKqsfYbFGWfTYuO06yRfxIyweoLuG+iMsFuBfvzFy7FqE33vs2BFqlfN5AAAAAElFTkSuQmCC"; + var icon_remove = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACrElEQVQ4T42Ty2sTQRzHv5tmk2yyjRNtpfZhL8V6s2KoUNC2XqwgaCsVQcGiFqpHi0c9iRdR/ANE9KR40FIQX4cueKoPaKFoLdSYNtE0abKT1+5s9iW7aUMiHtzTzO7v85md+c6PA4DrHbsPCKIgOWO1pA7dT6YXnXH949SE/F63pqwZtRrO+SCKgjQ5NUV+azpmHj2krMwaJC4c8Erj+/eRyloMMwWFKgbn1nC3ervlK1evkXBLGBZT8SOewotnTylTNLdgeg/pDgZDC2cPHSR8bB22DVC9hFe0SG/H0xFXcHlykjRHRDBWgJcZSCY38Xx2lhqMnRYE34Px/sN9vlQWeoHBAx2yXsRruVAVuFsIBaSJ8+eJGPaBqQV4NROJjTzez89jLBoFn6FgybQL54wS3uTyVDFQ3cL2IYpBv3RhdJSIIQ80tQyv7gEqJvS8AmUlBs7UXPhtjtZgh3UFNYngk86NHCfNAg9dMwHVBPu+CpsVkTXKeJeVG+AGgTOZ3tt6MSKKjy+NjEBjFrR4ElZmA4pdxstMFsyyJu6tZZ7Ux9vwB6EAL50ZGiRECEPPUOixVTRxHlicgSVWxEdZpuZWfNuS2hk48NjwMIkIYZglBnV5Cbqtws/5IaAJmsfCglrEl2y2QeKmEBJ80tixKmxrFpSVr0gV0viQoxho2YUuPohmeFD22PiklLC4ma5JuBvdrfLJI0dJd0s7bM0ES8aR/BXDXGaTskqlL+D3Lwy0tZEePoAd4EA5YF4tYymdonfjmQh3s6dTPjU4SHYGwjAKecSXFyGlM1TdytntE56T+ts7SC/vhw3gm6njc2Kd3vm5Ub1IwQAvnYhGiZpYw1wiWYPrIw7wnBTt7CLOOwdmut14kQQvqt24tfK/utGR6LaF+iRqMf4N/O/8D28HiiCRYqzAAAAAAElFTkSuQmCC"; + var icon_edit = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABEUlEQVQ4jZ3MMUsCYQDG8ee8IySQbNCLyyEKG/RLNAXicqvQcAeNLrcFLlE0+xHuNpt8wy04rrYm8Q4HQRE56BSC3lSqU1BwCoxM39dnffj9BWyxXvVeEzvtctBwHyRebNu2Nk2lzMlrgJB+qBEeTByiKYpihl+fIO8jTI9PDJEVF1+K2iw+M6PhDuyag4NkQi/c3FkCK5Z3ZbM76qLltpCbn+vXxq0FABsDy9hzPdBvqvtXvvXzrw1swmsDLPjfACteGeDBfwK8+FdgGwwAIgC0ncsjxGRSH/eiPBgAJADY2z8sJ4JBfNBsDqlADVYMANIzKalv/bHaefKsTH9iPFb8ISsGAJym0+Qinz3jQktbAHcxvx3559eSAAAAAElFTkSuQmCC"; + var icon_up = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAH3SURBVDhPlZK/a1NRFMe/9/1KotLcwbYiipo2NqmDpTpoiJiguPgPCC7iYAcbYp0LxUGkxVr6Y6lgu6iLm4uCQszi4JS6FHQRFMRBDOJrcvPyEs+97+V3A+2Bw7nvcr6f973nXoZ+8RQTqCGn1hrSuIOCWneF5tfO8MWZ2Ax/NPmYB1kop/Z2iV6AL56O3eNj4TP4Lf7iVnSKm7ByWOuFdAJ88d14lp8Oj+P9jw/Y/vMFBgK4GbnNLUaQpU5Iawbt4gEpzqPiVmirprqi/BRKjo0XW5vFSkmkMevNxAM0bPvidyQuuwJuvUrbdTBWh6ZpGOOjEM4Onn/aKIoyQR6gwBriTHzGs/09j5Jbxo5rw67aEDWhfuOiCkM3cO7wWdQcB68+viyKfyKt4zq2s/H7/OJQEj9Lv6BrOkbCJ/H62xvYBHFAx2Aurh5LITJwAgfMIA6GDiEyPBL8/LVwg2GdPHbF/PllLGwtI2CYlBYsqteOp7H6dhFkxEvHqxqmyGB70szkeU2ya+l0eSQOmh5ECR9SzzzlE8oVsN53IKm0LQGmQRCTXBBAfitAV/QBMBLoTRfyKMqBtN0VfQFymAogXZB4Xw6YD9DJheGDDFrvHUDlytHLSB1J4tJwAonBCwSk1l0ArafciDm6Vtko07+qZrqUz9o1wH8prahHZgYc2gAAAABJRU5ErkJggg=="; + + var foodlist = []; + var foodquickpick = []; + var foodquickpicktodelete = []; + var showhidden = false; + var categories = {}; + + + // Fetch data from mongo + $('#fe_status').hide().text('Loading food database ...').fadeIn("slow"); + $.ajax('/api/v1/food.json', { + success: function (records) { + records.forEach(function (r) { + if (r.type == 'food') { + foodlist.push(r); + if (r.category && !categories[r.category]) categories[r.category] = {}; + if (r.category && r.subcategory) categories[r.category][r.subcategory] = true; + } else if (r.type == 'quickpick') calculateCarbs(foodquickpick.push(r)-1); + else console.log('Unknown food database record'); + }); + $('#fe_status').hide().text(translate('Database loaded')).fadeIn("slow"); + foodquickpick.sort(function (a,b) { return cmp(parseInt(a.position),parseInt(b.position)) }); + }, + error: function () { + $('#fe_status').hide().text(translate('Error: Database failed to load')).fadeIn("slow"); + } + }).done(initeditor); + + function initeditor() { + // Add handlers for gui + $('#fe_editcreate').click(foodSubmit); + $('#fe_clear').click(clearRec); + $('#fe_id').change(updateSaveButton); + $('#fe_quickpick_add').click(quickpickCreateRecord); + $('#fe_quickpick_save').click(quickpickSave); + $('#fe_filter_category').change(fillSubcategories); + $('#fe_filter_subcategory').change(doFilter); + $('#fe_quickpick_showhidden').change(showHidden); + $('#fe_filter_name').on('input',doFilter); + $('#fe_category_list').change(function(event) { $('#fe_category').val($('#fe_category_list').val()); fillSubcategories(event,true) }); + $('#fe_subcategory_list').change(function(event) { $('#fe_subcategory').val($('#fe_subcategory_list').val())}); + $('#fe_unit').empty(); + for (var u=0; u'+ + ''+translate('Name')+''+ + ''+translate('Portion')+''+ + ''+translate('Unit')+''+ + ''+translate('Carbs')+' [g]'+ + ''+translate('GI')+' [1-3]'+ + ''+translate('Category')+''+ + ''+translate('Subcategory')+'' + ); + for (var i=0; i'; + html += ''; + html += ' '; + html += ''; + html += ''; + html += ''+foodlist[i].name+''; + html += ''+foodlist[i].portion+''; + html += ''+foodlist[i].unit+''; + html += ''+foodlist[i].carbs+''; + html += ''+foodlist[i].gi+''; + html += ''+foodlist[i].category+''; + html += ''+foodlist[i].subcategory+''; + html += '
'; + } + + html += ''; + $('#fe_data').html(html); + + $('.draggablefood').draggable({ + helper: function(){ + return $(this).clone().width($(this).width()).height($(this).height()); + }, +// start: function () { g_actions_dragorigin = 'draggablefood'; }, + scope: 'foodlist', + revert: "invalid" + }); + + if (event) { + event.preventDefault(); + } + } + + function deleteQuickpickRecord(index) { + foodquickpicktodelete.push(foodquickpick[index]._id); + foodquickpick.splice(index,1); + drawQuickpick(); + return false; + } + + function deleteFoodRecord(index) { + deleteRecord(foodlist[index]._id); + foodlist.splice(index,1); + fillForm(); + return false; + } + + function quickpickFindById(_id) { + for (var i=0; i'; + html += ''; + html += ''; + html += ' | '; + html += ' | '+translate('Name')+': '; + html += ' '+translate('Hidden'); + html += ' '+translate('Hide after use'); + html += ' | '+translate('Carbs')+': '+q.carbs.toFixed(0) + ' g'; + html += ''; +// html += '
'; + + if (q.foods.length) { + html += + ''+ + ''+translate('Name')+''+ + ''+translate('Portion')+' [g,ml]'+ + ''+translate('Carbs')+' [g]'+ + '
'; + } else { + html += '-> Drag&drop food here'; + } + + for (var j=0;j'; + html += ''; + html += ''; + html += ''; + html += ''+r.name+''; + html += ''+r.portion+''; + html += ''+r.carbs+''; + html += ''+translate('Portions')+': '; + html += ''; + } + html += '
'; + } + + $('#fe_picklist').html(html); + $('#fe_quickpick_hiddencount').text(hiddentotal ? (' ('+hiddentotal+')') : ''); + + $('.fq_name').change(function (event) { + var index = $(this).attr('index'); + foodquickpick[index].name = $(this).val(); + }); + $('.fq_hidden').change(function (event) { + var index = $(this).attr('index'); + foodquickpick[index].hidden = this.checked; + if (!this.checked) + foodquickpick.splice(0, 0, foodquickpick.splice(index, 1)[0]); + drawQuickpick(); + }); + + $('.fq_hideafteruse').change(function (event) { + var index = $(this).attr('index'); + foodquickpick[index].hideafteruse = this.checked; + }); + + $('.sortablequickpick').droppable({ + hoverClass: "ui-state-hover", + drop: dropFood, + scope: 'foodlist', + greedy: true + }); + $('#fe_picklist').sortable({ + revert: true + , axis: 'y' + , placeholder: "highlight" + , update: resortArray + }); + } + + function savePortions(i,j,val) { + foodquickpick[i].foods[j].portions=val.replace(/\,/g,'.'); + calculateCarbs(i); + drawQuickpick(); + return false; + } + + function deleteQuickpickFood(index,findex) { + foodquickpick[index].foods.splice(findex,1); + calculateCarbs(index); + drawQuickpick(); + return false; + } + + function dropFood(event,ui) { + + var item = ui.draggable; + var fi = foodlist[item.attr('index')]; + + var qi = $(this).attr('index'); + var q = foodquickpick[qi]; + + fi.portions = 1; + q.foods.push(fi); + calculateCarbs(qi); + + drawQuickpick(); + } + + function calculateCarbs(index) { + var qp = foodquickpick[index]; + qp.carbs = 0; + for (var j=0;j-1) { + + // remove _id when creating new record + delete foodrec._id; + + var dataJson = JSON.stringify(foodrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + var newrec = JSON.parse(xhr.responseText)[0]; + foodlist.push(newrec); + if (foodrec.category && !categories[foodrec.category]) categories[foodrec.category] = {}; + if (foodrec.category && foodrec.subcategory) categories[foodrec.category][foodrec.subcategory] = true; + clearRec(); + fillForm(); + } + } + xhr.send(dataJson); + } else { + // Update record + var dataJson = JSON.stringify(foodrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('PUT', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + updateFoodArray(foodrec); + clearRec(); + fillForm(); + } + } + xhr.send(dataJson); + } + + if (event) event.preventDefault(); + return false; + } + + function deleteRecord(_id) { + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + var dataJson = JSON.stringify(_id, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('DELETE', '/api/v1/food/'+_id, true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + } + } + xhr.send(null); + + return false; + + } + + function updateRecord(foodrec) { + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + var dataJson = JSON.stringify(foodrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('PUT', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.send(dataJson); + } + + function quickpickCreateRecord(event) { + try { + var newrec = clone(quickpickrec_template); + + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + // remove _id when creating new record + delete newrec._id; + + var dataJson = JSON.stringify(newrec, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + if (xhr.statusText=='OK') { + var newrec = JSON.parse(xhr.responseText)[0]; + foodquickpick.push(newrec); + drawQuickpick(); + } + } + xhr.send(dataJson); + + if (event) event.preventDefault(); + return false; + + } catch (e) { alert(e.message); return false; } + } + + function quickpickSave(event) { + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } + + for (var i=0; iv2?1:0)); + } + +})(); \ No newline at end of file diff --git a/static/js/food.js b/static/js/food.js deleted file mode 100644 index 1069cb61c91..00000000000 --- a/static/js/food.js +++ /dev/null @@ -1,630 +0,0 @@ -/* Code by Milos Kozak - -*/ - var foodrec_template = { - _id: '' - , type: 'food' - , category: '' - , subcategory: '' - , name: '' - , portion: 0 - , carbs: 0 - , gi: 2 - , unit: '' - }; - - var foodunits = ['g', 'ml', 'pcs']; - - var foodrec = clone(foodrec_template); - - var HIDDEN = 99999; - var quickpickrec_template = { - _id: '' - , type: 'quickpick' - , name: '' - , foods: [] - , carbs: 0 - , hideafteruse: true - , hidden: false - , position: HIDDEN - }; - - var filter = { - category: '' - , subcategory: '' - , name: '' - }; - - var GuiToVal = [ - // API secret - { "html":"fe_filter_category", "type":"dropdownval" , "settings":"filter.category" }, - { "html":"fe_filter_subcategory", "type":"dropdownval" , "settings":"filter.subcategory" }, - { "html":"fe_filter_name", "type":"text" , "settings":"filter.name" }, - - { "html":"fe_id", "type":"text" , "settings":"foodrec._id" }, - { "html":"fe_category", "type":"text" , "settings":"foodrec.category" }, - { "html":"fe_subcategory", "type":"text" , "settings":"foodrec.subcategory" }, - { "html":"fe_name", "type":"text" , "settings":"foodrec.name" }, - { "html":"fe_portion", "type":"int" , "settings":"foodrec.portion" }, - { "html":"fe_unit", "type":"dropdownval" , "settings":"foodrec.unit" }, - { "html":"fe_carbs", "type":"int" , "settings":"foodrec.carbs" }, - { "html":"fe_gi", "type":"int" , "settings":"foodrec.gi" }, - - { "html":"fe_quickpick_showhidden", "type":"checkbox" , "settings":"showhidden" } - - ]; - - //https://www.iconfinder.com/iconsets/musthave - var icon_add = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABa0lEQVQ4T6WTzysEYRjHP+/Mrv2hHOTuqJRNOfgPSORHokg4OClHcnVzVygHF6WIcuHMnyCHVRyI3ZYxa23vzOzs7LzamaI0e5i89fTWt/f5vPV5n1cQsXLbHepvfLv5JaLORoZNwMbyFo5vYfsWB0c7xAasLa5T/vCg45Oj48P4gJWFVYxCA63L5PzkND5gfm4Jo+Chd5W5OrtsDYgS1pQ1OTuNUfTQO8tcX9xE+QugYnS/X81MzGP7MpTWkEFVZY1KxcVPV3h27zAtA+oCagIcDfWUCgEje31qfHwK06gHjaF5iXQcHCV5lHmqqgQCNEAI0IsavCVDwNBurxoeGwmaAkDDwvYsqtIh//6AJUoklP97s62BbJYeAqIcpJNZsoM+r2aVbKKOekiBL8An3BuAEiGg1SSKAYnttpFxPdR9Jv4zipxFTUuQKqsfYbFGWfTYuO06yRfxIyweoLuG+iMsFuBfvzFy7FqE33vs2BFqlfN5AAAAAElFTkSuQmCC"; - var icon_remove = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACrElEQVQ4T42Ty2sTQRzHv5tmk2yyjRNtpfZhL8V6s2KoUNC2XqwgaCsVQcGiFqpHi0c9iRdR/ANE9KR40FIQX4cueKoPaKFoLdSYNtE0abKT1+5s9iW7aUMiHtzTzO7v85md+c6PA4DrHbsPCKIgOWO1pA7dT6YXnXH949SE/F63pqwZtRrO+SCKgjQ5NUV+azpmHj2krMwaJC4c8Erj+/eRyloMMwWFKgbn1nC3ervlK1evkXBLGBZT8SOewotnTylTNLdgeg/pDgZDC2cPHSR8bB22DVC9hFe0SG/H0xFXcHlykjRHRDBWgJcZSCY38Xx2lhqMnRYE34Px/sN9vlQWeoHBAx2yXsRruVAVuFsIBaSJ8+eJGPaBqQV4NROJjTzez89jLBoFn6FgybQL54wS3uTyVDFQ3cL2IYpBv3RhdJSIIQ80tQyv7gEqJvS8AmUlBs7UXPhtjtZgh3UFNYngk86NHCfNAg9dMwHVBPu+CpsVkTXKeJeVG+AGgTOZ3tt6MSKKjy+NjEBjFrR4ElZmA4pdxstMFsyyJu6tZZ7Ux9vwB6EAL50ZGiRECEPPUOixVTRxHlicgSVWxEdZpuZWfNuS2hk48NjwMIkIYZglBnV5Cbqtws/5IaAJmsfCglrEl2y2QeKmEBJ80tixKmxrFpSVr0gV0viQoxho2YUuPohmeFD22PiklLC4ma5JuBvdrfLJI0dJd0s7bM0ES8aR/BXDXGaTskqlL+D3Lwy0tZEePoAd4EA5YF4tYymdonfjmQh3s6dTPjU4SHYGwjAKecSXFyGlM1TdytntE56T+ts7SC/vhw3gm6njc2Kd3vm5Ub1IwQAvnYhGiZpYw1wiWYPrIw7wnBTt7CLOOwdmut14kQQvqt24tfK/utGR6LaF+iRqMf4N/O/8D28HiiCRYqzAAAAAAElFTkSuQmCC"; - var icon_edit = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABEUlEQVQ4jZ3MMUsCYQDG8ee8IySQbNCLyyEKG/RLNAXicqvQcAeNLrcFLlE0+xHuNpt8wy04rrYm8Q4HQRE56BSC3lSqU1BwCoxM39dnffj9BWyxXvVeEzvtctBwHyRebNu2Nk2lzMlrgJB+qBEeTByiKYpihl+fIO8jTI9PDJEVF1+K2iw+M6PhDuyag4NkQi/c3FkCK5Z3ZbM76qLltpCbn+vXxq0FABsDy9hzPdBvqvtXvvXzrw1swmsDLPjfACteGeDBfwK8+FdgGwwAIgC0ncsjxGRSH/eiPBgAJADY2z8sJ4JBfNBsDqlADVYMANIzKalv/bHaefKsTH9iPFb8ISsGAJym0+Qinz3jQktbAHcxvx3559eSAAAAAElFTkSuQmCC"; - var icon_up = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAH3SURBVDhPlZK/a1NRFMe/9/1KotLcwbYiipo2NqmDpTpoiJiguPgPCC7iYAcbYp0LxUGkxVr6Y6lgu6iLm4uCQszi4JS6FHQRFMRBDOJrcvPyEs+97+V3A+2Bw7nvcr6f973nXoZ+8RQTqCGn1hrSuIOCWneF5tfO8MWZ2Ax/NPmYB1kop/Z2iV6AL56O3eNj4TP4Lf7iVnSKm7ByWOuFdAJ88d14lp8Oj+P9jw/Y/vMFBgK4GbnNLUaQpU5Iawbt4gEpzqPiVmirprqi/BRKjo0XW5vFSkmkMevNxAM0bPvidyQuuwJuvUrbdTBWh6ZpGOOjEM4Onn/aKIoyQR6gwBriTHzGs/09j5Jbxo5rw67aEDWhfuOiCkM3cO7wWdQcB68+viyKfyKt4zq2s/H7/OJQEj9Lv6BrOkbCJ/H62xvYBHFAx2Aurh5LITJwAgfMIA6GDiEyPBL8/LVwg2GdPHbF/PllLGwtI2CYlBYsqteOp7H6dhFkxEvHqxqmyGB70szkeU2ya+l0eSQOmh5ECR9SzzzlE8oVsN53IKm0LQGmQRCTXBBAfitAV/QBMBLoTRfyKMqBtN0VfQFymAogXZB4Xw6YD9DJheGDDFrvHUDlytHLSB1J4tJwAonBCwSk1l0ArafciDm6Vtko07+qZrqUz9o1wH8prahHZgYc2gAAAABJRU5ErkJggg=="; - - var foodlist = []; - var foodquickpick = []; - var foodquickpicktodelete = []; - var showhidden = false; - var categories = {}; - - - // Fetch data from mongo - $('#fe_status').hide().text('Loading food database ...').fadeIn("slow"); - $.ajax('/api/v1/food.json', { - success: function (records) { - records.forEach(function (r) { - if (r.type == 'food') { - foodlist.push(r); - if (r.category && !categories[r.category]) categories[r.category] = {}; - if (r.category && r.subcategory) categories[r.category][r.subcategory] = true; - } else if (r.type == 'quickpick') calculateCarbs(foodquickpick.push(r)-1); - else console.log('Unknown food database record'); - }); - $('#fe_status').hide().text(translate('Database loaded')).fadeIn("slow"); - foodquickpick.sort(function (a,b) { return cmp(parseInt(a.position),parseInt(b.position)) }); - }, - error: function () { - $('#fe_status').hide().text(translate('Error: Database failed to load')).fadeIn("slow"); - } - }).done(initeditor); - - function initeditor() { - // Add handlers for gui - $('#fe_editcreate').click(foodSubmit); - $('#fe_clear').click(clearRec); - $('#fe_id').change(updateSaveButton); - $('#fe_quickpick_add').click(quickpickCreateRecord); - $('#fe_quickpick_save').click(quickpickSave); - $('#fe_filter_category').change(fillSubcategories); - $('#fe_filter_subcategory').change(doFilter); - $('#fe_quickpick_showhidden').change(showHidden); - $('#fe_filter_name').on('input',doFilter); - $('#fe_category_list').change(function(event) { $('#fe_category').val($('#fe_category_list').val()); fillSubcategories(event,true) }); - $('#fe_subcategory_list').change(function(event) { $('#fe_subcategory').val($('#fe_subcategory_list').val())}); - $('#fe_unit').empty(); - for (var u=0; u'+ - ''+translate('Name')+''+ - ''+translate('Portion')+''+ - ''+translate('Unit')+''+ - ''+translate('Carbs')+' [g]'+ - ''+translate('GI')+' [1-3]'+ - ''+translate('Category')+''+ - ''+translate('Subcategory')+'' - ); - for (var i=0; i'; - html += ''; - html += ' '; - html += ''; - html += ''; - html += ''+foodlist[i].name+''; - html += ''+foodlist[i].portion+''; - html += ''+foodlist[i].unit+''; - html += ''+foodlist[i].carbs+''; - html += ''+foodlist[i].gi+''; - html += ''+foodlist[i].category+''; - html += ''+foodlist[i].subcategory+''; - html += ''; - } - - html += ''; - $('#fe_data').html(html); - - $('.draggablefood').draggable({ - helper: function(){ - return $(this).clone().width($(this).width()).height($(this).height()); - }, -// start: function () { g_actions_dragorigin = 'draggablefood'; }, - scope: 'foodlist', - revert: "invalid" - }); - - if (event) { - event.preventDefault(); - } - } - - function deleteQuickpickRecord(index) { - foodquickpicktodelete.push(foodquickpick[index]._id); - foodquickpick.splice(index,1); - drawQuickpick(); - return false; - } - - function deleteFoodRecord(index) { - deleteRecord(foodlist[index]._id); - foodlist.splice(index,1); - fillForm(); - return false; - } - - function quickpickFindById(_id) { - for (var i=0; i'; - html += ''; - html += ''; - html += ' | '; - html += ' | '+translate('Name')+': '; - html += ' '+translate('Hidden'); - html += ' '+translate('Hide after use'); - html += ' | '+translate('Carbs')+': '+q.carbs.toFixed(0) + ' g'; - html += ''; -// html += '
'; - - if (q.foods.length) { - html += - ''+ - ''+translate('Name')+''+ - ''+translate('Portion')+' [g,ml]'+ - ''+translate('Carbs')+' [g]'+ - '
'; - } else { - html += '-> Drag&drop food here'; - } - - for (var j=0;j'; - html += ''; - html += ''; - html += ''; - html += ''+r.name+''; - html += ''+r.portion+''; - html += ''+r.carbs+''; - html += ''+translate('Portions')+': '; - html += ''; - } - html += ''; - } - - $('#fe_picklist').html(html); - $('#fe_quickpick_hiddencount').text(hiddentotal ? (' ('+hiddentotal+')') : ''); - - $('.fq_name').change(function (event) { - var index = $(this).attr('index'); - foodquickpick[index].name = $(this).val(); - }); - $('.fq_hidden').change(function (event) { - var index = $(this).attr('index'); - foodquickpick[index].hidden = this.checked; - if (!this.checked) - foodquickpick.splice(0, 0, foodquickpick.splice(index, 1)[0]); - drawQuickpick(); - }); - - $('.fq_hideafteruse').change(function (event) { - var index = $(this).attr('index'); - foodquickpick[index].hideafteruse = this.checked; - }); - - $('.sortablequickpick').droppable({ - hoverClass: "ui-state-hover", - drop: dropFood, - scope: 'foodlist', - greedy: true - }); - $('#fe_picklist').sortable({ - revert: true - , axis: 'y' - , placeholder: "highlight" - , update: resortArray - }); - } - - function savePortions(i,j,val) { - foodquickpick[i].foods[j].portions=val.replace(/\,/g,'.'); - calculateCarbs(i); - drawQuickpick(); - return false; - } - - function deleteQuickpickFood(index,findex) { - foodquickpick[index].foods.splice(findex,1); - calculateCarbs(index); - drawQuickpick(); - return false; - } - - function dropFood(event,ui) { - - var item = ui.draggable; - var fi = foodlist[item.attr('index')]; - - var qi = $(this).attr('index'); - var q = foodquickpick[qi]; - - fi.portions = 1; - q.foods.push(fi); - calculateCarbs(qi); - - drawQuickpick(); - } - - function calculateCarbs(index) { - var qp = foodquickpick[index]; - qp.carbs = 0; - for (var j=0;j-1) { - - // remove _id when creating new record - delete foodrec._id; - - var dataJson = JSON.stringify(foodrec, null, ' '); - - var xhr = new XMLHttpRequest(); - xhr.open('POST', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); - xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); - if (xhr.statusText=='OK') { - var newrec = JSON.parse(xhr.responseText)[0]; - foodlist.push(newrec); - if (foodrec.category && !categories[foodrec.category]) categories[foodrec.category] = {}; - if (foodrec.category && foodrec.subcategory) categories[foodrec.category][foodrec.subcategory] = true; - clearRec(); - fillForm(); - } - } - xhr.send(dataJson); - } else { - // Update record - var dataJson = JSON.stringify(foodrec, null, ' '); - - var xhr = new XMLHttpRequest(); - xhr.open('PUT', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); - xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); - if (xhr.statusText=='OK') { - updateFoodArray(foodrec); - clearRec(); - fillForm(); - } - } - xhr.send(dataJson); - } - - if (event) event.preventDefault(); - return false; - } - - function deleteRecord(_id) { - if (!Nightscout.auth.isAuthenticated()) { - alert(translate('Your device is not authenticated yet')); - return false; - } - - var dataJson = JSON.stringify(_id, null, ' '); - - var xhr = new XMLHttpRequest(); - xhr.open('DELETE', '/api/v1/food/'+_id, true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); - xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); - if (xhr.statusText=='OK') { - } - } - xhr.send(null); - - return false; - - } - - function updateRecord(foodrec) { - if (!Nightscout.auth.isAuthenticated()) { - alert(translate('Your device is not authenticated yet')); - return false; - } - - var dataJson = JSON.stringify(foodrec, null, ' '); - - var xhr = new XMLHttpRequest(); - xhr.open('PUT', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); - xhr.send(dataJson); - } - - function quickpickCreateRecord(event) { - try { - var newrec = clone(quickpickrec_template); - - if (!Nightscout.auth.isAuthenticated()) { - alert(translate('Your device is not authenticated yet')); - return false; - } - - // remove _id when creating new record - delete newrec._id; - - var dataJson = JSON.stringify(newrec, null, ' '); - - var xhr = new XMLHttpRequest(); - xhr.open('POST', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); - xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); - if (xhr.statusText=='OK') { - var newrec = JSON.parse(xhr.responseText)[0]; - foodquickpick.push(newrec); - drawQuickpick(); - } - } - xhr.send(dataJson); - - if (event) event.preventDefault(); - return false; - - } catch (e) { alert(e.message); return false; } - } - - function quickpickSave(event) { - if (!Nightscout.auth.isAuthenticated()) { - alert(translate('Your device is not authenticated yet')); - return false; - } - - for (var i=0; iv2?1:0)); - } - From 97b362405c34c8340f24f80a8b1c55096aac9592 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Wed, 12 Aug 2015 20:31:11 +0200 Subject: [PATCH 022/752] css, unit translation --- lib/language.js | 9 ++++++++ static/css/food.css | 29 ++++++++++++++++++++++++ static/food/index.html | 3 ++- static/food/js/food.js | 50 +++++++++++++++++++++--------------------- 4 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 static/css/food.css diff --git a/lib/language.js b/lib/language.js index d5f08e5b058..c610e2a96ee 100644 --- a/lib/language.js +++ b/lib/language.js @@ -4084,6 +4084,15 @@ function init() { ,bg: 'Език' ,it: 'Lingua' } + ,'g' : { // grams shortcut + cs: 'g' + } + ,'ml' : { // milliliters shortcut + cs: 'ml' + } + ,'pcs' : { // pieces shortcut + cs: 'ks' + } ,'Update' : { // Update button cs: 'Aktualizovat' ,sv: 'Uppdatera' diff --git a/static/css/food.css b/static/css/food.css new file mode 100644 index 00000000000..8b219785bc8 --- /dev/null +++ b/static/css/food.css @@ -0,0 +1,29 @@ +#food-editor h1 { + position: absolute; + top: 0; + right: 0; + left: 50px; + margin-top: 0; + margin-right: auto; + margin-left: auto; + text-align: center; +} +span.width50px { + display: inline-block; + width: 50px; +} + +span.width100px { + display: inline-block; + width: 100px; +} + +span.width150px { + display: inline-block; + width: 150px; +} + +span.width200px { + display: inline-block; + width: 200px; +} \ No newline at end of file diff --git a/static/food/index.html b/static/food/index.html index 51360ceddd7..89216e79ff6 100644 --- a/static/food/index.html +++ b/static/food/index.html @@ -7,6 +7,7 @@ + @@ -15,7 +16,7 @@ Status: Not loaded

Nightscout

-
+

Food editor

diff --git a/static/food/js/food.js b/static/food/js/food.js index b5d82e4a7a9..f57be893ec4 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -91,7 +91,7 @@ $('#fe_category_list').change(function(event) { $('#fe_category').val($('#fe_category_list').val()); fillSubcategories(event,true) }); $('#fe_subcategory_list').change(function(event) { $('#fe_subcategory').val($('#fe_subcategory_list').val())}); $('#fe_unit').empty(); - for (var u=0; u'+ - ''+translate('Name')+''+ - ''+translate('Portion')+''+ - ''+translate('Unit')+''+ - ''+translate('Carbs')+' [g]'+ - ''+translate('GI')+' [1-3]'+ - ''+translate('Category')+''+ - ''+translate('Subcategory')+'' + ''+ + ''+translate('Name')+''+ + ''+translate('Portion')+''+ + ''+translate('Unit')+''+ + ''+translate('Carbs')+' [g]'+ + ''+translate('GI')+' [1-3]'+ + ''+translate('Category')+''+ + ''+translate('Subcategory')+'' ); for (var i=0; i'; - html += ''; + html += ''; html += ' '; html += ''; html += ''; - html += ''+foodlist[i].name+''; - html += ''+foodlist[i].portion+''; - html += ''+foodlist[i].unit+''; - html += ''+foodlist[i].carbs+''; - html += ''+foodlist[i].gi+''; - html += ''+foodlist[i].category+''; - html += ''+foodlist[i].subcategory+''; + html += ''+foodlist[i].name+''; + html += ''+foodlist[i].portion+''; + html += ''+foodlist[i].unit+''; + html += ''+foodlist[i].carbs+''; + html += ''+foodlist[i].gi+''; + html += ''+foodlist[i].category+''; + html += ''+foodlist[i].subcategory+''; html += ''; } @@ -270,10 +270,10 @@ if (q.foods.length) { html += - ''+ - ''+translate('Name')+''+ - ''+translate('Portion')+' [g,ml]'+ - ''+translate('Carbs')+' [g]'+ + ''+ + ''+translate('Name')+''+ + ''+translate('Portion')+' [g,ml]'+ + ''+translate('Carbs')+' [g]'+ '
'; } else { html += '-> Drag&drop food here'; @@ -282,12 +282,12 @@ for (var j=0;j'; - html += ''; + html += ''; html += ''; html += ''; - html += ''+r.name+''; - html += ''+r.portion+''; - html += ''+r.carbs+''; + html += ''+r.name+''; + html += ''+r.portion+''; + html += ''+r.carbs+''; html += ''+translate('Portions')+': '; html += ''; } From 8b0c9d235f95bc5b22abc365011c7753f6db477a Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Thu, 13 Aug 2015 09:36:24 +0200 Subject: [PATCH 023/752] making codacy happy pass 1 --- lib/food.js | 10 ++- static/css/food.css | 8 +-- static/food/js/food.js | 158 ++++++++++++++++++++++++++--------------- 3 files changed, 109 insertions(+), 67 deletions(-) diff --git a/lib/food.js b/lib/food.js index 50b0bb1f35f..f916708fe17 100644 --- a/lib/food.js +++ b/lib/food.js @@ -23,15 +23,15 @@ function storage (env, ctx) { } function listquickpicks (fn) { - return api( ).find({ $and: [ { "type": "quickpick"} , { "hidden" : false } ] }).sort({"position": 1}).toArray(fn); + return api( ).find({ $and: [ { 'type': 'quickpick'} , { 'hidden' : false } ] }).sort({'position': 1}).toArray(fn); } function listregular (fn) { - return api( ).find( { "type": "food"} ).toArray(fn); + return api( ).find( { 'type': 'food'} ).toArray(fn); } function remove (_id, fn) { - return api( ).remove({ "_id": new ObjectID(_id) }, fn); + return api( ).remove({ '_id': new ObjectID(_id) }, fn); } @@ -46,10 +46,8 @@ function storage (env, ctx) { api.create = create; api.save = save; api.remove = remove; - api.indexedFields = indexedFields; + api.indexedFields = ['type','position','hidden']; return api; } -var indexedFields = ['type','position','hidden']; - module.exports = storage; diff --git a/static/css/food.css b/static/css/food.css index 8b219785bc8..8e22064d3cc 100644 --- a/static/css/food.css +++ b/static/css/food.css @@ -8,22 +8,22 @@ margin-left: auto; text-align: center; } -span.width50px { +.width50px { display: inline-block; width: 50px; } -span.width100px { +.width100px { display: inline-block; width: 100px; } -span.width150px { +.width150px { display: inline-block; width: 150px; } -span.width200px { +.width200px { display: inline-block; width: 200px; } \ No newline at end of file diff --git a/static/food/js/food.js b/static/food/js/food.js index f57be893ec4..1da423a52fa 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -45,10 +45,10 @@ }; //https://www.iconfinder.com/iconsets/musthave - var icon_add = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABa0lEQVQ4T6WTzysEYRjHP+/Mrv2hHOTuqJRNOfgPSORHokg4OClHcnVzVygHF6WIcuHMnyCHVRyI3ZYxa23vzOzs7LzamaI0e5i89fTWt/f5vPV5n1cQsXLbHepvfLv5JaLORoZNwMbyFo5vYfsWB0c7xAasLa5T/vCg45Oj48P4gJWFVYxCA63L5PzkND5gfm4Jo+Chd5W5OrtsDYgS1pQ1OTuNUfTQO8tcX9xE+QugYnS/X81MzGP7MpTWkEFVZY1KxcVPV3h27zAtA+oCagIcDfWUCgEje31qfHwK06gHjaF5iXQcHCV5lHmqqgQCNEAI0IsavCVDwNBurxoeGwmaAkDDwvYsqtIh//6AJUoklP97s62BbJYeAqIcpJNZsoM+r2aVbKKOekiBL8An3BuAEiGg1SSKAYnttpFxPdR9Jv4zipxFTUuQKqsfYbFGWfTYuO06yRfxIyweoLuG+iMsFuBfvzFy7FqE33vs2BFqlfN5AAAAAElFTkSuQmCC"; - var icon_remove = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACrElEQVQ4T42Ty2sTQRzHv5tmk2yyjRNtpfZhL8V6s2KoUNC2XqwgaCsVQcGiFqpHi0c9iRdR/ANE9KR40FIQX4cueKoPaKFoLdSYNtE0abKT1+5s9iW7aUMiHtzTzO7v85md+c6PA4DrHbsPCKIgOWO1pA7dT6YXnXH949SE/F63pqwZtRrO+SCKgjQ5NUV+azpmHj2krMwaJC4c8Erj+/eRyloMMwWFKgbn1nC3ervlK1evkXBLGBZT8SOewotnTylTNLdgeg/pDgZDC2cPHSR8bB22DVC9hFe0SG/H0xFXcHlykjRHRDBWgJcZSCY38Xx2lhqMnRYE34Px/sN9vlQWeoHBAx2yXsRruVAVuFsIBaSJ8+eJGPaBqQV4NROJjTzez89jLBoFn6FgybQL54wS3uTyVDFQ3cL2IYpBv3RhdJSIIQ80tQyv7gEqJvS8AmUlBs7UXPhtjtZgh3UFNYngk86NHCfNAg9dMwHVBPu+CpsVkTXKeJeVG+AGgTOZ3tt6MSKKjy+NjEBjFrR4ElZmA4pdxstMFsyyJu6tZZ7Ux9vwB6EAL50ZGiRECEPPUOixVTRxHlicgSVWxEdZpuZWfNuS2hk48NjwMIkIYZglBnV5Cbqtws/5IaAJmsfCglrEl2y2QeKmEBJ80tixKmxrFpSVr0gV0viQoxho2YUuPohmeFD22PiklLC4ma5JuBvdrfLJI0dJd0s7bM0ES8aR/BXDXGaTskqlL+D3Lwy0tZEePoAd4EA5YF4tYymdonfjmQh3s6dTPjU4SHYGwjAKecSXFyGlM1TdytntE56T+ts7SC/vhw3gm6njc2Kd3vm5Ub1IwQAvnYhGiZpYw1wiWYPrIw7wnBTt7CLOOwdmut14kQQvqt24tfK/utGR6LaF+iRqMf4N/O/8D28HiiCRYqzAAAAAAElFTkSuQmCC"; - var icon_edit = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABEUlEQVQ4jZ3MMUsCYQDG8ee8IySQbNCLyyEKG/RLNAXicqvQcAeNLrcFLlE0+xHuNpt8wy04rrYm8Q4HQRE56BSC3lSqU1BwCoxM39dnffj9BWyxXvVeEzvtctBwHyRebNu2Nk2lzMlrgJB+qBEeTByiKYpihl+fIO8jTI9PDJEVF1+K2iw+M6PhDuyag4NkQi/c3FkCK5Z3ZbM76qLltpCbn+vXxq0FABsDy9hzPdBvqvtXvvXzrw1swmsDLPjfACteGeDBfwK8+FdgGwwAIgC0ncsjxGRSH/eiPBgAJADY2z8sJ4JBfNBsDqlADVYMANIzKalv/bHaefKsTH9iPFb8ISsGAJym0+Qinz3jQktbAHcxvx3559eSAAAAAElFTkSuQmCC"; - var icon_up = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAH3SURBVDhPlZK/a1NRFMe/9/1KotLcwbYiipo2NqmDpTpoiJiguPgPCC7iYAcbYp0LxUGkxVr6Y6lgu6iLm4uCQszi4JS6FHQRFMRBDOJrcvPyEs+97+V3A+2Bw7nvcr6f973nXoZ+8RQTqCGn1hrSuIOCWneF5tfO8MWZ2Ax/NPmYB1kop/Z2iV6AL56O3eNj4TP4Lf7iVnSKm7ByWOuFdAJ88d14lp8Oj+P9jw/Y/vMFBgK4GbnNLUaQpU5Iawbt4gEpzqPiVmirprqi/BRKjo0XW5vFSkmkMevNxAM0bPvidyQuuwJuvUrbdTBWh6ZpGOOjEM4Onn/aKIoyQR6gwBriTHzGs/09j5Jbxo5rw67aEDWhfuOiCkM3cO7wWdQcB68+viyKfyKt4zq2s/H7/OJQEj9Lv6BrOkbCJ/H62xvYBHFAx2Aurh5LITJwAgfMIA6GDiEyPBL8/LVwg2GdPHbF/PllLGwtI2CYlBYsqteOp7H6dhFkxEvHqxqmyGB70szkeU2ya+l0eSQOmh5ECR9SzzzlE8oVsN53IKm0LQGmQRCTXBBAfitAV/QBMBLoTRfyKMqBtN0VfQFymAogXZB4Xw6YD9DJheGDDFrvHUDlytHLSB1J4tJwAonBCwSk1l0ArafciDm6Vtko07+qZrqUz9o1wH8prahHZgYc2gAAAABJRU5ErkJggg=="; + //var icon_add = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABa0lEQVQ4T6WTzysEYRjHP+/Mrv2hHOTuqJRNOfgPSORHokg4OClHcnVzVygHF6WIcuHMnyCHVRyI3ZYxa23vzOzs7LzamaI0e5i89fTWt/f5vPV5n1cQsXLbHepvfLv5JaLORoZNwMbyFo5vYfsWB0c7xAasLa5T/vCg45Oj48P4gJWFVYxCA63L5PzkND5gfm4Jo+Chd5W5OrtsDYgS1pQ1OTuNUfTQO8tcX9xE+QugYnS/X81MzGP7MpTWkEFVZY1KxcVPV3h27zAtA+oCagIcDfWUCgEje31qfHwK06gHjaF5iXQcHCV5lHmqqgQCNEAI0IsavCVDwNBurxoeGwmaAkDDwvYsqtIh//6AJUoklP97s62BbJYeAqIcpJNZsoM+r2aVbKKOekiBL8An3BuAEiGg1SSKAYnttpFxPdR9Jv4zipxFTUuQKqsfYbFGWfTYuO06yRfxIyweoLuG+iMsFuBfvzFy7FqE33vs2BFqlfN5AAAAAElFTkSuQmCC'; + var icon_remove = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACrElEQVQ4T42Ty2sTQRzHv5tmk2yyjRNtpfZhL8V6s2KoUNC2XqwgaCsVQcGiFqpHi0c9iRdR/ANE9KR40FIQX4cueKoPaKFoLdSYNtE0abKT1+5s9iW7aUMiHtzTzO7v85md+c6PA4DrHbsPCKIgOWO1pA7dT6YXnXH949SE/F63pqwZtRrO+SCKgjQ5NUV+azpmHj2krMwaJC4c8Erj+/eRyloMMwWFKgbn1nC3ervlK1evkXBLGBZT8SOewotnTylTNLdgeg/pDgZDC2cPHSR8bB22DVC9hFe0SG/H0xFXcHlykjRHRDBWgJcZSCY38Xx2lhqMnRYE34Px/sN9vlQWeoHBAx2yXsRruVAVuFsIBaSJ8+eJGPaBqQV4NROJjTzez89jLBoFn6FgybQL54wS3uTyVDFQ3cL2IYpBv3RhdJSIIQ80tQyv7gEqJvS8AmUlBs7UXPhtjtZgh3UFNYngk86NHCfNAg9dMwHVBPu+CpsVkTXKeJeVG+AGgTOZ3tt6MSKKjy+NjEBjFrR4ElZmA4pdxstMFsyyJu6tZZ7Ux9vwB6EAL50ZGiRECEPPUOixVTRxHlicgSVWxEdZpuZWfNuS2hk48NjwMIkIYZglBnV5Cbqtws/5IaAJmsfCglrEl2y2QeKmEBJ80tixKmxrFpSVr0gV0viQoxho2YUuPohmeFD22PiklLC4ma5JuBvdrfLJI0dJd0s7bM0ES8aR/BXDXGaTskqlL+D3Lwy0tZEePoAd4EA5YF4tYymdonfjmQh3s6dTPjU4SHYGwjAKecSXFyGlM1TdytntE56T+ts7SC/vhw3gm6njc2Kd3vm5Ub1IwQAvnYhGiZpYw1wiWYPrIw7wnBTt7CLOOwdmut14kQQvqt24tfK/utGR6LaF+iRqMf4N/O/8D28HiiCRYqzAAAAAAElFTkSuQmCC'; + var icon_edit = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABEUlEQVQ4jZ3MMUsCYQDG8ee8IySQbNCLyyEKG/RLNAXicqvQcAeNLrcFLlE0+xHuNpt8wy04rrYm8Q4HQRE56BSC3lSqU1BwCoxM39dnffj9BWyxXvVeEzvtctBwHyRebNu2Nk2lzMlrgJB+qBEeTByiKYpihl+fIO8jTI9PDJEVF1+K2iw+M6PhDuyag4NkQi/c3FkCK5Z3ZbM76qLltpCbn+vXxq0FABsDy9hzPdBvqvtXvvXzrw1swmsDLPjfACteGeDBfwK8+FdgGwwAIgC0ncsjxGRSH/eiPBgAJADY2z8sJ4JBfNBsDqlADVYMANIzKalv/bHaefKsTH9iPFb8ISsGAJym0+Qinz3jQktbAHcxvx3559eSAAAAAElFTkSuQmCC'; + var icon_up = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAH3SURBVDhPlZK/a1NRFMe/9/1KotLcwbYiipo2NqmDpTpoiJiguPgPCC7iYAcbYp0LxUGkxVr6Y6lgu6iLm4uCQszi4JS6FHQRFMRBDOJrcvPyEs+97+V3A+2Bw7nvcr6f973nXoZ+8RQTqCGn1hrSuIOCWneF5tfO8MWZ2Ax/NPmYB1kop/Z2iV6AL56O3eNj4TP4Lf7iVnSKm7ByWOuFdAJ88d14lp8Oj+P9jw/Y/vMFBgK4GbnNLUaQpU5Iawbt4gEpzqPiVmirprqi/BRKjo0XW5vFSkmkMevNxAM0bPvidyQuuwJuvUrbdTBWh6ZpGOOjEM4Onn/aKIoyQR6gwBriTHzGs/09j5Jbxo5rw67aEDWhfuOiCkM3cO7wWdQcB68+viyKfyKt4zq2s/H7/OJQEj9Lv6BrOkbCJ/H62xvYBHFAx2Aurh5LITJwAgfMIA6GDiEyPBL8/LVwg2GdPHbF/PllLGwtI2CYlBYsqteOp7H6dhFkxEvHqxqmyGB70szkeU2ya+l0eSQOmh5ECR9SzzzlE8oVsN53IKm0LQGmQRCTXBBAfitAV/QBMBLoTRfyKMqBtN0VfQFymAogXZB4Xw6YD9DJheGDDFrvHUDlytHLSB1J4tJwAonBCwSk1l0ArafciDm6Vtko07+qZrqUz9o1wH8prahHZgYc2gAAAABJRU5ErkJggg=='; var foodlist = []; var foodquickpick = []; @@ -58,22 +58,29 @@ // Fetch data from mongo - $('#fe_status').hide().text('Loading food database ...').fadeIn("slow"); + $('#fe_status').hide().text('Loading food database ...').fadeIn('slow'); $.ajax('/api/v1/food.json', { success: function (records) { records.forEach(function (r) { - if (r.type == 'food') { + if (r.type === 'food') { foodlist.push(r); - if (r.category && !categories[r.category]) categories[r.category] = {}; - if (r.category && r.subcategory) categories[r.category][r.subcategory] = true; - } else if (r.type == 'quickpick') calculateCarbs(foodquickpick.push(r)-1); - else console.log('Unknown food database record'); + if (r.category && !categories[r.category]) { + categories[r.category] = {}; + } + if (r.category && r.subcategory) { + categories[r.category][r.subcategory] = true; + } + } else if (r.type == 'quickpick') { + calculateCarbs(foodquickpick.push(r)-1); + } else { + console.log('Unknown food database record'); + } }); - $('#fe_status').hide().text(translate('Database loaded')).fadeIn("slow"); + $('#fe_status').hide().text(translate('Database loaded')).fadeIn('slow'); foodquickpick.sort(function (a,b) { return cmp(parseInt(a.position),parseInt(b.position)) }); }, error: function () { - $('#fe_status').hide().text(translate('Error: Database failed to load')).fadeIn("slow"); + $('#fe_status').hide().text(translate('Error: Database failed to load')).fadeIn('slow'); } }).done(initeditor); @@ -88,10 +95,18 @@ $('#fe_filter_subcategory').change(doFilter); $('#fe_quickpick_showhidden').change(showHidden); $('#fe_filter_name').on('input',doFilter); - $('#fe_category_list').change(function(event) { $('#fe_category').val($('#fe_category_list').val()); fillSubcategories(event,true) }); - $('#fe_subcategory_list').change(function(event) { $('#fe_subcategory').val($('#fe_subcategory_list').val())}); + $('#fe_category_list').change(function(event) { + $('#fe_category').val($('#fe_category_list').val()); + fillSubcategories(event,true); + }); + $('#fe_subcategory_list').change(function(event) { + $('#fe_subcategory').val($('#fe_subcategory_list').val()); + event.preventDefault(); + }); $('#fe_unit').empty(); - for (var u=0; u'+ @@ -186,9 +213,9 @@ ''+translate('Subcategory')+'' ); for (var i=0; i'; html += ''; html += ' '; @@ -213,7 +240,7 @@ }, // start: function () { g_actions_dragorigin = 'draggablefood'; }, scope: 'foodlist', - revert: "invalid" + revert: 'invalid' }); if (event) { @@ -237,7 +264,9 @@ function quickpickFindById(_id) { for (var i=0; i'; html += ''; html += ''; @@ -300,22 +329,26 @@ $('.fq_name').change(function (event) { var index = $(this).attr('index'); foodquickpick[index].name = $(this).val(); + event.preventDefault(); }); $('.fq_hidden').change(function (event) { var index = $(this).attr('index'); foodquickpick[index].hidden = this.checked; - if (!this.checked) + if (!this.checked) { foodquickpick.splice(0, 0, foodquickpick.splice(index, 1)[0]); + } drawQuickpick(); + event.preventDefault(); }); $('.fq_hideafteruse').change(function (event) { var index = $(this).attr('index'); foodquickpick[index].hideafteruse = this.checked; + event.preventDefault(); }); $('.sortablequickpick').droppable({ - hoverClass: "ui-state-hover", + hoverClass: 'ui-state-hover', drop: dropFood, scope: 'foodlist', greedy: true @@ -323,7 +356,7 @@ $('#fe_picklist').sortable({ revert: true , axis: 'y' - , placeholder: "highlight" + , placeholder: 'highlight' , update: resortArray }); } @@ -419,8 +452,10 @@ } function clearRec(event) { - if (event) event.preventDefault(); - foodrec = clone(foodrec_template); + if (event) { + event.preventDefault(); + } + foodrec = _.cloneDeep(foodrec_template); objectToGUI(); updateSaveButton(); return false; @@ -433,25 +468,29 @@ alert(translate('Your device is not authenticated yet')); return false; } - + + var dataJson = JSON.stringify(foodrec, null, ' '); + var xhr; if ($('#fe_editcreate').text().indexOf(translate('Create new record'))>-1) { // remove _id when creating new record delete foodrec._id; - var dataJson = JSON.stringify(foodrec, null, ' '); - - var xhr = new XMLHttpRequest(); + xhr = new XMLHttpRequest(); xhr.open('POST', '/api/v1/food/', true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText=='OK') { var newrec = JSON.parse(xhr.responseText)[0]; foodlist.push(newrec); - if (foodrec.category && !categories[foodrec.category]) categories[foodrec.category] = {}; - if (foodrec.category && foodrec.subcategory) categories[foodrec.category][foodrec.subcategory] = true; + if (foodrec.category && !categories[foodrec.category]) { + categories[foodrec.category] = {}; + } + if (foodrec.category && foodrec.subcategory) { + categories[foodrec.category][foodrec.subcategory] = true; + } clearRec(); fillForm(); } @@ -459,14 +498,12 @@ xhr.send(dataJson); } else { // Update record - var dataJson = JSON.stringify(foodrec, null, ' '); - - var xhr = new XMLHttpRequest(); + xhr = new XMLHttpRequest(); xhr.open('PUT', '/api/v1/food/', true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText=='OK') { updateFoodArray(foodrec); clearRec(); @@ -476,7 +513,9 @@ xhr.send(dataJson); } - if (event) event.preventDefault(); + if (event) { + event.preventDefault(); + } return false; } @@ -493,7 +532,7 @@ xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText=='OK') { } } @@ -520,7 +559,7 @@ function quickpickCreateRecord(event) { try { - var newrec = clone(quickpickrec_template); + var newrec = _.cloneDeep(quickpickrec_template); if (!Nightscout.auth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); @@ -537,7 +576,7 @@ xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn("slow"); + $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText=='OK') { var newrec = JSON.parse(xhr.responseText)[0]; foodquickpick.push(newrec); @@ -564,12 +603,17 @@ for (var i=0; i Date: Thu, 13 Aug 2015 13:50:23 +0200 Subject: [PATCH 024/752] 2nd pass of refactoring, added hashauth because it was not in dev --- lib/language.js | 3 + static/css/food.css | 20 +++ static/food/js/food.js | 281 +++++++++++++++++++++-------------------- 3 files changed, 169 insertions(+), 135 deletions(-) diff --git a/lib/language.js b/lib/language.js index c610e2a96ee..964111edc31 100644 --- a/lib/language.js +++ b/lib/language.js @@ -4093,6 +4093,9 @@ function init() { ,'pcs' : { // pieces shortcut cs: 'ks' } + ,'Drag&drop food here' : { + cs: 'Sem táhni & pusť jídlo' + } ,'Update' : { // Update button cs: 'Aktualizovat' ,sv: 'Uppdatera' diff --git a/static/css/food.css b/static/css/food.css index 8e22064d3cc..7a11d63e3f8 100644 --- a/static/css/food.css +++ b/static/css/food.css @@ -26,4 +26,24 @@ .width200px { display: inline-block; width: 200px; +} + +#fe_qpfieldset { + cursor: move; + background-color: #383838; +} + +.fe_qpeditimg .fe_qpremoveimg .fe_editimg .fe_removeimg .fe_upimg .fe_qpfoodremoveimg { + cursor: pointer; +} + +.draggablefood { + background-color: gray; + cursor: move; + border: 2px solid #000; +} + +.fe_foodinsideqp { + background-color:gray; + border: 2px solid; } \ No newline at end of file diff --git a/static/food/js/food.js b/static/food/js/food.js index 1da423a52fa..34c3a9724d1 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -70,7 +70,7 @@ if (r.category && r.subcategory) { categories[r.category][r.subcategory] = true; } - } else if (r.type == 'quickpick') { + } else if (r.type === 'quickpick') { calculateCarbs(foodquickpick.push(r)-1); } else { console.log('Unknown food database record'); @@ -122,16 +122,12 @@ } else { $('#fe_editcreate').text(translate('Save record')); } - if (event) { - event.preventDefault(); - } + maybePreventDefault(event); } function fillSubcategories(event,editrec) { - if (event) { - event.preventDefault(); - GUIToObject(); - } + maybePreventDefault(event,GUIToObject); + if (!editrec) { filter.subcategory = ''; $('#fe_filter_subcategory').empty().append(new Option(translate('(none)'),'')); @@ -157,10 +153,13 @@ } } - function doEdit(index) { + function doEdit(event) { + var index = $(this).attr('index'); foodrec = _.cloneDeep(foodlist[index]); objectToGUI(); updateSaveButton(); + maybePreventDefault(event); + return false; } function updateFoodArray(newrec) { @@ -191,9 +190,7 @@ drawQuickpick(); objectToGUI(); - if (event) { - event.preventDefault(); - } + maybePreventDefault(event); return false; } @@ -201,64 +198,67 @@ if (event) { GUIToObject(); } - var html = ''; - $('#fe_data_header').html( - ''+ - ''+translate('Name')+''+ - ''+translate('Portion')+''+ - ''+translate('Unit')+''+ - ''+translate('Carbs')+' [g]'+ - ''+translate('GI')+' [1-3]'+ - ''+translate('Category')+''+ - ''+translate('Subcategory')+'' - ); + + $('#fe_data_header') + .empty() + .append($('').attr('class','width50px')) + .append($('').attr('class','width200px').append(translate('Name'))) + .append($('').attr('class','width150px').css('text-align','center').append(translate('Portion'))) + .append($('').attr('class','width50px').css('text-align','center').append(translate('Unit'))) + .append($('').attr('class','width100px').css('text-align','center').append(translate('Carbs'))) + .append($('').attr('class','width100px').css('text-align','center').append(translate('GI')+' [1-3]')) + .append($('').attr('class','width150px').append(translate('Category'))) + .append($('').attr('class','width150px').append(translate('Subcategory'))); + for (var i=0; i'; - html += ''; - html += ' '; - html += ''; - html += ''; - html += ''+foodlist[i].name+''; - html += ''+foodlist[i].portion+''; - html += ''+foodlist[i].unit+''; - html += ''+foodlist[i].carbs+''; - html += ''+foodlist[i].gi+''; - html += ''+foodlist[i].category+''; - html += ''+foodlist[i].subcategory+''; - html += ''; + + $('#fe_data') + .append($('
').attr('index',i).addClass('draggablefood') + .append($('').addClass('width50px') + .append($('').attr('title',translate('Edit record')).attr('src',icon_edit).attr('index',i).attr('class','fe_editimg')) + .append($('').attr('title',translate('Delete record')).attr('src',icon_remove).attr('index',i).attr('class','fe_removeimg')) + ) + .append($('').addClass('width200px').append(foodlist[i].name)) + .append($('').addClass('width150px').css('text-align','center').append(foodlist[i].portion)) + .append($('').addClass('width50px').css('text-align','center').append(foodlist[i].unit)) + .append($('').addClass('width100px').css('text-align','center').append(foodlist[i].carbs)) + .append($('').addClass('width100px').css('text-align','center').append(foodlist[i].gi)) + .append($('').addClass('width150px').append(foodlist[i].category)) + .append($('').addClass('width150px').append(foodlist[i].subcategory)) + ); } - html += ''; - $('#fe_data').html(html); - + $('.fe_editimg').click(doEdit); + $('.fe_removeimg').click(deleteFoodRecord); + $('.draggablefood').draggable({ helper: function(){ return $(this).clone().width($(this).width()).height($(this).height()); }, -// start: function () { g_actions_dragorigin = 'draggablefood'; }, scope: 'foodlist', revert: 'invalid' }); - - if (event) { - event.preventDefault(); - } + maybePreventDefault(event); } - function deleteQuickpickRecord(index) { + function deleteQuickpickRecord(event) { + var index = $(this).attr('index'); foodquickpicktodelete.push(foodquickpick[index]._id); foodquickpick.splice(index,1); drawQuickpick(); + maybePreventDefault(event); return false; } - function deleteFoodRecord(index) { + function deleteFoodRecord(event) { + var index = $(this).attr('index'); deleteRecord(foodlist[index]._id); foodlist.splice(index,1); fillForm(); + maybePreventDefault(event); return false; } @@ -274,9 +274,7 @@ function showHidden(event) { GUIToObject(); drawQuickpick(); - if (event) { - event.preventDefault(); - } + maybePreventDefault(event); return false; } @@ -286,47 +284,58 @@ for (var i=0; i'; - html += ''; - html += ''; - html += ' | '; - html += ' | '+translate('Name')+': '; - html += ' '+translate('Hidden'); - html += ' '+translate('Hide after use'); - html += ' | '+translate('Carbs')+': '+q.carbs.toFixed(0) + ' g'; - html += ''; -// html += '
'; - + $('#fe_picklist') + .append($('
').attr('id','fe_qpfieldset').addClass('sortablequickpick').attr('index',i).attr('_id',q._id) + .append($('') + .append($('').attr('title',translate('Move to the top')).attr('src',icon_up).attr('index',i).addClass('fe_qpupimg')) + .append(' | ') + .append($('').attr('title',translate('Delete record')).attr('src',icon_remove).attr('index',i).addClass('fe_qpremoveimg')) + .append(' | ' + translate('Name') + ': ') + .append($('').addClass('fe_qpname').attr('index',i).attr('value',q.name)) + .append($('').addClass('fq_hidden').attr('index',i).prop('checked',q.hidden ? 'checked' : '')) + .append(translate('Hidden')) + .append($('').addClass('fq_hideafteruse').attr('index',i).prop('checked',q.hideafteruse ? 'checked' : '')) + .append(translate('Hide after use')) + .append(' | ' + translate('Carbs') + ': ' + q.carbs.toFixed(0) + ' g') + ) + ) + if (q.foods.length) { - html += - ''+ - ''+translate('Name')+''+ - ''+translate('Portion')+' [g,ml]'+ - ''+translate('Carbs')+' [g]'+ - '
'; + $('#fe_qpfieldset') + .append($('').addClass('width50px')) + .append($('').addClass('width200px').append(translate('Name'))) + .append($('').addClass('width150px').css('text-align','center').append(translate('Portion'))) + .append($('').addClass('width100px').css('text-align','center').append(translate('Carbs'))) + .append($('
')); } else { - html += '-> Drag&drop food here'; + $('#fe_qpfieldset') + .append($('').append('-> ' + translate('Drag&drop food here'))); } for (var j=0;j'; - html += ''; - html += ''; - html += ''; - html += ''+r.name+''; - html += ''+r.portion+''; - html += ''+r.carbs+''; - html += ''+translate('Portions')+': '; - html += '
'; + $('#fe_qpfieldset') + .append($('
').attr('id','fqp_food_'+i+'_'+j).addClass('fe_foodinsideqp') + .append($('').addClass('width50px') + .append($('').attr('title',translate('Delete record')).attr('src',icon_remove).attr('index',i).attr('findex',j).addClass('fe_qpfoodremoveimg')) + ) + .append($('').addClass('width200px').append(r.name)) + .append($('').addClass('width150px').css('text-align','center').append(r.portion)) + .append($('').addClass('width100px').css('text-align','center').append(r.carbs)) + .append(translate('Portions')+': ') + .append($('').attr('id','fq_portions_'+q._id+'_'+j).attr('index',i).attr('findex',j).attr('value',r.portions).addClass('fe_qpportions')) + ) } - html += ''; } - $('#fe_picklist').html(html); + $('.fe_qpupimg').click(quickpickMoveToTop); + $('.fe_qpremoveimg').click(deleteQuickpickRecord); + $('.fe_qpfoodremoveimg').click(deleteQuickpickFood); + $('.fe_qpportions').change(savePortions); + $('#fe_quickpick_hiddencount').text(hiddentotal ? (' ('+hiddentotal+')') : ''); - $('.fq_name').change(function (event) { + $('.fe_qpname').change(function (event) { var index = $(this).attr('index'); foodquickpick[index].name = $(this).val(); event.preventDefault(); @@ -361,17 +370,24 @@ }); } - function savePortions(i,j,val) { - foodquickpick[i].foods[j].portions=val.replace(/\,/g,'.'); - calculateCarbs(i); + function savePortions(event) { + var index = $(this).attr('index'); + var findex = $(this).attr('findex'); + var val = parseFloat($(this).val().replace(/\,/g,'.')); + foodquickpick[index].foods[findex].portions=val; + calculateCarbs(index); drawQuickpick(); + event.preventDefault(); return false; } - function deleteQuickpickFood(index,findex) { + function deleteQuickpickFood(event) { + var index = $(this).attr('index'); + var findex = $(this).attr('findex'); foodquickpick[index].foods.splice(findex,1); calculateCarbs(index); drawQuickpick(); + event.preventDefault(); return false; } @@ -403,14 +419,16 @@ var oldArrayIndex = ui.item.attr('index'); var insertBeforArrayIndex = this.childNodes[newHtmlIndex+1].attributes.index.nodeValue; foodquickpick.splice(insertBeforArrayIndex, 0, foodquickpick.splice(oldArrayIndex, 1)[0]); - //drawQuickpick(); + maybePreventDefault(event); return; } - function quickpickMoveToTop(index) { + function quickpickMoveToTop(event) { + var index = $(this).attr('index'); foodquickpick.splice(0, 0, foodquickpick.splice(index, 1)[0]); drawQuickpick(); - return; + maybePreventDefault(event); + return false; } // fill GUI with values from object @@ -452,12 +470,10 @@ } function clearRec(event) { - if (event) { - event.preventDefault(); - } foodrec = _.cloneDeep(foodrec_template); objectToGUI(); updateSaveButton(); + maybePreventDefault(event); return false; } @@ -482,7 +498,7 @@ xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); xhr.onload = function () { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText=='OK') { + if (xhr.statusText==='OK') { var newrec = JSON.parse(xhr.responseText)[0]; foodlist.push(newrec); if (foodrec.category && !categories[foodrec.category]) { @@ -494,7 +510,7 @@ clearRec(); fillForm(); } - } + }; xhr.send(dataJson); } else { // Update record @@ -504,38 +520,33 @@ xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); xhr.onload = function () { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText=='OK') { + if (xhr.statusText==='OK') { updateFoodArray(foodrec); clearRec(); fillForm(); } - } + }; xhr.send(dataJson); } - - if (event) { - event.preventDefault(); - } + maybePreventDefault(event); return false; } function deleteRecord(_id) { - if (!Nightscout.auth.isAuthenticated()) { + if (!Nightscout.client.hashauth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); return false; } - var dataJson = JSON.stringify(_id, null, ' '); - var xhr = new XMLHttpRequest(); xhr.open('DELETE', '/api/v1/food/'+_id, true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); xhr.onload = function () { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText=='OK') { } - } + }; xhr.send(null); return false; @@ -553,42 +564,39 @@ var xhr = new XMLHttpRequest(); xhr.open('PUT', '/api/v1/food/', true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); xhr.send(dataJson); } function quickpickCreateRecord(event) { - try { - var newrec = _.cloneDeep(quickpickrec_template); - - if (!Nightscout.auth.isAuthenticated()) { - alert(translate('Your device is not authenticated yet')); - return false; - } - - // remove _id when creating new record - delete newrec._id; + var newrec = _.cloneDeep(quickpickrec_template); + + if (!Nightscout.auth.isAuthenticated()) { + alert(translate('Your device is not authenticated yet')); + return false; + } - var dataJson = JSON.stringify(newrec, null, ' '); + // remove _id when creating new record + delete newrec._id; + + var dataJson = JSON.stringify(newrec, null, ' '); - var xhr = new XMLHttpRequest(); - xhr.open('POST', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); - xhr.onload = function () { - $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText=='OK') { - var newrec = JSON.parse(xhr.responseText)[0]; - foodquickpick.push(newrec); - drawQuickpick(); - } + var xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.onload = function () { + $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); + if (xhr.statusText=='OK') { + var newrec = JSON.parse(xhr.responseText)[0]; + foodquickpick.push(newrec); + drawQuickpick(); } - xhr.send(dataJson); - - if (event) event.preventDefault(); - return false; + } + xhr.send(dataJson); - } catch (e) { alert(e.message); return false; } + maybePreventDefault(event); + return false; } function quickpickSave(event) { @@ -610,10 +618,7 @@ } updateRecord(fqp); } - - if (event) { - event.preventDefault(); - } + maybePreventDefault(event); return false; } @@ -621,4 +626,10 @@ return (v1v2?1:0)); } + function maybePreventDefault(event,after) { + if (event) { + event.preventDefault(); + after(); + } +} })(); \ No newline at end of file From 555045c1cfacfd8994fd21466a66a78c505f2170 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Thu, 13 Aug 2015 18:18:06 +0200 Subject: [PATCH 025/752] making codacy happy pass 2, some fixies --- static/css/food.css | 2 +- static/food/js/food.js | 93 +++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/static/css/food.css b/static/css/food.css index 7a11d63e3f8..000b9eedfbe 100644 --- a/static/css/food.css +++ b/static/css/food.css @@ -28,7 +28,7 @@ width: 200px; } -#fe_qpfieldset { +.sortablequickpick { cursor: move; background-color: #383838; } diff --git a/static/food/js/food.js b/static/food/js/food.js index 34c3a9724d1..3a7bc72fe53 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -91,13 +91,13 @@ $('#fe_id').change(updateSaveButton); $('#fe_quickpick_add').click(quickpickCreateRecord); $('#fe_quickpick_save').click(quickpickSave); - $('#fe_filter_category').change(fillSubcategories); + $('#fe_filter_category').change(fillFilterSubcategories); $('#fe_filter_subcategory').change(doFilter); $('#fe_quickpick_showhidden').change(showHidden); $('#fe_filter_name').on('input',doFilter); $('#fe_category_list').change(function(event) { $('#fe_category').val($('#fe_category_list').val()); - fillSubcategories(event,true); + fillEditSubcategories(event); }); $('#fe_subcategory_list').change(function(event) { $('#fe_subcategory').val($('#fe_subcategory_list').val()); @@ -125,32 +125,36 @@ maybePreventDefault(event); } - function fillSubcategories(event,editrec) { + function fillFilterSubcategories(event) { + var s; maybePreventDefault(event,GUIToObject); - if (!editrec) { - filter.subcategory = ''; - $('#fe_filter_subcategory').empty().append(new Option(translate('(none)'),'')); - if (filter.category !== '') { - for (s in categories[filter.category]) { - if (categories.hasOwnProperty(s)) { - $('#fe_filter_subcategory').append(new Option(s,s)); - } + filter.subcategory = ''; + $('#fe_filter_subcategory').empty().append(new Option(translate('(none)'),'')); + if (filter.category !== '') { + for (s in categories[filter.category]) { + if (categories.hasOwnProperty(s)) { + $('#fe_filter_subcategory').append(new Option(s,s)); } } - doFilter(); - } else { - foodrec.subcategory = ''; - $('#fe_subcategory_list').empty().append(new Option(translate('(none)'),'')); - if (foodrec.category !== '') { - for (s in categories[foodrec.category]) { - if (categories.hasOwnProperty(s)) { - $('#fe_subcategory_list').append(new Option(s,s)); - } + } + doFilter(); + } + + function fillEditSubcategories(event) { + var s; + maybePreventDefault(event,GUIToObject); + + foodrec.subcategory = ''; + $('#fe_subcategory_list').empty().append(new Option(translate('(none)'),'')); + if (foodrec.category !== '') { + for (s in categories[foodrec.category]) { + if (categories.hasOwnProperty(s)) { + $('#fe_subcategory_list').append(new Option(s,s)); } } - $('#fe_subcategory').val(''); } + $('#fe_subcategory').val(''); } function doEdit(event) { @@ -186,7 +190,7 @@ $('#fe_category_list').append(new Option(s,s)); } } - fillSubcategories(); + fillFilterSubcategories(); drawQuickpick(); objectToGUI(); @@ -262,15 +266,6 @@ return false; } - function quickpickFindById(_id) { - for (var i=0; i').attr('id','fe_qpfieldset').addClass('sortablequickpick').attr('index',i).attr('_id',q._id) + .append($('
').attr('id','fe_qpfieldset_'+i).addClass('sortablequickpick').attr('index',i).attr('_id',q._id) .append($('') .append($('').attr('title',translate('Move to the top')).attr('src',icon_up).attr('index',i).addClass('fe_qpupimg')) .append(' | ') @@ -298,23 +293,23 @@ .append(translate('Hide after use')) .append(' | ' + translate('Carbs') + ': ' + q.carbs.toFixed(0) + ' g') ) - ) + ); if (q.foods.length) { - $('#fe_qpfieldset') + $('#fe_qpfieldset_'+i) .append($('').addClass('width50px')) .append($('').addClass('width200px').append(translate('Name'))) .append($('').addClass('width150px').css('text-align','center').append(translate('Portion'))) .append($('').addClass('width100px').css('text-align','center').append(translate('Carbs'))) .append($('
')); } else { - $('#fe_qpfieldset') + $('#fe_qpfieldset_'+i) .append($('').append('-> ' + translate('Drag&drop food here'))); } for (var j=0;j').attr('id','fqp_food_'+i+'_'+j).addClass('fe_foodinsideqp') .append($('').addClass('width50px') .append($('').attr('title',translate('Delete record')).attr('src',icon_remove).attr('index',i).attr('findex',j).addClass('fe_qpfoodremoveimg')) @@ -324,7 +319,7 @@ .append($('').addClass('width100px').css('text-align','center').append(r.carbs)) .append(translate('Portions')+': ') .append($('').attr('id','fq_portions_'+q._id+'_'+j).attr('index',i).attr('findex',j).attr('value',r.portions).addClass('fe_qpportions')) - ) + ); } } @@ -415,11 +410,13 @@ } function resortArray(event,ui) { - var newHtmlIndex = ui.item.index(); + var newHtmlIndex = ui.item.index(); var oldArrayIndex = ui.item.attr('index'); - var insertBeforArrayIndex = this.childNodes[newHtmlIndex+1].attributes.index.nodeValue; - foodquickpick.splice(insertBeforArrayIndex, 0, foodquickpick.splice(oldArrayIndex, 1)[0]); +// var insertBeforArrayIndex = this.childNodes[newHtmlIndex+1].attributes.index.nodeValue; +// foodquickpick.splice(insertBeforArrayIndex, 0, foodquickpick.splice(oldArrayIndex, 1)[0]); + foodquickpick.splice(newHtmlIndex, 0, foodquickpick.splice(oldArrayIndex, 1)[0]); maybePreventDefault(event); + console.log(foodquickpick); return; } @@ -482,6 +479,7 @@ if (!Nightscout.auth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); + maybePreventDefault(event); return false; } @@ -535,6 +533,7 @@ function deleteRecord(_id) { if (!Nightscout.client.hashauth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); + maybePreventDefault(event); return false; } @@ -544,7 +543,7 @@ xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); xhr.onload = function () { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText=='OK') { + if (xhr.statusText==='OK') { } }; xhr.send(null); @@ -556,6 +555,7 @@ function updateRecord(foodrec) { if (!Nightscout.auth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); + maybePreventDefault(event); return false; } @@ -573,6 +573,7 @@ if (!Nightscout.auth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); + maybePreventDefault(event); return false; } @@ -587,12 +588,12 @@ xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); xhr.onload = function () { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText=='OK') { + if (xhr.statusText==='OK') { var newrec = JSON.parse(xhr.responseText)[0]; foodquickpick.push(newrec); drawQuickpick(); } - } + }; xhr.send(dataJson); maybePreventDefault(event); @@ -609,7 +610,7 @@ deleteRecord(foodquickpicktodelete[i]); } - for (var i=0; i Date: Fri, 14 Aug 2015 00:24:00 +0200 Subject: [PATCH 026/752] making codacy happy pass 3 --- static/food/js/food.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static/food/js/food.js b/static/food/js/food.js index 3a7bc72fe53..abd5f89a45b 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -630,7 +630,9 @@ function maybePreventDefault(event,after) { if (event) { event.preventDefault(); - if (after) after(); + } + if (after) { + after(); } } })(); \ No newline at end of file From f7a3332f7127ca2963293993f9a6c2d60ebc14e5 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Fri, 14 Aug 2015 09:10:01 +0200 Subject: [PATCH 027/752] name anonymous functions --- static/food/js/food.js | 65 ++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/static/food/js/food.js b/static/food/js/food.js index abd5f89a45b..7ba9ef43db8 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -60,8 +60,8 @@ // Fetch data from mongo $('#fe_status').hide().text('Loading food database ...').fadeIn('slow'); $.ajax('/api/v1/food.json', { - success: function (records) { - records.forEach(function (r) { + success: function ajaxSuccess(records) { + records.forEach(function processRecords(r) { if (r.type === 'food') { foodlist.push(r); if (r.category && !categories[r.category]) { @@ -77,9 +77,9 @@ } }); $('#fe_status').hide().text(translate('Database loaded')).fadeIn('slow'); - foodquickpick.sort(function (a,b) { return cmp(parseInt(a.position),parseInt(b.position)) }); + foodquickpick.sort(function compare(a,b) { return cmp(parseInt(a.position),parseInt(b.position)) }); }, - error: function () { + error: function ajaxError() { $('#fe_status').hide().text(translate('Error: Database failed to load')).fadeIn('slow'); } }).done(initeditor); @@ -95,11 +95,11 @@ $('#fe_filter_subcategory').change(doFilter); $('#fe_quickpick_showhidden').change(showHidden); $('#fe_filter_name').on('input',doFilter); - $('#fe_category_list').change(function(event) { + $('#fe_category_list').change(function categoryListChange(event) { $('#fe_category').val($('#fe_category_list').val()); fillEditSubcategories(event); }); - $('#fe_subcategory_list').change(function(event) { + $('#fe_subcategory_list').change(function subcategoryListChange(event) { $('#fe_subcategory').val($('#fe_subcategory_list').val()); event.preventDefault(); }); @@ -239,7 +239,7 @@ $('.fe_removeimg').click(deleteFoodRecord); $('.draggablefood').draggable({ - helper: function(){ + helper: function keepDraggedSize(){ return $(this).clone().width($(this).width()).height($(this).height()); }, scope: 'foodlist', @@ -274,6 +274,25 @@ } function drawQuickpick() { + + function addHeaderOrHint(q,i) { + if (q.foods.length) { + $('#fe_qpfieldset_'+i) + .append($('').addClass('width50px')) + .append($('').addClass('width200px').append(translate('Name'))) + .append($('').addClass('width150px').css('text-align','center').append(translate('Portion'))) + .append($('').addClass('width100px').css('text-align','center').append(translate('Carbs'))) + .append($('
')); + } else { + $('#fe_qpfieldset_'+i) + .append($('').append('-> ' + translate('Drag&drop food here'))); + } + } + + function addHiddenCount(hiddentotal) { + $('#fe_quickpick_hiddencount').text(hiddentotal ? (' ('+hiddentotal+')') : ''); + } + var hiddentotal = 0; $('#fe_picklist').empty(); for (var i=0; i').addClass('width50px')) - .append($('').addClass('width200px').append(translate('Name'))) - .append($('').addClass('width150px').css('text-align','center').append(translate('Portion'))) - .append($('').addClass('width100px').css('text-align','center').append(translate('Carbs'))) - .append($('
')); - } else { - $('#fe_qpfieldset_'+i) - .append($('').append('-> ' + translate('Drag&drop food here'))); - } - + addHeaderOrHint(q,i); + for (var j=0;j Date: Fri, 14 Aug 2015 14:56:03 +0200 Subject: [PATCH 028/752] fixing bugs --- lib/api/food/index.js | 2 +- lib/food.js | 2 +- static/css/food.css | 6 ++++++ static/food/index.html | 6 +++--- static/food/js/food.js | 42 +++++++++++++++++++++++------------------- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/api/food/index.js b/lib/api/food/index.js index 2fef129cab5..8d8f32dbc36 100644 --- a/lib/api/food/index.js +++ b/lib/api/food/index.js @@ -46,7 +46,7 @@ function configure (app, wares, ctx) { console.log(err); } else { res.json(created); - console.log('food created'); + console.log('food created',created); } }); }); diff --git a/lib/food.js b/lib/food.js index f916708fe17..84ecc962011 100644 --- a/lib/food.js +++ b/lib/food.js @@ -6,7 +6,7 @@ function storage (env, ctx) { function create (obj, fn) { obj.created_at = (new Date( )).toISOString( ); api().insert(obj, function (err, doc) { - fn(null, doc); + fn(null, doc.ops); }); } diff --git a/static/css/food.css b/static/css/food.css index 000b9eedfbe..25a8d99c068 100644 --- a/static/css/food.css +++ b/static/css/food.css @@ -8,6 +8,12 @@ margin-left: auto; text-align: center; } + +#fe_form { + margin-left: 10px; + margin-right: 10px; +} + .width50px { display: inline-block; width: 50px; diff --git a/static/food/index.html b/static/food/index.html index 89216e79ff6..4a1e9b30d60 100644 --- a/static/food/index.html +++ b/static/food/index.html @@ -8,7 +8,7 @@ - +
@@ -22,7 +22,7 @@

Food editor


-
+
Your database
@@ -88,7 +88,7 @@

Food editor

Authentication status: - +
diff --git a/static/food/js/food.js b/static/food/js/food.js index 7ba9ef43db8..2fbe19562d6 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -148,12 +148,10 @@ foodrec.subcategory = ''; $('#fe_subcategory_list').empty().append(new Option(translate('(none)'),'')); if (foodrec.category !== '') { - for (s in categories[foodrec.category]) { - if (categories.hasOwnProperty(s)) { - $('#fe_subcategory_list').append(new Option(s,s)); - } + _(categories[foodrec.category]).forEach(function addSubcatOption(s) { + $('#fe_subcategory_list').append(new Option(s,s)); } - } + )}; $('#fe_subcategory').val(''); } @@ -214,6 +212,8 @@ .append($('').attr('class','width150px').append(translate('Category'))) .append($('').attr('class','width150px').append(translate('Subcategory'))); + $('#fe_data').empty(); + for (var i=0; i-1) { // remove _id when creating new record delete foodrec._id; + dataJson = JSON.stringify(foodrec, null, ' '); xhr = new XMLHttpRequest(); xhr.open('POST', '/api/v1/food/', true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); xhr.onload = function onload() { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText==='OK') { @@ -521,10 +524,11 @@ xhr.send(dataJson); } else { // Update record + dataJson = JSON.stringify(foodrec, null, ' '); xhr = new XMLHttpRequest(); xhr.open('PUT', '/api/v1/food/', true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); xhr.onload = function onload() { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText==='OK') { @@ -562,7 +566,7 @@ } function updateRecord(foodrec) { - if (!Nightscout.auth.isAuthenticated()) { + if (!Nightscout.client.hashauth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); maybePreventDefault(event); return false; @@ -580,7 +584,7 @@ function quickpickCreateRecord(event) { var newrec = _.cloneDeep(quickpickrec_template); - if (!Nightscout.auth.isAuthenticated()) { + if (!Nightscout.client.hashauth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); maybePreventDefault(event); return false; @@ -594,7 +598,7 @@ var xhr = new XMLHttpRequest(); xhr.open('POST', '/api/v1/food/', true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.auth.hash()); + xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); xhr.onload = function onload() { $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); if (xhr.statusText==='OK') { @@ -610,7 +614,7 @@ } function quickpickSave(event) { - if (!Nightscout.auth.isAuthenticated()) { + if (!Nightscout.client.hashauth.isAuthenticated()) { alert(translate('Your device is not authenticated yet')); return false; } From 8c56b2bfcc8fe32ee737a6d726c1aae09ce575be Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Fri, 14 Aug 2015 20:51:51 +0200 Subject: [PATCH 029/752] more bugs solved, refactor to $.jax --- static/css/food.css | 3 +- static/food/js/food.js | 180 +++++++++++++++++++++++------------------ 2 files changed, 105 insertions(+), 78 deletions(-) diff --git a/static/css/food.css b/static/css/food.css index 25a8d99c068..c05972d93d7 100644 --- a/static/css/food.css +++ b/static/css/food.css @@ -37,9 +37,10 @@ .sortablequickpick { cursor: move; background-color: #383838; + border-radius: 8px; } -.fe_qpeditimg .fe_qpremoveimg .fe_editimg .fe_removeimg .fe_upimg .fe_qpfoodremoveimg { +.fe_qpeditimg, .fe_qpremoveimg, .fe_editimg, .fe_removeimg, .fe_qpupimg, .fe_qpfoodremoveimg { cursor: pointer; } diff --git a/static/food/js/food.js b/static/food/js/food.js index 2fbe19562d6..74cc6ffb334 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -1,5 +1,11 @@ 'use strict'; +//for the tests window isn't the global object +var $ = window.$; +var _ = window._; +var moment = window.moment; +var Nightscout = window.Nightscout; + (function () { if (serverSettings === undefined) { @@ -19,7 +25,7 @@ , portion: 0 , carbs: 0 , gi: 2 - , unit: '' + , unit: 'g' }; var foodunits = ['g', 'ml', 'pcs']; @@ -56,12 +62,19 @@ var showhidden = false; var categories = {}; - + function restoreBoolValue(record,key) { + if (typeof record[key] !== 'undefined') { + record[key] = record[key] === "true"; + } + } + // Fetch data from mongo $('#fe_status').hide().text('Loading food database ...').fadeIn('slow'); $.ajax('/api/v1/food.json', { success: function ajaxSuccess(records) { records.forEach(function processRecords(r) { + restoreBoolValue(r,'hidden'); + restoreBoolValue(r,'hideafteruse'); if (r.type === 'food') { foodlist.push(r); if (r.category && !categories[r.category]) { @@ -72,6 +85,7 @@ } } else if (r.type === 'quickpick') { calculateCarbs(foodquickpick.push(r)-1); + console.log('Loaded quick pick: ',r); } else { console.log('Unknown food database record'); } @@ -142,7 +156,6 @@ } function fillEditSubcategories(event) { - var s; maybePreventDefault(event,GUIToObject); foodrec.subcategory = ''; @@ -306,9 +319,9 @@ .append($('').attr('title',translate('Delete record')).attr('src',icon_remove).attr('index',i).addClass('fe_qpremoveimg')) .append(' | ' + translate('Name') + ': ') .append($('').addClass('fe_qpname').attr('index',i).attr('value',q.name)) - .append($('').addClass('fq_hidden').attr('index',i).prop('checked',q.hidden ? 'checked' : '')) + .append($('').addClass('fq_hidden').attr('index',i).prop('checked',q.hidden)) .append(translate('Hidden')) - .append($('').addClass('fq_hideafteruse').attr('index',i).prop('checked',q.hideafteruse ? 'checked' : '')) + .append($('').addClass('fq_hideafteruse').attr('index',i).prop('checked',q.hideafteruse)) .append(translate('Hide after use')) .append(' | ' + translate('Carbs') + ': ' + q.carbs.toFixed(0) + ' g') ) @@ -413,8 +426,12 @@ function calculateCarbs(index) { var qp = foodquickpick[index]; qp.carbs = 0; - for (var j=0;j-1) { // remove _id when creating new record delete foodrec._id; - dataJson = JSON.stringify(foodrec, null, ' '); - xhr = new XMLHttpRequest(); - xhr.open('POST', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); - xhr.onload = function onload() { - $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText==='OK') { - var newrec = JSON.parse(xhr.responseText)[0]; - foodlist.push(newrec); - if (foodrec.category && !categories[foodrec.category]) { - categories[foodrec.category] = {}; - } - if (foodrec.category && foodrec.subcategory) { - categories[foodrec.category][foodrec.subcategory] = true; - } - clearRec(); - fillForm(); + $.ajax({ + method: 'POST', + url: '/api/v1/food/', + data: foodrec, + beforeSend: function (request) + { + request.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); + }, + }).done(function success (response) { + $('#fe_status').hide().text('OK').fadeIn('slow'); + foodrec._id = response[0]._id; + foodlist.push(foodrec); + if (foodrec.category && !categories[foodrec.category]) { + categories[foodrec.category] = {}; + } + if (foodrec.category && foodrec.subcategory) { + categories[foodrec.category][foodrec.subcategory] = true; } - }; - xhr.send(dataJson); + clearRec(); + fillForm(); + }).fail(function fail() { + $('#fe_status').hide().text(translate('Error')).fadeIn('slow'); + }); } else { // Update record - dataJson = JSON.stringify(foodrec, null, ' '); - xhr = new XMLHttpRequest(); - xhr.open('PUT', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); - xhr.onload = function onload() { - $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText==='OK') { - updateFoodArray(foodrec); - clearRec(); - fillForm(); - } - }; - xhr.send(dataJson); + $.ajax({ + method: 'PUT', + url: '/api/v1/food/', + data: foodrec, + beforeSend: function (request) + { + request.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); + }, + }).done(function success (response) { + $('#fe_status').hide().text('OK').fadeIn('slow'); + updateFoodArray(foodrec); + clearRec(); + fillForm(); + }).fail(function fail() { + $('#fe_status').hide().text(translate('Error')).fadeIn('slow'); + }); } maybePreventDefault(event); return false; @@ -549,20 +568,22 @@ maybePreventDefault(event); return false; } - - var xhr = new XMLHttpRequest(); - xhr.open('DELETE', '/api/v1/food/'+_id, true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); - xhr.onload = function onload() { - $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText==='OK') { - } - }; - xhr.send(null); - return false; + $.ajax({ + method: 'DELETE', + url: '/api/v1/food/'+_id, + data: foodrec, + beforeSend: function (request) + { + request.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); + }, + }).done(function success (response) { + $('#fe_status').hide().text('OK').fadeIn('slow'); + }).fail(function fail() { + $('#fe_status').hide().text(translate('Error')).fadeIn('slow'); + }); + return false; } function updateRecord(foodrec) { @@ -572,13 +593,17 @@ return false; } - var dataJson = JSON.stringify(foodrec, null, ' '); - - var xhr = new XMLHttpRequest(); - xhr.open('PUT', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); - xhr.send(dataJson); + $.ajax({ + method: 'PUT', + url: '/api/v1/food/', + data: foodrec, + beforeSend: function (request) + { + request.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); + }, + }).done(function success (response) { + console.log('Updated record: ',response); + }); } function quickpickCreateRecord(event) { @@ -593,21 +618,22 @@ // remove _id when creating new record delete newrec._id; - var dataJson = JSON.stringify(newrec, null, ' '); - - var xhr = new XMLHttpRequest(); - xhr.open('POST', '/api/v1/food/', true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); - xhr.onload = function onload() { - $('#fe_status').hide().text(xhr.statusText).fadeIn('slow'); - if (xhr.statusText==='OK') { - var newrec = JSON.parse(xhr.responseText)[0]; - foodquickpick.push(newrec); - drawQuickpick(); - } - }; - xhr.send(dataJson); + $.ajax({ + method: 'POST', + url: '/api/v1/food/', + data: newrec, + beforeSend: function (request) + { + request.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); + }, + }).done(function success (response) { + $('#fe_status').hide().text('OK').fadeIn('slow'); + newrec._id = response[0]._id; + foodquickpick.push(newrec); + drawQuickpick(); + }).fail(function fail() { + $('#fe_status').hide().text(translate('Error')).fadeIn('slow'); + }); maybePreventDefault(event); return false; From 0d915077907b4e5753dd23fea13405016fdbd985 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Fri, 14 Aug 2015 22:45:17 +0200 Subject: [PATCH 030/752] codacy --- static/food/js/food.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/static/food/js/food.js b/static/food/js/food.js index 74cc6ffb334..e9995367af3 100644 --- a/static/food/js/food.js +++ b/static/food/js/food.js @@ -3,7 +3,6 @@ //for the tests window isn't the global object var $ = window.$; var _ = window._; -var moment = window.moment; var Nightscout = window.Nightscout; (function () { @@ -64,7 +63,7 @@ var Nightscout = window.Nightscout; function restoreBoolValue(record,key) { if (typeof record[key] !== 'undefined') { - record[key] = record[key] === "true"; + record[key] = record[key] === 'true'; } } @@ -549,7 +548,7 @@ var Nightscout = window.Nightscout; { request.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); }, - }).done(function success (response) { + }).done(function success () { $('#fe_status').hide().text('OK').fadeIn('slow'); updateFoodArray(foodrec); clearRec(); @@ -577,7 +576,7 @@ var Nightscout = window.Nightscout; { request.setRequestHeader('api-secret', Nightscout.client.hashauth.hash()); }, - }).done(function success (response) { + }).done(function success () { $('#fe_status').hide().text('OK').fadeIn('slow'); }).fail(function fail() { $('#fe_status').hide().text(translate('Error')).fadeIn('slow'); From e0750e0889a2dd56724ee277e2dd9d31da916f93 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Sun, 30 Aug 2015 23:18:19 +0200 Subject: [PATCH 031/752] fix language.js after rebase --- lib/language.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/language.js b/lib/language.js index 964111edc31..f039097da62 100644 --- a/lib/language.js +++ b/lib/language.js @@ -4096,7 +4096,7 @@ function init() { ,'Drag&drop food here' : { cs: 'Sem táhni & pusť jídlo' } - ,'Update' : { // Update button + ,'Update' : { // Update button cs: 'Aktualizovat' ,sv: 'Uppdatera' ,nb: 'Oppdater' From bacefd4aae9ccf8c46e74f8a084782c1bee54138 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Fri, 11 Sep 2015 20:19:38 +0200 Subject: [PATCH 032/752] fix in reports --- static/report/js/report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/report/js/report.js b/static/report/js/report.js index 97b60c020db..3cae18ca326 100644 --- a/static/report/js/report.js +++ b/static/report/js/report.js @@ -64,7 +64,7 @@ }); filter.category = ''; fillFoodSubcategories(); - + $('#rp_category').change(fillFoodSubcategories); $('#rp_subcategory').change(doFoodFilter); $('#rp_name').on('input',doFoodFilter); From 71b09361865b8432d7ba39c54651c25743952fd2 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Fri, 11 Sep 2015 20:54:53 +0200 Subject: [PATCH 033/752] boluscalc files merge --- lib/client/boluscalc.js | 720 +++++++++++++++++++++++++++++++++++++++ lib/client/careportal.js | 2 +- lib/client/index.js | 4 + lib/language.js | 5 +- static/css/drawer.css | 42 ++- static/index.html | 266 +++++++++++++++ 6 files changed, 1036 insertions(+), 3 deletions(-) create mode 100644 lib/client/boluscalc.js diff --git a/lib/client/boluscalc.js b/lib/client/boluscalc.js new file mode 100644 index 00000000000..72b7147a14f --- /dev/null +++ b/lib/client/boluscalc.js @@ -0,0 +1,720 @@ +'use strict'; + +var moment = require('moment-timezone'); + +function init(client, $) { + var boluscalc = { }; + + var translate = client.translate; + var storage = $.localStorage; + var iob = Nightscout.plugins('iob'); + var cob = Nightscout.plugins('cob'); + + var eventTime = $('#bc_eventTimeValue'); + var eventDate = $('#bc_eventDateValue'); + + var quickpicks = []; + var foods = []; + + var icon_remove = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACrElEQVQ4T42Ty2sTQRzHv5tmk2yyjRNtpfZhL8V6s2KoUNC2XqwgaCsVQcGiFqpHi0c9iRdR/ANE9KR40FIQX4cueKoPaKFoLdSYNtE0abKT1+5s9iW7aUMiHtzTzO7v85md+c6PA4DrHbsPCKIgOWO1pA7dT6YXnXH949SE/F63pqwZtRrO+SCKgjQ5NUV+azpmHj2krMwaJC4c8Erj+/eRyloMMwWFKgbn1nC3ervlK1evkXBLGBZT8SOewotnTylTNLdgeg/pDgZDC2cPHSR8bB22DVC9hFe0SG/H0xFXcHlykjRHRDBWgJcZSCY38Xx2lhqMnRYE34Px/sN9vlQWeoHBAx2yXsRruVAVuFsIBaSJ8+eJGPaBqQV4NROJjTzez89jLBoFn6FgybQL54wS3uTyVDFQ3cL2IYpBv3RhdJSIIQ80tQyv7gEqJvS8AmUlBs7UXPhtjtZgh3UFNYngk86NHCfNAg9dMwHVBPu+CpsVkTXKeJeVG+AGgTOZ3tt6MSKKjy+NjEBjFrR4ElZmA4pdxstMFsyyJu6tZZ7Ux9vwB6EAL50ZGiRECEPPUOixVTRxHlicgSVWxEdZpuZWfNuS2hk48NjwMIkIYZglBnV5Cbqtws/5IaAJmsfCglrEl2y2QeKmEBJ80tixKmxrFpSVr0gV0viQoxho2YUuPohmeFD22PiklLC4ma5JuBvdrfLJI0dJd0s7bM0ES8aR/BXDXGaTskqlL+D3Lwy0tZEePoAd4EA5YF4tYymdonfjmQh3s6dTPjU4SHYGwjAKecSXFyGlM1TdytntE56T+ts7SC/vhw3gm6njc2Kd3vm5Ub1IwQAvnYhGiZpYw1wiWYPrIw7wnBTt7CLOOwdmut14kQQvqt24tfK/utGR6LaF+iRqMf4N/O/8D28HiiCRYqzAAAAAAElFTkSuQmCC'; + + function roundTo (x, step) { + return Math.round(x / step) * step; + } + + function maybePrevent (event) { + if (event) { + event.preventDefault(); + } + } + + function isTouch() { + try { document.createEvent('TouchEvent'); return true; } + catch (e) { return false; } + } + + function setDateAndTime (time) { + time = time || moment(); + eventTime.val(time.format('HH:mm')); + eventDate.val(time.format('YYYY-MM-DD')); + } + + function mergeDateAndTime ( ) { + return client.utils.mergeInputTime(eventTime.val(), eventDate.val()); + } + + function updateTime(ele, time) { + ele.attr('oldminutes', time.minutes()); + ele.attr('oldhours', time.hours()); + } + + boluscalc.updateVisualisations = function updateVisualisations (sbx) { + boluscalc.lastsbx = sbx; + boluscalc.oldbg = false; + var eventTime = mergeDateAndTime().toDate(); + var sensorbg = 0; + var lastSGV = _.last(sbx.data.sgvs); + if (lastSGV) { + sensorbg = lastSGV.mgdl; + if (sensorbg < 39) sensorbg = 0; + else sensorbg = client.utils.scaleMgdl(sensorbg); + if (eventTime.getTime() - lastSGV.mills > 10 * 60 * 1000) { + boluscalc.oldbg = true; // Do not use if record is older than 10 min + sensorbg = 0; + } + } + + // Set BG + if ($('#bc_bgfromsensor').is(':checked')) { + $('#bc_bg').val(sensorbg ? sensorbg : ''); +// console.log('New BG received', sensorbg, 'Sec diff', (new Date().getTime() - lastSGV.mills)/1000); +// console.log('Current', new Date().toLocaleString(), 'LastSGV', new Date(lastSGV.mills).toLocaleString()); + } + boluscalc.calculateInsulin(); + } + + boluscalc.dateTimeFocus = function dateTimeFocus(event) { + $('#bc_othertime').prop('checked', true); + updateTime($(this), mergeDateAndTime()); + maybePrevent(event); + }; + + boluscalc.dateTimeChange = function dateTimeChange(event) { + $('#bc_othertime').prop('checked', true); + boluscalc.calculateInsulin(); +// client.utils.setYAxisOffset(50); //50% of extend + var ele = $(this); + var merged = mergeDateAndTime(); + + if (ele.attr('oldminutes') === '59' && merged.minutes() === 0) { + merged.add(1, 'hours'); + } + if (ele.attr('oldminutes') === '0' && merged.minutes() === 59) { + merged.add(-1, 'hours'); + } + + setDateAndTime(merged); + updateTime(ele, merged); + maybePrevent(event); +// Nightscout.utils.updateBrushToTime(moment.toDate()); + }; + + boluscalc.eventTimeTypeChange = function eventTimeTypeChange(event) { + if ($('#bc_othertime').is(':checked')) { + $('#bc_eventTimeValue').focus(); + $('#bc_retro').css('display',''); + } else { + $('#bc_retro').css('display','none'); + boluscalc.calculateInsulin(); +// Nightscout.utils.setYAxisOffset(50); //50% of extend +// Nightscout.utils.updateBrushToTime(Nightscout.utils.mergeInputTime($('#bc_eventTimeValue').val(), $('#bc_eventDateValue').val()).toDate()); + } + maybePrevent(event); + }; + + boluscalc.toggleDrawer = function toggleDrawer (event) { + client.browserUtils.toggleDrawer('#boluscalcDrawer', boluscalc.prepare); + maybePrevent(event); + }; + + boluscalc.prepare = function prepare( ) { + foods = []; + $('#bc_usebg').prop('checked','checked'); + $('#bc_usecarbs').prop('checked','checked'); + $('#bc_usecob').prop('checked',''); + $('#bc_useiob').prop('checked','checked'); + $('#bc_bgfromsensor').prop('checked','checked'); + $('#bc_carbs').val(''); + $('#bc_quickpick').val(-1); + $('#bc_preBolus').val(0); + $('#bc_notes').val(''); + $('#bc_enteredBy').val($.localStorage.get('enteredBy') || ''); + $('#bc_nowtime').prop('checked', true); + $('#bc_othercorrection').val(0); + setDateAndTime(); + if (boluscalc.lastsbx) { + boluscalc.updateVisualisations(boluscalc.lastsbx); + } + boluscalc.calculateInsulin(); + } + + boluscalc.calculateInsulin = function calculateInsulin (event) { + maybePrevent(event); + boluscalc.gatherBoluscalcData( ); + boluscalc.updateGui(boluscalc.record); + return boluscalc.record; + } + + boluscalc.updateGui = function updateGui (record) { + record = record || boluscalc.record; + + if (record.eventTime === undefined) { + return; + } + + var targetBGLow = record.targetBGLow; + var targetBGHigh = record.targetBGHigh; + var isf = record.isf; + var ic = record.ic; + + // Clear results before calculation + $('#bc_insulintotal').text('0.00'); + $('#bc_carbsneeded').text('0.00'); + $('#bc_inzulinbg').text('0.00'); + $('#bc_inzulincarbs').text('0.00'); + + // Show IOB + if ($('#bc_useiob').is(':checked')) { + $('#bc_iob').text((record.iob > 0 ? '-' : '') + record.iob.toFixed(2)); + } else { + $('#bc_iob').text(''); + } + + // Show COB + if ($('#bc_usecob').is(':checked')) { + $('#bc_cob').text(record.cob.toFixed(2)); + $('#bc_cobu').text(record.insulincob.toFixed(2)); + } else { + $('#bc_cob').text(''); + $('#bc_cobu').text(''); + } + + // Show BG + if ($('#bc_usebg').is(':checked')) { + if (record.bg === 0 || (boluscalc.oldbg && $('#bc_bgfromsensor').is(':checked'))) { + $('#bc_bg').css('background-color', 'red'); + } else { + $('#bc_bg').css('background-color', ''); + } + $('#bc_inzulinbg').text(record.insulinbg.toFixed(2)); + $('#bc_inzulinbg').attr('title', + 'Target BG range: '+targetBGLow + ' - ' + targetBGHigh + + '\nISF: ' + isf + + '\nBG diff: ' + record.bgdiff.toFixed(1) + ); + } else { + $('#bc_inzulinbgtd').css('background-color', ''); + $('#bc_bg').css('background-color', ''); + $('#bc_inzulinbg').text(''); + $('#bc_inzulinbg').attr('title', ''); + } + + // Show foods + if (record.foods.length) { + var html = ''; + var carbs = 0; + for (var fi=0; fi'; + } + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + } + html += '
'+ f.name + ''+ (f.portion*f.portions).toFixed(1) + ' ' + translate(f.unit) + '('+ (f.carbs*f.portions).toFixed(1) + ' g)
'; + $('#bc_food').html(html); + $('.deleteFoodRecord').click(deleteFoodRecord); + $('#bc_carbs').val(carbs.toFixed(0)); + $('#bc_carbs').attr('disabled',true); + $('#bc_gi').css('display','none'); + $('#bc_gicalculated').css('display',''); + $('#bc_gicalculated').text(record.gi); + } else { + $('#bc_food').html(''); + $('#bc_carbs').attr('disabled',false); + $('#bc_gi').css('display',''); + $('#bc_gicalculated').css('display','none'); + $('#bc_gicalculated').text(''); + } + + // Show Carbs + if ($('#bc_usecarbs').is(':checked')) { + if ($('#bc_carbs').val() === '') { + $('#bc_carbs').css('background-color',''); + } else if (isNaN(parseInt($('#bc_carbs').val().replace(',','.')))) { + $('#bc_carbs').css('background-color','red'); + } else { + $('#bc_carbs').css('background-color',''); + } + $('#bc_inzulincarbs').text(record.insulincarbs.toFixed(2)); + $('#bc_inzulincarbs').attr('title','IC: ' + ic); + } else { + $('#bc_carbs').css('background-color',''); + $('#bc_inzulincarbs').text(''); + $('#bc_inzulincarbs').attr('title',''); + $('#bc_carbs').text(''); + } + + // Show Total + $('#bc_rouding').text(record.roundingcorrection.toFixed(2)); + $('#bc_insulintotal').text(record.insulin.toFixed(2)); + + // Carbs needed if too much iob + if (record.insulin<0) { + $('#bc_carbsneeded').text(record.carbsneeded+' g'); + $('#bc_insulinover').text(record.insulin.toFixed(2)); + $('#bc_carbsneededtr').css('display',''); + $('#bc_insulinneededtr').css('display','none'); + } else { + $('#bc_carbsneeded').text(''); + $('#bc_insulinover').text(''); + $('#bc_carbsneededtr').css('display','none'); + $('#bc_insulinneededtr').css('display',''); + } + + // Show basal rate + $('#bc_basal').text(client.sbx.data.profile.getBasal(record.eventTime).toFixed(3)); + } + + boluscalc.gatherBoluscalcData = function gatherBoluscalcData() { + boluscalc.record = {}; + var record = boluscalc.record; + + if (!client.sbx) { + console.log('No sandbox data yet. Exiting gatherData()'); + return; + } + + // Calculate event time from date & time + record.eventTime = new Date(); + if ($('#othertime').is(':checked')) { + record.eventTime = mergeDateAndTime().toDate(); + } + + // Load profile + var targetBGLow = client.sbx.data.profile.getLowBGTarget(record.eventTime); + var targetBGHigh = client.sbx.data.profile.getHighBGTarget(record.eventTime); + var isf = client.sbx.data.profile.getSensitivity(record.eventTime); + var ic = client.sbx.data.profile.getCarbRatio(record.eventTime); + record.targetBGLow = targetBGLow; + record.targetBGHigh = targetBGHigh; + record.isf = isf; + record.ic = ic; + + if (targetBGLow === 0 || targetBGHigh === 0 || isf === 0) { + $('#bc_inzulinbgtd').css('background-color','red'); + record = {}; + return; + } else { + $('#bc_inzulinbgtd').css('background-color',''); + } + + if (ic === 0) { + $('#bc_inzulincarbstd').css('background-color','red'); + record = {}; + return; + } else { + $('#bc_inzulincarbstd').css('background-color',''); + } + + // Load IOB + record.iob = 0; + if ($('#bc_useiob').is(':checked')) { + record.iob = roundTo(iob.calcTotal(client.sbx.data.treatments,client.sbx.data.profile,record.eventTime).iob, 0.01); + } + + // Load COB + record.cob = 0; + record.insulincob = 0; + if ($('#bc_usecob').is(':checked')) { + record.cob = roundTo(cob.cobTotal(client.sbx.data.treatments,client.sbx.data.profile,record.eventTime).cob, 0.01); + record.insulincob = roundTo(record.cob / ic, 0.01); + } + + // Load BG + record.bg = 0; + record.insulinbg = 0; + record.bgdiff = 0; + if ($('#bc_usebg').is(':checked')) { + record.bg = parseFloat($('#bc_bg').val().replace(',','.')); + if (isNaN(record.bg)) record.bg = 0; + if (record.bg <= targetBGLow) { + record.bgdiff = record.bg - targetBGLow; + } else if (record.bg >= targetBGHigh) { + record.bgdiff = record.bg - targetBGHigh; + } + record.bgdiff = roundTo(record.bgdiff, 0.1); + if (record.bg !== 0){ + record.insulinbg = roundTo(record.bgdiff / isf, 0.01); + } + } + + // Load foods + record.foods = _.cloneDeep(foods); + if (record.foods.length) { + var carbs = 0, gisum = 0; + for (var fi=0; fi= 0) { + var qp = quickpicks[qpiselected]; + if (qp.hideafteruse) { + qp.hidden = true; + + var apisecrethash = localStorage.getItem('apisecrethash'); + var dataJson = JSON.stringify(qp, null, ' '); + + var xhr = new XMLHttpRequest(); + xhr.open('PUT', '/api/v1/food/', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.setRequestHeader('api-secret', apisecrethash); + xhr.send(dataJson); + } + } + + boluscalc.calculateInsulin(); + if (event) event.preventDefault(); + } + + var categories = []; + var foodlist = []; + var databaseloaded = false; + var filter = { + category: '' + , subcategory: '' + , name: '' + }; + + boluscalc.loadFoodDatabase = function loadFoodDatabase(callback) { + categories = []; + foodlist = []; + $.ajax('/api/v1/food/regular.json', { + success: function (records) { + records.forEach(function (r) { + foodlist.push(r); + if (r.category && !categories[r.category]) categories[r.category] = {}; + if (r.category && r.subcategory) categories[r.category][r.subcategory] = true; + }); + databaseloaded = true; + console.log('Food database loaded'); + fillForm(); + } + }).done(function() { if (callback) callback(); } ); + } + + boluscalc.loadFoodQuickpicks = function loadFoodQuickpicks( ) { + // Load quickpicks + $.ajax('/api/v1/food/quickpicks.json', { + success: function (records) { + quickpicks = records; + $('#bc_quickpick').empty().append(new Option('(none)',-1)); + for (var i=0; i0) { + foodlist[index].portions = portions; + foods.push(_.cloneDeep(foodlist[index])); + boluscalc.calculateInsulin(); + $( this ).dialog( 'close' ); + } + } + }, + { text: translate('Reload database'), + class: 'leftButton', + click: boluscalc.loadFoodDatabase + } + ] + , open : function(ev, ui) { + $(this).parent().css('box-shadow', '20px 20px 20px 0px black'); + $(this).parent().find('.ui-dialog-buttonset' ).css({'width':'100%','text-align':'right'}) + $(this).parent().find('button:contains("'+translate('Add')+'")').css({'float':'left'}); + $('#bc_filter_name').focus(); + } + + }); + if (event) event.preventDefault(); + return false; + } + + function findClosestSGVToPastTime(time) { + var closeBGs = client.sbx.data.filter(function(d) { + if (d.color === 'transparent') { return false; } + if (d.type !== 'sgv') { return false; } + if (!d.y) { + return false; + } else { + return Math.abs((new Date(d.date)).getTime() - time) <= 10 * 60 * 1000; + } + }); + + // If there are any in 10 min range try 5 min 1st + var closeBG5m = closeBGs.filter(function(d) { + return Math.abs((new Date(d.date)).getTime() - time) <= 5 * 60 * 1000; + }); + + if (closeBG5m.length>0) { closeBGs = closeBG5m; } + + var totalBG = 0; + closeBGs.forEach(function(d) { + totalBG += Number(d.y); + }); + return totalBG > 0 ? client.utils.scaleMgdl(totalBG / closeBGs.length) : 0; + } + + if (isTouch()) { + // Make it faster on mobile devices + $('.insulincalculationpart').change(boluscalc.calculateInsulin); + } else { + $('.insulincalculationpart').on('input',boluscalc.calculateInsulin); + $('input:checkbox.insulincalculationpart').change(boluscalc.calculateInsulin); + } + $('#bc_bgfrommeter').change(boluscalc.calculateInsulin); + $('#bc_addfromdatabase').click(addFoodFromDatabase); + $('#bc_bgfromsensor').change(function bc_bgfromsensor_click(event) { + if (boluscalc.lastsbx) { + boluscalc.updateVisualisations(boluscalc.lastsbx); + } + boluscalc.calculateInsulin(); + maybePrevent(event); + }); + + $('#boluscalcDrawerToggle').click(boluscalc.toggleDrawer); + $('#boluscalcDrawer').find('button').click(boluscalc.submit); + $('#bc_eventTime input:radio').change(boluscalc.eventTimeTypeChange); + + $('.bc_eventtimeinput').focus(boluscalc.dateTimeFocus).change(boluscalc.dateTimeChange); + + boluscalc.loadFoodQuickpicks(); + setDateAndTime(); + + return boluscalc; +}; + +module.exports = init; \ No newline at end of file diff --git a/lib/client/careportal.js b/lib/client/careportal.js index 6e1c3f9f0fd..f86eefc75b6 100644 --- a/lib/client/careportal.js +++ b/lib/client/careportal.js @@ -159,7 +159,7 @@ function init (client, $) { var data = { enteredBy: $('#enteredBy').val() , eventType: $('#eventType').val() - , glucose: $('#glucoseValue').val() + , glucose: $('#glucoseValue').val().replace(',','.') , glucoseType: $('#treatment-form').find('input[name=glucoseType]:checked').val() , carbs: $('#carbsGiven').val() , insulin: $('#insulinGiven').val() diff --git a/lib/client/index.js b/lib/client/index.js index 4461e28c6fb..f656e8f217f 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -74,6 +74,7 @@ client.init = function init(serverSettings, plugins) { client.renderer = require('./renderer')(client, d3, $); client.careportal = require('./careportal')(client, $); + client.boluscalc = require('./boluscalc')(client, $); client.profilefunctions = profile; @@ -294,6 +295,9 @@ client.init = function init(serverSettings, plugins) { //only shown plugins get a chance to update visualisations plugins.updateVisualisations(client.sbx); + + //send data to boluscalc too + client.boluscalc.updateVisualisations(client.sbx); } function clearCurrentSGV ( ) { diff --git a/lib/language.js b/lib/language.js index f039097da62..7eff996ae37 100644 --- a/lib/language.js +++ b/lib/language.js @@ -4096,7 +4096,10 @@ function init() { ,'Drag&drop food here' : { cs: 'Sem táhni & pusť jídlo' } - ,'Update' : { // Update button + ,'Medium/Unknown' : { // GI of food + cs: 'Střední/Neznámá' + } + ,'Update' : { // Update button cs: 'Aktualizovat' ,sv: 'Uppdatera' ,nb: 'Oppdater' diff --git a/static/css/drawer.css b/static/css/drawer.css index c80155cb5d7..270a01b517d 100644 --- a/static/css/drawer.css +++ b/static/css/drawer.css @@ -1,4 +1,4 @@ -#drawer button, #treatmentDrawer button{ +#drawer button, #treatmentDrawer button, #boluscalcDrawer button{ font-size: 18px; font-weight: bolder; background-color: #ccc; @@ -300,6 +300,7 @@ h1, legend, font-size: 1.5em; } +<<<<<<< HEAD ul.navigation { padding: 0; margin: 0; @@ -321,3 +322,42 @@ ul.navigation { .navigation a:hover { background-color: #989898; } + +#boluscalcDrawer { + background-color: #666; + border-left: 1px solid #999; + box-shadow: inset 4px 4px 5px 0px rgba(50, 50, 50, 0.75); + color: #eee; + display: none; + font-size: 16px; + height: 100%; + overflow-y: auto; + position: absolute; + margin-top: 45px; + right: -200px; + width: 320px; + top: 0; + z-index: 1; +} + +#boluscalcDrawer a { + color: white; + font-size: 12px; +} + +#boluscalcDrawer .insulincalculation { + background: gray; +} + +#boluscalcDrawer .insulintotalcalculation { + background: Sienna; +} + +#boluscalcDrawer tr.border_bottom td { + border-bottom:1pt solid #eee; +} + +#boluscalcDrawer .foodinput { + background: #505050; + margin: 10px; +} \ No newline at end of file diff --git a/static/index.html b/static/index.html index ae5ccd342d2..70d2d44b5c7 100644 --- a/static/index.html +++ b/static/index.html @@ -39,6 +39,7 @@ +
Nightscout
@@ -273,6 +274,271 @@ +
+
+
+ + Bolus Wizard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + BG: + + + + +
+ + + + + + + + + + 0.00 +
+ GI: + + +
+ Quickpick: + +
+ + or + + Add from database + + +
+
+
+ + + Carbs: + + g + + + +
+ + + COB: + + g + + + +
+ + + IOB: + + 0.00 +
+ + Other correction: + + +
+ + Rounding: + + 0.00 +
+ + + Insulin needed: + + 0.00 +
+ + Carbs needed: + + + +
+ + Basal rate: + + +
+
+ + +
+ + + + +
+ + + + +
+ Event Time: + + + + + + +
+ +
+ +
+
+ Profile editor +
+ Food editor +
+ Reporting tool +
+ + +
+ +
From 5aa0346e74f13c416b5e858f982407c011fe6b12 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Fri, 11 Sep 2015 22:19:03 +0200 Subject: [PATCH 034/752] wrong id --- lib/client/boluscalc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/boluscalc.js b/lib/client/boluscalc.js index 72b7147a14f..33f1b6bbbac 100644 --- a/lib/client/boluscalc.js +++ b/lib/client/boluscalc.js @@ -283,7 +283,7 @@ function init(client, $) { // Calculate event time from date & time record.eventTime = new Date(); - if ($('#othertime').is(':checked')) { + if ($('#bc_othertime').is(':checked')) { record.eventTime = mergeDateAndTime().toDate(); } From 02511618e2730ed4f3d5320504b467066ddc1c9c Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Sun, 13 Sep 2015 10:49:27 +0200 Subject: [PATCH 035/752] date not changed when now clicked --- lib/client/boluscalc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/client/boluscalc.js b/lib/client/boluscalc.js index 33f1b6bbbac..7664b953496 100644 --- a/lib/client/boluscalc.js +++ b/lib/client/boluscalc.js @@ -105,6 +105,7 @@ function init(client, $) { $('#bc_retro').css('display',''); } else { $('#bc_retro').css('display','none'); + setDateAndTime(); boluscalc.calculateInsulin(); // Nightscout.utils.setYAxisOffset(50); //50% of extend // Nightscout.utils.updateBrushToTime(Nightscout.utils.mergeInputTime($('#bc_eventTimeValue').val(), $('#bc_eventDateValue').val()).toDate()); From 8944eb27c8710e1f652caed39468ad44adba6189 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Sun, 13 Sep 2015 10:58:42 +0200 Subject: [PATCH 036/752] replace new Option --- lib/client/boluscalc.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/client/boluscalc.js b/lib/client/boluscalc.js index 7664b953496..27fcaaab50c 100644 --- a/lib/client/boluscalc.js +++ b/lib/client/boluscalc.js @@ -558,10 +558,10 @@ function init(client, $) { $.ajax('/api/v1/food/quickpicks.json', { success: function (records) { quickpicks = records; - $('#bc_quickpick').empty().append(new Option('(none)',-1)); + $('#bc_quickpick').empty().append(''); for (var i=0; i' + r.name + ' (' + r.carbs + ' g)'); }; $('#bc_quickpick').val(-1); $('#bc_quickpick').change(quickpickChange); @@ -570,9 +570,9 @@ function init(client, $) { }; function fillForm(event) { - $('#bc_filter_category').empty().append(new Option('(none)','')); + $('#bc_filter_category').empty().append(''); for (var s in categories) { - $('#bc_filter_category').append(new Option(s,s)); + $('#bc_filter_category').append(''); } filter.category = ''; fillSubcategories(); @@ -591,10 +591,10 @@ function init(client, $) { } filter.category = $('#bc_filter_category').val(); filter.subcategory = ''; - $('#bc_filter_subcategory').empty().append(new Option('(none)','')); + $('#bc_filter_subcategory').empty().append(''); if (filter.category !== '') { for (var s in categories[filter.category]) { - $('#bc_filter_subcategory').append(new Option(s,s)); + $('#bc_filter_subcategory').append(''); } } doFilter(); @@ -616,7 +616,7 @@ function init(client, $) { o += 'Portion: ' + foodlist[i].portion + ' '; o += foodlist[i].unit + ' | '; o += 'Carbs: ' + foodlist[i].carbs+' g'; - $('#bc_data').append(new Option(o,i)); + $('#bc_data').append(''); } $('#bc_addportions').val('1'); From bbc0b6b3239f915c307e0d42b6b19feaf2ace467 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Sun, 13 Sep 2015 12:08:18 +0200 Subject: [PATCH 037/752] proper handling carbs from food --- lib/client/boluscalc.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/client/boluscalc.js b/lib/client/boluscalc.js index 27fcaaab50c..bd3bf01577a 100644 --- a/lib/client/boluscalc.js +++ b/lib/client/boluscalc.js @@ -347,24 +347,26 @@ function init(client, $) { } // Load foods + record.carbs = 0; record.foods = _.cloneDeep(foods); if (record.foods.length) { - var carbs = 0, gisum = 0; + var gisum = 0; for (var fi=0; fi0) { foodlist[index].portions = portions; foods.push(_.cloneDeep(foodlist[index])); - boluscalc.calculateInsulin(); $( this ).dialog( 'close' ); + boluscalc.calculateInsulin(); } } }, From c3df485c04f57e39d8d13bf7c8d8f7ba7b2a8d88 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Sun, 13 Sep 2015 12:15:38 +0200 Subject: [PATCH 038/752] use iso for date --- lib/client/boluscalc.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/client/boluscalc.js b/lib/client/boluscalc.js index bd3bf01577a..f4fbe2f8fe6 100644 --- a/lib/client/boluscalc.js +++ b/lib/client/boluscalc.js @@ -419,6 +419,9 @@ function init(client, $) { data.eventTime = mergeDateAndTime().toDate(); } + // replace boluscalc.eventTime by ISO string + data.boluscalc.eventTime = data.boluscalc.eventTime.toISOString(); + return data; } From c613a61b7133f1566817d4cc91b9b9f76c77c58d Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Mon, 14 Sep 2015 08:05:47 +0200 Subject: [PATCH 039/752] some improvements, BW option to treatments report --- lib/client/boluscalc.js | 21 ++++++++++++++++++--- lib/language.js | 3 +++ lib/report_plugins/treatments.js | 1 + 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/client/boluscalc.js b/lib/client/boluscalc.js index f4fbe2f8fe6..08c7a696e5e 100644 --- a/lib/client/boluscalc.js +++ b/lib/client/boluscalc.js @@ -70,6 +70,11 @@ function init(client, $) { // console.log('New BG received', sensorbg, 'Sec diff', (new Date().getTime() - lastSGV.mills)/1000); // console.log('Current', new Date().toLocaleString(), 'LastSGV', new Date(lastSGV.mills).toLocaleString()); } + + // Update time + if ($('#bc_nowtime').is(':checked')) { + setDateAndTime(); + } boluscalc.calculateInsulin(); } @@ -81,7 +86,6 @@ function init(client, $) { boluscalc.dateTimeChange = function dateTimeChange(event) { $('#bc_othertime').prop('checked', true); - boluscalc.calculateInsulin(); // client.utils.setYAxisOffset(50); //50% of extend var ele = $(this); var merged = mergeDateAndTime(); @@ -95,6 +99,8 @@ function init(client, $) { setDateAndTime(merged); updateTime(ele, merged); + boluscalc.eventTimeTypeChange(); + boluscalc.calculateInsulin(); maybePrevent(event); // Nightscout.utils.updateBrushToTime(moment.toDate()); }; @@ -103,6 +109,13 @@ function init(client, $) { if ($('#bc_othertime').is(':checked')) { $('#bc_eventTimeValue').focus(); $('#bc_retro').css('display',''); + if (mergeDateAndTime()moment()) { + $('#bc_retro').css('background-color','blue').text(translate('IN THE FUTURE')); + } else { + $('#bc_retro').css('display','none'); + } } else { $('#bc_retro').css('display','none'); setDateAndTime(); @@ -114,7 +127,8 @@ function init(client, $) { }; boluscalc.toggleDrawer = function toggleDrawer (event) { - client.browserUtils.toggleDrawer('#boluscalcDrawer', boluscalc.prepare); + boluscalc.prepare(); + client.browserUtils.toggleDrawer('#boluscalcDrawer'); maybePrevent(event); }; @@ -133,6 +147,7 @@ function init(client, $) { $('#bc_nowtime').prop('checked', true); $('#bc_othercorrection').val(0); setDateAndTime(); + boluscalc.eventTimeTypeChange(); if (boluscalc.lastsbx) { boluscalc.updateVisualisations(boluscalc.lastsbx); } @@ -447,7 +462,7 @@ function init(client, $) { } pushIf(data.glucose, translate('Blood Glucose') + ': ' + data.glucose); - pushIf(data.glucoseType, translate('Measurement Method') + ': ' + translate(data.glucoseType)); + pushIf(data.glucose, translate('Measurement Method') + ': ' + translate(data.glucoseType)); pushIf(data.carbs, translate('Carbs Given') + ': ' + data.carbs); pushIf(data.insulin, translate('Insulin Given') + ': ' + data.insulin); diff --git a/lib/language.js b/lib/language.js index 7eff996ae37..1bc59a2a3e2 100644 --- a/lib/language.js +++ b/lib/language.js @@ -4099,6 +4099,9 @@ function init() { ,'Medium/Unknown' : { // GI of food cs: 'Střední/Neznámá' } + ,'IN THE FUTURE' : { + cs: 'V BUDOUCNOSTI' + } ,'Update' : { // Update button cs: 'Aktualizovat' ,sv: 'Uppdatera' diff --git a/lib/report_plugins/treatments.js b/lib/report_plugins/treatments.js index 5a2eca016cc..c37768d0a8a 100644 --- a/lib/report_plugins/treatments.js +++ b/lib/report_plugins/treatments.js @@ -168,6 +168,7 @@ treatments.report = function report_treatments(datastorage, sorteddaystoshow, op $('#rped_eventType').append(''); }); $('#rped_eventType').append(''); + $('#rped_eventType').append(''); $('#rped_profile').empty().append(''); client.profilefunctions.listBasalProfiles().forEach(function (p) { From 2b2e89b53cbf3ccd9cd7d48b286d736138da9820 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Mon, 14 Sep 2015 08:53:18 +0200 Subject: [PATCH 040/752] added enable option --- lib/api/status.js | 1 + lib/client/index.js | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/api/status.js b/lib/api/status.js index 2e3eeefb8a2..1debeac785d 100644 --- a/lib/api/status.js +++ b/lib/api/status.js @@ -16,6 +16,7 @@ function configure (app, wares, env) { , serverTime: new Date().toISOString() , apiEnabled: app.enabled('api') , careportalEnabled: app.enabled('api') && env.settings.enable.indexOf('careportal') > -1 + , boluscalcEnabled: app.enabled('api') && env.settings.enable.indexOf('boluscalc') > -1 , head: wares.get_head( ) , settings: env.settings , extendedSettings: app.extendedClientSettings diff --git a/lib/client/index.js b/lib/client/index.js index f656e8f217f..c62251fd129 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -771,6 +771,7 @@ client.init = function init(serverSettings, plugins) { $('.serverSettings').show(); } $('#treatmentDrawerToggle').toggle(serverSettings.careportalEnabled); + $('#boluscalcDrawerToggle').toggle(serverSettings.boluscalcEnabled); container.toggleClass('has-minor-pills', plugins.hasShownType('pill-minor', client.settings)); function prepareData ( ) { From ef7a948b59de21c97c3b8f106d16903972e340f6 Mon Sep 17 00:00:00 2001 From: MilosKozak Date: Wed, 16 Sep 2015 10:55:22 +0200 Subject: [PATCH 041/752] forgotten <<< Date: Wed, 16 Sep 2015 20:07:22 +0200 Subject: [PATCH 042/752] move links to burger --- lib/language.js | 2 +- static/index.html | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/language.js b/lib/language.js index 1bc59a2a3e2..974a1f11bfa 100644 --- a/lib/language.js +++ b/lib/language.js @@ -1664,7 +1664,7 @@ function init() { ,fi: 'Ei ladattu' ,nb: 'Ikke lest' } - ,'Food editor' : { + ,'Food Editor' : { cs: 'Editor jídel' ,de: 'Nahrungsmittel Editor' ,es: 'Editor de alimentos' diff --git a/static/index.html b/static/index.html index 70d2d44b5c7..0d3ff969ed7 100644 --- a/static/index.html +++ b/static/index.html @@ -83,6 +83,7 @@
@@ -514,13 +515,6 @@
-
-
- Profile editor -
- Food editor -
- Reporting tool