(function() {
let app = angular.module('chrisjsherm.app', [
'ui.router',
'chrisjsherm.mock-api',
'chrisjsherm.service.web-worker',
])
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link data-require="bootstrap-css@3.3.7" data-semver="3.3.7" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.css" />
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.5.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
<script data-require="angular-mocks@1.5.5" data-semver="1.5.5" src="https://code.angularjs.org/1.5.5/angular-mocks.js"></script>
<script data-require="ui-router@0.3.1" data-semver="0.3.1" src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.js"></script>
<script src="mock-api.js"></script>
<script src="uuid.service.js"></script>
<script src="web-worker.service.js"></script>
<script src="app.module.js"></script>
<script src="app.config.js"></script>
<script src="app.controller.js"></script>
</head>
<body ng-app="chrisjsherm.app">
<section ui-view></section>
</body>
</html>
/* Put your css in here */
body {
margin: 20px;
}
(function() {
let app = angular.module('chrisjsherm.app');
app.config(MyAppConfig);
function MyAppConfig(
$urlRouterProvider, $stateProvider
) {
// For any unmatched url, redirect here.
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: 'home.html',
controller: 'AppController',
controllerAs: 'appCtrl',
resolve: {
httpService: '$http',
annualHomeValuesRequest: function getAnnualHomeValues(
httpService
) {
return httpService.get(
'/api/region/washington-dc/home-value-index?years=2009-2013'
);
},
},
});
}
})();
(function() {
let app = angular.module('chrisjsherm.app');
app.controller('AppController', AppController);
function AppController(
annualHomeValuesRequest, WebWorkerService, $scope
) {
// Private properties.
let vm = this;
let messageId,
eventListener;
// Public properties.
vm.cagrPercent = undefined;
// Activation.
activate();
// Private methods.
function activate() {
messageId = WebWorkerService.calculateCompoundAnnualGrowthRate(
annualHomeValuesRequest.data
);
eventListener = $scope.$on(messageId, onMessageReceived);
}
function onMessageReceived(event, args) {
vm.cagrPercent = (args * 100).toFixed(2);
// De-register event listener.
eventListener();
}
}
})();
<div class="row">
<div class="col-sm-12">
<h1>
Compound Annual Growth Rate
</h1>
<p>
Washington, D.C. Home Value Index 2009-13:
<span ng-bind="appCtrl.cagrPercent + '%'"></span>
</p>
</div>
</div>
(function() {
let service = angular.module('chrisjsherm.service.web-worker', [
'chrisjsherm.service.uuid',
]);
service.factory('WebWorkerService', WebWorkerService);
function WebWorkerService(
$rootScope, UuidService
) {
// Private properties.
let webWorker = new Worker('web-worker.js');
// Activation.
activate();
// Public interface.
let serviceInterface = {
calculateCompoundAnnualGrowthRate: calculateCompoundAnnualGrowthRate,
};
return serviceInterface;
// Private methods.
function activate() {
webWorker.addEventListener(
'message',
onWebWorkerResponse,
false
);
}
function calculateCompoundAnnualGrowthRate(values) {
const MESSAGE_ID = UuidService.generate();
webWorker.postMessage(
JSON.stringify([
MESSAGE_ID,
'calculateCompoundAnnualGrowthRate',
values
])
);
return MESSAGE_ID;
}
function onWebWorkerResponse(message) {
let data = JSON.parse(message.data),
messageId = data[0],
result = data[1];
// Call $apply around async events, including anywhere it calls
// broadcast/emit.
// Docs: https://github.com/angular/angular.js/wiki/When-to-use-$scope.$apply()
$rootScope.$apply($rootScope.$broadcast(
messageId,
result
));
}
}
})();
(function () {
let service = angular.module('chrisjsherm.service.uuid', []);
service.factory('UuidService', UuidService);
function UuidService() {
/**
* Generates a rfc4122 UUID based on:
* http://stackoverflow.com/a/21963136/1579559.
*/
const lut = Array(256).fill().map((_, i) => (i < 16 ? '0' : '') + (i).toString(16));
const formatUuid = ({ d0, d1, d2, d3 }) =>
lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff] + '-' +
lut[d1 & 0xff] + lut[d1 >> 8 & 0xff] + '-' +
lut[d1 >> 16 & 0x0f | 0x40] + lut[d1 >> 24 & 0xff] + '-' +
lut[d2 & 0x3f | 0x80] + lut[d2 >> 8 & 0xff] + '-' +
lut[d2 >> 16 & 0xff] + lut[d2 >> 24 & 0xff] +
lut[d3 & 0xff] + lut[d3 >> 8 & 0xff] +
lut[d3 >> 16 & 0xff] + lut[d3 >> 24 & 0xff];
const getRandomValuesFunc = window.crypto && window.crypto.getRandomValues ?
() => {
const dvals = new Uint32Array(4);
window.crypto.getRandomValues(dvals);
return {
d0: dvals[0],
d1: dvals[1],
d2: dvals[2],
d3: dvals[3],
};
} :
() => ({
d0: Math.random() * 0x100000000 >>> 0,
d1: Math.random() * 0x100000000 >>> 0,
d2: Math.random() * 0x100000000 >>> 0,
d3: Math.random() * 0x100000000 >>> 0,
});
const uuid = () => formatUuid(getRandomValuesFunc());
var service = {
generate: generateUuid,
};
return service;
// Private functions.
function generateUuid() {
return uuid();
}
}
})();
let self = this;
self.addEventListener('message', receiveMessage, false);
// Main.
function receiveMessage(message) {
var data = JSON.parse(message.data),
workerResult;
const MESSAGE_ID = data[0];
switch (data[1]) {
// Web worker functions.
case 'close':
self.close();
break;
// Service functions.
case 'calculateCompoundAnnualGrowthRate':
workerResult = calculateCompoundAnnualGrowthRate(
data[2]
);
break;
}
self.postMessage(JSON.stringify(
[
MESSAGE_ID,
workerResult
]
));
}
// Private functions.
function calculateCompoundAnnualGrowthRate(
values
) {
if (!values) {
return;
}
let cagr,
endingValue,
numberOfYears = values.length,
startingValue = values[0];
endingValue = values[numberOfYears - 1];
cagr = Math.pow((endingValue / startingValue), (1 / numberOfYears)) - 1;
return cagr;
}
(function() {
let app = angular.module('chrisjsherm.mock-api', ['ngMockE2E']);
app.run(RunFn);
function RunFn($httpBackend) {
$httpBackend.whenGET('/api/region/washington-dc/home-value-index?years=2009-2013')
.respond([
356000,
355000,
362000,
364000,
388000,
]);
$httpBackend.whenGET(/.*/).passThrough();
}
})();