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:
vincentmdealmeida 2018-06-18 17:12:37 +01:00 committed by GitHub
commit 11abbb8a64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 410 additions and 171 deletions

View File

@ -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[:] = []

View File

@ -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 %}

View File

@ -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 %}

View File

@ -111,6 +111,10 @@ tbody.trustee-formset > tr {
margin-bottom: 10px;
}
.dialogFormField {
margin-top: 0.65em;
}
div.formset_object {
animation: 0.3s drop-intro;
}

View File

@ -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");
@ -845,4 +922,90 @@ $('.formset-form-remove').click(function (e) { // Ported from DEMOS1
validateFormField(areTrusteesEmailsValid, "trustees-input-error-block");
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();
});