2017-11-23 18:18:01 +11:00
{% extends 'base.html' %}
{#
Eos - Verifiable elections
2019-01-14 17:54:31 +11:00
Copyright © 2017-2019 RunasSudo (Yingtong Li)
2017-11-23 18:18:01 +11:00
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see < http: / / www . gnu . org / licenses / > .
#}
{% block title %}{{ election.name }} – Voting booth{% endblock %}
2017-11-27 22:56:43 +11:00
{% block head %}
{{ super() }}
< link rel = "stylesheet" href = "{{ url_for('static', filename='bower_components/dragula.js/dist/dragula.min.css') }}" type = "text/css" >
2018-01-07 23:54:07 +11:00
< link rel = "stylesheet" href = "{{ url_for('static', filename='bower_components/progress-tracker/app/styles/progress-tracker.css') }}" type = "text/css" >
2017-11-27 22:56:43 +11:00
{% endblock %}
2017-11-23 18:18:01 +11:00
{% block content %}
2017-11-23 21:07:16 +11:00
< div class = "ui container" id = "booth-content" >
< div class = "ui active text loader" > Loading voting booth. Please wait.< / div >
< / div >
{% endblock %}
{% block basecontent %}
{{ super() }}
2017-11-27 22:56:43 +11:00
2017-11-23 21:07:16 +11:00
< script src = "{{ url_for('static', filename='bower_components/nunjucks/browser/nunjucks.min.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/eosjs.js') }}" > < / script >
2017-11-27 22:56:43 +11:00
< script src = "{{ url_for('static', filename='bower_components/dragula.js/dist/dragula.min.js') }}" > < / script >
2017-12-11 13:53:25 +11:00
{% if eosweb.app.config['CAST_FINGERPRINT'] %}
< script src = "{{ url_for('static', filename='bower_components/fingerprintjs2/dist/fingerprint2.min.js') }}" > < / script >
{% endif %}
2017-11-27 22:56:43 +11:00
2017-11-23 21:07:16 +11:00
< script >
2019-01-14 17:54:31 +11:00
var eosjs = require("eosjs");
2017-11-23 21:07:16 +11:00
var templates = {};
var election = null;
var booth = null;
var boothWorker = null;
var boothTasks = [];
var currentBoothTask = 0;
var selection_model_view_map = {{ selection_model_view_map|safe }}; {# :rooWut: #}
2017-11-26 20:48:15 +11:00
var username = {% if session.user %}"{{ session.user.name }}"{% else %}null{% endif %};
2017-11-25 23:16:29 +11:00
var auth_methods = {{ auth_methods|safe }};
2017-12-11 13:53:25 +11:00
var should_do_fingerprint = {{ 'true' if eosweb.app.config['CAST_FINGERPRINT'] else 'false' }};
2017-11-25 23:16:29 +11:00
2017-11-23 21:07:16 +11:00
function resetBooth() {
booth = {
"questionNum": 0,
2017-11-23 23:10:57 +11:00
"answers": [],
2017-12-11 13:53:25 +11:00
"q_state": [],
"fingerprint": []
2017-11-23 21:07:16 +11:00
};
}
resetBooth();
function loadElection() {
2018-03-26 21:36:14 +11:00
// Verify booth
if (should_do_fingerprint) {
if (typeof Fingerprint2 === 'undefined') {
boothError('Your browser did not load fingerprintj2 correctly. Please try again after disabling your ad blockers and similar software. If the issue persists, try using a different browser.');
return;
}
}
2017-11-23 21:07:16 +11:00
$.ajax({ url: "{{ url_for('election_api_json', election_id=election._id) }}", dataType: "text" })
.done(function(data) {
try {
2019-01-14 17:54:31 +11:00
election = eosjs.eos.core.objects.EosObject.deserialise_and_unwrap(eosjs.eos.core.objects.EosObject.from_json(data), null);
2017-11-23 21:07:16 +11:00
boothWorker = new Worker("{{ url_for('static', filename='js/booth_worker.js') }}");
electionLoaded();
} catch (err) {
loadError(err);
throw err;
}
})
.fail(function(xhr, status, err) {
loadError(err);
throw err;
});
}
function electionLoaded() {
// Load templates for the question types
2017-11-24 18:09:43 +11:00
for (var i = 0; i < election.questions.__len__ ( ) ; i + + ) {
var question = election.questions.__getitem__(i);
2017-11-23 21:07:16 +11:00
templates[selection_model_view_map[question._name]['selections_make']] = null;
2017-11-23 23:10:57 +11:00
templates[selection_model_view_map[question._name]['selections_review']] = null;
2017-11-23 21:07:16 +11:00
}
loadTemplates();
}
function loadTemplates() {
// Load all the templates
var templateUrls = Object.keys(templates);
var numTemplatesLoaded = 0;
for (var templateUrl of templateUrls) {
(function(templateUrl) {
$.ajax({
url: "{{ url_for('static', filename='nunjucks') }}/" + templateUrl,
2017-11-28 13:58:22 +11:00
dataType: "text"
2017-11-23 21:07:16 +11:00
})
.done(function(data) {
try {
2019-01-14 17:54:31 +11:00
templates[templateUrl] = nunjucks.compile(data, null, templateUrl);
2017-11-23 21:07:16 +11:00
numTemplatesLoaded += 1;
if (numTemplatesLoaded == templateUrls.length) {
// All templates loaded. Show voting booth
boothTasks[0].activate(true);
}
} catch (err) {
loadError(err);
throw err;
}
})
.fail(function(xhr, status, err) {
loadError(err);
throw err;
});
})(templateUrl);
}
}
function loadError(err) {
var techDetails = '';
if (err) {
techDetails = '< p > Technical details: ' + err + '< / p > ';
}
2017-12-04 14:30:41 +11:00
$("#booth-content").html('< div class = "ui error message" > < p > We were unable to load the voting booth for this {{ election.kind }}. Please try again. If this problem persists, contact the {{ election.kind }} administrator.< / p > ' + techDetails + '< / div > ');
2017-11-23 21:07:16 +11:00
}
function boothError(err) {
resetBooth();
var techDetails = '';
if (err) {
techDetails = '< p > Technical details: ' + err + '< / p > ';
}
2017-12-04 14:30:41 +11:00
$("#booth-content").html('< div class = "ui error message" > < p > We were unable to display the next page of the voting booth. For your security, your ballot selections have been cleared. Please try again. If this problem persists, contact the {{ election.kind }} administrator.< / p > ' + techDetails + '< / div > ');
2017-12-11 12:22:22 +11:00
console.error(err);
2017-11-23 21:07:16 +11:00
}
function showTemplate(template, opts, destination) {
try {
if (!destination) {
destination = "#booth-content";
}
if (!opts) {
opts = {};
}
opts = $.extend({
"templates": templates,
"template": template,
"election_base_url": "{{ url_for('election_api_json', election_id=election._id) }}",
2017-11-23 23:10:57 +11:00
"static_base_url": "{{ url_for('static', filename='') }}",
2017-11-23 21:07:16 +11:00
"election": election,
"booth": booth,
"eosjs": eosjs,
2017-11-25 23:16:29 +11:00
"selection_model_view_map": selection_model_view_map,
"username": username,
"auth_methods": auth_methods
2017-11-23 21:07:16 +11:00
}, opts);
$(destination).html(templates[template].render(opts));
} catch (err) {
boothError(err);
throw err;
}
}
2017-11-23 23:10:57 +11:00
function nextTemplate(num) {
if (!num) {
num = 1;
}
currentBoothTask += num;
2017-11-23 21:07:16 +11:00
boothTasks[currentBoothTask].activate(true);
}
2017-11-23 23:10:57 +11:00
function prevTemplate(num) {
if (!num) {
num = 1;
}
currentBoothTask -= num;
2017-11-23 21:07:16 +11:00
boothTasks[currentBoothTask].activate(false);
}
// === BOOTH TASKS ===
// TODO: Make modular
templates['booth/base.html'] = null;
2017-12-11 17:34:57 +11:00
2018-01-28 22:28:10 +11:00
if (location.search.indexOf('?cast') < 0 ) {
// Normal booth
2017-12-11 17:34:57 +11:00
boothTasks.append({
activate: function(fromLeft) {
2018-01-28 22:28:10 +11:00
showTemplate('booth/welcome.html');
2017-12-11 17:34:57 +11:00
}
});
2018-01-28 22:28:10 +11:00
templates['booth/welcome.html'] = null;
2017-12-11 17:34:57 +11:00
boothTasks.append({
activate: function(fromLeft) {
2018-01-28 22:28:10 +11:00
showTemplate('booth/selections.html');
2017-12-11 17:34:57 +11:00
}
});
2018-01-28 22:28:10 +11:00
templates['booth/selections.html'] = null;
2017-12-11 17:34:57 +11:00
boothTasks.append({
activate: function(fromLeft) {
2018-01-28 22:28:10 +11:00
if (fromLeft) {
showTemplate('booth/encrypt.html');
} else {
prevTemplate();
}
2017-12-11 17:34:57 +11:00
}
});
2018-01-28 22:28:10 +11:00
templates['booth/encrypt.html'] = null;
if (location.search.indexOf('?prepoll') >= 0) {
// Pre-poll
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/review_prepoll.html', {ballot: booth.ballot});
}
});
templates['booth/review_prepoll.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/audit.html', {ballot: booth.ballot});
}
});
templates['booth/audit.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/cast_prepoll.html', {ballot: booth.ballot});
}
});
templates['booth/cast_prepoll.html'] = null;
} else {
// Real voting booth
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/review.html', {ballot: booth.ballot});
}
});
templates['booth/review.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/audit.html', {ballot: booth.ballot});
}
});
templates['booth/audit.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/cast.html', {ballot: booth.ballot, is_cast: false});
}
});
templates['booth/cast.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/complete.html', {voter: booth.voter, vote: booth.vote});
}
});
templates['booth/complete.html'] = null;
}
2017-12-11 17:34:57 +11:00
} else {
2018-01-28 22:28:10 +11:00
// Cast immediately
{% if session.staged_ballot %}
2019-01-14 17:54:31 +11:00
booth.ballot = eosjs.eos.core.objects.EosObject.deserialise_and_unwrap(eosjs.eos.core.objects.EosObject.from_json('{{ eos.core.objects.EosObject.to_json(session.staged_ballot.ballot)|safe }}'), null);
2018-01-28 22:28:10 +11:00
{% endif %}
2017-12-11 17:34:57 +11:00
boothTasks.append({
activate: function(fromLeft) {
2018-01-28 22:28:10 +11:00
showTemplate('booth/cast.html', {ballot: booth.ballot, is_cast: true});
2017-12-11 17:34:57 +11:00
}
});
templates['booth/cast.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/complete.html', {voter: booth.voter, vote: booth.vote});
}
});
templates['booth/complete.html'] = null;
}
2017-11-23 21:07:16 +11:00
// === END BOOTH TASKS ===
loadElection();
< / script >
2017-11-23 18:18:01 +11:00
{% endblock %}