diff --git a/html/index.html b/html/index.html
index 44c2475..8b484e3 100644
--- a/html/index.html
+++ b/html/index.html
@@ -139,12 +139,12 @@
-
+
diff --git a/html/index.js b/html/index.js
index 2013800..33129fd 100644
--- a/html/index.js
+++ b/html/index.js
@@ -90,11 +90,22 @@ async function clickCount() {
}
// Read BLT file
- let filePath = document.getElementById('bltFile').value;
- filePath = filePath.substring(Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/')) + 1);
+ let bltPath = document.getElementById('bltFile').value;
+ bltPath = bltPath.substring(Math.max(bltPath.lastIndexOf('\\'), bltPath.lastIndexOf('/')) + 1);
let bltFile = document.getElementById('bltFile').files[0];
- let electionData = await bltFile.text();
+ let bltData = await bltFile.text();
+
+ // Read CON file (if applicable)
+ let conPath = null;
+ let conData = null;
+ if (document.getElementById('conFile').files.length > 0) {
+ conPath = document.getElementById('conFile').value;
+ conPath = conPath.substring(Math.max(conPath.lastIndexOf('\\'), conPath.lastIndexOf('/')) + 1);
+
+ let conFile = document.getElementById('conFile').files[0];
+ conData = await conFile.text();
+ }
// Init STV options
let optsStr = [
@@ -127,9 +138,11 @@ async function clickCount() {
// Dispatch to worker
worker.postMessage({
'type': 'countElection',
- 'electionData': electionData,
+ 'bltData': bltData,
+ 'conData': conData,
'optsStr': optsStr,
- 'filePath': filePath,
+ 'bltPath': bltPath,
+ 'conPath': conPath,
'numbers': document.getElementById('selNumbers').value,
'decimals': document.getElementById('txtDP').value,
'normaliseBallots': document.getElementById('chkNormaliseBallots').checked,
diff --git a/html/worker.js b/html/worker.js
index c5dde8f..9123168 100644
--- a/html/worker.js
+++ b/html/worker.js
@@ -27,12 +27,17 @@ onmessage = function(evt) {
}
// Init election
- election = wasm['election_from_blt_' + numbers](evt.data.electionData);
+ election = wasm['election_from_blt_' + numbers](evt.data.bltData);
if (evt.data.normaliseBallots) {
wasm['election_normalise_ballots_' + numbers](election);
}
+ // Init constraints if applicable
+ if (evt.data.conData) {
+ wasm['election_load_constraints_' + numbers](election, evt.data.conData);
+ }
+
// Init STV options
opts = wasm.STVOptions.new.apply(null, evt.data.optsStr);
@@ -40,7 +45,7 @@ onmessage = function(evt) {
opts.validate();
// Describe count
- postMessage({'type': 'describeCount', 'content': wasm['describe_count_' + numbers](evt.data.filePath, election, opts)});
+ postMessage({'type': 'describeCount', 'content': wasm['describe_count_' + numbers](evt.data.bltPath, election, opts)});
// Init results table
postMessage({'type': 'initResultsTable', 'content': wasm['init_results_table_' + numbers](election, opts)});
diff --git a/src/stv/wasm.rs b/src/stv/wasm.rs
index d34fc03..a7ef5d4 100644
--- a/src/stv/wasm.rs
+++ b/src/stv/wasm.rs
@@ -17,6 +17,7 @@
#![allow(rustdoc::private_intra_doc_links)]
+use crate::constraints::Constraints;
use crate::election::{CandidateState, CountState, Election};
use crate::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational};
use crate::stv;
@@ -66,6 +67,13 @@ macro_rules! impl_type {
election.0.normalise_ballots();
}
+ /// Call [Constraints::from_con] and set [Election::constraints]
+ #[wasm_bindgen]
+ #[allow(non_snake_case)]
+ pub fn [](election: &mut [], text: String) {
+ election.0.constraints = Some(Constraints::from_con(text.lines().map(|s| s.to_string()).into_iter()));
+ }
+
/// Wrapper for [stv::count_init]
#[wasm_bindgen]
#[allow(non_snake_case)]
@@ -303,22 +311,31 @@ fn update_results_table(stage_num: usize, state: &CountState, opts
result.push(&format!(r#"{} "#, tdclasses1, state.title).into());
for candidate in state.election.candidates.iter() {
let count_card = state.candidates.get(candidate).unwrap();
- if count_card.state == stv::CandidateState::Elected {
- result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
- result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
- } else if count_card.state == stv::CandidateState::Excluded {
- result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
- if count_card.votes.is_zero() {
- result.push(&format!(r#"Ex "#, tdclasses2).into());
- } else {
+ match count_card.state {
+ CandidateState::Hopeful | CandidateState::Guarded => {
+ result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
+ result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
+ }
+ CandidateState::Elected => {
+ result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
+ result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
+ }
+ CandidateState::Doomed => {
+ result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
}
- } else if count_card.state == stv::CandidateState::Withdrawn {
- result.push(&format!(r#" "#, tdclasses2).into());
- result.push(&format!(r#"WD "#, tdclasses2).into());
- } else {
- result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
- result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
+ CandidateState::Withdrawn => {
+ result.push(&format!(r#" "#, tdclasses2).into());
+ result.push(&format!(r#"WD "#, tdclasses2).into());
+ }
+ CandidateState::Excluded => {
+ result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
+ if count_card.votes.is_zero() {
+ result.push(&format!(r#"Ex "#, tdclasses2).into());
+ } else {
+ result.push(&format!(r#"{} "#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
+ }
+ }
}
}
result.push(&format!(r#"{} "#, tdclasses2, pp(&state.exhausted.transfers, opts.pp_decimals)).into());