Upgraded the Create Event page and back-end view to handle the creation of multiple polls in one go. The validation has been appropriately updated to cope with the UI changes but requires further testing. Event list has been updated to include a delete action which requires wiring up to a back-end function which hasn't been created yet

This commit is contained in:
vince0656 2018-06-18 17:09:44 +01:00
parent 6b70b01e06
commit 1314e3be1a
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");
@ -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();
});