├── .gitignore ├── .vscode └── tasks.json ├── Gruntfile.js ├── LICENSE ├── README.md ├── content ├── style │ └── main.css └── templates │ └── toggleBlock.html ├── index.html ├── package.json └── src ├── app.js ├── common ├── Conversions.js ├── UnitOfMeasureState.js ├── UserProfile.js ├── formulaBmi.js ├── formulaBmr.js └── formulaThr.js ├── controllers ├── FormulaController.js ├── UomController.js └── UserProfileController.js ├── directives └── ToggleBlock.js └── filters ├── bmiFilter.js ├── genderFilter.js ├── heightFilter.js ├── uomFilter.js └── weightFilter.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "grunt", 4 | "isShellCommand": true, 5 | "tasks": [{ 6 | "taskName": "watch", 7 | "isBuildCommand": false 8 | }, { 9 | "taskName": "build", 10 | "isBuildCommand": true 11 | }] 12 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('package.json'), 4 | connect: { 5 | server: { 6 | options: { 7 | livereload: true 8 | } 9 | } 10 | }, 11 | browserify: { 12 | dist: { 13 | options: { 14 | transform: [["babelify"]] 15 | }, 16 | files: { 17 | "./dist/app.js" : ["./src/app.js"] 18 | } 19 | } 20 | }, 21 | watch: { 22 | compile: { 23 | files: 'src/**/*.js', 24 | tasks: ['browserify'] 25 | }, 26 | run: { 27 | options: { 28 | livereload: true, 29 | base: '.' 30 | }, 31 | files: ['./index.html', './dist/**/*.js'] 32 | } 33 | } 34 | }); 35 | grunt.loadNpmTasks('grunt-contrib-watch'); 36 | grunt.loadNpmTasks('grunt-browserify'); 37 | grunt.loadNpmTasks('grunt-connect'); 38 | grunt.registerTask('livereload', [ 39 | 'connect', 40 | 'watch' 41 | ]); 42 | grunt.registerTask('build', [ 43 | 'browserify' 44 | ]); 45 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jeremy Likness 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular ES6 Health App 2 | 3 | The [Angular 1.x Health App](https://github.com/JeremyLikness/AngularHealthApp) implemented with ECMAScript 6 (ECMAScript2015) and Babel.js 4 | 5 | 1. Navigate to the parent folder you would like to put the project in. 6 | 2. `git clone https://github.com/JeremyLikness/AngularES6HealthApp.git` 7 | 3. `cd AngularES6HealthApp` 8 | 4. `npm install` 9 | 5. `npm install -g grunt-cli` 10 | 6. `grunt build` 11 | 7. `grunt connect` 12 | 8. Navigate to the port indicated 13 | -------------------------------------------------------------------------------- /content/style/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: arial, verdana, sans-serif; 3 | line-height: 1.2em; 4 | } 5 | 6 | h1 { 7 | font-size: 2.0em; 8 | font-weight: bold; 9 | color: darkblue; 10 | } 11 | 12 | button { 13 | width: 60px; 14 | border-color: black; 15 | border-radius: 0px; 16 | border-style: solid; 17 | border-width: 2px; 18 | padding: 5px; 19 | } 20 | 21 | div.break { 22 | clear: both; 23 | } 24 | 25 | div.tile { 26 | width: 290px; 27 | height: 150px; 28 | float: left; 29 | margin: 5px; 30 | padding: 5px; 31 | background: lightblue; 32 | border: solid 2px darkblue; 33 | } 34 | 35 | div.unit { 36 | min-width: 200px; 37 | max-width: 310px; 38 | float: left; 39 | margin: 5px; 40 | padding: 5px; 41 | } 42 | 43 | div.Obese { 44 | background: red; 45 | } 46 | 47 | div.Overweight { 48 | background: lightcoral; 49 | } 50 | 51 | div.Underweight { 52 | background: lightcoral; 53 | } 54 | 55 | input.error { 56 | border: 2px solid red; 57 | } 58 | 59 | .label { 60 | max-width: 60%; 61 | float: left; 62 | font-weight: bold; 63 | text-align: right; 64 | padding-right: 5px; 65 | vertical-align: middle; 66 | } 67 | 68 | .labelTarget { 69 | float: left; 70 | text-align: left; 71 | vertical-align: middle; 72 | } -------------------------------------------------------------------------------- /content/templates/toggleBlock.html: -------------------------------------------------------------------------------- 1 |
2 |
{{label}}
3 |
4 | 5 |
6 |
-------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Health Calculator App 6 | 7 | 8 | 9 | 10 | 11 | 12 | 24 | 25 | 41 | 42 | 57 | 58 |

Health Calculator

59 |
60 | 63 |
64 |
65 |
66 | 69 |
70 |
71 |
72 |
73 |
74 | 75 |
76 | 77 |
78 |
79 |

BMR:

80 |

{{ctrl.bmrValue}} Calories

81 |
82 |
85 |

BMI:

86 |

{{ctrl.bmiValue}}

87 | {{ctrl.bmiValue | bmi}} 88 |
89 |
90 |

THR (50%-85%):

91 |

{{ctrl.thrValue.min}} — {{ctrl.thrValue.max}} BPM

92 |
93 |
94 | 95 | 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angulares6healthapp", 3 | "version": "1.0.0", 4 | "description": "Example of the Angular Health App written in ECMAScript6 using Babel", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Jeremy Likness", 10 | "license": "MIT", 11 | "dependencies": { 12 | "angular" : "^1.4.8", 13 | "common": "^0.2.5" 14 | }, 15 | "babel": { 16 | "presets": [ 17 | "es2015" 18 | ] 19 | }, 20 | "devDependencies": { 21 | "grunt-babel" : "^6.0.0", 22 | "babel" : "^6.3.13", 23 | "grunt" : "^0.4.5", 24 | "browserify" : "^12.0.1", 25 | "grunt-browserify" : "^4.0.1", 26 | "babelify" : "^7.2.0", 27 | "grunt-contrib-watch" : "^0.6.1", 28 | "grunt-connect" : "^0.2.0", 29 | "connect" : "^3.4.0", 30 | "babel-preset-es2015": "^6.3.13" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import {bmiFilterFactory} from './filters/bmiFilter.js'; 2 | import {genderFilterFactory} from './filters/genderFilter.js'; 3 | import {uomFilterFactory} from './filters/uomFilter.js'; 4 | import {weightFilterFactory} from './filters/weightFilter.js'; 5 | import {heightFilterFactory} from './filters/heightFilter.js'; 6 | 7 | import {ToggleBlock} from './directives/toggleBlock.js'; 8 | 9 | import {UnitOfMeasureState} from './common/UnitOfMeasureState.js'; 10 | import {Conversions} from './common/Conversions.js'; 11 | import {UserProfile} from './common/UserProfile.js'; 12 | 13 | import {UomController} from './controllers/UomController.js'; 14 | import {FormulaController} from './controllers/FormulaController.js'; 15 | import {UserProfileController} from './controllers/UserProfileController.js'; 16 | 17 | let toggleBlockFactory = () => new ToggleBlock(); 18 | 19 | class AngularApp { 20 | 21 | constructor() { 22 | 23 | var healthApp = angular.module("healthApp", []); 24 | 25 | healthApp.filter("bmi", bmiFilterFactory); 26 | healthApp.filter("gender", genderFilterFactory); 27 | healthApp.filter("uom", uomFilterFactory); 28 | healthApp.filter("weight", weightFilterFactory); 29 | healthApp.filter("height", heightFilterFactory); 30 | 31 | healthApp.directive("toggleBlock", toggleBlockFactory); 32 | 33 | healthApp.service("uomService", UnitOfMeasureState); 34 | healthApp.service("conversionService", Conversions); 35 | 36 | healthApp.value("userProfile", new UserProfile()); 37 | 38 | healthApp.controller("uomCtrl", ["uomService", UomController]); 39 | healthApp.controller("formulaCtrl", ["uomService", "userProfile", 40 | FormulaController]); 41 | healthApp.controller("userProfileCtrl", ["userProfile", "uomService", 42 | "conversionService", UserProfileController]); 43 | } 44 | } 45 | 46 | var app = new AngularApp(); -------------------------------------------------------------------------------- /src/common/Conversions.js: -------------------------------------------------------------------------------- 1 | const centimetersPerInch = 2.54; 2 | const inchesPerFoot = 12; 3 | const poundsPerKilogram = 2.205; 4 | 5 | export class Conversions { 6 | 7 | inchesToCentimeters(inches) { 8 | var input = Number(inches); 9 | return input * centimetersPerInch; 10 | } 11 | 12 | inchesToFeet(inches) { 13 | var input = Number(inches); 14 | return input / inchesPerFoot; 15 | } 16 | 17 | centimetersToInches(centimeters) { 18 | var input = Number(centimeters); 19 | return input / centimetersPerInch; 20 | } 21 | 22 | poundsToKilograms(pounds) { 23 | var input = Number(pounds); 24 | return input / poundsPerKilogram; 25 | } 26 | 27 | kilogramsToPounds(kg) { 28 | var input = Number(kg); 29 | return input * poundsPerKilogram; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/common/UnitOfMeasureState.js: -------------------------------------------------------------------------------- 1 | export class UnitOfMeasureState { 2 | 3 | constructor() { 4 | this._usMeasure = true; 5 | this._metricMeasure = false; 6 | } 7 | 8 | toggle() { 9 | this._usMeasure = !this._usMeasure; 10 | } 11 | 12 | get usMeasure() { 13 | return this._usMeasure; 14 | } 15 | 16 | set usMeasure(value) { 17 | this._usMeasure = !!value; 18 | this._metricMeasure = !this._usMeasure; 19 | } 20 | 21 | get metricMeasure() { 22 | return this._metricMeasure; 23 | } 24 | 25 | set metricMeasure(value) { 26 | this._metricMeasure = !!value; 27 | this._usMeasure = !this._metricMeasure; 28 | } 29 | } -------------------------------------------------------------------------------- /src/common/UserProfile.js: -------------------------------------------------------------------------------- 1 | export class UserProfile { 2 | constructor() { 3 | this._isMale = true; 4 | this._isFemale = false; 5 | this.heightInches = 60; 6 | this.weightPounds = 130; 7 | this.ageYears = 40; 8 | } 9 | 10 | toggleGender() { 11 | this._isMale = !this._isMale; 12 | } 13 | 14 | get isMale() { 15 | return this._isMale; 16 | } 17 | 18 | set isMale(value) { 19 | this._isMale = !!value; 20 | this._isFemale = !this._isMale; 21 | } 22 | 23 | get isFemale() { 24 | return this._isFemale; 25 | } 26 | 27 | set isFemale(value) { 28 | this._isFemale = !!value; 29 | this._isMale = !this._isFemale; 30 | } 31 | } -------------------------------------------------------------------------------- /src/common/formulaBmi.js: -------------------------------------------------------------------------------- 1 | function formulaBmi (profile) { 2 | 3 | // BMI = (weight in pound * 703) / (height in inches)^2 4 | var bmi = (profile.weight * 703) / (profile.height * profile.height); 5 | 6 | // round it 7 | return Math.round(bmi * 10.0)/10.0; 8 | } 9 | 10 | export {formulaBmi} -------------------------------------------------------------------------------- /src/common/formulaBmr.js: -------------------------------------------------------------------------------- 1 | function formulaBmr (profile) { 2 | 3 | // Women - 655 + (4.35 x weight in pounds) + (4.7 x height in inches) - (4.7 x age in years) 4 | function woman(weight, height, age) { 5 | return Math.floor(655 + (4.35 * weight) + (4.7 * height) - (4.7 * age)); 6 | } 7 | 8 | // Men - 66 + (6.23 x weight in pounds) + (12.7 x height in inches) - (6.8 x age in years ) 9 | function man(weight, height, age) { 10 | return Math.floor(66 + (6.23 * weight) + (12.7 * height) - (6.8 * age)); 11 | } 12 | 13 | return profile.isMale ? man(profile.weight, profile.height, profile.age) : 14 | woman(profile.weight, profile.height, profile.age); 15 | } 16 | 17 | export {formulaBmr} -------------------------------------------------------------------------------- /src/common/formulaThr.js: -------------------------------------------------------------------------------- 1 | function formulaThr (age) { 2 | 3 | // max heart rate = 220 - age; 4 | var max = 220.0 - Number(age); 5 | 6 | // min range = 50% 7 | var min = Math.round(5.0 * max) / 10.0; 8 | 9 | // max range = 85% 10 | var maxRate = Math.round(8.5 * max) / 10.0; 11 | 12 | return { 13 | min: min, 14 | max: maxRate 15 | }; 16 | } 17 | 18 | export {formulaThr} -------------------------------------------------------------------------------- /src/controllers/FormulaController.js: -------------------------------------------------------------------------------- 1 | import {formulaBmr} from '../common/formulaBmr.js'; 2 | import {formulaBmi} from '../common/formulaBmi.js'; 3 | import {formulaThr} from '../common/formulaThr.js'; 4 | 5 | export class FormulaController { 6 | constructor(uomService, userProfile) { 7 | this.uomService = uomService; 8 | this.userProfile = userProfile; 9 | } 10 | 11 | makeParameters() { 12 | return { 13 | isMale: this.userProfile.isMale, 14 | height: this.userProfile.heightInches, 15 | weight: this.userProfile.weightPounds, 16 | age: this.userProfile.ageYears 17 | }; 18 | } 19 | 20 | get bmrValue() { 21 | return formulaBmr(this.makeParameters()); 22 | } 23 | 24 | get bmiValue() { 25 | return formulaBmi(this.makeParameters()); 26 | } 27 | 28 | get thrValue() { 29 | return formulaThr(this.userProfile.ageYears); 30 | } 31 | } -------------------------------------------------------------------------------- /src/controllers/UomController.js: -------------------------------------------------------------------------------- 1 | export class UomController { 2 | constructor(uomService) { 3 | this.uomService = uomService; 4 | } 5 | } -------------------------------------------------------------------------------- /src/controllers/UserProfileController.js: -------------------------------------------------------------------------------- 1 | export class UserProfileController { 2 | constructor(userProfile, uomService, conversionService) { 3 | this.userProfile = userProfile; 4 | this.uomService = uomService; 5 | this.conversionService = conversionService; 6 | this._weightVal = uomService.usMeasure ? 7 | userProfile.weightPounds : conversionService.poundsToKilograms( 8 | userProfile.weightPounds); 9 | this._wasMetric = uomService.metricMeasure; 10 | this._ageValue = userProfile.ageYears; 11 | } 12 | 13 | get minHeightRange() { 14 | return this.uomService.usMeasure ? 24 : 60; 15 | } 16 | 17 | get maxHeightRange() { 18 | return this.uomService.usMeasure ? 84 : 215; 19 | } 20 | 21 | get heightValue() { 22 | return this.uomService.usMeasure ? 23 | this.userProfile.heightInches : 24 | this.conversionService.inchesToCentimeters(this.userProfile.heightInches); 25 | } 26 | 27 | set heightValue(value) { 28 | var incoming = Number(value); 29 | this.userProfile.heightInches = this.uomService.usMeasure ? 30 | incoming : 31 | this.conversionService.centimetersToInches(incoming); 32 | } 33 | 34 | get minWeightRange() { 35 | return this.uomService.metricMeasure ? 9 : 20; 36 | } 37 | 38 | get maxWeightRange() { 39 | return this.uomService.metricMeasure ? 182 : 400; 40 | } 41 | 42 | get weightValue() { 43 | if (this.uomService.metricMeasure !== this._wasMetric) { 44 | this._wasMetric = this.uomService.metricMeasure; 45 | if (this._wasMetric) { 46 | this._weightVal = Math.round(this.conversionService.poundsToKilograms( 47 | Number(this._weightVal) 48 | )); 49 | } 50 | else { 51 | this._weightVal = Math.round(this.conversionService.kilogramsToPounds( 52 | Number(this._weightVal) 53 | )); 54 | } 55 | } 56 | return this._weightVal; 57 | } 58 | 59 | set weightValue(value) { 60 | var incoming = Number(value), adjustedWeight = incoming; 61 | this._weightVal = value; 62 | if (this.uomService.metricMeasure) { 63 | adjustedWeight = this.conversionService.kilogramsToPounds(incoming); 64 | } 65 | if (adjustedWeight >= 20 && adjustedWeight <= 400) { 66 | this.userProfile.weightPounds = adjustedWeight; 67 | } 68 | } 69 | 70 | get ageValue() { 71 | return this._ageValue; 72 | } 73 | 74 | set ageValue(value) { 75 | var incoming = Number(value); 76 | this._ageValue = value; 77 | if (incoming >= 13 && incoming <= 120) { 78 | this.userProfile.ageYears = incoming; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/directives/ToggleBlock.js: -------------------------------------------------------------------------------- 1 | export class ToggleBlock { 2 | constructor() { 3 | this.restrict = "E"; 4 | this.replace = true; 5 | this.scope = { 6 | label: "@", 7 | buttonText: "=", 8 | toggleFunction: "&" 9 | }; 10 | this.templateUrl = "content/templates/toggleBlock.html"; 11 | } 12 | } -------------------------------------------------------------------------------- /src/filters/bmiFilter.js: -------------------------------------------------------------------------------- 1 | export function bmiFilterFactory() { 2 | return function (input) { 3 | var value = Number(input); 4 | 5 | if (value >= 30.0) { 6 | return 'Obese'; 7 | } 8 | 9 | if (value >= 25.0) { 10 | return 'Overweight'; 11 | } 12 | 13 | if (value < 18.5) { 14 | return 'Underweight'; 15 | } 16 | 17 | return 'Normal'; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/filters/genderFilter.js: -------------------------------------------------------------------------------- 1 | export function genderFilterFactory () { 2 | return function (input) { 3 | var check = !!input; 4 | return check ? 'Male' : 'Female'; 5 | }; 6 | } -------------------------------------------------------------------------------- /src/filters/heightFilter.js: -------------------------------------------------------------------------------- 1 | var heightFilterFactory = [ 2 | "uomService", 3 | "conversionService", 4 | function (uomSvc, conversionSvc) { 5 | return function (input, convert) { 6 | 7 | var heightInches = Number(input), heightCentimeters, ft, result = ''; 8 | 9 | if (uomSvc.usMeasure) { 10 | ft = Math.floor(conversionSvc.inchesToFeet(heightInches)); 11 | if (ft > 0) { 12 | result = ft + " ft. "; 13 | } 14 | heightInches -= ft * 12; 15 | if (heightInches >= 1) { 16 | result += (Math.floor(heightInches) + ' in.'); 17 | } 18 | } 19 | else { 20 | if (convert !== undefined && !!convert === convert && !convert) { 21 | heightCentimeters = heightInches; 22 | } 23 | else { 24 | heightCentimeters = Math.round( 25 | conversionSvc.inchesToCentimeters(heightInches), 1); 26 | } 27 | result = heightCentimeters + ' cm.'; 28 | } 29 | return result; 30 | } 31 | } 32 | ]; 33 | 34 | export {heightFilterFactory}; -------------------------------------------------------------------------------- /src/filters/uomFilter.js: -------------------------------------------------------------------------------- 1 | export function uomFilterFactory() { 2 | return function (input) { 3 | var check = !!input; 4 | return check ? 'U.S.' : 'Metric'; 5 | }; 6 | } -------------------------------------------------------------------------------- /src/filters/weightFilter.js: -------------------------------------------------------------------------------- 1 | export function weightFilterFactory () { 2 | return function (input) { 3 | var check = !!input; 4 | return check ? 'lbs.' : 'kgs.'; 5 | }; 6 | } --------------------------------------------------------------------------------