Like learning? More free knowledge.
Sign up to receive our bimonthly Business Break newsletter and invitations to upcoming webinars.
';if(!$(field).closest(this.FORM_GROUP_CLASS).find(this.FORM_GROUP_EXAMPLE_CLASS).length > 0) {$(field).closest(this.FORM_GROUP_CLASS).append(exampleText);}$(targetElement).attr('aria-describedby', exampleId);}// Sets a value in the messaging object that is specific to the field based on the field's id that is used to indicate whether the specific field has been validated through a change or submit events. The desired result is to only show the first error message when the user leaves the field, otherwise the user will see error messaging as soon as they start typing in the field.this.setFieldReadyForValidationStatus(field, false);$(field).on('change', function(event) {this.validateField(field, event);}.bind(this));// input listens to character strokes within a field$(field).on('input', function(event) {this.validateField(field, event);}.bind(this));},setUpFormSectionValidation: function setUpFormSectionValidation() {//for buttons on a normal form that want to validate in sections$(this.FORM_CLASS + ' button[data-form-validate-section]:not([data-hide-path-chunks]),' + this.FORM_CLASS + " button[data-form-validate-section]:not([data-show-path-chunks])").each(function(index, element) {element.addEventListener('click', function(event) {element.disabled = true;this.validateFormSection($(event.target).closest(this.FORM_SECTION_CLASS), event);element.disabled = false;}.bind(this));}.bind(this));//for buttons on a form that's been set up to hide/show in sections$(this.FORM_CLASS + ' button[data-form-validate-section][data-hide-path-chunks],' + this.FORM_CLASS + " button[data-form-validate-section][data-show-path-chunks]").each(function(index, element) {//need to unbind the default click event, otherwise Chrome will submit this guy twiceelement.addEventListener('click', function(event) {var hasShow = event.target.dataset.showPathChunks;var hasHide = event.target.dataset.hidePathChunks;element.disabled = true;var allSectionFieldsValidated = this.validateFormSection($(event.target).closest(this.FORM_SECTION_CLASS), event);element.disabled = false;//if all the fields validated, do the button hide/showif(allSectionFieldsValidated) {if(hasHide) {this.addHideShowListeners(element, hasHide);}if(hasShow) {this.addHideShowListeners(element, false);}if(hasHide || hasShow) {event.target.click();}} else {//if fields are not validated, prevent the hide show listeners from firingevent.preventDefault();event.stopImmediatePropagation();}}.bind(this));}.bind(this));},validateFormSection: function validateFormSection(formSection, event) {var allFieldsValid = true;var firstInvalidField = null;$(formSection).find('input, textarea, select').each(function(index, element) {if(allFieldsValid) {if($(element).hasClass(this.VALIDATION_CLASS) && !$(element).hasClass(this.VALIDATION_ADDED_CLASS)) {this.setUpFieldValidation(element);}this.setFieldReadyForValidationStatus(element, true);var returnVal = this.validateField(element, event);if(!returnVal) {allFieldsValid = false;firstInvalidField = element;}}}.bind(this));if(!allFieldsValid) {firstInvalidField.focus();}return allFieldsValid;},updateHideShowPathChunks: function updateHideShowPathChunks(pathChunks, newRowIndex) {var componentsToToggle = this.getComponentsToToggle(pathChunks);var newVal = "";componentsToToggle.forEach(function(item, index) {if(item.indexOf("/multifield") > -1) {newVal += (item + "-" + newRowIndex);if(componentsToToggle.length > 1 && index !== componentsToToggle.length) {newVal += ","}} else {//newVal += item;//we only allow targeting entries within the same multifield//TODO enforce checking we are within the same multifield and not just a multifield}});return newVal;},setUpFormMultiFields: function setUpFormMultiFields() {$(this.FORM_MULTI_CLASS).each(function(index, element) {if(element.hasChildNodes()) {//clone the row whenever the button is clicked//and add a delete button which removes the row when clickedvar buttonElement = element.querySelector("button[name='multi-field-add-another']");if(buttonElement) {buttonElement.addEventListener("click", function(event) {event.stopImmediatePropagation();event.preventDefault();var rows = element.querySelectorAll(".js-form-multi-row");if(rows && rows[0]) {var newRowIndex = rows.length - 1;function recurseAndAddNewIndex(i, child) {// iterate through and alter any ids or data names so that we aren't conflictingif(child.id) {child.id = child.id + "-" + newRowIndex;}if(child.htmlFor) {child.htmlFor = child.htmlFor + "-" + newRowIndex;}if(child.hasAttribute("name")) {child.setAttribute("name", child.getAttribute("name") + "-" + newRowIndex);}if(child.dataset.pathChunk) {child.dataset.pathChunk = child.dataset.pathChunk + "-" + newRowIndex;}//items on a multifield with hide/show are only allowed to affect the same row//that they are on. add the current index to any path chunks that contain multifield// hoping/assuming users would not target a field on another multifield to hide showif(child.dataset.hidePathChunks) {child.dataset.hidePathChunks = this.updateHideShowPathChunks(child.dataset.hidePathChunks, newRowIndex);this.addHideShowListeners(child, true);}if(child.dataset.showPathChunks) {child.dataset.showPathChunks = this.updateHideShowPathChunks(child.dataset.showPathChunks, newRowIndex);this.addHideShowListeners(child, false);}//clear entered form valuesif(child.nodeType == 1 && child.nodeName == "INPUT") {child.value = "";if(child.checked) {child.checked = false;}}if(child.nodeType == 1 && child.nodeName == "DIV" && child.classList.contains("sun-select-container")) {//if we are on the sun-select-container, grab the child select and set it back to the parent//need to do this because there is some logic applied to all selects on the page during page loadif(child.hasChildNodes()) {var selectField = child.querySelector("select.js-form-validation");var ariaLabel = child.querySelector("span[aria-hidden='true']");if(selectField) {child.removeChild(ariaLabel);child.removeChild(selectField);var parentNode = child.parentNode;parentNode.removeChild(child);parentNode.append(selectField);$(selectField).uniform({selectClass: 'sun-select-container',selectAutoWidth: false}).each(function() {$(this).siblings("span").attr("aria-hidden", true);});}}}if($(child).children()) {$(child).children().each(recurseAndAddNewIndex.bind(this));}};//detach the first row element so we can copy itvar original = $(rows[0]).detach();var cloned = original[0].cloneNode(true);//iterate through the children and change the name, id, for, etc to make it a unique entry$(cloned).children().each(recurseAndAddNewIndex.bind(this));//put back our detached element we copied$(element).prepend(original);//add a delete buttonvar hiddenDeleteButton = $(element).find("button[name='multi-field-delete-entry']")[0].cloneNode(true);hiddenDeleteButton.hidden = false;hiddenDeleteButton.addEventListener("click", function(event) {element.removeChild(cloned);element.removeChild(hiddenDeleteButton);});$(hiddenDeleteButton).insertBefore($(buttonElement));//add our new element$(cloned).insertBefore($(buttonElement));//need to set up field validations within multifield elementsthis.setUpFormValidation();}}.bind(this));}}}.bind(this));},valOrFunction: function valOrFunction(val, ctx, args) {/* eslint-disable no-else-return */// returns static error messages and error messages that include the field value as part of the error messageif(typeof val === 'function') {return val.apply(ctx, args);} else {return val;}/* eslint-enable no-else-return */},fillAgain: function fillAgain(form, event) {$(form)[0].reset();$(form).show();$(form).siblings(this.FORM_FILL_AGAIN_CLASS).hide();},getMessaging: function getMessaging(field) {// returns the object that houses messages associated with the fieldvar targetElement = this.getTargetElement(field);var elementId = targetElement.id;if(targetElement && elementId) {if(!this.messaging[elementId]) {this.messaging[elementId] = {requiredMessage: this.DEFAULT_REQUIRED_MESSAGE};}if(targetElement.dataset) {if(targetElement.dataset.requiredMessage) {this.messaging[elementId][this.REQUIRED_MESSAGE] = targetElement.dataset.requiredMessage;}if(targetElement.dataset.exampleMessage) {this.messaging[elementId][this.EXAMPLE_MESSAGE] = targetElement.dataset.exampleMessage;}if(targetElement.dataset.validationMessage) {this.messaging[elementId][this.VALIDATION_MESSAGE] = targetElement.dataset.validationMessage;}}return this.messaging[elementId]}return null},getTargetElement: function getTargetElement(field) {// the target element for displaying error messages is actually the group and not the individual element for radio buttons and checkboxes// for all form fields other than radio buttons and checkboxesvar targetElement = field;// For radio buttonsif($(field).is('[type=radio]')) {targetElement = $(field).closest('fieldset')[0];}// For checkboxesif($(field).is('[type=checkbox]')) {targetElement = $(field).closest('fieldset')[0];}return targetElement;},getFieldReadyForValidationStatus: function getFieldReadyForValidationStatus(field) {// gets a field's status as ready (true) or not ready (false) for validation// a field is ready after a change or submit eventvar fieldMessaging = this.getMessaging(field);var readyStatus = true;if(!fieldMessaging) {return readyStatus;}// For all form fields other than radio buttons and checkboxesif(!$(field).is('[type=radio]') && !$(field).is('[type=checkbox]')) {readyStatus = fieldMessaging[$(field).prop('id')];}// For radio buttonsif($(field).is('[type=radio]')) {var radioGroup = $(field).closest('fieldset');readyStatus = fieldMessaging[$(radioGroup).prop('id')];}// For checkboxesif($(field).is('[type=checkbox]')) {var checkboxGroup = $(field).closest('fieldset');readyStatus = fieldMessaging[$(checkboxGroup).prop('id')];}return readyStatus;},setFieldReadyForValidationStatus: function setFieldReadyForValidationStatus(field, readyStatus) {// marks a specific field as ready (true) or not ready (false) for validation// a field is ready after a change or submit eventvar fieldMessaging = this.getMessaging(field);if(!fieldMessaging) {return;}// For all form fields other than radio buttons and checkboxesif(!$(field).is('[type=radio]') && !$(field).is('[type=checkbox]')) {fieldMessaging[$(field).prop('id')] = readyStatus;}// For radio buttonsif($(field).is('[type=radio]')) {var radioGroup = $(field).closest('fieldset');fieldMessaging[$(radioGroup).prop('id')] = readyStatus;}// For checkboxesif($(field).is('[type=checkbox]')) {var checkboxGroup = $(field).closest('fieldset');fieldMessaging[$(checkboxGroup).prop('id')] = readyStatus;}},validateField: function validateField(field, event) {// validates form fields and displays an error message if the field is invalidvar fieldMessaging = this.getMessaging(field);var isValid = true;// indicate if the field is being validated via a changeif(event.type === 'change') {this.setFieldReadyForValidationStatus(field, true);}// only validate if the field has been flagged as having had a change or submit eventif(this.getFieldReadyForValidationStatus(field)) {// the targetElement is the group for radio buttons and checkboxes - check for validation status at the group level but use the validate API on individual radio buttons/checkboxesvar targetElement = this.getTargetElement(field);var targetElementParent = this.getTargetElement($(field).closest(this.FORM_GROUP_CLASS));var ariaDescribedByAttr = $(targetElement).attr('aria-describedby');// -- CLEAR existing error messages --if($(targetElementParent).find(this.ERROR_MESSAGE_CLASS).length) {$(targetElementParent).find(this.ERROR_MESSAGE_CLASS).remove();}// clear any visual error formatting on the parent and children$(targetElementParent).removeClass(this.INVALID_CLASS_NO_DOT);$(targetElementParent).find('.is-invalid').removeClass(this.INVALID_CLASS_NO_DOT);// clear any aria-describedby mappingif(typeof ariaDescribedByAttr !== typeof undefined && ariaDescribedByAttr !== false) {$(targetElement).removeAttr('aria-describedby');}// -- SET validity on the actual field --// reset the custom validity message on the field to the empty stringfield.setCustomValidity('');// set the empty field or invalid field message if appropriateif(field.validity.valueMissing) {field.setCustomValidity(this.valOrFunction(fieldMessaging.requiredMessage, window, [field]));} else if(!field.validity.valid) {field.setCustomValidity(this.valOrFunction(fieldMessaging.validationMessage, window, [field]));}if(field === targetElement) {// confirmation fields have separate validation logicif($(targetElement).hasClass(this.CONFIRMATION_CLASS)) {//check if we are valid by verifying this input has same value as confirmation targetvar targetElementName = $(targetElement).prop("name");if(targetElementName && targetElementName.indexOf(this.CONFIRM_POSTFIX) > -1) {targetElementName = targetElementName.substring(0, targetElementName.indexOf(this.CONFIRM_POSTFIX));var targetConfirm = document.querySelector("input[name='" + targetElementName + "']");isValid = targetConfirm && targetConfirm.value === targetElement.value;}} else {// for all form fields other than radio buttons and checkboxesisValid = targetElement.validity.valid;}} else if($(field).is('[type=radio]')) {// for radio buttons check to see if at least one radio button in the group is checkedif($(field).prop('required')) {if($(targetElement).find('input[type=radio]:checked').length === 0) {isValid = false;}}} else if($(field).is('[type=checkbox]')) {// for checkboxes check to see if at least one checkbox in the group is checkedif($(field).prop('required')) {if($(targetElement).find('input[type=checkbox]:checked').length === 0) {isValid = false;}}}// -- SET new error message --// only show the visible error and add an aria-describedby mapping if the field is invalidif(!isValid) {var errorMessage;//if the field is empty, and is required, show required messageif(field.validity.typeMismatch || field.validity.patternMismatch && fieldMessaging.validationMessage) {errorMessage = fieldMessaging.validationMessage;} else {errorMessage = fieldMessaging.requiredMessage;}var errorId = $(targetElement).prop('id') + '-error';errorMessage = '
' + '' + errorMessage + '
';$(targetElementParent).append(errorMessage).addClass(this.INVALID_CLASS_NO_DOT);$(targetElementParent).find(this.FIELDSET_LEGEND_CLASS).addClass(this.INVALID_CLASS_NO_DOT);if(!$(targetElementParent).find(this.LABEL_CLASS).hasClass(this.LABEL_FIELDSET_CLASS_NO_DOT)) {$(targetElementParent).find(this.LABEL_CLASS).addClass(this.INVALID_CLASS_NO_DOT);}if($(targetElementParent).find(this.EXAMPLE_CLASS_NO_JS).length) {var exampleId = $(targetElement).prop('id') + "-example";$(targetElement).attr('aria-describedby', errorId + ' ' + exampleId);$(targetElementParent).find(this.EXAMPLE_CLASS).addClass(this.INVALID_CLASS_NO_DOT);} else {$(targetElement).attr('aria-describedby', errorId);}if($(targetElement).parent().hasClass(this.INPUT_GROUP_CLASS)) {$(targetElement).parent().addClass(this.INVALID_CLASS_NO_DOT);} else {$(targetElement).addClass(this.INVALID_CLASS_NO_DOT);}} else if($(targetElementParent).find(this.EXAMPLE_CLASS_NO_JS).length) {// reset the aria-describedby attribute if there is example textvar exampleId = $(targetElement).prop('id') + '-example';$(targetElement).attr('aria-describedby', exampleId);}}return isValid;},isFillAgainSubmit: function(form) {return form && $(form).siblings(this.FORM_FILL_AGAIN_CLASS).length > 0;},isNoRedirectSubmit: function(form) {return form && form.length > 0 && form[0] && form[0].dataset && form[0].dataset.noRedirect && form[0].dataset.noRedirect == "noredirect";},validateForm: function validateForm(form, event) {//clear thank you / error messagesvar show = false;this.hideShowFormThankYou(form, show);var formId = '#' + $(form).prop('id');var elementSelector = formId + ' input:not([type=hidden]), ' + formId + ' select';var firstInvalidField = null;for(var i = 0; i < $(elementSelector).length; i++) {var element = $(elementSelector)[i];this.setFieldReadyForValidationStatus(element, true);var isValid = this.validateField(element, event);if(!isValid) {if(firstInvalidField === null) {firstInvalidField = element;}}}if(firstInvalidField !== null) {firstInvalidField.focus();//enable submit button again$(form).find("button[data-form-submit]")[0].disabled = false;return;}var currentThis = this;//all fields are valid, check recaptcha value and include in request to server// grecaptcha.ready(function() {// grecaptcha.execute('6Ld4WY8iAAAAAOrnyjLnapE6Ddj00KRmWzAcujgS', {action: 'submit'}).then( function(token) {//add hidden input so that it will be included//if we are doing the async submit, need to update the value instead// if(form.find("input[name='__g_recaptcha']").length > 0){//form.find("input[name='__g_recaptcha']")[0].value = token//}else{//form.append("");//}if(isRecaptchaValidated) {if(currentThis.isFillAgainSubmit(form) || currentThis.isNoRedirectSubmit(form)) {currentThis.performAsyncSend(form);} else {form.submit();}} else {return false;}//});//});},performAsyncSend: function performAsyncSend(form) {var action = form[0] ? form[0].action : "";if(!this.httpRequest || !action) {//unable to create requestreturn;}this.httpRequest.onreadystatechange = function() {if(this.httpRequest.readyState === XMLHttpRequest.DONE) {if(this.requestedId) {var form = $(this.requestedId);if(form) {var formButton = form.find("button[data-form-submit]");//todo handle difference between good and bad request (need design)var response = this.httpRequest.responseText;if(this.isFillAgainSubmit(form)) {var form = $(this.requestedId);form.hide();var display = $(form).siblings(this.FORM_FILL_AGAIN_CLASS);display.show();} else {//assuming we are on a no redirect submitvar show = true;if(formButton) {this.hideShowFormThankYou(form, show);//do hide show for whatever is set on the form submit button because the listener is preventedif(formButton && formButton[0]) {if(formButton[0].dataset) {if(formButton[0].dataset.hidePathChunks) {this.hideAndShowComponentsForPathChunks(formButton[0].dataset.hidePathChunks, true);}if(formButton[0].dataset.showPathChunks) {this.hideAndShowComponentsForPathChunks(formButton[0].dataset.showPathChunks, true);}}}}}if(this.httpRequest.status != 200) {console.log("Problem with request, status " + this.httpRequest.status);}//renable button after request no matter whatif(formButton) {formButton[0].disabled = false;}}}}}.bind(this);this.httpRequest.open('POST', action);this.httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');var formData = form.serialize();if(formData) {this.requestedId = '#' + $(form).prop('id');this.httpRequest.send(formData);}},hideShowFormThankYou: function hideShowFormThankYou(form, isShow) {var formButton = form.find("button[data-form-submit]");if(formButton) {//should only be one submit-form button,//could be multiple submit-container buttons//TODO scope submit to containersif(formButton[0]) {formButton[0].disabled = false;var buttonParent = formButton[0].parentNode;if(buttonParent) {var thankYouSpan = $(buttonParent).find("span.form-thank-you");var formparent = buttonParent.parentNode;if(thankYouSpan && thankYouSpan.length > 0) {if(isShow) {// thankYouSpan[0].classList.remove("form-hidden");// $(formparent).find(".js-form-group").addClass("tli-form-fields-hide");// $(formButton).addClass("tli-form-fields-hide")testSlideRight();//$(formparent).find(".js-form-group").addClass("slide-out-bottom");//$(formButton).addClass("slide-out-bottom")//$(thankYouSpan[0]).addClass("slide-in-right");//thankYouSpan[0].hidden = false;} else {// thankYouSpan[0].classList.add("form-hidden");// $(formparent).find(".js-form-group").removeClass("tli-form-fields-hide");// $(formButton).removeClass("tli-form-fields-hide")$(thankYouSpan[0]).removeClass("slide-in-right");$(formparent).find(".js-form-group").removeClass("slide-out-bottom");$(formButton).removeClass("slide-out-bottom")//thankYouSpan[0].hidden = true;}}}}}},init: function init() {this.bindEvents();this.setUpFormValidation();this.setUpFormSectionValidation();this.setUpFormFillAgain();this.setUpFormMultiFields();}};_internal.init();};$(function() {if($('.js-form').length) {new FormComponent();}});function doRegisterAgain() {var form = document.getElementById("form-container-div");var formButton = $(form).find("button[data-form-submit]");var buttonParent = formButton[0].parentNode;var thankYouSpan = $(buttonParent).find("span.form-thank-you");var formparent = buttonParent.parentNode;$(thankYouSpan[0]).removeClass("slide-in-right");$(formparent).find(".js-form-group").removeClass("slide-out-bottom");$(formButton).removeClass("slide-out-bottom")$(form).parents('form').get(0).reset()grecaptcha.reset();return false;}function testSlideRight() {var form = document.getElementById("form-container-div");var formButton = $(form).find("button[data-form-submit]");var buttonParent = formButton[0].parentNode;var thankYouSpan = $(buttonParent).find("span.form-thank-you");var formparent = buttonParent.parentNode;$(thankYouSpan[0]).addClass("slide-in-right");$(formparent).find(".js-form-group").addClass("slide-out-bottom");$(formButton).addClass("slide-out-bottom")thankYouSpan[0].hidden = false;//slide-in-right}