2018-06-13 12:01:55 +00:00
|
|
|
// Form submission and validation
|
|
|
|
var submitBtn = $("#submit-event-create");
|
|
|
|
var dateRegex = /^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))\s[0-9]{2}:[0-9]{2}\s\+[0-9]{2}:[0-9]{2}$/;
|
|
|
|
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
|
var reCaptchaValid = false;
|
|
|
|
|
|
|
|
$("#election-form").submit(function(e) {
|
|
|
|
// Intercept submission of form and temporarily suspend it
|
|
|
|
e.preventDefault();
|
|
|
|
var form = this;
|
|
|
|
|
|
|
|
// Get a reference to the submit button
|
|
|
|
submitBtn.prop('disabled', true);
|
|
|
|
submitBtn.val('Please wait...');
|
|
|
|
|
|
|
|
// Disable the cancel button during validation
|
|
|
|
var cancelBtn = $("#cancel-event-create");
|
|
|
|
cancelBtn.prop('disabled', true);
|
|
|
|
|
|
|
|
// Perform input validation
|
|
|
|
var formDataValid = isFormValid();
|
|
|
|
|
|
|
|
if( formDataValid === true ) {
|
|
|
|
form.submit();
|
|
|
|
} else {
|
|
|
|
submitBtn.val('Errors Found');
|
|
|
|
cancelBtn.removeAttr('disabled');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
function isFormValid() {
|
|
|
|
var nameValid = isNameValid();
|
|
|
|
var slugValid = isSlugValid();
|
|
|
|
var voteStartValid = isVoteStartValid();
|
|
|
|
var voteEndValid = isVoteEndValid();
|
|
|
|
var pollOptsValid = arePollsAndOptsValid();
|
|
|
|
var minSelectionValid = isMinSelectionValid();
|
|
|
|
var maxSelectionValid = isMaxSelectionValid();
|
|
|
|
var organisersEmailsValid = areOrganisersEmailsValid();
|
|
|
|
var trusteesEmailsValid = areTrusteesEmailsValid();
|
|
|
|
var votersListValid = isVotersListValid();
|
|
|
|
var reCaptchaValid = isReCaptchaStillValid();
|
|
|
|
|
|
|
|
return nameValid && slugValid && voteStartValid && voteEndValid
|
|
|
|
&& pollOptsValid && minSelectionValid && maxSelectionValid
|
|
|
|
&& organisersEmailsValid && trusteesEmailsValid && votersListValid
|
|
|
|
&& reCaptchaValid;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isNameValid() {
|
|
|
|
// Based on a list of names supplied
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isSlugValid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isVoteStartValid() {
|
|
|
|
var start_date_time = $('#vote-start-input').val();
|
|
|
|
return isDateValid(start_date_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isVoteEndValid() {
|
|
|
|
var end_date_time = $('#vote-end-input').val();
|
|
|
|
return isDateValid(end_date_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isDateValid(date_time) {
|
|
|
|
return dateRegex.test(date_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
function arePollsAndOptsValid() {
|
|
|
|
// Future validation could be added here
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isMinSelectionValid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isMaxSelectionValid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function areOrganisersEmailsValid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function areTrusteesEmailsValid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isVotersListValid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isReCaptchaStillValid() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$('.input-control').on('input', function(e) {
|
|
|
|
if(reCaptchaValid === true) {
|
|
|
|
submitBtn.val('Create Event');
|
|
|
|
submitBtn.removeAttr('disabled');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-06-12 12:31:38 +00:00
|
|
|
// File handling
|
|
|
|
|
|
|
|
function processFileChange(event) {
|
|
|
|
var files = event.target.files;
|
|
|
|
|
|
|
|
// By parsing the file we ensure that it's valid CSV at the client side. Papa parse
|
|
|
|
// also allows us to aggregate emails from multiple rows in a file.
|
|
|
|
if(files !== undefined
|
|
|
|
&& files[0] !== undefined) {
|
|
|
|
Papa.parse(files[0], {
|
|
|
|
complete: function(results) {
|
|
|
|
var errors = results.errors;
|
|
|
|
|
|
|
|
if(errors.length === 0) {
|
|
|
|
var data = results.data;
|
|
|
|
var numRows = data.length;
|
|
|
|
var totalNumEmails = 0;
|
|
|
|
var emails = [];
|
|
|
|
|
|
|
|
if(numRows > 1) {
|
|
|
|
for(var i = 0; i < numRows; i++) {
|
|
|
|
var numEmails = data[i].length;
|
|
|
|
totalNumEmails += numEmails;
|
|
|
|
|
|
|
|
for(var j = 0; j < numEmails; j++) {
|
|
|
|
emails.push(data[i][j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if(numRows === 1) {
|
|
|
|
totalNumEmails = data[0].length;
|
|
|
|
|
|
|
|
for(var i = 0; i < totalNumEmails; i++) {
|
|
|
|
emails.push(data[0][i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$('#result').removeClass("hidden").html(
|
|
|
|
totalNumEmails + " email(s) have been successfully uploaded.");
|
|
|
|
|
|
|
|
$('#voters-list-input').html(emails.join(', '));
|
|
|
|
} else {
|
|
|
|
// There were errors, so inform the user
|
|
|
|
$('#result')
|
|
|
|
.removeClass("hidden")
|
|
|
|
.html("Error reading uploaded file! Check the format and try again")
|
|
|
|
.addClass("errorText");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
document.getElementById('files').addEventListener('change', processFileChange, false);
|
|
|
|
|
|
|
|
// reCAPTCHA
|
|
|
|
|
2018-06-12 13:30:23 +00:00
|
|
|
function reCVerificationCallback() {
|
2018-06-13 12:01:55 +00:00
|
|
|
// TODO: call isFormValid before doing this and highlighting errors if any found
|
|
|
|
reCaptchaValid = true;
|
|
|
|
submitBtn.removeAttr('disabled');
|
2018-06-12 12:31:38 +00:00
|
|
|
}
|
|
|
|
|
2018-06-12 13:30:23 +00:00
|
|
|
function reCExpiredCallback() {
|
2018-06-13 12:01:55 +00:00
|
|
|
reCaptchaValid = false;
|
|
|
|
submitBtn.prop('disabled', true);
|
2018-06-12 13:30:23 +00:00
|
|
|
}
|
|
|
|
|
2018-06-12 12:31:38 +00:00
|
|
|
// Slug field.
|
|
|
|
|
|
|
|
function slugify(value) {
|
|
|
|
return value.toString()
|
|
|
|
.replace(/[^\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\w\s-]+/g, '')
|
|
|
|
.replace(/^[-\s]+/, '')
|
|
|
|
.replace(/[-\s]+$/, '')
|
|
|
|
.replace(/[-\s]+/g, '-')
|
|
|
|
.toLowerCase();
|
|
|
|
}
|
|
|
|
|
|
|
|
$('#name-input').on('input', function (e) {
|
|
|
|
var slugField = $('#identifier-input');
|
|
|
|
if (!slugField.data('changed')) {
|
|
|
|
var name = $(this).val();
|
|
|
|
var maxLength = parseInt(slugField.attr('maxlength'));
|
|
|
|
var slug = slugify(name).substring(0, maxLength);
|
|
|
|
slugField.val(slug);
|
|
|
|
slugField.trigger('input');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#identifier-input').change(function (e) {
|
|
|
|
$(this).data('changed', $(this).val().length > 0);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Poll start and end
|
|
|
|
|
|
|
|
var datetime_now = window.moment().seconds(0);
|
|
|
|
var datetime_format = "YYYY-MM-DD H:mm";
|
|
|
|
$("#vote-start-input, #vote-end-input").each(function(index, element) {
|
|
|
|
|
|
|
|
// Set datetimepickers' current, default and minimum date/time
|
|
|
|
|
|
|
|
var datetime_picker = $(element);
|
|
|
|
var datetime_iso8601 = datetime_picker.siblings(".datetime-iso8601-input").val();
|
|
|
|
var datetime_local = moment(datetime_iso8601);
|
|
|
|
|
|
|
|
datetime_picker.datetimepicker({
|
|
|
|
sideBySide: false,
|
|
|
|
minDate: datetime_now.clone().startOf("day"),
|
|
|
|
format: datetime_format,
|
|
|
|
widgetParent: $(datetime_picker)
|
|
|
|
});
|
|
|
|
|
|
|
|
var minutes = (Math.ceil(datetime_now.minute() / 5) * 5) + 5 * index;
|
|
|
|
var datetime_default = datetime_now.clone().minutes(minutes);
|
|
|
|
|
|
|
|
datetime_picker.data("DateTimePicker").defaultDate(datetime_default);
|
|
|
|
|
|
|
|
datetime_local = datetime_local.isValid() ? datetime_local.format(datetime_format) : "";
|
|
|
|
datetime_picker.children("input").val(datetime_local);
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#vote-start-input, #vote-end-input').parent('.date').datetimepicker({
|
|
|
|
allowInputToggle: true,
|
|
|
|
icons: {
|
|
|
|
time: 'fa fa-clock-o',
|
|
|
|
date: 'fa fa-calendar',
|
|
|
|
up: 'fa fa-chevron-up',
|
|
|
|
down: 'fa fa-chevron-down',
|
|
|
|
previous: 'fa fa-chevron-left',
|
|
|
|
next: 'fa fa-chevron-right'
|
|
|
|
},
|
|
|
|
minDate: moment().startOf('day'),
|
|
|
|
useCurrent: false,
|
2018-06-13 12:01:55 +00:00
|
|
|
locale: moment.utc()
|
2018-06-12 12:31:38 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Form management and Sortable rows
|
|
|
|
|
|
|
|
function update(event, ui) {
|
|
|
|
var formsetPrefix = $(event.target.lastElementChild).attr('data-formset-prefix');
|
|
|
|
var formset = $('.formset[data-formset-prefix="' + formsetPrefix + '"]');
|
|
|
|
updateFormset(formset);
|
|
|
|
}
|
|
|
|
|
|
|
|
$("#options-input-table, #organisers-input-table, #trustees-input-table").sortable({
|
|
|
|
items: "tr",
|
|
|
|
update: update
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function updateFormset(formset) { // Ported from DEMOS 1. Updates the row number for the # and performs any removals.
|
|
|
|
var forms = formset.children('.formset-form:not(.formset-form-empty, .formset-form-removed)');
|
|
|
|
var removedForms = formset.children('.formset-form.formset-form-removed');
|
|
|
|
forms.each(function(index) {
|
|
|
|
updateForm($(this), index);
|
|
|
|
});
|
|
|
|
removedForms.each(function(index) {
|
|
|
|
updateForm($(this), forms.length + index);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateForm(form, formIndex) { // Ported from DEMOS 1.
|
|
|
|
// Specific update for option forms
|
|
|
|
var mayBeTextInput = form.find('input:text')[0];
|
|
|
|
if(mayBeTextInput.placeholder !== undefined
|
|
|
|
&& mayBeTextInput.placeholder.indexOf("Candidate") > -1) {
|
|
|
|
mayBeTextInput.placeholder = "Example: Candidate " + (formIndex + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
var formset = form.parent('.formset');
|
|
|
|
var formsetPrefix = formset.attr('data-formset-prefix');
|
|
|
|
var formPrefix = formsetPrefix + '-' + formIndex;
|
|
|
|
var formPrefixRegex = new RegExp(formsetPrefix + '-(?:__prefix__|\\d+)');
|
|
|
|
form.find('*').addBack().each(function(index, element) {
|
|
|
|
$.each(this.attributes, function(index, attr) {
|
|
|
|
$(element).attr(attr.nodeName, function(index, attrValue) {
|
|
|
|
return attrValue.replace(formPrefixRegex, formPrefix);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
form.find('input[name="' + formPrefix + '-ORDER"]').val(formIndex);
|
|
|
|
form.find('.formset-form-index:first').text(formIndex + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
function manageTotalForms(formset, value) { // Ported from DEMOS1.
|
|
|
|
var formsetPrefix = formset.attr('data-formset-prefix');
|
|
|
|
var totalForms = $('#id_' + formsetPrefix + '-TOTAL_FORMS');
|
|
|
|
var maxNumForms = $('#id_' + formsetPrefix + '-MAX_NUM_FORMS');
|
|
|
|
totalForms.val(parseInt(totalForms.val()) + value);
|
|
|
|
var addButton = $('.formset-add[data-formset-prefix="' + formsetPrefix + '"]');
|
|
|
|
var removedForms = formset.children('.formset-form.formset-form-removed');
|
|
|
|
addButton.prop('disabled', parseInt(totalForms.val()) - removedForms.length >= parseInt(maxNumForms.val()));
|
|
|
|
}
|
|
|
|
|
|
|
|
$('.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');
|
|
|
|
var formIndex = formset.children('.formset-form:not(.formset-form-empty)').length;
|
|
|
|
|
|
|
|
formset.append(form);
|
|
|
|
updateForm(form, formIndex);
|
|
|
|
emptyFormCheckedInputs.each(function (index) {
|
|
|
|
$(this).prop('checked', true);
|
|
|
|
});
|
|
|
|
switch (formset.attr('data-formset-type')) {
|
|
|
|
case 'modal':
|
|
|
|
$('#formset-modal').data('form', form).data('formAdd', true).modal('show');
|
|
|
|
break;
|
|
|
|
case 'inline':
|
|
|
|
manageTotalForms(formset, +1);
|
|
|
|
form.removeClass('hidden');
|
|
|
|
formset.trigger('formsetFormAdded', [form]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$('.formset-form-remove').click(function (e) { // Ported from DEMOS1
|
|
|
|
var form = $(this).closest('.formset-form');
|
|
|
|
var formPrefix = form.attr('data-formset-form-prefix');
|
|
|
|
var formset = form.parent('.formset');
|
|
|
|
if ($('#id_' + formPrefix + '-id').val()) {
|
|
|
|
$('#id_' + formPrefix + '-DELETE').prop('checked', true);
|
|
|
|
form.addClass('formset-form-removed hidden');
|
|
|
|
} else {
|
|
|
|
form.remove();
|
|
|
|
manageTotalForms(formset, -1);
|
|
|
|
}
|
|
|
|
updateFormset(formset);
|
|
|
|
formset.trigger('formsetFormRemoved');
|
|
|
|
});
|