Last active
December 18, 2015 02:49
-
-
Save pamelafox/5714109 to your computer and use it in GitHub Desktop.
Forum Utility functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function setupFormSave($form, xhrOptions, autoSave) { | |
xhrOptions = xhrOptions || {}; | |
var lastFormData = $form.serialize(); | |
xhrOptions.url = xhrOptions.url || $form.attr('action'); | |
xhrOptions.type = xhrOptions.type || $form.attr('type'); | |
var $saveButton = $form.find('button[type="submit"]'); | |
var $saveStatus = $($saveButton.attr('data-update')); | |
function changeButtonText(message) { | |
var messageText = $saveButton.attr('data-' + message + '-message'); | |
$saveButton.html(messageText); | |
} | |
function resetButton() { | |
var $requiredCheckboxes = $form.find('input[type="checkbox"]').filter('[required]'); | |
if ($requiredCheckboxes.length && !$requiredCheckboxes.is(':checked')) { | |
$saveButton.attr('disabled', 'disabled'); | |
return; | |
} | |
$saveButton.removeAttr('disabled'); | |
changeButtonText('default'); | |
} | |
// Re-enable button when form changes | |
$form.find('input, select') | |
.on('change', resetButton); | |
$form.find('input, textarea') | |
.on('keyup', resetButton); | |
// Do not submit on enter in an input besides the very last one | |
var $allInputs = $form.find('input, select'); | |
$allInputs.each(function(ind, elem) { | |
if (ind != (($allInputs.length) - 1)) { | |
$(elem) | |
.on('keypress', function(event) { | |
return event.keyCode != 13; | |
}); | |
} | |
}); | |
function sendXHR() { | |
lastFormData = $form.serialize(); | |
var type = (xhrOptions.type || 'POST') | |
.toLowerCase(); | |
if (Coursera.api[type]) { | |
Coursera.api[type](xhrOptions.url, { | |
data: xhrOptions.data || $form.serialize() || null, | |
headers: xhrOptions.headers || {}, | |
dataType: xhrOptions.dataType || 'json' | |
}) | |
.done(function(responseJSON, textStatus, xhr) { | |
if (xhrOptions.success) { | |
xhrOptions.success(responseJSON, textStatus, xhr); | |
} | |
changeButtonText('success'); | |
$saveStatus.html('(Saved!)'); | |
renderAllErrors($form, null); | |
}) | |
.fail(function(xhr, textStatus, errorThrown) { | |
var data = $.parseJSON(xhr.responseText); | |
if (xhrOptions.error) { | |
xhrOptions.error(xhr, textStatus, errorThrown); | |
} | |
resetButton(); | |
// Clear as-we-type validators as server errors take precedence | |
// For example: a user registering with previously registered email | |
$form.find('input') | |
.off('.validate'); | |
// Render errors from the server | |
renderAllErrors($form, data); | |
}) | |
} | |
} | |
var shouldValidateForm = $form.attr('data-validate') == 'true'; | |
if (shouldValidateForm) { | |
$form.find('input') | |
.each(function() { | |
var $field = $(this); | |
// It doesn't make sense to validate checkboxes on user interaction, | |
// only on form submit | |
if ($field.attr('type') == 'checkbox') { | |
return; | |
} | |
// While the user is typing, we *do* want to clear any errors if the field now validates | |
// but we don't want to show new errors yet. | |
$field.on('keyup.validate', function() { | |
if (!validateField($field)) { | |
renderFieldErrors($field, null); | |
} | |
}); | |
// When the user moves away from the field (blur) or pastes, | |
// we do want to check for success and errors | |
$field.on('blur.validate paste.validate', function() { | |
renderFieldErrors($field, validateField($field)); | |
}); | |
}); | |
} | |
$saveButton.on('click', function(event) { | |
event.preventDefault(); | |
if (!shouldValidateForm || (shouldValidateForm && !validateForm($form))) { | |
changeButtonText('inflight'); | |
$saveButton.attr('disabled', 'disabled'); | |
sendXHR(xhrOptions); | |
} | |
}); | |
function triggerSave() { | |
if ($form.serialize() != lastFormData) { | |
$saveButton.trigger('click'); | |
$saveStatus.html('(Saving...)'); | |
} | |
} | |
if (autoSave) { | |
var triggerSaveD = _.debounce(triggerSave, 1000); | |
$form.find('input, select') | |
.on('change.autosave', triggerSaveD); | |
$form.find('input, textarea') | |
.on('keyup.autosave', triggerSaveD); | |
$form.find('input, select, blur') | |
.on('blur.autosave paste.autosave', triggerSave); | |
} | |
} | |
function validateForm($form) { | |
var foundError = null; | |
$form.find('input') | |
.each(function() { | |
var $field = $(this); | |
var fieldError = validateField($field); | |
renderFieldErrors($field, fieldError); | |
foundError = foundError || fieldError; | |
if (fieldError && !foundError && $('input:focus') | |
.length === 0) { | |
$field.focus(); | |
} | |
}); | |
if (foundError) { | |
var invalidMessage = $form.attr('data-invalid-message') || 'Uh-oh, please check the form!'; | |
renderFormError($form, invalidMessage); | |
// Shake the button | |
$form.find('button[type="submit"]') | |
.animate({ | |
opacity: 0.4 | |
}, 100) | |
.animate({ | |
"margin-left": "-=10" | |
}, 100) | |
.animate({ | |
"margin-left": "+=20" | |
}, 100) | |
.animate({ | |
"margin-left": "-=20" | |
}, 100) | |
.animate({ | |
"margin-left": "+=10" | |
}, 100) | |
.animate({ | |
opacity: 1 | |
}, 100); | |
} | |
return foundError; | |
} | |
function validateField($field) { | |
var fieldType = $field.attr('type'); | |
var fieldValue = $field.val(); | |
var fieldPattern = $field.attr('pattern'); | |
var error = null; | |
// Check pattern | |
var regEx = null; | |
if (fieldType == 'email') { | |
regEx = new RegExp(/[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?/); | |
} else if (fieldPattern) { | |
regEx = new RegExp(fieldPattern); | |
} | |
if (regEx) { | |
if (!regEx.test(fieldValue)) { | |
error = $field.attr('data-invalid-message') || 'Invalid field'; | |
} | |
} | |
// Check required | |
if ($field.attr('required')) { | |
if (fieldType == 'text' && fieldValue.length === 0) { | |
error = 'This field is required.'; | |
} | |
if (fieldType == 'checkbox' && !$field.is(':checked')) { | |
error = '⬆ Please check this!'; | |
} | |
} | |
return error; | |
} | |
function renderAllErrors($form, data) { | |
var $errorsDom; | |
$form.find('.form-errors').remove(); | |
$form.find('.error .help-inline').remove(); | |
$form.find('.error').removeClass('error'); | |
if (!data) return; | |
if (data.error_message) { | |
renderFormError($form, data.error_message); | |
} | |
if (data.field_errors) { | |
for (var fieldName in data.field_errors) { | |
var $fieldDom = $form.find('*[name="' + fieldName + '"]'); | |
renderFieldErrors($fieldDom, data.field_errors[fieldName]); | |
$fieldDom.focus(); | |
} | |
} | |
} | |
function renderFormError($form, error) { | |
$form.find('.form-errors').remove(); | |
$errorsDom = $('<span></span>') | |
.addClass('form-errors') | |
.attr('role', 'alert') | |
.attr('aria-live', 'assertive') | |
.html(error); | |
if ($form.find('.actions').length) { | |
$form.find('.actions').children(':last').after($errorsDom); | |
} else if ($form.find('.modal-footer').length) { | |
$form.find('.modal-footer').children(':last').after($errorsDom); | |
} else if ($form.find('button[type="submit"]')) { | |
$form.find('button[type="submit"]').after($errorsDom); | |
} | |
} | |
function renderFieldErrors($fieldDom, fieldErrors) { | |
var $parentGroup = ($fieldDom.parents('.control-mini-group').length && $fieldDom.parents('.control-mini-group') | |
|| $fieldDom.parents('.control-group')); | |
$parentGroup.removeClass('error'); | |
$parentGroup.find('.help-inline.error').remove(); | |
if (fieldErrors) { | |
var errorId = ($fieldDom.attr('id') || '') + '-form-field-error-' + (new Date().getTime()); | |
var describedBy = ($fieldDom.attr('aria-describedby') || '').split(','); | |
describedBy.push(errorId); | |
$fieldDom.attr('aria-describedby', describedBy.join(',')); | |
$errorsDom = $('<span></span>') | |
.addClass('help-inline error') | |
.attr('role', 'alert') | |
.attr('aria-live', 'assertive') | |
.attr('id', errorId); | |
if (fieldErrors instanceof Array) { | |
for (var i = 0; i < fieldErrors.length; i++) { | |
$errorsDom.append('<span>' + fieldErrors[i] + '</span>'); | |
} | |
} else { | |
$errorsDom.append(fieldErrors); | |
} | |
$parentGroup.addClass('error'); | |
if ($fieldDom.attr('type') == 'checkbox') { | |
$parentGroup.find('.controls').append($errorsDom); | |
} else { | |
$fieldDom.after($errorsDom); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment