311 lines
9.3 KiB
HTML
311 lines
9.3 KiB
HTML
{% extends 'base.html' %}
|
|
|
|
{#
|
|
Eos - Verifiable elections
|
|
Copyright © 2017-2021 RunasSudo (Yingtong Li)
|
|
|
|
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 %}
|
|
|
|
{% block head %}
|
|
{{ super() }}
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='bower_components/dragula.js/dist/dragula.min.css') }}" type="text/css">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='bower_components/progress-tracker/app/styles/progress-tracker.css') }}" type="text/css">
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="ui container" id="booth-content">
|
|
<div class="ui active text loader">Loading voting booth. Please wait.</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block basecontent %}
|
|
{{ super() }}
|
|
|
|
<script src="{{ url_for('static', filename='bower_components/nunjucks/browser/nunjucks.min.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/eosjs.js') }}"></script>
|
|
|
|
<script src="{{ url_for('static', filename='bower_components/dragula.js/dist/dragula.min.js') }}"></script>
|
|
{% if eosweb.app.config['CAST_FINGERPRINT'] %}
|
|
<script src="{{ url_for('static', filename='bower_components/fingerprintjs2/dist/fingerprint2.min.js') }}"></script>
|
|
{% endif %}
|
|
|
|
<script>
|
|
var eosjs = require("eosjs");
|
|
|
|
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: #}
|
|
|
|
var username = {% if session.user %}"{{ session.user.name }}"{% else %}null{% endif %};
|
|
var auth_methods = {{ auth_methods|safe }};
|
|
var should_do_fingerprint = {{ 'true' if eosweb.app.config['CAST_FINGERPRINT'] else 'false' }};
|
|
|
|
function resetBooth() {
|
|
booth = {
|
|
"questionNum": 0,
|
|
"answers": [],
|
|
"q_state": [],
|
|
"fingerprint": []
|
|
};
|
|
}
|
|
resetBooth();
|
|
|
|
function loadElection() {
|
|
// Verify booth
|
|
if (should_do_fingerprint) {
|
|
if (typeof Fingerprint2 === 'undefined') {
|
|
boothError('Your browser did not load fingerprintjs2 correctly. Please try again after disabling your ad blockers and similar software. If the issue persists, try using a different browser.');
|
|
return;
|
|
}
|
|
}
|
|
|
|
$.ajax({ url: "{{ url_for('election_api_json', election_id=election._id) }}", dataType: "text" })
|
|
.done(function(data) {
|
|
try {
|
|
election = eosjs.eos.core.objects.EosObject.deserialise_and_unwrap(eosjs.eos.core.objects.EosObject.from_json(data), null);
|
|
|
|
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
|
|
for (var i = 0; i < election.questions.__len__(); i++) {
|
|
var question = election.questions.__getitem__(i);
|
|
templates[selection_model_view_map[question._name]['selections_make']] = null;
|
|
templates[selection_model_view_map[question._name]['selections_review']] = null;
|
|
}
|
|
|
|
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,
|
|
dataType: "text"
|
|
})
|
|
.done(function(data) {
|
|
try {
|
|
templates[templateUrl] = nunjucks.compile(data, null, templateUrl);
|
|
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>';
|
|
}
|
|
$("#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>');
|
|
}
|
|
|
|
function boothError(err) {
|
|
resetBooth();
|
|
var techDetails = '';
|
|
if (err) {
|
|
techDetails = '<p>Technical details: ' + err + '</p>';
|
|
}
|
|
$("#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>');
|
|
console.error(err);
|
|
}
|
|
|
|
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) }}",
|
|
"static_base_url": "{{ url_for('static', filename='') }}",
|
|
"election": election,
|
|
"booth": booth,
|
|
"eosjs": eosjs,
|
|
"selection_model_view_map": selection_model_view_map,
|
|
"username": username,
|
|
"auth_methods": auth_methods
|
|
}, opts);
|
|
$(destination).html(templates[template].render(opts));
|
|
} catch (err) {
|
|
boothError(err);
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
function nextTemplate(num) {
|
|
if (!num) {
|
|
num = 1;
|
|
}
|
|
currentBoothTask += num;
|
|
boothTasks[currentBoothTask].activate(true);
|
|
}
|
|
|
|
function prevTemplate(num) {
|
|
if (!num) {
|
|
num = 1;
|
|
}
|
|
currentBoothTask -= num;
|
|
boothTasks[currentBoothTask].activate(false);
|
|
}
|
|
|
|
// === BOOTH TASKS ===
|
|
// TODO: Make modular
|
|
|
|
templates['booth/base.html'] = null;
|
|
|
|
if (location.search.indexOf('?cast') < 0) {
|
|
// Normal booth
|
|
boothTasks.append({
|
|
activate: function(fromLeft) {
|
|
showTemplate('booth/welcome.html');
|
|
}
|
|
});
|
|
templates['booth/welcome.html'] = null;
|
|
|
|
boothTasks.append({
|
|
activate: function(fromLeft) {
|
|
showTemplate('booth/selections.html');
|
|
}
|
|
});
|
|
templates['booth/selections.html'] = null;
|
|
|
|
boothTasks.append({
|
|
activate: function(fromLeft) {
|
|
if (fromLeft) {
|
|
showTemplate('booth/encrypt.html');
|
|
} else {
|
|
prevTemplate();
|
|
}
|
|
}
|
|
});
|
|
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;
|
|
}
|
|
} else {
|
|
// Cast immediately
|
|
{% if session.staged_ballot %}
|
|
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);
|
|
{% endif %}
|
|
boothTasks.append({
|
|
activate: function(fromLeft) {
|
|
showTemplate('booth/cast.html', {ballot: booth.ballot, is_cast: true});
|
|
}
|
|
});
|
|
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;
|
|
}
|
|
|
|
// === END BOOTH TASKS ===
|
|
|
|
loadElection();
|
|
</script>
|
|
{% endblock %}
|