705 lines
No EOL
22 KiB
JavaScript
705 lines
No EOL
22 KiB
JavaScript
var dialogOpen = false;
|
|
var DIALOG_BTN_STATES = {
|
|
STEP_1: 1,
|
|
STEP_2: 2,
|
|
STEP_3: 3,
|
|
VOTE_SUCCESS: 4,
|
|
VOTE_ERROR: 5
|
|
};
|
|
|
|
function showDialogWithText(titleTxt, bodyTxt) {
|
|
var modalDialog = $('#modalDialog');
|
|
var title = modalDialog.find('.modal-title');
|
|
var body = modalDialog.find('.modal-body');
|
|
|
|
title.text(titleTxt);
|
|
|
|
var p = document.createElement("p");
|
|
p.innerHTML = bodyTxt;
|
|
body.empty();
|
|
body.append(p);
|
|
|
|
if (!dialogOpen) {
|
|
modalDialog.modal('toggle');
|
|
dialogOpen = true;
|
|
}
|
|
}
|
|
|
|
function updateDialogButtons(state) {
|
|
// Trigger the btn selectors once here
|
|
let nextDialogBtn = $('#nextDialogBtn');
|
|
let cancelDialogBtn = $('#cancelDialogBtn');
|
|
let closeDialogBtn = $('#closeDialogBtn');
|
|
let startOverDialogBtn = $('#startOverDialogBtn');
|
|
let submitDialogBtn = $('#submitDialogBtn');
|
|
|
|
switch(state) {
|
|
case DIALOG_BTN_STATES.STEP_1:
|
|
nextDialogBtn.removeClass("hidden");
|
|
cancelDialogBtn.removeClass("hidden");
|
|
closeDialogBtn.addClass("hidden");
|
|
startOverDialogBtn.addClass("hidden");
|
|
submitDialogBtn.addClass("hidden");
|
|
break;
|
|
case DIALOG_BTN_STATES.STEP_2:
|
|
nextDialogBtn.addClass("hidden");
|
|
cancelDialogBtn.removeClass("hidden");
|
|
closeDialogBtn.addClass("hidden");
|
|
startOverDialogBtn.addClass("hidden");
|
|
submitDialogBtn.addClass("hidden");
|
|
break;
|
|
case DIALOG_BTN_STATES.STEP_3:
|
|
nextDialogBtn.addClass("hidden");
|
|
cancelDialogBtn.addClass("hidden");
|
|
closeDialogBtn.addClass("hidden");
|
|
startOverDialogBtn.removeClass("hidden");
|
|
submitDialogBtn.removeClass("hidden");
|
|
break;
|
|
case DIALOG_BTN_STATES.VOTE_SUCCESS:
|
|
case DIALOG_BTN_STATES.VOTE_ERROR:
|
|
nextDialogBtn.addClass("hidden");
|
|
cancelDialogBtn.addClass("hidden");
|
|
closeDialogBtn.removeClass("hidden");
|
|
startOverDialogBtn.addClass("hidden");
|
|
submitDialogBtn.addClass("hidden");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// This should stop people ticking more than the maximum permitted
|
|
function updateCheckboxInteractivity() {
|
|
var inputs = $("label input[type=checkbox]");
|
|
|
|
if(selectedCount === MAX_SELECTIONS) {
|
|
inputs.each(function() {
|
|
var input = $(this);
|
|
|
|
if(!input.prop('checked')){
|
|
input.prop('disabled', true);
|
|
}
|
|
});
|
|
} else {
|
|
inputs.each(function() {
|
|
var input = $(this);
|
|
|
|
if(!input.prop('checked')) {
|
|
input.prop('disabled', false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
$("label input[type=checkbox]").change(function() {
|
|
// Increment the selectedCount counter if a box has been checked
|
|
if(this.checked) {
|
|
selectedCount += 1;
|
|
} else {
|
|
selectedCount -= 1;
|
|
|
|
// Just incase this falls below zero to avoid any nasty bugs
|
|
if(selectedCount < 0) {
|
|
selectedCount = 0;
|
|
}
|
|
}
|
|
|
|
updateCheckboxInteractivity();
|
|
});
|
|
|
|
function isVotingInputValid() {
|
|
var valid = true;
|
|
|
|
// First establish if the user's selection count is valid
|
|
if(!(selectedCount >= MIN_SELECTIONS && selectedCount <= MAX_SELECTIONS)) {
|
|
valid = false;
|
|
}
|
|
|
|
// This will highlight when people haven't selected enough options
|
|
|
|
if(!valid) {
|
|
let errText = "You've only selected " + selectedCount;
|
|
|
|
if(selectedCount > 1) {
|
|
errText += " options.";
|
|
} else {
|
|
errText = " You haven't selected any options.";
|
|
}
|
|
|
|
errText += " The minimum number you need to select is " + MIN_SELECTIONS + " and the maximum is "
|
|
+ MAX_SELECTIONS + ". Please go back and correct this.";
|
|
|
|
let titleTxt = 'Voting Error';
|
|
|
|
showDialogWithText(titleTxt, errText);
|
|
updateDialogButtons(DIALOG_BTN_STATES.VOTE_ERROR);
|
|
return;
|
|
}
|
|
|
|
return valid;
|
|
}
|
|
|
|
var progressBar = document.getElementById("progress-bar");
|
|
|
|
$('#gen-ballots-btn').click(function() {
|
|
// Ensure that the user selections are valid
|
|
if(isVotingInputValid()) {
|
|
// Hide the button
|
|
$(this).toggleClass('hidden');
|
|
|
|
// Inject the description progress bar which can then be updated by the encrypt btn
|
|
$('#progress-bar-description').toggleClass('hidden');
|
|
$('#progress-bar-container').toggleClass('hidden');
|
|
|
|
setTimeout(generateBallots, 25);
|
|
}
|
|
});
|
|
|
|
// Based on the user's vote in the current poll, this generates a ballot which
|
|
// does not leak information about how many options the user has selected
|
|
function generateBallot() {
|
|
// Elliptic curve cryptography params used for encryption of encrypted vote
|
|
// fragments
|
|
var ctx = new CTX("BN254CX");
|
|
var n = new ctx.BIG();
|
|
var g1 = new ctx.ECP();
|
|
var g2 = new ctx.ECP2();
|
|
|
|
var parameter = $('#event-param').val();
|
|
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
|
|
|
|
//copying the values
|
|
n.copy(tempParams.n);
|
|
g1.copy(tempParams.g1);
|
|
g2.copy(tempParams.g2);
|
|
|
|
var params = {
|
|
n:n,
|
|
g1:g1,
|
|
g2:g2
|
|
};
|
|
|
|
var tempPK = JSON.parse($('#comb_pk').val());
|
|
var pk = new ctx.ECP(0);
|
|
pk.copy(tempPK.PK);
|
|
|
|
// Collect together the unencrypted votes (which correspond to selected options)
|
|
var checkboxInputs = $("label input[type=checkbox]");
|
|
var unencryptedVotes = [];
|
|
checkboxInputs.each(function() {
|
|
var checkbox = $(this);
|
|
|
|
// Push the selected option values (ones that have been checked) to an array
|
|
if(checkbox.prop('checked')) {
|
|
unencryptedVotes.push(checkbox.val());
|
|
}
|
|
});
|
|
|
|
// If there is a dif between the num selected and the max allowed, push blank votes to the array to pad this
|
|
// to prevent information leakage
|
|
if(unencryptedVotes.length < MAX_SELECTIONS) {
|
|
let blankVotesToPush = MAX_SELECTIONS - unencryptedVotes.length;
|
|
|
|
for(let i = 0; i < blankVotesToPush; i++) {
|
|
unencryptedVotes.push(genBlankVote());
|
|
}
|
|
}
|
|
|
|
// Encrypt all of the votes for this ballot
|
|
var encryptedVotes = [];
|
|
unencryptedVotes.forEach(function(unencryptedVote) {
|
|
var encFragments = [];
|
|
|
|
// Encrypt each fragment of the unencrypted vote
|
|
unencryptedVote.split(',').forEach(function(fragment) {
|
|
var cipher = encrypt(params, pk, parseInt(fragment));
|
|
|
|
// Store C1, C2 and r from the cipher in the fragment
|
|
var c1Bytes = [];
|
|
cipher.C1.toBytes(c1Bytes);
|
|
|
|
var c2Bytes = [];
|
|
cipher.C2.toBytes(c2Bytes);
|
|
|
|
var rBytes = [];
|
|
cipher.r.toBytes(rBytes);
|
|
|
|
encFragments.push({
|
|
C1 : c1Bytes.toString(),
|
|
C2 : c2Bytes.toString(),
|
|
r : rBytes.toString()
|
|
});
|
|
});
|
|
|
|
// Store all fragments in a single 'encrypted vote'
|
|
encryptedVotes.push({
|
|
fragments: encFragments
|
|
});
|
|
});
|
|
|
|
return {
|
|
encryptedVotes: encryptedVotes
|
|
};
|
|
}
|
|
|
|
// Generates a blank vote as a string using the binary encoding scheme
|
|
function genBlankVote() {
|
|
var vote = "";
|
|
|
|
for(var i = 0; i < OPTION_COUNT; i++) {
|
|
vote += "0";
|
|
|
|
if (i !== (OPTION_COUNT - 1)) {
|
|
vote += ",";
|
|
}
|
|
}
|
|
|
|
return vote;
|
|
}
|
|
|
|
var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val();
|
|
function csrfSafeMethod(method) {
|
|
// these HTTP methods do not require CSRF protection
|
|
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
|
}
|
|
|
|
var bytestostring = function(b) {
|
|
var s = "";
|
|
var len = b.length;
|
|
var ch;
|
|
for (var i = 0; i < len; i++) {
|
|
ch = b[i];
|
|
s += ((ch >>> 4) & 15).toString(16);
|
|
s += (ch & 15).toString(16);
|
|
}
|
|
return s;
|
|
};
|
|
|
|
var stringtobytes = function(s) {
|
|
var b = [];
|
|
for (var i = 0; i < s.length; i++) {
|
|
b.push(s.charCodeAt(i));
|
|
}
|
|
return b;
|
|
};
|
|
|
|
function SHA256Hash(bytes, toStr) {
|
|
var ctx = new CTX();
|
|
|
|
var R = [];
|
|
var H = new ctx.HASH256();
|
|
|
|
H.process_array(bytes);
|
|
R = H.hash();
|
|
|
|
if (R.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
if(toStr) {
|
|
// If toStr is true we return the stringified version of the bytes of the hash
|
|
return bytestostring(R);
|
|
} else {
|
|
// If toStr is false we return the bytes of the hash
|
|
return R;
|
|
}
|
|
}
|
|
|
|
function generateBallots() {
|
|
// Get the user's selected option
|
|
let inputs = $("label input[type=checkbox]");
|
|
let selectedOption = "";
|
|
inputs.each(function() {
|
|
let input = $(this);
|
|
|
|
if(input.prop('checked')) {
|
|
selectedOption = input.val();
|
|
selectedOption = document.getElementById(selectedOption).innerText;
|
|
}
|
|
});
|
|
|
|
// Generate Ballot A and Ballot B to be displayed to the user
|
|
// This fn starts the process
|
|
var ballotA = generateBallot();
|
|
|
|
// Update the progress bar once the generation has completed
|
|
progressBar.setAttribute("style", "width: 50%;");
|
|
|
|
// This delay allows the execution thread to update the above CSS on the progress bar
|
|
setTimeout(function () {
|
|
var ballotB = generateBallot();
|
|
progressBar.setAttribute("style", "width: 100%;");
|
|
|
|
showIDsQRCode(ballotA, ballotB, selectedOption);
|
|
}, 150);
|
|
}
|
|
|
|
function showIDsQRCode(ballotA, ballotB, selectedOption) {
|
|
var voterID = window.location.search.slice(1).split(/=(.+)/)[1];
|
|
var eventID = window.location.href.split('/')[4];
|
|
var pollID = $('#poll-num').text();
|
|
|
|
// Display a QR code with ID details
|
|
var modalDialog = $('#modalDialog');
|
|
var title = modalDialog.find('.modal-title');
|
|
var body = modalDialog.find('.modal-body');
|
|
|
|
body.empty();
|
|
title.text('Step 0 of 3: Scan this');
|
|
|
|
let pleaseScanP = document.createElement('p');
|
|
pleaseScanP.innerHTML = "Please scan the following QR code from your DEMOS 2 mobile application:";
|
|
|
|
let QRDiv = document.createElement('div');
|
|
var QRCodeImg = document.createElement('img');
|
|
QRCodeImg.setAttribute('class', 'QR-code');
|
|
QRCodeImg.setAttribute('id', "qr-img");
|
|
new QRCode(QRCodeImg, voterID+";"+eventID+";"+pollID);
|
|
QRDiv.append(QRCodeImg);
|
|
|
|
body.append(pleaseScanP);
|
|
body.append(QRDiv);
|
|
|
|
// Prepare the appropriate dialog buttons
|
|
updateDialogButtons(DIALOG_BTN_STATES.STEP_1);
|
|
|
|
if(!dialogOpen) {
|
|
modalDialog.modal('toggle');
|
|
dialogOpen = true;
|
|
}
|
|
|
|
$('#nextDialogBtn').click(function(e) {
|
|
showHashQRCode(ballotA, ballotB, selectedOption);
|
|
});
|
|
};
|
|
|
|
// Called in stage 1 of 3 in the voting process
|
|
function showHashQRCode(ballotA, ballotB, selectedOption) {
|
|
var ballots = new Array(ballotA, ballotB);
|
|
var ballotHashes = new Array(2);
|
|
|
|
// Hash both ballots and store
|
|
for (let i = 0; i <= 1; i++)
|
|
ballotHashes[i] = SHA256Hash(stringtobytes(JSON.stringify(ballots[i])), true);
|
|
|
|
// With the ballots and their hashes generated, we can display the QR code of both hashes
|
|
var modalDialog = $('#modalDialog');
|
|
var title = modalDialog.find('.modal-title');
|
|
var body = modalDialog.find('.modal-body');
|
|
|
|
body.empty();
|
|
title.text('Step 1 of 3: Link Your Vote');
|
|
|
|
let pleaseScanP = document.createElement('p');
|
|
pleaseScanP.innerHTML = "Please scan the following QR code from your DEMOS 2 auditor:";
|
|
|
|
let QRDiv = document.createElement('div');
|
|
var QRCodeImg = document.createElement('img');
|
|
QRCodeImg.setAttribute('class', 'QR-code');
|
|
QRCodeImg.setAttribute('id', "qr-img");
|
|
new QRCode(QRCodeImg, ballotHashes[0] + ';' + ballotHashes[1]);
|
|
QRDiv.append(QRCodeImg);
|
|
|
|
// ----------------------------------------------
|
|
|
|
var hashGroupDiv = document.createElement('div');
|
|
var br = document.createElement('br');
|
|
hashGroupDiv.append( br );
|
|
|
|
var hashA = document.createElement("span");
|
|
hashA.innerHTML = "Hash A: " + ballotHashes[0];
|
|
hashGroupDiv.append( hashA );
|
|
|
|
var br2 = document.createElement('br');
|
|
hashGroupDiv.append( br2 );
|
|
|
|
var hashB = document.createElement("span");
|
|
hashB.innerHTML = "Hash B: " + ballotHashes[1];
|
|
hashGroupDiv.append( hashB );
|
|
|
|
// -----------------------------------------------
|
|
|
|
body.append(pleaseScanP);
|
|
body.append(QRDiv);
|
|
body.append(hashGroupDiv);
|
|
|
|
// Prepare the appropriate dialog buttons
|
|
updateDialogButtons(DIALOG_BTN_STATES.STEP_1);
|
|
|
|
if(!dialogOpen) {
|
|
modalDialog.modal('toggle');
|
|
dialogOpen = true;
|
|
}
|
|
|
|
$('#nextDialogBtn').click(function(e) {
|
|
showBallotChoiceDialog(ballots, ballotHashes, selectedOption, modalDialog);
|
|
});
|
|
}
|
|
|
|
// Called in stage 2 of 3 in the voting process
|
|
function showBallotChoiceDialog(ballots, ballotHashes, selectedOption, dialog) {
|
|
// Display the ballot choice dialog
|
|
var title = dialog.find('.modal-title');
|
|
var body = dialog.find('.modal-body');
|
|
|
|
body.empty();
|
|
title.text('Step 2 of 3: Select a Ballot');
|
|
|
|
// Generate the body of the dialog which consists of a button for A and for B
|
|
var choiceGroupDiv = document.createElement('div');
|
|
choiceGroupDiv.setAttribute('class', 'choice-group');
|
|
|
|
var btnChoiceA = document.createElement('a');
|
|
btnChoiceA.setAttribute('id', 'choice-A');
|
|
btnChoiceA.setAttribute('class', 'btn btn-sq btn-primary');
|
|
btnChoiceA.innerHTML = 'A';
|
|
choiceGroupDiv.append(btnChoiceA);
|
|
|
|
var btnChoiceB = document.createElement('a');
|
|
btnChoiceB.setAttribute('id', 'choice-B');
|
|
btnChoiceB.setAttribute('class', 'btn btn-sq btn-warning choice');
|
|
btnChoiceB.innerHTML = 'B';
|
|
choiceGroupDiv.append(btnChoiceB);
|
|
|
|
// ----------------------------------------------
|
|
|
|
var hashGroupDiv = document.createElement('div');
|
|
var br = document.createElement('br');
|
|
hashGroupDiv.append( br );
|
|
|
|
var hashA = document.createElement("span");
|
|
hashA.innerHTML = "Hash A: " + ballotHashes[0];
|
|
hashGroupDiv.append( hashA );
|
|
|
|
var br2 = document.createElement('br');
|
|
hashGroupDiv.append( br2 );
|
|
|
|
var hashB = document.createElement("span");
|
|
hashB.innerHTML = "Hash B: " + ballotHashes[1];
|
|
hashGroupDiv.append( hashB );
|
|
|
|
// -----------------------------------------------
|
|
|
|
body.append(choiceGroupDiv);
|
|
body.append(hashGroupDiv);
|
|
|
|
// Register callback functions for the selection of either A or B
|
|
$('#choice-A').click(function(e) {
|
|
showSelectionConfirmationDialog("A", ballots[0], ballotHashes[0], ballots[1], selectedOption, dialog);
|
|
});
|
|
|
|
$('#choice-B').click(function(e) {
|
|
showSelectionConfirmationDialog("B", ballots[1], ballotHashes[1], ballots[0], selectedOption, dialog);
|
|
});
|
|
|
|
updateDialogButtons(DIALOG_BTN_STATES.STEP_2);
|
|
|
|
if(!dialogOpen) {
|
|
modalDialog.modal('toggle');
|
|
dialogOpen = true;
|
|
}
|
|
}
|
|
|
|
// Called in stage 3 of 3 in the voting process
|
|
function showSelectionConfirmationDialog(selection, selectedBallot, selectedBallotHash,
|
|
otherBallot, selectedOption, dialog) {
|
|
let title = dialog.find('.modal-title');
|
|
let body = dialog.find('.modal-body');
|
|
body.empty();
|
|
|
|
title.text("Step 3 of 3: Confirm Ballot Selection");
|
|
|
|
// Ballot detail section
|
|
let selectedInfoSecDiv = document.createElement('div');
|
|
|
|
let detailsP = document.createElement('p');
|
|
detailsP.innerHTML = "Please check the following details are correct: ";
|
|
selectedInfoSecDiv.append(detailsP);
|
|
|
|
let ul = document.createElement('ul');
|
|
|
|
let selectedOptionLi = document.createElement('li');
|
|
selectedOptionLi.innerHTML = "Selected Option: " + selectedOption;
|
|
|
|
let ballotSelectionLi = document.createElement('li');
|
|
ballotSelectionLi.innerHTML = "Selected Ballot: " + selection;
|
|
|
|
let ballotHashLi = document.createElement('li');
|
|
ballotHashLi.innerHTML = "SHA256 Ballot Fingerprint: " + selectedBallotHash;
|
|
|
|
ul.append(selectedOptionLi);
|
|
ul.append(ballotSelectionLi);
|
|
ul.append(ballotHashLi);
|
|
selectedInfoSecDiv.append(ul);
|
|
|
|
// Instruction section
|
|
let instructionsP = document.createElement('p');
|
|
instructionsP.innerHTML = "If you are happy with your selection you can click on the 'Submit' button below to store"
|
|
+ " your vote. Otherwise you can select 'Start Over' to go through the voting process again.";
|
|
selectedInfoSecDiv.append(instructionsP);
|
|
|
|
let additionalInstructionsP = document.createElement('p');
|
|
additionalInstructionsP.innerHTML = "You can overwrite your vote later by re-visiting this page and voting again.";
|
|
selectedInfoSecDiv.append(additionalInstructionsP);
|
|
|
|
body.append(selectedInfoSecDiv);
|
|
|
|
// Update the dialog buttons accordingly
|
|
updateDialogButtons(DIALOG_BTN_STATES.STEP_3);
|
|
|
|
$('#submitDialogBtn').click(function() {
|
|
// Dispatch the ballot to the server
|
|
sendBallotsToServer(selection, selectedBallot, otherBallot);
|
|
});
|
|
|
|
if(!dialogOpen) {
|
|
modalDialog.modal('toggle');
|
|
dialogOpen = true;
|
|
}
|
|
}
|
|
|
|
function sendBallotsToServer(selection, selectedBallot, otherBallot) {
|
|
$.ajaxSetup({
|
|
beforeSend: function(xhr, settings) {
|
|
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
|
|
xhr.setRequestHeader("X-CSRFToken", CSRF);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Elliptic curve cryptography params used for encryption of encrypted vote
|
|
// fragments
|
|
var ctx = new CTX("BN254CX");
|
|
var n = new ctx.BIG();
|
|
var g1 = new ctx.ECP();
|
|
var g2 = new ctx.ECP2();
|
|
|
|
var parameter = $('#event-param').val();
|
|
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
|
|
|
|
//copying the values
|
|
n.copy(tempParams.n);
|
|
g1.copy(tempParams.g1);
|
|
g2.copy(tempParams.g2);
|
|
|
|
var params = {
|
|
n:n,
|
|
g1:g1,
|
|
g2:g2
|
|
};
|
|
|
|
var tempPK = JSON.parse($('#comb_pk').val());
|
|
var pk = new ctx.ECP(0);
|
|
pk.copy(tempPK.PK);
|
|
|
|
// Prepares the unselected ballot to send, encrypted, to the server.
|
|
// Creates the unique ballot ID for the unselected ballot
|
|
var voterID = window.location.search.slice(1).split(/=(.+)/)[1];
|
|
var eventID = window.location.href.split('/')[4];
|
|
var pollNum = $('#poll-num').text();
|
|
var ballotID = encodeURIComponent(btoa(JSON.stringify({voterID: voterID, eventID: eventID, pollNum: pollNum})));
|
|
// TODO: Generate a SK rather than using a static one. UUID generated server side and then injected JS side?
|
|
var SK = "temporary";
|
|
var encAlt = sjcl.encrypt(SK, JSON.stringify({ballotID: ballotID, ballot: otherBallot}));
|
|
|
|
// Generates an HMAC to protect the encrypted ballot from tampering.
|
|
var out = (new sjcl.misc.hmac(key, sjcl.hash.sha256)).mac(encAlt);
|
|
var hmac = sjcl.codec.hex.fromBits(out);
|
|
|
|
// Prepares the selected ballot to submit to the server.
|
|
let selectedBallotAsStr = JSON.stringify(selectedBallot);
|
|
$.ajax({
|
|
type : "POST",
|
|
url : window.location,
|
|
data : { handle: ballotID, encBallot: encAlt, ballot: selectedBallotAsStr, selection: selection },
|
|
success : function(){
|
|
onAfterBallotSend(ballotID, SK, hmac);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Called once the ballot has been sent to the back-end and dialog has closed
|
|
function onAfterBallotSend(ballotID, SK, hmac) {
|
|
// With one ballot selected, we can display a QR code of the ballot ID
|
|
var modalDialog = $('#modalDialog');
|
|
var title = modalDialog.find('.modal-title');
|
|
var body = modalDialog.find('.modal-body');
|
|
body.empty();
|
|
|
|
let titleText = 'Vote Successfully Received';
|
|
title.text(titleText);
|
|
|
|
// Add the first section: Instructions on next steps
|
|
let instructions1Txt = "Thank you for voting! Please note down the ballot identifier by scanning " +
|
|
"this QR code using the DEMOS2 mobile application: ";
|
|
|
|
var instructions1P = document.createElement("p");
|
|
instructions1P.innerHTML = instructions1Txt;
|
|
body.append(instructions1P);
|
|
|
|
// Add the second section: QR code that contains the ballot identifier and HMAC
|
|
var QRCodeImg = document.createElement('img');
|
|
QRCodeImg.setAttribute('class', 'QR-code');
|
|
new QRCode(QRCodeImg, ballotID+";"+btoa(hmac));
|
|
|
|
body.append(QRCodeImg);
|
|
|
|
// Add the third section: instructions on Ballot ID and SK
|
|
let instructions2Div = document.createElement('div');
|
|
instructions2Div.setAttribute('class', 'containerMarginTop');
|
|
|
|
let instructions2Txt = "You will also be emailed the ballot identifier. However, you will need to note down the following " +
|
|
"secret in order to later verify your ballot was cast as recorded: ";
|
|
let instructions2P = document.createElement('p');
|
|
instructions2P.innerHTML = instructions2Txt;
|
|
instructions2Div.append(instructions2P);
|
|
body.append(instructions2Div);
|
|
|
|
// Add the fourth section: SK plain text
|
|
let SKContainerDiv = document.createElement('div');
|
|
SKContainerDiv.setAttribute("class", "containerMarginTop");
|
|
|
|
let SKDiv = document.createElement('div');
|
|
SKDiv.setAttribute("class", "skDIV");
|
|
|
|
let SKP = document.createElement('p');
|
|
SKP.innerHTML = SK;
|
|
SKDiv.append(SKP);
|
|
|
|
SKContainerDiv.append(SKDiv);
|
|
body.append(SKContainerDiv);
|
|
|
|
// Conditional fifth section: Instructions on how to vote on the next poll for the event
|
|
if(POLL_NUM !== POLL_COUNT) {
|
|
let instructions3Txt = "You can vote on the next poll by closing down this dialog and clicking 'Next Poll'.";
|
|
let instructions3P = document.createElement('p');
|
|
instructions3P.innerHTML = instructions3Txt;
|
|
body.append(instructions3P);
|
|
}
|
|
|
|
updateDialogButtons(DIALOG_BTN_STATES.VOTE_SUCCESS);
|
|
}
|
|
|
|
$('#modalDialog').on('hide.bs.modal', function (e) {
|
|
var titleText = $(this).find('.modal-title').text();
|
|
|
|
if(titleText.indexOf("Received") > -1) {
|
|
// Update page to reflect the fact that a vote has taken place
|
|
location.reload();
|
|
} else if (titleText.indexOf("Error") === -1) {
|
|
// Reset poll voting to allow user to vote again
|
|
progressBar.setAttribute("style", "width: 0%;");
|
|
$('#gen-ballots-btn').toggleClass("hidden");
|
|
$('#progress-bar-description').toggleClass('hidden');
|
|
$('#progress-bar-container').toggleClass('hidden');
|
|
|
|
var inputs = $("label input[type=checkbox]");
|
|
inputs.each(function () {
|
|
var input = $(this);
|
|
input.prop('checked', false);
|
|
input.prop('disabled', false);
|
|
});
|
|
|
|
selectedCount = 0;
|
|
}
|
|
|
|
dialogOpen = false;
|
|
}); |