{% extends 'base.html' %} {# Eos - Verifiable elections Copyright © 2017-18 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 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() { $.ajax({ url: "{{ url_for('election_api_json', election_id=election._id) }}", dataType: "text" }) .done(function(data) { try { election = eosjs.eos.core.objects.__all__.EosObject.deserialise_and_unwrap(eosjs.eos.core.objects.__all__.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); 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 boothTasks.append({ activate: function(fromLeft) { showTemplate('booth/welcome.html'); } }); templates['booth/base.html'] = null; 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}); } }); 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 %}