239 lines
7.6 KiB
HTML
239 lines
7.6 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>pyRCV2 test</title>
|
|
<style type="text/css">
|
|
html, body {
|
|
font-family: 'Liberation Sans', FreeSans, Helvetica, Arial, sans-serif;
|
|
}
|
|
table {
|
|
border-collapse: collapse;
|
|
}
|
|
td {
|
|
padding: 0px 8px;
|
|
}
|
|
td.count {
|
|
text-align: right;
|
|
}
|
|
tr:first-child td {
|
|
vertical-align: bottom;
|
|
}
|
|
td.excluded {
|
|
background-color: #fecfcfff;
|
|
}
|
|
td.elected {
|
|
background-color: #d1fca7ff;
|
|
}
|
|
tr.info td {
|
|
background-color: #edededff;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<input type="file" id="bltFile">
|
|
<button onclick="clickBtn();">OK</button>
|
|
<select id="numbers"><option value="native" selected>Native Float</option><option value="int">Native Integer</option><option value="rational">Rational</option><option value="fixed">Fixed (6 d.p.)</option></select>
|
|
|
|
<table id="result"></table>
|
|
|
|
<script src="http://peterolson.github.com/BigRational.js/BigInt_BigRat.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/big.js@6.0.0/big.min.js"></script>
|
|
<script src="bundle.js"></script>
|
|
|
|
<script>
|
|
async function clickBtn() {
|
|
// Read BLT file
|
|
let bltFile = document.getElementById('bltFile').files[0];
|
|
let text = await bltFile.text();
|
|
|
|
// Initialise table rows
|
|
let tblResults = document.getElementById('result');
|
|
tblResults.innerHTML = '';
|
|
let candMap = {}; // candidate name -> rows
|
|
|
|
// Step election
|
|
let worker = new Worker('worker.js');
|
|
let election, elComment, elExhausted1, elExhausted2, elLTF1, elLTF2, elTotal, elQuota;
|
|
|
|
worker.onmessage = function(evt) {
|
|
if (evt.data.type === 'init') {
|
|
election = evt.data.election;
|
|
|
|
// Comment row
|
|
elComment = document.createElement('tr');
|
|
let elTd = document.createElement('td');
|
|
elComment.appendChild(elTd);
|
|
tblResults.appendChild(elComment);
|
|
|
|
// Candidates
|
|
for (let candidate of election.candidates) {
|
|
let elTr1 = document.createElement('tr');
|
|
let elTr2 = document.createElement('tr');
|
|
|
|
elTd = document.createElement('td');
|
|
elTd.setAttribute('rowspan', '2');
|
|
elTd.style.borderTop = '1px solid black';
|
|
elTd.innerText = candidate;
|
|
elTr1.appendChild(elTd);
|
|
|
|
tblResults.appendChild(elTr1);
|
|
tblResults.appendChild(elTr2);
|
|
|
|
candMap[candidate] = [elTr1, elTr2];
|
|
}
|
|
|
|
// Exhausted votes row
|
|
elExhausted1 = document.createElement('tr');
|
|
elExhausted1.classList.add('info');
|
|
elExhausted2 = document.createElement('tr');
|
|
elExhausted2.classList.add('info');
|
|
|
|
elTd = document.createElement('td');
|
|
elTd.setAttribute('rowspan', '2');
|
|
elTd.style.borderTop = '1px solid black';
|
|
elTd.innerText = 'Exhausted';
|
|
elExhausted1.appendChild(elTd);
|
|
|
|
tblResults.appendChild(elExhausted1);
|
|
tblResults.appendChild(elExhausted2);
|
|
|
|
// Loss to fraction row
|
|
elLTF1 = document.createElement('tr');
|
|
elLTF1.classList.add('info');
|
|
elLTF2 = document.createElement('tr');
|
|
elLTF2.classList.add('info');
|
|
|
|
elTd = document.createElement('td');
|
|
elTd.setAttribute('rowspan', '2');
|
|
elTd.style.borderTop = '1px solid black';
|
|
elTd.innerText = 'Loss to fraction';
|
|
elLTF1.appendChild(elTd);
|
|
|
|
if (document.getElementById('numbers').value === 'int') {
|
|
tblResults.appendChild(elLTF1);
|
|
tblResults.appendChild(elLTF2);
|
|
}
|
|
|
|
// Total row
|
|
elTotal = document.createElement('tr');
|
|
elTotal.classList.add('info');
|
|
elTd = document.createElement('td');
|
|
elTd.style.borderTop = '1px solid black';
|
|
elTd.innerText = 'Total';
|
|
elTotal.appendChild(elTd);
|
|
tblResults.appendChild(elTotal);
|
|
|
|
// Quota row
|
|
elQuota = document.createElement('tr');
|
|
elQuota.classList.add('info');
|
|
elTd = document.createElement('td');
|
|
elTd.style.borderTop = '1px solid black';
|
|
elTd.style.borderBottom = '1px solid black';
|
|
elTd.innerText = 'Quota';
|
|
elQuota.appendChild(elTd);
|
|
tblResults.appendChild(elQuota);
|
|
}
|
|
|
|
if (evt.data.type === 'result') {
|
|
let result = evt.data.result;
|
|
|
|
// Display results
|
|
elTd = document.createElement('td');
|
|
elTd.innerText = result.comment;
|
|
elComment.appendChild(elTd);
|
|
|
|
for (let [candidate, countCard] of result.candidates) {
|
|
[elTr1, elTr2] = candMap[candidate];
|
|
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
if (countCard.state === py.pyRCV2.model.CandidateState.WITHDRAWN || countCard.state === py.pyRCV2.model.CandidateState.EXCLUDED) {
|
|
elTd.classList.add('excluded');
|
|
} else if (countCard.state === py.pyRCV2.model.CandidateState.ELECTED || countCard.state === py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
|
elTd.classList.add('elected');
|
|
}
|
|
elTd.style.borderTop = '1px solid black';
|
|
if (countCard.transfers != '0.00') {
|
|
elTd.innerText = countCard.transfers;
|
|
}
|
|
elTr1.appendChild(elTd);
|
|
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
if (countCard.state === py.pyRCV2.model.CandidateState.WITHDRAWN) {
|
|
elTd.classList.add('excluded');
|
|
elTd.innerText = 'WD';
|
|
} else if (countCard.state === py.pyRCV2.model.CandidateState.ELECTED || countCard.state === py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
|
elTd.classList.add('elected');
|
|
elTd.innerText = countCard.votes;
|
|
elTd.style.fontWeight = 'bold';
|
|
|
|
elTr1.querySelector('td:first-child').classList.add('elected');
|
|
} else if (countCard.state === py.pyRCV2.model.CandidateState.EXCLUDED) {
|
|
elTd.classList.add('excluded');
|
|
elTd.innerText = 'EX';
|
|
} else {
|
|
elTd.innerText = countCard.votes;
|
|
}
|
|
elTr2.appendChild(elTd);
|
|
}
|
|
|
|
// Display exhausted votes
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
elTd.style.borderTop = '1px solid black';
|
|
if (result.exhausted.transfers != '0.00') {
|
|
elTd.innerText = result.exhausted.transfers;
|
|
}
|
|
elExhausted1.appendChild(elTd);
|
|
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
elTd.innerText = result.exhausted.votes;
|
|
elExhausted2.appendChild(elTd);
|
|
|
|
// Display loss to fraction
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
elTd.style.borderTop = '1px solid black';
|
|
if (result.loss_fraction.transfers != '0.00') {
|
|
elTd.innerText = result.loss_fraction.transfers;
|
|
}
|
|
elLTF1.appendChild(elTd);
|
|
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
elTd.innerText = result.loss_fraction.votes;
|
|
elLTF2.appendChild(elTd);
|
|
|
|
// Display total
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
elTd.style.borderTop = '1px solid black';
|
|
elTd.innerText = result.total;
|
|
elTotal.appendChild(elTd);
|
|
|
|
// Display quota
|
|
elTd = document.createElement('td');
|
|
elTd.classList.add('count');
|
|
elTd.style.borderTop = '1px solid black';
|
|
elTd.style.borderBottom = '1px solid black';
|
|
elTd.innerText = result.quota;
|
|
elQuota.appendChild(elTd);
|
|
}
|
|
}
|
|
|
|
worker.onerror = function(evt) {
|
|
throw evt;
|
|
}
|
|
|
|
worker.postMessage({
|
|
'numbers': document.getElementById('numbers').value,
|
|
'data': text
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|