Calorie counting web application written in the Go language

grassfed.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. (function ($) {
  2. $.each(['show', 'hide'], function (i, ev) {
  3. var el = $.fn[ev];
  4. $.fn[ev] = function () {
  5. this.trigger(ev);
  6. return el.apply(this, arguments);
  7. };
  8. });
  9. })(jQuery);
  10. $(function () {
  11. var chart = $('#goalChart')[0];
  12. var trends = $('#trendsChart')[0];
  13. var doughnutChart;
  14. var lineChart;
  15. var trendData;
  16. function startEngine() {
  17. // Pull the information we need first.
  18. loadStatistics();
  19. loadTrends();
  20. loadHistory();
  21. // Set focus.
  22. $('input[name="products"][type="text"]').focus();
  23. }
  24. // Check to see if the profile is not hidden. If it is not then start your engines!
  25. if (!$('div#profile').is(':hidden')) {
  26. startEngine();
  27. }
  28. else {
  29. $('div#profile').on('show', function () {
  30. startEngine();
  31. });
  32. }
  33. $('form#entry').on('submit', function (e) {
  34. e.preventDefault();
  35. // Get the current values.
  36. var product = $('input[name="product"][type="text"]').val();
  37. var calories = parseInt($('input[name="calories"][type="number"]').val());
  38. $.post('/me/add', { 'product': product, 'calories': calories })
  39. .done(function (response) {
  40. if (response) {
  41. // Reset the values.
  42. $('input[name="product"][type="text"]').val('');
  43. $('input[name="calories"][type="number"]').val(0);
  44. // Record the moment in history.
  45. prependProduct(response.Id, response.Product, response.Calories);
  46. // Update the chart.
  47. var previousCalories = parseInt($('input[name="current"][type="hidden"]').val());
  48. $('input[name="current"][type="hidden"]').val(previousCalories + response.Calories);
  49. updateCalorieChart();
  50. loadTrends();
  51. // Reset focus.
  52. $('input[name="products"][type="text"]').focus();
  53. }
  54. });
  55. return false;
  56. });
  57. $('form#recordWeight').on('submit', function (e) {
  58. e.preventDefault();
  59. var weight = $('input[name="weight"][type="number"]').val();
  60. $.post('/me/weight', { 'weight': weight })
  61. .done(function (response) {
  62. if (response) {
  63. // Reset the values.
  64. $('input[name="weight"][type="number"]').val('');
  65. loadTrends();
  66. }
  67. });
  68. });
  69. $(document).on('click', 'button.delete-history', function (e) {
  70. e.preventDefault();
  71. var moment = parseInt($(this).parents('tr').attr('data-moment'));
  72. $.ajax({
  73. url: '/me/history/' + moment,
  74. type: 'DELETE'
  75. })
  76. .done(function (response) {
  77. if (response) {
  78. var media = $('tr[data-moment="' + moment + '"]');
  79. var panel = $(media).parents('div.panel');
  80. var previousCalories = parseInt($('input[name="current"][type="hidden"]').val());
  81. var calories = parseInt($(media).find('span.calories').text());
  82. // Remove the current media element which has been trashed.
  83. $(media).remove();
  84. // Update our calorie chart.
  85. $('input[name="current"][type="hidden"]').val(previousCalories - calories);
  86. updateCalorieChart();
  87. loadTrends();
  88. // Check if there are any more media elements in the panel. If not remove the panel.
  89. if ($(panel).find('tr').length == 0) {
  90. $(panel).remove();
  91. }
  92. }
  93. });
  94. });
  95. $(document).on('change', 'input[name="calories"][type="range"]', function (e) {
  96. var goal = $(this).val();
  97. // Send the new goal to the server.
  98. setGoal(goal);
  99. // Update the text for immediate feedback.
  100. $('span.goal').text(goal);
  101. // Update the chart.
  102. updateCalorieChart();
  103. loadTrends();
  104. });
  105. function prependProduct(id, product, calories) {
  106. var momentDate = new Date().toDateString();
  107. var panelTitle = $('h3.panel-title:contains("' + momentDate + '")');
  108. var panelBody;
  109. if (panelTitle.length > 0) {
  110. panelBody = $(panelTitle).parents('div.panel').find('.table');
  111. }
  112. else {
  113. $('div#history').prepend(
  114. $('<div class="panel panel-default">')
  115. .append('<div class="panel-heading"><h3 class="panel-title"><a data-toggle="collapse" data-parent="#history" href="#' + momentDate.replace(/\s+/g, '-') + '">' + momentDate + '</a></h3></div>')
  116. .append($('<div id="' + momentDate.replace(/\s+/g, '-') + '" class="panel-collapse">')
  117. .append($('<div class="panel-body">')
  118. .append('<table class="table">'))));
  119. panelBody = $('h3.panel-title:contains("' + momentDate + '")').parents('div.panel').find('.table');
  120. }
  121. $(panelBody).prepend(
  122. $('<tr data-moment="' + id + '">')
  123. .append(
  124. '<td>' + product + '</td>'
  125. + '<td class="text-right" style="width: 4em;"><span class="calories">' + calories + '</span></td>'
  126. + '<td class="text-center" style="width: 6em;"><button class="btn btn-danger delete-history" style="display: none;"><span class="glyphicon glyphicon-fire"></span></button></td>'));
  127. $('.panel.panel-default > .panel-collapse').addClass('collapse').find('.delete-history').hide();
  128. $('.panel.panel-default:first > .panel-collapse').addClass('in').find('.delete-history').show();
  129. }
  130. function getDailyCalories() {
  131. return parseInt($('input[name="calories"][type="range"]').val());
  132. }
  133. function getCurrentCalories() {
  134. return parseInt($('input[name="current"][type="hidden"]').val());
  135. }
  136. function setGoal(calories) {
  137. $.post('/me/goal', { 'calories': calories });
  138. }
  139. function loadStatistics() {
  140. $.get('/me/stats')
  141. .done(function (response) {
  142. if (response) {
  143. $('input[name="calories"][type="range"]').val(response.Goal);
  144. $('span.goal').text(response.Goal);
  145. $('input[name="current"][type="hidden"]').val(response.Current);
  146. $('.streak').text(response.Streak);
  147. $('.streak-unit').text('days');
  148. }
  149. updateCalorieChart();
  150. });
  151. }
  152. function loadTrends() {
  153. $.get('/me/trends')
  154. .done(function (response) {
  155. if (response) {
  156. goals = [];
  157. calories = [];
  158. weights = [];
  159. for (var i = 0; i < response.Labels.length; i++) {
  160. goals.push(response.Goals[i]);
  161. calories.push(response.History[i]);
  162. weights.push(response.Weights[i]);
  163. }
  164. updateTrendChart(
  165. response.Labels,
  166. goals,
  167. calories,
  168. weights);
  169. }
  170. });
  171. }
  172. function loadHistory() {
  173. $.get('/me/history')
  174. .done(function (response) {
  175. if (response) {
  176. var history = $('div#history');
  177. var moments = [];
  178. var lastDate;
  179. var panel;
  180. for (var i = 0; i < response.length; i++) {
  181. if (response[i]) {
  182. var momentDate = new Date(response[i].Date).toDateString();
  183. var moment = $('<tr data-moment="' + response[i].Id + '">')
  184. .append(
  185. '<td>' + response[i].Product + '</td>'
  186. + '<td class="text-right" style="width: 4em;"><span class="calories">' + response[i].Calories + '</span></td>'
  187. + '<td class="text-center" style="width: 6em;"><button class="btn btn-danger delete-history" style="display: none;"><span class="glyphicon glyphicon-fire"></span></button></td>');
  188. if (!lastDate || lastDate != momentDate) {
  189. lastDate = momentDate;
  190. if (panel) {
  191. $(panel).find('.table').append(moments);
  192. $(history).append(panel);
  193. moments = [];
  194. panel = null;
  195. }
  196. panel = $('<div class="panel panel-default">')
  197. .append('<div class="panel-heading"><h3 class="panel-title"><a data-toggle="collapse" data-parent="#history" href="#' + lastDate.replace(/\s+/g, '-') + '">' + lastDate + '</a></h3></div>')
  198. .append($('<div id="' + lastDate.replace(/\s+/g, '-') + '" class="panel-collapse">')
  199. .append($('<div class="panel-body">')
  200. .append('<table class="table">')));
  201. }
  202. moments.push(moment);
  203. }
  204. }
  205. if (panel) {
  206. $(panel).find('.table').append(moments);
  207. $(history).append(panel);
  208. }
  209. $('.panel.panel-default > .panel-collapse').addClass('collapse').find('.delete-history').hide();
  210. $('.panel.panel-default:first > .panel-collapse').addClass('in').find('.delete-history').show();
  211. }
  212. });
  213. }
  214. function updateTrendChart(labels, goals, calories, weights) {
  215. //if (!lineChart) {
  216. var ctxTrends = trends.getContext("2d");
  217. lineChart = new Chart(ctxTrends).Line(
  218. {
  219. labels: labels,
  220. datasets: [
  221. {
  222. label: 'Goal',
  223. fillColor: 'rgba(220, 220, 220, 0.2)',
  224. strokeColor: 'rgba(220, 220, 220, 1)',
  225. pointColor: 'rgba(220, 220, 220, 1)',
  226. pointStrokeColor: '#fff',
  227. pointHighlightFill: '#fff',
  228. pointHighlightStroke: 'rgba(220, 220, 220, 1)',
  229. data: goals
  230. },
  231. {
  232. label: 'Calories',
  233. fillColor: 'rgba(196, 46, 42, 0.2)',
  234. strokeColor: 'rgba(196, 46, 42, 0.5)',
  235. pointColor: 'rgba(220, 220, 220, 1)',
  236. pointStrokeColor: '#fff',
  237. pointHighlightFill: '#fff',
  238. pointHighlightStroke: 'rgba(220, 220, 220, 1)',
  239. data: calories
  240. },
  241. {
  242. label: 'Weight',
  243. fillColor: 'rgba(51, 51, 204, 0.2)',
  244. strokeColor: 'rgba(51, 51, 204, 0.5)',
  245. pointColor: 'rgba(51, 51, 204, 1)',
  246. pointStrokeColor: '#fff',
  247. pointHighlightFill: '#fff',
  248. pointHighlightStroke: 'rgba(220, 220, 220, 1)',
  249. data: weights
  250. }
  251. ]
  252. },
  253. {
  254. animateScale: true,
  255. pointDot: false
  256. });
  257. //}
  258. };
  259. function updateCalorieChart() {
  260. var goal = getDailyCalories();
  261. var count = getCurrentCalories();
  262. var goalColor = "#ddd";
  263. var countColor = "#46bfbd";
  264. if (count >= goal) {
  265. countColor = "#f7464a";
  266. }
  267. if (!doughnutChart) {
  268. var ctxCalories = chart.getContext("2d");
  269. doughnutChart = new Chart(ctxCalories).Doughnut(
  270. [
  271. {
  272. value: count,
  273. color: "#46bfbd",
  274. highlight: "#46bfbd",
  275. label: "Today"
  276. },
  277. {
  278. value: Math.max(goal - count, 0),
  279. color: "#ddd",
  280. highlight: "#ddd",
  281. label: "Remaining"
  282. }
  283. ],
  284. {
  285. animateScale: true
  286. });
  287. }
  288. if (doughnutChart) {
  289. doughnutChart.segments[0].value = count;
  290. doughnutChart.segments[0].color = countColor;
  291. doughnutChart.segments[0].fillColor = countColor;
  292. doughnutChart.segments[0].highlight = countColor;
  293. doughnutChart.segments[1].value = Math.max(goal - count, 0);
  294. doughnutChart.segments[1].color = goalColor;
  295. doughnutChart.segments[1].fillColor = goalColor;
  296. doughnutChart.segments[1].highlight = goalColor;
  297. doughnutChart.update();
  298. }
  299. }
  300. });