Merge pull request #5 from vincentmdealmeida/MultiplePolls
Upgraded the Create Event page and back-end view to handle the creati…
This commit is contained in:
commit
11abbb8a64
5 changed files with 410 additions and 171 deletions
|
@ -14,7 +14,7 @@ from allauthdemo.auth.models import DemoUser
|
|||
|
||||
Author: Vincent de Almeida
|
||||
|
||||
Created: 11/07/2018
|
||||
Created: 11/06/2018
|
||||
'''
|
||||
|
||||
# TODO: Define a validation function that can do back-end verification on top of the front end validation
|
||||
|
@ -31,8 +31,6 @@ class CreateNewEventModelAdaptor:
|
|||
identifier = None
|
||||
starts_at = None
|
||||
ends_at = None
|
||||
min_num_selections = 0
|
||||
max_num_selections = 0
|
||||
organisers = []
|
||||
trustees = []
|
||||
voters = []
|
||||
|
@ -47,6 +45,8 @@ class CreateNewEventModelAdaptor:
|
|||
self.form_data = form_data.copy()
|
||||
self.user = user
|
||||
# TODO: Call validation func here (incl functionality for verifying CSRF + reCAPTCHA)
|
||||
print("Form Data:")
|
||||
print(self.form_data)
|
||||
self.__extractData()
|
||||
|
||||
|
||||
|
@ -108,11 +108,6 @@ class CreateNewEventModelAdaptor:
|
|||
else:
|
||||
self.voters.append(EmailUser(email=voter_email))
|
||||
|
||||
|
||||
# Extract the min and max number of selections
|
||||
self.min_num_selections = int(self.form_data.pop('minimum-input')[0])
|
||||
self.max_num_selections = int(self.form_data.pop('maximum-input')[0])
|
||||
|
||||
# Create the Event model object - this does not persist it to the DB
|
||||
self.event = Event(start_time=self.starts_at,
|
||||
end_time=self.ends_at,
|
||||
|
@ -124,27 +119,35 @@ class CreateNewEventModelAdaptor:
|
|||
|
||||
|
||||
def __gen_polls_options_map(self):
|
||||
# At the time of writing, you can only define one poll at event-creation time
|
||||
# Get the poll count (the number of poll and options that have been defined)
|
||||
poll_count = int(self.form_data.pop('poll-count-input')[0])
|
||||
|
||||
# Generate PollOption objects from the option data defined in form_data
|
||||
options = self.form_data.pop('option-name-input')
|
||||
poll_options_list = []
|
||||
for i in range(poll_count):
|
||||
# String version of i
|
||||
i_str = str(i)
|
||||
|
||||
for option in options:
|
||||
if option != '':
|
||||
poll_options_list.append(PollOption(choice_text=option, votes=0))
|
||||
# Generate PollOption objects from the option data defined in form_data
|
||||
options = self.form_data.pop('option-name-input-' + i_str)
|
||||
poll_options_list = []
|
||||
votes = 0
|
||||
|
||||
# Extract required Poll object data and create a poll with its PollOption objects
|
||||
text = self.form_data.pop('question-input')[0]
|
||||
votes = 0
|
||||
for option in options:
|
||||
if option != '':
|
||||
poll_options_list.append(PollOption(choice_text=option, votes=votes))
|
||||
|
||||
poll = Poll(question_text=text,
|
||||
total_votes=votes,
|
||||
min_num_selections=self.min_num_selections,
|
||||
max_num_selections=self.max_num_selections,
|
||||
event=self.event)
|
||||
# Extract required Poll object data and create a poll with its PollOption objects
|
||||
text = self.form_data.pop('question-name-input-' + i_str)[0]
|
||||
min_num_selections = int(self.form_data.pop('minimum-input-' + i_str)[0])
|
||||
max_num_selections = int(self.form_data.pop('maximum-input-' + i_str)[0])
|
||||
|
||||
poll = Poll(question_text=text,
|
||||
total_votes=votes,
|
||||
min_num_selections=min_num_selections,
|
||||
max_num_selections=max_num_selections,
|
||||
event=self.event)
|
||||
|
||||
self.polls_options_map.append([poll, poll_options_list])
|
||||
|
||||
self.polls_options_map.append([poll, poll_options_list])
|
||||
|
||||
# Instantiate all the polls and their associated poll options
|
||||
def __get_instantiated_polls(self):
|
||||
|
@ -210,8 +213,6 @@ class CreateNewEventModelAdaptor:
|
|||
self.identifier = None
|
||||
self.starts_at = None
|
||||
self.ends_at = None
|
||||
self.min_num_selections = 0
|
||||
self.max_num_selections = 0
|
||||
self.organisers[:] = []
|
||||
self.trustees[:] = []
|
||||
self.voters[:] = []
|
||||
|
|
|
@ -107,132 +107,180 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Question / Statement -->
|
||||
<!-- Questions -->
|
||||
<div class="form-group">
|
||||
<label for="question-input" class="col-sm-3 col-md-2 control-label">Question / Statement:</label> <!-- This text can be a template variable -->
|
||||
<div class="col-sm-9 col-md-10">
|
||||
<input type="text" class="form-control input-control" id="question-input" placeholder="Example: Elections for the European Parliament" name="question-input" maxlength="200">
|
||||
<span id="question-input-help-block" class="help-block">
|
||||
Question / Statement that will be put forward to voters along with the below options.
|
||||
</span>
|
||||
<span id="question-input-error-block" class="help-block errorText">
|
||||
<!-- Errors flagged here -->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Options -->
|
||||
<div class="form-group">
|
||||
<label for="options-input" class="col-sm-3 col-md-2 control-label">Options:</label> <!-- This text can be a template variable -->
|
||||
<label for="questions-input" class="col-sm-3 col-md-2 control-label">Polls:</label> <!-- This text can be a template variable -->
|
||||
<div class="col-sm-9 col-md-10">
|
||||
<div class="form-group">
|
||||
<table id="options-input-table" class="table table-hover">
|
||||
<table id="questions-input-table" class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">#</th>
|
||||
<th>Option</th>
|
||||
<th>Question / Statement</th>
|
||||
<th class="text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sort" class="formset option-formset" data-formset-prefix="options" data-formset-type="inline">
|
||||
<!-- Option -->
|
||||
<tr class="formset-form sorting-row" data-formset-form-prefix="option">
|
||||
<tbody class="formset questions-formset" data-formset-prefix="questions" data-formset-type="modal" data-formset-modal-title="Add a New Poll">
|
||||
<!-- Question / Statement -->
|
||||
<tr class="formset-form formset-form-empty hidden" data-formset-form-prefix="question">
|
||||
<!-- # -->
|
||||
<th class="formset-form-index text-center" scope=row>
|
||||
<td class="formset-form-index text-center" scope=row>
|
||||
1
|
||||
</th>
|
||||
<!-- Option Label -->
|
||||
<td>
|
||||
<div>
|
||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate 1" id="option-name-input" name="option-name-input" maxlength="200">
|
||||
</div>
|
||||
</td>
|
||||
<!-- Delete Action -->
|
||||
<!-- Question / Statement Label -->
|
||||
<th class="formset-form-name">
|
||||
<!-- Q Label Goes Here -->
|
||||
</th>
|
||||
<td class="formset-form-actions text-center">
|
||||
<button type="button" class="btn btn-sm btn-default formset-form-edit" aria-label="Edit">
|
||||
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default formset-form-remove" aria-label="Remove">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Option -->
|
||||
<tr class="formset-form sorting-row" data-formset-form-prefix="option">
|
||||
<!-- # -->
|
||||
<th class="formset-form-index text-center" scope=row>
|
||||
2
|
||||
</th>
|
||||
<!-- Option Label -->
|
||||
<td>
|
||||
<div>
|
||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate 2" id="option-name-input" name="option-name-input" maxlength="200">
|
||||
</div>
|
||||
</td>
|
||||
<!-- Delete Action -->
|
||||
<td class="formset-form-actions text-center">
|
||||
<button type="button" class="btn btn-sm btn-default formset-form-remove" aria-label="Remove">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Option -->
|
||||
<tr class="formset-form sorting-row formset-form-empty hidden" data-formset-form-prefix="option">
|
||||
<!-- # -->
|
||||
<th class="formset-form-index text-center" scope=row>
|
||||
X
|
||||
</th>
|
||||
<!-- Option Label -->
|
||||
<td>
|
||||
<div>
|
||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate X" id="option-name-input" name="option-name-input" maxlength="200">
|
||||
</div>
|
||||
</td>
|
||||
<!-- Delete Action -->
|
||||
<td class="formset-form-actions text-center">
|
||||
<button type="button" class="btn btn-sm btn-default formset-form-remove" aria-label="Remove">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
<td class="hidden">
|
||||
<div class="formset-form-fields">
|
||||
<!-- Name -->
|
||||
<div class="form-group dialogFormField">
|
||||
<label for="question-name-input">Question / Statement</label>
|
||||
<input type="text" class="form-control dialogQ" id="question-name-input" name="question-name-input" placeholder="Example: Elections for the European Parliament" maxlength="200">
|
||||
<span id="question-name-input-help-block" class="help-block">
|
||||
Question / Statement that will be put forward to voters along with the below options.
|
||||
</span>
|
||||
<span id="question-input-error-block" class="help-block errorText">
|
||||
<!-- Errors flagged here -->
|
||||
</span>
|
||||
</div>
|
||||
<!-- Options -->
|
||||
<div class="form-group dialogFormField">
|
||||
<label for="options-table">Options</label>
|
||||
<table id="options-table" class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">#</th>
|
||||
<th>Option</th>
|
||||
<th class="text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sort" class="formset option-formset" data-formset-prefix="options" data-formset-type="inline">
|
||||
<!-- Option -->
|
||||
<tr class="formset-form sorting-row" data-formset-form-prefix="option">
|
||||
<!-- # -->
|
||||
<th class="formset-form-index text-center" scope=row>
|
||||
1
|
||||
</th>
|
||||
<!-- Option Label -->
|
||||
<td>
|
||||
<div>
|
||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||
<input type="text" class="form-control input-sm input-control dialogO" placeholder="Example: Candidate 1" id="option-name-input" name="option-name-input" maxlength="200">
|
||||
</div>
|
||||
</td>
|
||||
<!-- Delete Action -->
|
||||
<td class="formset-form-actions text-center">
|
||||
<button type="button" class="btn btn-sm btn-default formset-form-remove" aria-label="Remove">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Option -->
|
||||
<tr class="formset-form sorting-row" data-formset-form-prefix="option">
|
||||
<!-- # -->
|
||||
<th class="formset-form-index text-center" scope=row>
|
||||
2
|
||||
</th>
|
||||
<!-- Option Label -->
|
||||
<td>
|
||||
<div>
|
||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||
<input type="text" class="form-control input-sm input-control dialogO" placeholder="Example: Candidate 2" id="option-name-input" name="option-name-input" maxlength="200">
|
||||
</div>
|
||||
</td>
|
||||
<!-- Delete Action -->
|
||||
<td class="formset-form-actions text-center">
|
||||
<button type="button" class="btn btn-sm btn-default formset-form-remove" aria-label="Remove">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Option -->
|
||||
<tr class="formset-form sorting-row formset-form-empty hidden" data-formset-form-prefix="option">
|
||||
<!-- # -->
|
||||
<th class="formset-form-index text-center" scope=row>
|
||||
X
|
||||
</th>
|
||||
<!-- Option Label -->
|
||||
<td>
|
||||
<div>
|
||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||
<input type="text" class="form-control input-sm input-control dialogO" placeholder="Example: Candidate X" id="option-name-input" name="option-name-input" maxlength="200">
|
||||
</div>
|
||||
</td>
|
||||
<!-- Delete Action -->
|
||||
<td class="formset-form-actions text-center">
|
||||
<button type="button" class="btn btn-sm btn-default formset-form-remove" aria-label="Remove">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="clearfix">
|
||||
<button type="button" class="btn btn-primary formset-add" data-formset-prefix="options">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i>
|
||||
Add Poll Option
|
||||
</button>
|
||||
</div>
|
||||
<span id="question-input-help-block" class="help-block">
|
||||
Drag and drop to re-order options.
|
||||
</span>
|
||||
<span id="options-input-error-block" class="help-block errorText">
|
||||
<!-- Errors flagged here -->
|
||||
</span>
|
||||
</div>
|
||||
<!-- Number of option selections -->
|
||||
<div class="form-group dialogFormField">
|
||||
<label for="selections-input" class="control-label">Number of Selections:</label> <!-- This text can be a template variable -->
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label class="sr-only" for="minimum-input">Minimum</label>
|
||||
<input type="number" class="form-control min-input" id="minimum-input" placeholder="Minimum" value="" name="minimum-input" min="0"> <!-- TODO: Max should be set to the number of options -->
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<label class="sr-only" for="maximum-input">Maximum</label>
|
||||
<input type="number" class="form-control max-input" id="maximum-input" placeholder="Maximum" value="" name="maximum-input" min="1"> <!-- TODO: Max should be set to the number of options -->
|
||||
</div>
|
||||
</div>
|
||||
<span id="question-input-help-block" class="help-block">
|
||||
Minimum and maximum number of option selections that a voter can make for the specified question / statement.
|
||||
</span>
|
||||
<span id="selections-input-error-block" class="help-block errorText">
|
||||
<!-- Errors flagged here -->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="clearfix">
|
||||
<button type="button" class="btn btn-primary formset-add" data-formset-prefix="options">
|
||||
<button type="button" class="btn btn-primary formset-add" data-formset-prefix="questions">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i>
|
||||
Add Question Option
|
||||
Add Poll
|
||||
</button>
|
||||
</div>
|
||||
<span id="question-input-help-block" class="help-block">
|
||||
Drag and drop to re-order options.
|
||||
Drag and drop to re-order polls.
|
||||
</span>
|
||||
<span id="options-input-error-block" class="help-block errorText">
|
||||
<span id="question-input-error-block" class="help-block errorText">
|
||||
<!-- Errors flagged here -->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Number of option selections -->
|
||||
<div class="form-group">
|
||||
<label for="selections-input" class="col-sm-3 col-md-2 control-label">Number of Selections:</label> <!-- This text can be a template variable -->
|
||||
<div class="col-sm-9 col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label class="sr-only" for="minimum-input">Minimum</label>
|
||||
<input type="number" class="form-control input-control" id="minimum-input" placeholder="Minimum" value="" name="minimum-input" min="0"> <!-- TODO: Max should be set to the number of options -->
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<label class="sr-only" for="maximum-input">Maximum</label>
|
||||
<input type="number" class="form-control input-control" id="maximum-input" placeholder="Maximum" value="" name="maximum-input" min="1"> <!-- TODO: Max should be set to the number of options -->
|
||||
</div>
|
||||
</div>
|
||||
<span id="question-input-help-block" class="help-block">
|
||||
Minimum and maximum number of option selections that a voter can make for the specified question / statement.
|
||||
</span>
|
||||
<span id="selections-input-error-block" class="help-block errorText">
|
||||
<!-- Errors flagged here -->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Number of Polls -->
|
||||
<input type="number" id="poll-count-input" name="poll-count-input" class="hidden">
|
||||
<!-- Organisers -->
|
||||
<div class="form-group">
|
||||
<label for="organisers-input" class="col-sm-3 col-md-2 control-label">Organisers:</label> <!-- This text can be a template variable -->
|
||||
|
@ -453,5 +501,25 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- This dialog has been imported from DEMOS1 -->
|
||||
<div class="modal fade" id="formset-modal" tabindex="-1" role="dialog" data-backdrop="static" data-keyboard="false" aria-labelledby="formset-modal-label">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="formset-modal-label"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-success formset-form-save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -26,7 +26,7 @@
|
|||
<th class="text-center">Start Time</th>
|
||||
<th class="text-center">End Time</th>
|
||||
<th class="text-center">No. Polls</th>
|
||||
<th class="text-center">Edit</th>
|
||||
<th class="text-center">Actions</th>
|
||||
<!-- Could also add a delete column to easily remove an event -->
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -40,7 +40,10 @@
|
|||
<td class="text-center">
|
||||
<a href="{% url 'polls:edit-event' event.id %}">
|
||||
<span class="btn btn-default glyphicon glyphicon-pencil"></span>
|
||||
</a>
|
||||
</a>
|
||||
<a href="#">
|
||||
<span class="btn btn-default glyphicon glyphicon-trash"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -111,6 +111,10 @@ tbody.trustee-formset > tr {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dialogFormField {
|
||||
margin-top: 0.65em;
|
||||
}
|
||||
|
||||
div.formset_object {
|
||||
animation: 0.3s drop-intro;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,19 @@ var dateRegex = /^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-
|
|||
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
var reCaptchaValid = false;
|
||||
var generalErrorBlock = document.getElementById('all-errors-help-block');
|
||||
|
||||
var errors = [];
|
||||
var create = true;
|
||||
var pollCount = 0;
|
||||
|
||||
function finalisePolls() {
|
||||
// Update the value of the poll count input
|
||||
$('#poll-count-input').val(pollCount);
|
||||
|
||||
// Remove the empty and hidden poll row from the poll table
|
||||
var formset = $(".formset[data-formset-prefix='questions']");
|
||||
var emptyForm = formset.children('.formset-form-empty');
|
||||
emptyForm.remove();
|
||||
}
|
||||
|
||||
$("#election-form").submit(function(e) {
|
||||
// Intercept submission of form and temporarily suspend it
|
||||
|
@ -28,6 +39,7 @@ $("#election-form").submit(function(e) {
|
|||
|
||||
if( formDataValid === true ) {
|
||||
clearErrors();
|
||||
finalisePolls();
|
||||
form.submit();
|
||||
} else {
|
||||
submitBtn.val(submitBtnErrLabel);
|
||||
|
@ -54,12 +66,11 @@ function isFormValid() {
|
|||
var slugValid = isSlugValid();
|
||||
var voteStartValid = isVoteStartValid();
|
||||
var voteEndValid = isVoteEndValid();
|
||||
var pollOptsValid = isPollAndOptsValid();
|
||||
var organisersEmailsValid = areOrganisersEmailsValid();
|
||||
var trusteesEmailsValid = areTrusteesEmailsValid();
|
||||
var votersListValid = isVotersListValid();
|
||||
|
||||
return nameValid && slugValid && voteStartValid && voteEndValid && pollOptsValid
|
||||
return nameValid && slugValid && voteStartValid && voteEndValid
|
||||
&& organisersEmailsValid && trusteesEmailsValid && votersListValid;
|
||||
}
|
||||
|
||||
|
@ -86,7 +97,7 @@ function isNameValid() {
|
|||
// Based on a list of names supplied from the create_event html template
|
||||
if(events_list !== undefined) {
|
||||
var valid = true;
|
||||
var event_name = $('#name-input').val();
|
||||
var event_name = $('#name-input').val().trim();
|
||||
|
||||
if(event_name === '') {
|
||||
checkAndAddError({
|
||||
|
@ -215,33 +226,16 @@ function isDateValid(date_time) {
|
|||
return dateRegex.test(date_time);
|
||||
}
|
||||
|
||||
function isPollAndOptsValid() {
|
||||
var pollValid = true;
|
||||
var optsValid = true;
|
||||
var minMaxSelValid = true;
|
||||
|
||||
// Check question is valid
|
||||
pollValid = isPollValid();
|
||||
|
||||
// Check opts are valid
|
||||
optsValid = isPollOptionsValid();
|
||||
|
||||
// Check min and max selections are valid
|
||||
minMaxSelValid = isMinMaxSelectionValid();
|
||||
|
||||
return pollValid && optsValid && minMaxSelValid;
|
||||
}
|
||||
|
||||
function isPollValid() {
|
||||
function isPollQValid() {
|
||||
var valid = true;
|
||||
|
||||
// Check question is valid
|
||||
var question = $('#question-input').val();
|
||||
var question = $('#question-name-input-' + pollCount).val();
|
||||
|
||||
if(question === '') {
|
||||
checkAndAddError({
|
||||
error: "Question / Statement for the poll is blank.",
|
||||
helpBlockId: "question-input-error-block"
|
||||
helpBlockId: "question-input-error-block-" + pollCount
|
||||
});
|
||||
|
||||
valid = false;
|
||||
|
@ -252,8 +246,8 @@ function isPollValid() {
|
|||
|
||||
function isPollOptionsValid() {
|
||||
var valid = true;
|
||||
var optsInputs = $('.option-formset #option-name-input');
|
||||
var helpBlockId = "options-input-error-block";
|
||||
var optsInputs = $('.option-formset #option-name-input-' + pollCount);
|
||||
var helpBlockId = "options-input-error-block-" + pollCount;
|
||||
|
||||
var index = 0;
|
||||
var errorStr = "Option ";
|
||||
|
@ -284,36 +278,28 @@ function isPollOptionsValid() {
|
|||
return valid;
|
||||
}
|
||||
|
||||
$('#question-input').on('input', function (e) {
|
||||
validateFormField(isPollValid, "question-input-error-block");
|
||||
});
|
||||
|
||||
$('.option-formset #option-name-input').on('input', function(e) {
|
||||
validateFormField(isPollOptionsValid, "options-input-error-block");
|
||||
});
|
||||
|
||||
function isMinMaxSelectionValid() {
|
||||
var valid = true;
|
||||
var minInput = $('#minimum-input');
|
||||
var minInput = $('#minimum-input-' + pollCount);
|
||||
var minInputMinAttr = parseInt(minInput[0].min);
|
||||
var minInputVal = minInput.val();
|
||||
var helpBlockId = "selections-input-error-block";
|
||||
var helpBlockId = "selections-input-error-block-" + pollCount;
|
||||
var errorStr = "";
|
||||
|
||||
if(minInputVal < minInputMinAttr) {
|
||||
errorStr = "The minimum option selection cannot be less than " + minInputMinAttr;
|
||||
if(minInputVal === "" || minInputVal < minInputMinAttr) {
|
||||
errorStr = "The minimum option selection cannot be less than " + minInputMinAttr + " or blank";
|
||||
valid = false;
|
||||
}
|
||||
|
||||
var maxInput = $('#maximum-input');
|
||||
var maxInput = $('#maximum-input-' + pollCount);
|
||||
var maxInputMinAttr = parseInt(maxInput[0].min);
|
||||
var maxInputVal = maxInput.val();
|
||||
|
||||
if(maxInputVal < maxInputMinAttr) {
|
||||
if(maxInputVal === "" || maxInputVal < maxInputMinAttr) {
|
||||
if(errorStr !== '') {
|
||||
errorStr = errorStr + " and the maximum cannot be less than " + maxInputMinAttr;
|
||||
errorStr = errorStr + " and the maximum cannot be less than " + maxInputMinAttr + " or blank";
|
||||
} else {
|
||||
errorStr = "The maximum option selection cannot be less than " + maxInputMinAttr;
|
||||
errorStr = "The maximum option selection cannot be less than " + maxInputMinAttr + " or blank";
|
||||
}
|
||||
|
||||
valid = false;
|
||||
|
@ -331,10 +317,6 @@ function isMinMaxSelectionValid() {
|
|||
return valid;
|
||||
}
|
||||
|
||||
$('#minimum-input, #maximum-input').on('input', function(e) {
|
||||
validateFormField(isMinMaxSelectionValid, "selections-input-error-block");
|
||||
});
|
||||
|
||||
function areOrganisersEmailsValid() {
|
||||
var valid = true;
|
||||
var organiserInputs = $('.organiser-formset #organiser-email-input');
|
||||
|
@ -739,7 +721,7 @@ function update(event, ui) {
|
|||
updateFormset(formset);
|
||||
}
|
||||
|
||||
$("#options-input-table, #organisers-input-table, #trustees-input-table").sortable({
|
||||
$("#questions-input-table, #organisers-input-table, #trustees-input-table").sortable({
|
||||
items: "tr",
|
||||
update: update
|
||||
});
|
||||
|
@ -759,7 +741,7 @@ function updateFormset(formset) { // Ported from DEMOS 1. Updates the row number
|
|||
function updateForm(form, formIndex) { // Ported from DEMOS 1.
|
||||
// Specific update for option forms
|
||||
var mayBeTextInput = form.find('input:text')[0];
|
||||
if(mayBeTextInput.placeholder !== undefined) {
|
||||
if(mayBeTextInput !== undefined && mayBeTextInput.placeholder !== undefined) {
|
||||
if( mayBeTextInput.placeholder.indexOf("Candidate") > -1) {
|
||||
mayBeTextInput.placeholder = "Example: Candidate " + (formIndex + 1);
|
||||
} else if (mayBeTextInput.placeholder.indexOf("trusteeX") > -1) {
|
||||
|
@ -794,12 +776,107 @@ function manageTotalForms(formset, value) { // Ported from DEMOS1.
|
|||
addButton.prop('disabled', parseInt(totalForms.val()) - removedForms.length >= parseInt(maxNumForms.val()));
|
||||
}
|
||||
|
||||
function updateQuestionFormInputs(form) {
|
||||
// Update the name and IDs of all the dialog input fields
|
||||
var clonedFields = form.find('.formset-form-fields:first >');
|
||||
|
||||
// Update the table ID
|
||||
var table = form.find('.table:first');
|
||||
table.attr("id", "options-table-" + pollCount);
|
||||
|
||||
// Update the poll question / statement ID
|
||||
clonedFields.find(".dialogQ:first")
|
||||
.attr("id", "question-name-input-" + pollCount)
|
||||
.attr("name", "question-name-input-" + pollCount);
|
||||
|
||||
// Update one of the help block IDs for various sections of the dialog
|
||||
var pollQuestionErrorHelpBlock = clonedFields.find("#question-input-error-block");
|
||||
pollQuestionErrorHelpBlock.attr("id", "question-input-error-block-" + pollCount);
|
||||
|
||||
var pollOptionsErrorHelpBlock = clonedFields.find("#options-input-error-block");
|
||||
pollOptionsErrorHelpBlock.attr("id", "options-input-error-block-" + pollCount);
|
||||
|
||||
var pollSelectionsErrorHelpBlock = clonedFields.find("#selections-input-error-block");
|
||||
pollSelectionsErrorHelpBlock.attr("id", "selections-input-error-block-" + pollCount);
|
||||
|
||||
// Update the poll option input IDs
|
||||
var optsInputs = clonedFields.find(".dialogO");
|
||||
|
||||
for(var i = 0; i < optsInputs.length; i++) {
|
||||
var input = optsInputs[i];
|
||||
input.id = "option-name-input-" + pollCount;
|
||||
input.name = "option-name-input-" + pollCount;
|
||||
}
|
||||
|
||||
// Update the data-formset-prefix for correct referencing
|
||||
var dataFormsetPrefix = "options-" + pollCount;
|
||||
var optionFormSet = clonedFields.find(".option-formset");
|
||||
optionFormSet.attr("data-formset-prefix", dataFormsetPrefix);
|
||||
|
||||
var addPollOptBtn = clonedFields.find('.formset-add');
|
||||
addPollOptBtn.attr("data-formset-prefix", dataFormsetPrefix);
|
||||
|
||||
// Update the poll min and max selection
|
||||
clonedFields.find(".min-input:first")
|
||||
.attr("id", "minimum-input-" + pollCount)
|
||||
.attr("name", "minimum-input-" + pollCount);
|
||||
|
||||
clonedFields.find(".max-input:first")
|
||||
.attr("id", "maximum-input-" + pollCount)
|
||||
.attr("name", "maximum-input-" + pollCount);
|
||||
}
|
||||
|
||||
function isDialogFormValid() {
|
||||
var pollQValid = true;
|
||||
var optsValid = true;
|
||||
var minMaxSelValid = true;
|
||||
|
||||
// Check question is valid
|
||||
var pollQErrorHelpBlockId = "question-input-error-block-" + pollCount;
|
||||
pollQValid = isPollQValid();
|
||||
|
||||
if(pollQValid === true) {
|
||||
clearError(pollQErrorHelpBlockId);
|
||||
} else {
|
||||
highlightError(pollQErrorHelpBlockId);
|
||||
}
|
||||
|
||||
// Check opts are valid
|
||||
var pollOptsErrorHelpBlockId = "options-input-error-block-" + pollCount;
|
||||
optsValid = isPollOptionsValid();
|
||||
|
||||
if(optsValid === true) {
|
||||
clearError(pollOptsErrorHelpBlockId);
|
||||
} else {
|
||||
highlightError(pollOptsErrorHelpBlockId);
|
||||
}
|
||||
|
||||
// Check min and max selections are valid
|
||||
var pollSelErrorHelpBlockId = "selections-input-error-block-" + pollCount;
|
||||
minMaxSelValid = isMinMaxSelectionValid();
|
||||
|
||||
if(minMaxSelValid === true) {
|
||||
clearError(pollSelErrorHelpBlockId);
|
||||
} else {
|
||||
highlightError(pollSelErrorHelpBlockId);
|
||||
}
|
||||
|
||||
return pollQValid && optsValid && minMaxSelValid;
|
||||
}
|
||||
|
||||
$('.formset-add').click(function (e) { // Ported from DEMOS1
|
||||
var formsetPrefix = $(this).attr('data-formset-prefix');
|
||||
var formset = $('.formset[data-formset-prefix="' + formsetPrefix + '"]');
|
||||
var emptyForm = formset.children('.formset-form-empty');
|
||||
var emptyFormCheckedInputs = emptyForm.find('input:checkbox:checked, input:radio:checked');
|
||||
var form = emptyForm.clone(true).removeClass('formset-form-empty');
|
||||
|
||||
// Update the IDs of the inputs of the dialogs based on the number of questions
|
||||
if(formsetPrefix === "questions") {
|
||||
// Update the IDs and names of all of the cloned input form fields
|
||||
updateQuestionFormInputs(form);
|
||||
}
|
||||
|
||||
var formIndex = formset.children('.formset-form:not(.formset-form-empty)').length;
|
||||
|
||||
formset.append(form);
|
||||
|
@ -836,7 +913,7 @@ $('.formset-form-remove').click(function (e) { // Ported from DEMOS1
|
|||
// Perform validation now that a row has been removed
|
||||
switch (formPrefix) {
|
||||
case 'option':
|
||||
validateFormField(isPollOptionsValid, "options-input-error-block");
|
||||
validateFormField(isPollOptionsValid, "options-input-error-block-" + pollCount);
|
||||
break;
|
||||
case 'organiser':
|
||||
validateFormField(areOrganisersEmailsValid, "organisers-input-error-block");
|
||||
|
@ -846,3 +923,89 @@ $('.formset-form-remove').click(function (e) { // Ported from DEMOS1
|
|||
break;
|
||||
}
|
||||
});
|
||||
|
||||
$('.formset-form-save').click(function (e) {
|
||||
var dialogValid = isDialogFormValid();
|
||||
|
||||
if(dialogValid === true) {
|
||||
// TODO: Clear errors
|
||||
var modal = $(this).closest('.modal');
|
||||
var form = modal.data('form');
|
||||
var name = $('#question-name-input-' + pollCount).val();
|
||||
form.find('.formset-form-name:first').text(name);
|
||||
modal.data('formSave', true);
|
||||
modal.modal('hide');
|
||||
pollCount++;
|
||||
} else {
|
||||
//highlightErrors();
|
||||
}
|
||||
});
|
||||
|
||||
$('.formset-form-edit').click(function (e) {
|
||||
var form = $(this).closest('.formset-form');
|
||||
$('#formset-modal').data('form', form).modal('show');
|
||||
});
|
||||
|
||||
$('#formset-modal').on('show.bs.modal', function (e) { // Ported from DEMOS1
|
||||
var modal = $(this);
|
||||
var modalBody = modal.find('.modal-body > .row > [class^="col-"]');
|
||||
var modalTitle = modal.find('.modal-title');
|
||||
var form = modal.data('form');
|
||||
var formset = form.parent('.formset');
|
||||
var formFields = form.find('.formset-form-fields:first >').detach();
|
||||
modal.data('formFields', formFields);
|
||||
|
||||
var clonedFields = formFields.clone(true);
|
||||
modalBody.append(clonedFields);
|
||||
|
||||
modalTitle.text(formset.attr('data-formset-modal-title'));
|
||||
formset.trigger('formsetModalShow', [modalBody]);
|
||||
|
||||
// Attach an event handler for poll option row sorting
|
||||
$("#options-table-" + pollCount).sortable({
|
||||
items: "tr",
|
||||
update: update
|
||||
});
|
||||
});
|
||||
|
||||
$('#formset-modal').on('hide.bs.modal', function (e) {
|
||||
var modal = $(this);
|
||||
var modalBody = modal.find('.modal-body > .row > [class^="col-"]');
|
||||
var form = modal.data('form');
|
||||
var formset = form.parent('.formset');
|
||||
if (modal.data('formSave')) {
|
||||
var formset = form.parent('.formset');
|
||||
if (modal.data('formAdd')) {
|
||||
manageTotalForms(formset, +1);
|
||||
form.removeClass('hidden');
|
||||
}
|
||||
} else {
|
||||
if (modal.data('formAdd')) {
|
||||
form.remove();
|
||||
}
|
||||
}
|
||||
formset.trigger('formsetModalHide', [modalBody]);
|
||||
});
|
||||
|
||||
$('#formset-modal').on('hidden.bs.modal', function (e) {
|
||||
var modal = $(this);
|
||||
var modalBody = modal.find('.modal-body > .row > [class^="col-"]');
|
||||
var form = modal.data('form');
|
||||
var formset = form.parent('.formset');
|
||||
var formFields = form.find('.formset-form-fields:first');
|
||||
if (modal.data('formSave')) {
|
||||
formFields.append(modalBody.children().detach());
|
||||
if (modal.data('formAdd')) {
|
||||
formset.trigger('formsetFormAdded', [form]);
|
||||
} else {
|
||||
formset.trigger('formsetFormEdited', [form]);
|
||||
}
|
||||
} else {
|
||||
modalBody.empty();
|
||||
if (!modal.data('formAdd')) {
|
||||
formFields.append(modal.data('formFields'));
|
||||
}
|
||||
}
|
||||
modal.find('.modal-title').text('');
|
||||
modal.removeData();
|
||||
});
|
Reference in a new issue