JSFiddle - React, Tailwind, and code Playground
HTML
<div ng-app>
<h2>Delegate Salary Calculator</h2>
<div ng-controller="DelegateCtrl" ng-init="reload()">
<form ng-submit="reload()">
<input type="submit" value="Reload fresh data" />
</form>
<div>
<table>
<tr>
<td>BitUSD price feed:</td>
<td>{{feedPrice}} USD/BTS</td>
</tr>
<tr>
<td>Basic delegate salary:</td>
<td>
<input type="text" size="3" ng-model="basicSalary" ng-blur="updateSalary()" />USD/round (${{salaryRoundToWeek(basicSalary)}}/week)</td>
</tr>
<tr>
<td>Maximum inflation per round:</td>
<td>
<input type="text" size="3" ng-model="maxRewardPerRound" ng-blur="updateSalary()" />BTS</td>
</tr>
<tr>
<td>Maximum budget per round:</td>
<td>${{maxRoundBudget()}} (${{salaryRoundToWeek(maxRoundBudget())}}/week)</td>
</tr>
<tr>
<td>Median approval:</td>
<td>{{medianApproval}}%</td>
</tr>
<tr>
<td>Salary limit on bottom half delegates:</td>
<td>${{salaryLimit}}/round (${{salaryRoundToWeek(salaryLimit)}}/week)</td>
</tr>
</table>
</div>
<p>
Actual salary is colored black if the delegate's full requested salary will be received. It is colored <span class="limitedSalary">orange</span> if the full requested salary will not be received, and it is colored <span class="basicSalary">red</span> if the actual salary to be received is no greater than the basic salary. Also, the values have a ⚠ icon appended if the requested salary is limited because of the salary restrictions (which are a function of the...
CSS
.standard-table {
margin:0px;
padding:0px;
width:100%;
border:1px solid #000000;
}
.standard-table table {
border-collapse: collapse;
border-spacing: 0;
width:100%;
height:100%;
margin:0px;
padding:0px;
}
.standard-table tr:nth-child(odd) {
background-color:#aad4ff;
}
.standard-table tr:nth-child(even) {
background-color:#ffffff;
}
.standard-table td {
vertical-align:middle;
border:1px solid #000000;
border-width:0px 1px 1px 0px;
text-align:left;
padding:7px;
font-size:10px;
font-family:Arial;
font-weight:normal;
color:#000000;
}
.standard-table tr:last-child td {
border-width:0px 1px 0px 0px;
}
.standard-table tr td:last-child {
border-width:0px 0px 1px 0px;
}
.standard-table tr:last-child td:last-child {
border-width:0px 0px 0px 0px;
}
.standard-table tr:first-child th {
background:-o-linear-gradient(bottom, #005fbf 5%, #003f7f 100%);
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #005fbf), color-stop(1, #003f7f));
background:-moz-linear-gradient(center top, #005fbf 5%, #003f7f 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#005fbf", endColorstr="#003f7f");
background: -o-linear-gradient(top, #005fbf, 003f7f);
background-color:#005fbf;
border:0px solid #000000;
text-align:center;
border-width:0px 0px 1px 1px;
font-size:14px;
font-family:Arial;
font-weight:bold;
color:#ffffff;
}
.standard-table tr:first-child:hover th {
background:-o-linear-gradient(bottom, #005fbf 5%, #003f7f 100%);
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #005fbf), color-stop(1, #003f7f));
background:-moz-linear-gradient(center top, #005fbf 5%, #003f7f 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#005fbf", endColorstr="#003f7f");
background: -o-linear-gradient(top, #005fbf, 003f7f);
...
JavaScript
function DelegateCtrl($scope, $http) {
var delegateAPI = "http://www.bitsharesblocks.com:2095/api/activedelegates?callback=JSON_CALLBACK";
var USDAPI = "http://www.bitsharesblocks.com:2095/api/assets/USD?callback=JSON_CALLBACK";
var roundsPerWeek = 60480.0 / 101;
$scope.feedPrice = 0.0;
$scope.medianApproval = 0.0;
$scope.salaryLimit = 0.0;
$scope.delegates = [];
$scope.basicSalary = 0.03;
$scope.maxRewardPerRound = 5050.0;
$scope.updateSalary = function () {
var maxBudget = parseFloat($scope.maxRoundBudget());
var workingBudget = 0.0;
var topHalfRequestedSalaries = [];
var extraSalaries = [];
var basicSalary = parseFloat($scope.basicSalary);
for (i = 0; i < Math.floor($scope.delegates.length/2); ++i) {
var sal = parseFloat($scope.delegates[i].requestedSalary);
topHalfRequestedSalaries.push(sal);
extraSalaries.push(Math.max(0.0, sal - basicSalary));
sal = Math.min(sal, basicSalary);
workingBudget += sal;
$scope.delegates[i].actualSalary = sal;
$scope.delegates[i].salaryClass = "basicSalary";
}
topHalfRequestedSalaries.sort( function(a,b) {return a - b;} );
var medianIndx = Math.floor(topHalfRequestedSalaries.length/2);
if(topHalfRequestedSalaries.length % 2 != 0) {
$scope.salaryLimit = (topHalfRequestedSalaries[medianIndx]).toFixed(2);
} else {
$scope.salaryLimit = ((topHalfRequestedSalaries[medianIndx-1] + topHalfRequestedSalaries[medianIndx]) / 2.0).toFixed(2);
}
for (i = Math.floor($scope.delegates.length/2); i < $scope.delegates.length; i++) {
var sal = parseFloat($scope.delegates[i].requestedSalary);
var limit = $scope.f(parseFloat($scope.delegates[i].approval) / parseFloat($scope.medianApproval)) * parseFloat($scope.salaryLimit);
if (sal > limit)...