This commit is contained in:
Nivanka Fonseka 2024-03-05 05:31:22 +13:00 committed by GitHub
commit afacbebf56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 622 additions and 788 deletions

5
.gitignore vendored
View File

@ -1,3 +1,8 @@
.DS_Store .DS_Store
.sass-cache/ .sass-cache/
node_modules/ node_modules/
userforms-cms.js.map
userforms.js.map
userforms-cms.css.map
userforms.css.map

View File

@ -1 +1 @@
!function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};e.m=t,e.c=r,e.i=function(t){return t},e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s="./client/src/bundles/bundle.js")}({"./client/src/bundles/UserForms.js":function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{default:t}}var i=r(1),s=n(i),o=r(0),a=n(o);(0,s.default)(document).ready(function(t){function e(e){return this.$el=e instanceof t?e:t(e),this.$el.find("h4").text(a.default._t("UserForms.ERROR_CONTAINER_HEADER","Please correct the following errors and try again:")),this}function r(r){var n=this;this.$el=r instanceof t?r:t(r);var i=this.$el.closest(".userform").data("inst");return this.$elButton=t(".step-button-wrapper[data-for='"+this.$el.prop("id")+"']"),this.viewed=!1,this.valid=!1,this.id=null,this.hide(),u.DISPLAY_ERROR_MESSAGES_AT_TOP&&(this.errorContainer=new e(this.$el.find(".error-container")),i.$el.on("userform.form.error",function(e,r){n.$el.is(":visible")&&t.each(r.errorList,function(e,r){n.errorContainer.updateErrorMessage(t(r.element),r.message)})}),i.$el.on("userform.form.valid",function(t,e){n.errorContainer.removeErrorMessage(e)})),this.$elButton.on("userform.field.hide userform.field.show",function(){i.$el.trigger("userform.form.conditionalstep")}),this}function n(e){var r=this;this.$el=e instanceof t?e:t(e),this.$buttons=this.$el.find(".step-button-jump"),this.$jsAlign=this.$el.find(".js-align");var n=this.$el.closest(".userform").data("inst");return this.$buttons.each(function(e,n){t(n).on("click",function(e){e.preventDefault();var n=parseInt(t(e.target).data("step"),10);r.$el.trigger("userform.progress.changestep",n)})}),n.$el.on("userform.form.changestep",function(t,e){r.update(e)}),n.$el.on("userform.form.conditionalstep",function(){var e=r.$buttons.filter(":visible");e.each(function(e,r){t(r).text(e+1)}),r.$el.find(".progress-bar").attr("aria-valuemax",e.length),r.$el.find(".total-step-number").text(e.length)}),this.$jsAlign.each(function(e,n){var i=t(n),s=100/(r.$jsAlign.length-1)*e,o=s+"%",a=i.innerWidth()/2*-1;i.css({left:o,marginLeft:a}),e===r.$jsAlign.length-1?i.css({marginLeft:2*a}):0===e&&i.css({marginLeft:0})}),this}function i(e){var r=this;this.$el=e instanceof t?e:t(e);var n=this.$el.closest(".userform");this.userformInstance=n.data("inst"),this.$prevButton=this.$el.find(".step-button-prev"),this.$nextButton=this.$el.find(".step-button-next"),this.$prevButton.parent().attr("aria-hidden",!1).show(),this.$nextButton.parent().attr("aria-hidden",!1).show();var i=function(){var e=n.offset();t("html, body").animate({scrollTop:e.top},"slow")};return this.$prevButton.on("click",function(t){t.preventDefault(),i(),r.$el.trigger("userform.action.prev")}),this.$nextButton.on("click",function(t){t.preventDefault(),i(),r.$el.trigger("userform.action.next")}),this.userformInstance.$el.on("userform.form.changestep userform.form.conditionalstep",function(){r.update()}),this}function s(r){var n=this;return this.$el=r instanceof t?r:t(r),this.steps=[],this.errorContainer=new e(this.$el.children(".error-container")),this.$el.on("userform.action.prev",function(){n.prevStep()}),this.$el.on("userform.action.next",function(){n.nextStep()}),this.$el.find(".userform-progress").on("userform.progress.changestep",function(t,e){n.jumpToStep(e-1)}),this.$el.on("userform.form.valid",function(t,e){n.errorContainer.removeStepLink(e)}),this.$el.validate(this.validationOptions),this.$el.find(".optionset.requiredField input").each(function(e,r){t(r).rules("add",{required:!0})}),this}function o(o,d){var f=this,c=t(d);if(0!==c.length){u.ENABLE_LIVE_VALIDATION=void 0!==c.data("livevalidation"),u.DISPLAY_ERROR_MESSAGES_AT_TOP=void 0!==c.data("toperrors"),!1===u.ENABLE_LIVE_VALIDATION&&t.extend(s.prototype.validationOptions,{onfocusout:!1}),u.DISPLAY_ERROR_MESSAGES_AT_TOP&&t.extend(s.prototype.validationOptions,{invalidHandler:function(t,e){c.trigger("userform.form.error",[e])},onfocusout:!1}),c.find(".userform-progress, .step-navigation").attr("aria-hidden",!1).show(),t.extend(r.prototype,l),t.extend(e.prototype,l);var h=new s(c);c.data("inst",h),u.HIDE_FIELD_LABELS&&c.find("label.left").each(function(){var e=t(f);t('[name="'+e.attr("for")+'"]').attr("placeholder",e.text()),e.remove()}),h.$el.find(".form-step").each(function(t,e){var n=new r(e);h.addStep(n)}),h.setCurrentStep(h.steps[0]);var p=c.find(".userform-progress");p.length&&new n(p).update(0);var m=c.find(".step-navigation");m.length&&new i(m).update(),t(document).on("click","input.text[data-showcalendar]",function(){var e=t(f);e.ssDatepicker(),e.data("datepicker")&&e.datepicker("show")}),setInterval(function(){t.ajax({url:"UserDefinedFormController/ping"})},18e4),void 0!==c.areYouSure&&c.areYouSure({message:a.default._t("UserForms.LEAVE_CONFIRMATION","You have unsaved changes!")})}}var u={},l={show:function(){this.$el.attr("aria-hidden",!1).show()},hide:function(){this.$el.attr("aria-hidden",!0).hide()}};e.prototype.hasErrors=function(){return this.$el.find(".error-list").children().length>0},e.prototype.removeErrorMessage=function(t){this.$el.find("#"+t+"-top-error").remove(),this.hasErrors()||this.hide()},e.prototype.addStepLink=function(e){var r=this.$el.closest(".userform").data("inst"),n=e.$el.attr("id")+"-error-link",i=this.$el.find("#"+n),s=e.$el.attr("id"),o=e.$el.data("title");i.length||(i=t('<li id="'+n+'"><a href="#'+s+'">'+o+"</a></li>"),i.on("click",function(t){t.preventDefault(),r.jumpToStep(e.id)}),this.$el.find(".error-list").append(i))},e.prototype.removeStepLink=function(e){var r=t("#"+e).closest(".form-step").attr("id");this.$el.find("#"+r+"-error-link").remove(),this.$el.find(".error-list").is(":empty")&&this.hide()},e.prototype.updateErrorMessage=function(e,r){var n=this,i=e.attr("id"),s="#"+i,o=i+"-top-error",a=t("#"+o),u=e.attr("aria-describedby");if(!r)return void a.addClass("fixed");a.removeClass("fixed"),this.show(),1===a.length?a.show().find("a").html(r):(e.closest(".field[id]").each(function(){var e=t(n).attr("id");e&&(s="#"+e)}),a=t("<li><a></a></li>"),a.attr("id",o).find("a").attr("href",location.pathname+location.search+s).html(r),this.$el.find("ul").append(a),u?u.match(new RegExp("\\b"+o+"\\b"))||(u+=" "+o):u=o,e.attr("aria-describedby",u))},r.prototype.conditionallyHidden=function(){return!this.$elButton.find("button").is(":visible")},n.prototype.update=function(e){var r=t(this.$el.parent(".userform").find(".form-step")[e]),n=0,i=e/(this.$buttons.length-1)*100;this.$buttons.each(function(r,i){return!(r>e||(t(i).is(":visible")&&(n+=1),0))}),this.$el.find(".current-step-number").each(function(e,r){t(r).text(n)}),this.$el.find("[aria-valuenow]").each(function(e,r){t(r).attr("aria-valuenow",n)}),this.$buttons.each(function(e,r){var i=t(r),s=i.parent();if(parseInt(i.data("step"),10)===n&&i.is(":visible"))return s.addClass("current viewed"),void i.removeAttr("disabled");s.removeClass("current")}),this.$el.siblings(".progress-title").text(r.data("title")),i=i?i+"%":"",this.$el.find(".progress-bar").width(i)},i.prototype.update=function(){var t=this.userformInstance.steps.length,e=this.userformInstance.currentStep?this.userformInstance.currentStep.id:0,r=null,n=null;for(this.$el.find(".step-button-prev")[0===e?"hide":"show"](),r=t-1;r>=0;r--)if(n=this.userformInstance.steps[r],!n.conditionallyHidden()){this.$el.find(".step-button-next")[e>=r?"hide":"show"](),this.$el.find(".btn-toolbar")[e>=r?"show":"hide"]();break}},s.prototype.validationOptions={ignore:":hidden,ul",errorClass:"error",errorElement:"span",errorPlacement:function(t,e){t.addClass("message"),e.is(":radio")||e.parents(".checkboxset").length>0?t.appendTo(e.closest(".middleColumn, .field")):e.parents(".checkbox").length>0?t.appendTo(e.closest(".field")):t.insertAfter(e)},invalidHandler:function(t,e){setTimeout(function(){e.currentElements.filter(".error").first().focus()},0)},submitHandler:function(e){var r=!0,n=t(e).closest(".userform").data("inst");if(n.currentStep&&(n.currentStep.valid=t(e).valid()),t.each(n.steps,function(t,e){e.valid||e.conditionallyHidden()||(r=!1,n.errorContainer.addStepLink(e))}),r){var i=t(e).find(".field.requiredField.hide input");i.length>0&&i.removeAttr("required aria-required data-rule-required").valid(),t(e).removeClass("dirty"),e.submit(),n.$el.trigger("userform.form.submit")}else n.errorContainer.show()},success:function(e){var r=t(e).closest(".userform").data("inst"),n=t(e).attr("id"),i=n.substr(0,n.indexOf("-error")).replace(/[\\[\\]]/,"");e.remove(),r.$el.trigger("userform.form.valid",[i])}},s.prototype.addStep=function(t){t instanceof r&&(t.id=this.steps.length,this.steps.push(t))},s.prototype.setCurrentStep=function(t){t instanceof r&&(this.currentStep=t,this.currentStep.show(),this.currentStep.viewed=!0,this.currentStep.$el.addClass("viewed"))},s.prototype.jumpToStep=function(t,e){var r=this.steps[t],n=!1,i=void 0===e||e;if(void 0!==r){if(r.conditionallyHidden())return void(i?this.jumpToStep(t+1,e):this.jumpToStep(t-1,e));n=this.$el.valid(),this.currentStep.valid=n,!1===n&&!1===r.viewed||(this.currentStep.hide(),this.setCurrentStep(r),this.$el.trigger("userform.form.changestep",[r.id]))}},s.prototype.nextStep=function(){this.jumpToStep(this.steps.indexOf(this.currentStep)+1,!0)},s.prototype.prevStep=function(){this.jumpToStep(this.steps.indexOf(this.currentStep)-1,!1)},t(".userform").each(o)})},"./client/src/bundles/bundle.js":function(t,e,r){"use strict";r("./client/src/bundles/UserForms.js")},0:function(t,e){t.exports=i18n},1:function(t,e){t.exports=jQuery}}); !function(e){function t(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var r={};t.m=e,t.c=r,t.i=function(e){return e},t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/bundles/UserForms.js":function(e,t,r){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e){return"none"!==e.style.display&&"hidden"!==e.style.visibility&&!e.classList.contains("hide")}var u=function(){function e(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,r,n){return r&&e(t.prototype,r),n&&e(t,n),t}}(),s=r("./node_modules/async-validator/dist-web/index.js"),o=function(e){return e&&e.__esModule?e:{default:e}}(s),a=function(){function e(t,r){n(this,e),this.dom=t,this.userForm=r,this.progressTitle=this.userForm.dom.querySelector(".progress-title"),this.buttons=this.dom.querySelectorAll(".step-button-jump"),this.currentStepNumber=this.dom.querySelector(".current-step-number"),this.init()}return u(e,[{key:"init",value:function(){var e=this;this.dom.style.display="initial",this.buttons.forEach(function(t){t.addEventListener("click",function(r){r.preventDefault();var n=parseInt(t.getAttribute("data-step"),10);return e.userForm.jumpToStep(n-1),!1})}),this.userForm.dom.addEventListener("userform.form.changestep",function(t){e.update(t.detail.stepId)}),this.update(0)}},{key:"update",value:function(e){var t=this.userForm.getCurrentStepID()+1,r=this.userForm.getStep(e),n=r.step,u=e/(this.buttons.length-1)*100;this.currentStepNumber.innerText=t,this.dom.querySelectorAll("[aria-valuenow]").forEach(function(e){e.setAttribute("aria-valuenow",t)}),this.buttons.forEach(function(e){var r=e,n=r.parentNode;parseInt(r.getAttribute("data-step"),10)===t&&i(r)&&(n.classList.add("current"),n.classList.add("viewed"),r.disabled=!1),n.classList.remove("current")}),this.progressTitle.innerText=n.getAttribute("data-title"),u=u?u+"%":"",this.dom.querySelector(".progress-bar").style.width=u}}]),e}(),l=function(){function e(t,r){n(this,e),this.step=t,this.userForm=r,this.viewed=!1,this.buttonHolder=null,this.id=0,this.init()}return u(e,[{key:"init",value:function(){var e=this,t=this.getHTMLId();this.buttonHolder=document.querySelector(".step-button-wrapper[data-for='"+t+"']"),this.buttonHolder&&["userform.field.hide","userform.field.show"].forEach(function(t){e.buttonHolder.addEventListener(t,function(){e.userForm.dom.trigger("userform.form.conditionalstep")})})}},{key:"setId",value:function(e){this.id=e}},{key:"getHTMLId",value:function(){return this.step.getAttribute("id")}},{key:"show",value:function(){this.step.setAttribute("aria-hidden",!1),this.step.classList.remove("hide"),this.step.classList.add("viewed"),this.viewed=!0}},{key:"hide",value:function(){this.step.setAttribute("aria-hidden",!0),this.step.classList.add("hide")}},{key:"conditionallyHidden",value:function(){var e=this.buttonHolder.querySelector("button");return!("none"!==e.style.display&&"hidden"!==e.visibility&&!e.classList.contains("hide"))}},{key:"getValidatorType",value:function(e){return"email"===e.getAttribute("type")?"email":"date"===e.getAttribute("type")?"date":"file"===e.getAttribute("type")?"object":e.classList.contains("numeric")||"numeric"===e.getAttribute("type")?"number":"string"}},{key:"getValidatorMessage",value:function(e){return e.getAttribute("data-msg-required")?e.getAttribute("data-msg-required"):this.getFieldLabel(e)+" is required"}},{key:"getHolderForField",value:function(e){return window.closest(e,".field")}},{key:"getFieldLabel",value:function(e){var t=this.getHolderForField(e);if(t){var r=t.querySelector("label.left, legend.left");if(r)return r.innerText}return e.getAttribute("name")}},{key:"isInputNumeric",value:function(e){return"number"===this.getValidatorType(e)}},{key:"isInputFile",value:function(e){return"file"===e.getAttribute("type")}},{key:"getInputByName",value:function(e){return this.step.querySelector('input[name="'+e+'"]')}},{key:"getValidationsDescriptors",value:function(e){var t=this,r={};return this.step.querySelectorAll("input, textarea, select").forEach(function(n){if(i(n)&&(!e||e&&n.classList.contains("focused"))){var u=t.getFieldLabel(n),s=t.getHolderForField(n);r[n.getAttribute("name")]={title:u,type:t.getValidatorType(n),required:s.classList.contains("requiredField"),message:t.getValidatorMessage(n)};var o=n.getAttribute("data-rule-min"),a=n.getAttribute("data-rule-max");null===o&&null===a||(r[n.getAttribute("name")].asyncValidator=function(e,t){return new Promise(function(e,r){null!==o&&t<o?r(u+" cannot be less than "+o):null!==a&&t>a?r(u+" cannot be greater than "+a):e()})});var l=n.getAttribute("data-rule-minlength"),c=n.getAttribute("data-rule-maxlength");null===l&&null===c||(r[n.getAttribute("name")].asyncValidator=function(e,t){return new Promise(function(e,r){null!==l&&t.length<l?r(u+" cannot be shorter than "+l):null!==c&&t.length>c?r(u+" cannot be longer than "+c):e()})})}}),r}},{key:"validate",value:function(e){var t=this,r=this.getValidationsDescriptors(e);if(Object.keys(r).length){var n=new o.default(r),i=new FormData(this.userForm.dom),u={};return i.forEach(function(e,r){var n=e,i=t.getInputByName(r);n&&i&&t.isInputNumeric(i)&&(n=parseFloat(n)),u[r]=n}),this.step.querySelectorAll('input[type="radio"],input[type="checkbox"]').forEach(function(e){var t=e.getAttribute("name");void 0===u[t]&&(u[t]="")}),new Promise(function(e,r){n.validate(u,function(n){n&&n.length?(t.displayErrorMessages(n),r(n)):(t.displayErrorMessages([]),e())})})}return new Promise(function(e){e()})}},{key:"enableLiveValidation",value:function(){var e=this;this.step.querySelectorAll("input, textarea, select").forEach(function(t){t.addEventListener("focusin",function(){t.classList.add("focused")}),t.addEventListener("change",function(){t.classList.add("dirty")}),t.addEventListener("focusout",function(){e.validate(!0).then(function(){}).catch(function(){})})})}},{key:"displayErrorMessages",value:function(e){var t=this,r=[];e.forEach(function(e){var n=t.userForm.dom.querySelector("#"+e.field);if(n){var i=n.querySelector("span.error");i||(i=document.createElement("span"),i.classList.add("error"),i.setAttribute("data-id",e.field)),r.push(e.field),i.innerHTML=e.message,n.append(i)}}),this.step.querySelectorAll("span.error").forEach(function(e){var t=e.getAttribute("data-id");-1===r.indexOf(t)&&e.remove()})}}]),e}(),c=function(){function e(t,r){n(this,e),this.dom=t,this.userForm=r,this.prevButton=t.querySelector(".step-button-prev"),this.nextButton=t.querySelector(".step-button-next"),this.init()}return u(e,[{key:"init",value:function(){var e=this;this.prevButton.addEventListener("click",function(t){t.preventDefault(),window.triggerDispatchEvent(e.userForm.dom,"userform.action.prev")}),this.nextButton.addEventListener("click",function(t){t.preventDefault(),window.triggerDispatchEvent(e.userForm.dom,"userform.action.next")}),this.update(),this.userForm.dom.addEventListener("userform.form.changestep",function(){e.update()}),this.userForm.dom.addEventListener("userform.form.conditionalstep",function(){e.update()})}},{key:"update",value:function(){var e=this.userForm.getNumberOfSteps(),t=this.userForm.getCurrentStepID(),r=null,n=null;for(r=e-1;r>=0;r--)if(n=this.userForm.getStep(r),!n.conditionallyHidden()){t>=r?this.nextButton.parentNode.classList.add("hide"):this.nextButton.parentNode.classList.remove("hide"),t>0&&t<=r?this.prevButton.parentNode.classList.remove("hide"):this.prevButton.parentNode.classList.add("hide"),t>=r?this.dom.querySelector(".btn-toolbar").classList.remove("hide"):this.dom.querySelector(".btn-toolbar").classList.add("hide");break}}}]),e}(),f=function(){function e(t){n(this,e),this.dom=t,this.CONSTANTS={},this.steps=[],this.progressBar=null,this.actions=null,this.currentStep=null,this.CONSTANTS.ENABLE_LIVE_VALIDATION=void 0!==this.dom.getAttribute("livevalidation"),this.CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP=void 0!==this.dom.getAttribute("toperrors"),this.CONSTANTS.ENABLE_ARE_YOU_SURE=void 0!==this.dom.getAttribute("enableareyousure")}return u(e,[{key:"init",value:function(){this.initialiseFormSteps(),this.CONSTANTS.ENABLE_ARE_YOU_SURE&&this.initAreYouSure()}},{key:"initialiseFormSteps",value:function(){var e=this;this.dom.querySelectorAll(".form-step").forEach(function(t){var r=new l(t,e);r.hide(),e.addStep(r),e.CONSTANTS.ENABLE_LIVE_VALIDATION&&r.enableLiveValidation()}),this.setCurrentStep(this.steps[0]);var t=this.dom.querySelector(".userform-progress");t&&(this.progressBar=new a(t,this));var r=this.dom.querySelector(".step-navigation");r&&(this.formActions=new c(r,this),this.formActions.update()),this.setUpPing(),this.dom.addEventListener("userform.action.next",function(){e.nextStep()}),this.dom.addEventListener("userform.action.prev",function(){e.prevStep()}),this.dom.addEventListener("submit",function(t){e.validateForm(t)})}},{key:"validateForm",value:function(e){var t=this;e.preventDefault(),this.currentStep.validate().then(function(e){e||t.dom.submit()}).catch(function(){})}},{key:"setCurrentStep",value:function(e){e instanceof l&&(this.currentStep=e,this.currentStep.show())}},{key:"addStep",value:function(e){e instanceof l&&(e.setId(this.steps.length),this.steps.push(e))}},{key:"getNumberOfSteps",value:function(){return this.steps.length}},{key:"getCurrentStepID",value:function(){return this.currentStep.id?this.currentStep.id:0}},{key:"getStep",value:function(e){return this.steps[e]}},{key:"nextStep",value:function(){var e=this;this.currentStep.validate().then(function(){e.jumpToStep(e.steps.indexOf(e.currentStep)+1,!0)}).catch(function(){})}},{key:"prevStep",value:function(){this.jumpToStep(this.steps.indexOf(this.currentStep)-1,!0)}},{key:"jumpToStep",value:function(e,t){var r=this.steps[e],n=void 0===t||t;if(void 0!==r){if(r.conditionallyHidden())return void(n?this.jumpToStep(e+1,t):this.jumpToStep(e-1,t));this.currentStep&&this.currentStep.hide(),this.setCurrentStep(r),window.triggerDispatchEvent(this.dom,"userform.form.changestep",{stepId:r.id})}}},{key:"setUpPing",value:function(){window.setInterval(function(){fetch("UserDefinedFormController/ping")},18e4)}},{key:"doConfirm",value:function(e){if(0===this.dom.querySelectorAll(".dirty").length)return!0;if(navigator.userAgent.toLowerCase().match(/msie|chrome/)){if(window.hasUserFormsPropted)return!0;window.hasUserFormsPropted=!0,window.setTimeout(function(){window.hasUserFormsPropted=!1},900)}return e.preventDefault(),void 0!==window.i18n?event.returnValue=window.i18n._t("UserForms.LEAVE_CONFIRMATION","You have unsaved changes!"):event.returnValue="You have unsaved changes!",!0}},{key:"initAreYouSure",value:function(){var e=this.doConfirm.bind(this);this.dom.addEventListener("submit",function(t){window.removeEventListener("beforeunload",e)}),window.addEventListener("beforeunload",e)}}]),e}();document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("form.userform").forEach(function(e){new f(e).init()})})},"./client/src/bundles/bundle.js":function(e,t,r){"use strict";r("./client/src/bundles/UserForms.js")},"./node_modules/async-validator/dist-web/index.js":function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e){function n(){return n=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},n.apply(this,arguments)}function i(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,s(e,t)}function u(e){return(u=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function s(e,t){return(s=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function o(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}function a(e,t,r){return a=o()?Reflect.construct.bind():function(e,t,r){var n=[null];n.push.apply(n,t);var i=Function.bind.apply(e,n),u=new i;return r&&s(u,r.prototype),u},a.apply(null,arguments)}function l(e){return-1!==Function.toString.call(e).indexOf("[native code]")}function c(e){var t="function"==typeof Map?new Map:void 0;return(c=function(e){function r(){return a(e,arguments,u(this).constructor)}if(null===e||!l(e))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),s(r,e)})(e)}function f(e){if(!e||!e.length)return null;var t={};return e.forEach(function(e){var r=e.field;t[r]=t[r]||[],t[r].push(e)}),t}function d(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];var i=0,u=r.length;return"function"==typeof e?e.apply(null,r):"string"==typeof e?e.replace(E,function(e){if("%%"===e)return"%";if(i>=u)return e;switch(e){case"%s":return String(r[i++]);case"%d":return Number(r[i++]);case"%j":try{return JSON.stringify(r[i++])}catch(e){return"[Circular]"}break;default:return e}}):e}function p(e){return"string"===e||"url"===e||"hex"===e||"email"===e||"date"===e||"pattern"===e}function h(e,t){return void 0===e||null===e||!("array"!==t||!Array.isArray(e)||e.length)||!(!p(t)||"string"!=typeof e||e)}function m(e,t,r){function n(e){i.push.apply(i,e||[]),++u===s&&r(i)}var i=[],u=0,s=e.length;e.forEach(function(e){t(e,n)})}function v(e,t,r){function n(s){if(s&&s.length)return void r(s);var o=i;i+=1,o<u?t(e[o],n):r([])}var i=0,u=e.length;n([])}function y(e){var t=[];return Object.keys(e).forEach(function(r){t.push.apply(t,e[r]||[])}),t}function g(e,t,r,n,i){if(t.first){var u=new Promise(function(t,u){var s=function(e){return n(e),e.length?u(new x(e,f(e))):t(i)};v(y(e),r,s)});return u.catch(function(e){return e}),u}var s=!0===t.firstFields?Object.keys(e):t.firstFields||[],o=Object.keys(e),a=o.length,l=0,c=[],d=new Promise(function(t,u){var d=function(e){if(c.push.apply(c,e),++l===a)return n(c),c.length?u(new x(c,f(c))):t(i)};o.length||(n(c),t(i)),o.forEach(function(t){var n=e[t];-1!==s.indexOf(t)?v(n,r,d):m(n,r,d)})});return d.catch(function(e){return e}),d}function b(e){return!(!e||void 0===e.message)}function w(e,t){for(var r=e,n=0;n<t.length;n++){if(void 0==r)return r;r=r[t[n]]}return r}function F(e,t){return function(r){var n;return n=e.fullFields?w(t,e.fullFields):t[r.field||e.fullField],b(r)?(r.field=r.field||e.fullField,r.fieldValue=n,r):{message:"function"==typeof r?r():r,fieldValue:n,field:r.field||e.fullField}}}function q(e,t){if(t)for(var r in t)if(t.hasOwnProperty(r)){var i=t[r];"object"==typeof i&&"object"==typeof e[r]?e[r]=n({},e[r],i):e[r]=i}return e}function A(){return{default:"Validation error on field %s",required:"%s is required",enum:"%s must be one of %s",whitespace:"%s cannot be empty",date:{format:"%s date %s is invalid for format %s",parse:"%s date could not be parsed, %s is invalid ",invalid:"%s date %s is invalid"},types:{string:"%s is not a %s",method:"%s is not a %s (function)",array:"%s is not an %s",object:"%s is not an %s",number:"%s is not a %s",date:"%s is not a %s",boolean:"%s is not a %s",integer:"%s is not an %s",float:"%s is not a %s",regexp:"%s is not a valid %s",email:"%s is not a valid %s",url:"%s is not a valid %s",hex:"%s is not a valid %s"},string:{len:"%s must be exactly %s characters",min:"%s must be at least %s characters",max:"%s cannot be longer than %s characters",range:"%s must be between %s and %s characters"},number:{len:"%s must equal %s",min:"%s cannot be less than %s",max:"%s cannot be greater than %s",range:"%s must be between %s and %s"},array:{len:"%s must be exactly %s in length",min:"%s cannot be less than %s in length",max:"%s cannot be greater than %s in length",range:"%s must be between %s and %s in length"},pattern:{mismatch:"%s value %s does not match pattern %s"},clone:function(){var e=JSON.parse(JSON.stringify(this));return e.clone=this.clone,e}}}r.d(t,"default",function(){return te});var E=/%[sdj%]/g,S=function(){};void 0!==e&&r.i({NODE_ENV:"production"});var O,x=function(e){function t(t,r){var n;return n=e.call(this,"Async Validation Error")||this,n.errors=t,n.fields=r,n}return i(t,e),t}(c(Error)),L=function(e,t,r,n,i,u){!e.required||r.hasOwnProperty(e.field)&&!h(t,u||e.type)||n.push(d(i.messages.required,e.fullField))},T=function(e,t,r,n,i){(/^\s+$/.test(t)||""===t)&&n.push(d(i.messages.whitespace,e.fullField))},k=function(){if(O)return O;var e=function(e){return e&&e.includeBoundaries?"(?:(?<=\\s|^)(?=[a-fA-F\\d:])|(?<=[a-fA-F\\d:])(?=\\s|$))":""},t="(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}",r="[a-fA-F\\d]{1,4}",n=("\n(?:\n(?:"+r+":){7}(?:"+r+"|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8\n(?:"+r+":){6}(?:"+t+"|:"+r+"|:)| // 1:2:3:4:5:6:: 1:2:3:4:5:6::8 1:2:3:4:5:6::8 1:2:3:4:5:6::1.2.3.4\n(?:"+r+":){5}(?::"+t+"|(?::"+r+"){1,2}|:)| // 1:2:3:4:5:: 1:2:3:4:5::7:8 1:2:3:4:5::8 1:2:3:4:5::7:1.2.3.4\n(?:"+r+":){4}(?:(?::"+r+"){0,1}:"+t+"|(?::"+r+"){1,3}|:)| // 1:2:3:4:: 1:2:3:4::6:7:8 1:2:3:4::8 1:2:3:4::6:7:1.2.3.4\n(?:"+r+":){3}(?:(?::"+r+"){0,2}:"+t+"|(?::"+r+"){1,4}|:)| // 1:2:3:: 1:2:3::5:6:7:8 1:2:3::8 1:2:3::5:6:7:1.2.3.4\n(?:"+r+":){2}(?:(?::"+r+"){0,3}:"+t+"|(?::"+r+"){1,5}|:)| // 1:2:: 1:2::4:5:6:7:8 1:2::8 1:2::4:5:6:7:1.2.3.4\n(?:"+r+":){1}(?:(?::"+r+"){0,4}:"+t+"|(?::"+r+"){1,6}|:)| // 1:: 1::3:4:5:6:7:8 1::8 1::3:4:5:6:7:1.2.3.4\n(?::(?:(?::"+r+"){0,5}:"+t+"|(?::"+r+"){1,7}|:)) // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::1.2.3.4\n)(?:%[0-9a-zA-Z]{1,})? // %eth0 %1\n").replace(/\s*\/\/.*$/gm,"").replace(/\n/g,"").trim(),i=new RegExp("(?:^"+t+"$)|(?:^"+n+"$)"),u=new RegExp("^"+t+"$"),s=new RegExp("^"+n+"$"),o=function(r){return r&&r.exact?i:new RegExp("(?:"+e(r)+t+e(r)+")|(?:"+e(r)+n+e(r)+")","g")};o.v4=function(r){return r&&r.exact?u:new RegExp(""+e(r)+t+e(r),"g")},o.v6=function(t){return t&&t.exact?s:new RegExp(""+e(t)+n+e(t),"g")};var a=o.v4().source,l=o.v6().source,c="(?:(?:(?:[a-z]+:)?//)|www\\.)(?:\\S+(?::\\S*)?@)?(?:localhost|"+a+"|"+l+'|(?:(?:[a-z\\u00a1-\\uffff0-9][-_]*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:[/?#][^\\s"]*)?';return O=new RegExp("(?:^"+c+"$)","i")},j={email:/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$/,hex:/^#?([a-f0-9]{6}|[a-f0-9]{3})$/i},N={integer:function(e){return N.number(e)&&parseInt(e,10)===e},float:function(e){return N.number(e)&&!N.integer(e)},array:function(e){return Array.isArray(e)},regexp:function(e){if(e instanceof RegExp)return!0;try{return!!new RegExp(e)}catch(e){return!1}},date:function(e){return"function"==typeof e.getTime&&"function"==typeof e.getMonth&&"function"==typeof e.getYear&&!isNaN(e.getTime())},number:function(e){return!isNaN(e)&&"number"==typeof e},object:function(e){return"object"==typeof e&&!N.array(e)},method:function(e){return"function"==typeof e},email:function(e){return"string"==typeof e&&e.length<=320&&!!e.match(j.email)},url:function(e){return"string"==typeof e&&e.length<=2048&&!!e.match(k())},hex:function(e){return"string"==typeof e&&!!e.match(j.hex)}},P=function(e,t,r,n,i){if(e.required&&void 0===t)return void L(e,t,r,n,i);var u=["integer","float","array","regexp","object","method","email","number","date","url","hex"],s=e.type;u.indexOf(s)>-1?N[s](t)||n.push(d(i.messages.types[s],e.fullField,e.type)):s&&typeof t!==e.type&&n.push(d(i.messages.types[s],e.fullField,e.type))},_=function(e,t,r,n,i){var u="number"==typeof e.len,s="number"==typeof e.min,o="number"==typeof e.max,a=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,l=t,c=null,f="number"==typeof t,p="string"==typeof t,h=Array.isArray(t);if(f?c="number":p?c="string":h&&(c="array"),!c)return!1;h&&(l=t.length),p&&(l=t.replace(a,"_").length),u?l!==e.len&&n.push(d(i.messages[c].len,e.fullField,e.len)):s&&!o&&l<e.min?n.push(d(i.messages[c].min,e.fullField,e.min)):o&&!s&&l>e.max?n.push(d(i.messages[c].max,e.fullField,e.max)):s&&o&&(l<e.min||l>e.max)&&n.push(d(i.messages[c].range,e.fullField,e.min,e.max))},D=function(e,t,r,n,i){e.enum=Array.isArray(e.enum)?e.enum:[],-1===e.enum.indexOf(t)&&n.push(d(i.messages.enum,e.fullField,e.enum.join(", ")))},I=function(e,t,r,n,i){if(e.pattern)if(e.pattern instanceof RegExp)e.pattern.lastIndex=0,e.pattern.test(t)||n.push(d(i.messages.pattern.mismatch,e.fullField,t,e.pattern));else if("string"==typeof e.pattern){var u=new RegExp(e.pattern);u.test(t)||n.push(d(i.messages.pattern.mismatch,e.fullField,t,e.pattern))}},V={required:L,whitespace:T,type:P,range:_,enum:D,pattern:I},R=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t,"string")&&!e.required)return r();V.required(e,t,n,u,i,"string"),h(t,"string")||(V.type(e,t,n,u,i),V.range(e,t,n,u,i),V.pattern(e,t,n,u,i),!0===e.whitespace&&V.whitespace(e,t,n,u,i))}r(u)},C=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i),void 0!==t&&V.type(e,t,n,u,i)}r(u)},B=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(""===t&&(t=void 0),h(t)&&!e.required)return r();V.required(e,t,n,u,i),void 0!==t&&(V.type(e,t,n,u,i),V.range(e,t,n,u,i))}r(u)},M=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i),void 0!==t&&V.type(e,t,n,u,i)}r(u)},H=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i),h(t)||V.type(e,t,n,u,i)}r(u)},U=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i),void 0!==t&&(V.type(e,t,n,u,i),V.range(e,t,n,u,i))}r(u)},$=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i),void 0!==t&&(V.type(e,t,n,u,i),V.range(e,t,n,u,i))}r(u)},z=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if((void 0===t||null===t)&&!e.required)return r();V.required(e,t,n,u,i,"array"),void 0!==t&&null!==t&&(V.type(e,t,n,u,i),V.range(e,t,n,u,i))}r(u)},Y=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i),void 0!==t&&V.type(e,t,n,u,i)}r(u)},J=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i),void 0!==t&&V.enum(e,t,n,u,i)}r(u)},Z=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t,"string")&&!e.required)return r();V.required(e,t,n,u,i),h(t,"string")||V.pattern(e,t,n,u,i)}r(u)},G=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t,"date")&&!e.required)return r();if(V.required(e,t,n,u,i),!h(t,"date")){var s;s=t instanceof Date?t:new Date(t),V.type(e,s,n,u,i),s&&V.range(e,s.getTime(),n,u,i)}}r(u)},W=function(e,t,r,n,i){var u=[],s=Array.isArray(t)?"array":typeof t;V.required(e,t,n,u,i,s),r(u)},K=function(e,t,r,n,i){var u=e.type,s=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t,u)&&!e.required)return r();V.required(e,t,n,s,i,u),h(t,u)||V.type(e,t,n,s,i)}r(s)},Q=function(e,t,r,n,i){var u=[];if(e.required||!e.required&&n.hasOwnProperty(e.field)){if(h(t)&&!e.required)return r();V.required(e,t,n,u,i)}r(u)},X={string:R,method:C,number:B,boolean:M,regexp:H,integer:U,float:$,array:z,object:Y,enum:J,pattern:Z,date:G,url:K,hex:K,email:K,required:W,any:Q},ee=A(),te=function(){function e(e){this.rules=null,this._messages=ee,this.define(e)}var t=e.prototype;return t.define=function(e){var t=this;if(!e)throw new Error("Cannot configure a schema with no rules");if("object"!=typeof e||Array.isArray(e))throw new Error("Rules must be an object");this.rules={},Object.keys(e).forEach(function(r){var n=e[r];t.rules[r]=Array.isArray(n)?n:[n]})},t.messages=function(e){return e&&(this._messages=q(A(),e)),this._messages},t.validate=function(t,r,i){function u(e){for(var t=[],r={},n=0;n<e.length;n++)!function(e){if(Array.isArray(e)){var r;t=(r=t).concat.apply(r,e)}else t.push(e)}(e[n]);t.length?(r=f(t),l(t,r)):l(null,o)}var s=this;void 0===r&&(r={}),void 0===i&&(i=function(){});var o=t,a=r,l=i;if("function"==typeof a&&(l=a,a={}),!this.rules||0===Object.keys(this.rules).length)return l&&l(null,o),Promise.resolve(o);if(a.messages){var c=this.messages();c===ee&&(c=A()),q(c,a.messages),a.messages=c}else a.messages=this.messages();var p={};(a.keys||Object.keys(this.rules)).forEach(function(e){var r=s.rules[e],i=o[e];r.forEach(function(r){var u=r;"function"==typeof u.transform&&(o===t&&(o=n({},o)),i=o[e]=u.transform(i)),u="function"==typeof u?{validator:u}:n({},u),u.validator=s.getValidationMethod(u),u.validator&&(u.field=e,u.fullField=u.fullField||e,u.type=s.getType(u),p[e]=p[e]||[],p[e].push({rule:u,value:i,source:o,field:e}))})});var h={};return g(p,a,function(t,r){function i(e,t){return n({},t,{fullField:s.fullField+"."+e,fullFields:s.fullFields?[].concat(s.fullFields,[e]):[e]})}function u(u){void 0===u&&(u=[]);var c=Array.isArray(u)?u:[u];!a.suppressWarning&&c.length&&e.warning("async-validator:",c),c.length&&void 0!==s.message&&(c=[].concat(s.message));var f=c.map(F(s,o));if(a.first&&f.length)return h[s.field]=1,r(f);if(l){if(s.required&&!t.value)return void 0!==s.message?f=[].concat(s.message).map(F(s,o)):a.error&&(f=[a.error(s,d(a.messages.required,s.field))]),r(f);var p={};s.defaultField&&Object.keys(t.value).map(function(e){p[e]=s.defaultField}),p=n({},p,t.rule.fields);var m={};Object.keys(p).forEach(function(e){var t=p[e],r=Array.isArray(t)?t:[t];m[e]=r.map(i.bind(null,e))});var v=new e(m);v.messages(a.messages),t.rule.options&&(t.rule.options.messages=a.messages,t.rule.options.error=a.error),v.validate(t.value,t.rule.options||a,function(e){var t=[];f&&f.length&&t.push.apply(t,f),e&&e.length&&t.push.apply(t,e),r(t.length?t:null)})}else r(f)}var s=t.rule,l=!("object"!==s.type&&"array"!==s.type||"object"!=typeof s.fields&&"object"!=typeof s.defaultField);l=l&&(s.required||!s.required&&t.value),s.field=t.field;var c;if(s.asyncValidator)c=s.asyncValidator(s,t.value,u,t.source,a);else if(s.validator){try{c=s.validator(s,t.value,u,t.source,a)}catch(e){null==console.error||console.error(e),a.suppressValidatorError||setTimeout(function(){throw e},0),u(e.message)}!0===c?u():!1===c?u("function"==typeof s.message?s.message(s.fullField||s.field):s.message||(s.fullField||s.field)+" fails"):c instanceof Array?u(c):c instanceof Error&&u(c.message)}c&&c.then&&c.then(function(){return u()},function(e){return u(e)})},function(e){u(e)},o)},t.getType=function(e){if(void 0===e.type&&e.pattern instanceof RegExp&&(e.type="pattern"),"function"!=typeof e.validator&&e.type&&!X.hasOwnProperty(e.type))throw new Error(d("Unknown rule type %s",e.type));return e.type||"string"},t.getValidationMethod=function(e){if("function"==typeof e.validator)return e.validator;var t=Object.keys(e),r=t.indexOf("message");return-1!==r&&t.splice(r,1),1===t.length&&"required"===t[0]?X.required:X[this.getType(e)]||void 0},e}();te.register=function(e,t){if("function"!=typeof t)throw new Error("Cannot register a validator by type, validator is not a function");X[e]=t},te.warning=S,te.messages=ee,te.validators=X}.call(t,r("./node_modules/process/browser.js"))},"./node_modules/process/browser.js":function(e,t){function r(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function i(e){if(c===setTimeout)return setTimeout(e,0);if((c===r||!c)&&setTimeout)return c=setTimeout,setTimeout(e,0);try{return c(e,0)}catch(t){try{return c.call(null,e,0)}catch(t){return c.call(this,e,0)}}}function u(e){if(f===clearTimeout)return clearTimeout(e);if((f===n||!f)&&clearTimeout)return f=clearTimeout,clearTimeout(e);try{return f(e)}catch(t){try{return f.call(null,e)}catch(t){return f.call(this,e)}}}function s(){m&&p&&(m=!1,p.length?h=p.concat(h):v=-1,h.length&&o())}function o(){if(!m){var e=i(s);m=!0;for(var t=h.length;t;){for(p=h,h=[];++v<t;)p&&p[v].run();v=-1,t=h.length}p=null,m=!1,u(e)}}function a(e,t){this.fun=e,this.array=t}function l(){}var c,f,d=e.exports={};!function(){try{c="function"==typeof setTimeout?setTimeout:r}catch(e){c=r}try{f="function"==typeof clearTimeout?clearTimeout:n}catch(e){f=n}}();var p,h=[],m=!1,v=-1;d.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)t[r-1]=arguments[r];h.push(new a(e,t)),1!==h.length||m||i(o)},a.prototype.run=function(){this.fun.apply(null,this.array)},d.title="browser",d.browser=!0,d.env={},d.argv=[],d.version="",d.versions={},d.on=l,d.addListener=l,d.once=l,d.off=l,d.removeListener=l,d.removeAllListeners=l,d.emit=l,d.prependListener=l,d.prependOnceListener=l,d.listeners=function(e){return[]},d.binding=function(e){throw new Error("process.binding is not supported")},d.cwd=function(){return"/"},d.chdir=function(e){throw new Error("process.chdir is not supported")},d.umask=function(){return 0}}});

View File

@ -1 +1 @@
.userform-progress .progress{position:relative;height:1em;background:#ced5e1}.userform-progress .progress-bar{position:absolute;height:1em;background:#566b8d}.userform-progress .step-buttons{margin-left:0;position:relative}.userform-progress .step-button-wrapper{display:inline-block;list-style-type:none}.userform-progress .step-button-wrapper.viewed .step-button-jump{opacity:1}.userform-progress .step-button-jump{position:absolute;top:0;opacity:.7}.step-navigation .step-buttons{margin-left:0}.step-navigation .step-button-wrapper{display:inline-block;list-style-type:none}.userform{clear:both;width:100%;max-width:100%}.userform .hide{display:none}.userform .field label.right{color:#303b4d}.userformsgroup{border:1px solid #aebace;border-radius:4px;padding:8px;margin-top:12px;margin-bottom:12px}.userformsgroup>legend{padding-left:4px;padding-right:4px;border:0;width:auto}.right-title{clear:both;display:block}.checkbox .right-title{display:inline}.userform .left{margin-bottom:5px;font-weight:700} .userform-progress .progress{position:relative;height:1em;background:#ced5e1}.userform-progress .progress-bar{position:absolute;height:1em;background:#566b8d}.userform-progress .step-buttons{margin-left:0;padding-left:0;width:100%;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;position:relative}.userform-progress .step-button-wrapper{display:inline-block;list-style-type:none}.userform-progress .step-button-wrapper.viewed .step-button-jump{opacity:1}.userform-progress .step-button-jump{opacity:.7}.step-navigation .step-buttons{margin-left:0}.step-navigation .step-button-wrapper{display:inline-block;list-style-type:none}.userform{clear:both;width:100%;max-width:100%}.userform .hide{display:none}.userform .field label.right{color:#303b4d}.userformsgroup{border:1px solid #aebace;border-radius:4px;padding:8px;margin-top:12px;margin-bottom:12px}.userformsgroup>legend{padding-left:4px;padding-right:4px;border:0;width:auto}.right-title{clear:both;display:block}.checkbox .right-title{display:inline}.userform .left{margin-bottom:5px;font-weight:700}

View File

@ -1,774 +1,577 @@
/** /**
* @file Manages the multi-step navigation. * @file Manages the multi-step navigation.
*/ */
import Schema from 'async-validator';
import jQuery from 'jquery'; const DIRTY_CLASS = 'dirty';
import i18n from 'i18n'; const FOCUSED_CLASS = 'focused';
jQuery(document).ready(($) => { function isVisible(element) {
// Settings that come from the CMS. return element.style.display !== 'none'
const CONSTANTS = {}; && element.style.visibility !== 'hidden'
&& !element.classList.contains('hide');
}
// Common functions that extend multiple classes. class ProgressBar {
const commonMixin = { constructor(dom, userForm) {
/** this.dom = dom;
* @func show this.userForm = userForm;
* @desc Show the form step. Looks after aria attributes too. this.progressTitle = this.userForm.dom.querySelector('.progress-title');
*/ this.buttons = this.dom.querySelectorAll('.step-button-jump');
show() { this.currentStepNumber = this.dom.querySelector('.current-step-number');
this.$el.attr('aria-hidden', false).show(); this.init();
},
/**
* @func hide
* @desc Hide the form step. Looks after aria attributes too.
*/
hide() {
this.$el.attr('aria-hidden', true).hide();
},
};
/**
* @func ErrorContainer
* @constructor
* @param {object} element - The error container element.
* @return {object} - The ErrorContainer instance.
* @desc Creates an error container. Used to display step error messages at the top.
*/
function ErrorContainer(element) {
this.$el = element instanceof $ ? element : $(element);
// Set the error container's heading.
this.$el.find('h4').text(i18n._t('UserForms.ERROR_CONTAINER_HEADER',
'Please correct the following errors and try again:'));
return this;
} }
/** init() {
* @func hasErrors this.dom.style.display = 'initial';
* @return boolean const buttons = this.buttons;
* @desc Checks if the error container has any error messages. buttons.forEach((button) => {
*/ button.addEventListener('click', (e) => {
ErrorContainer.prototype.hasErrors = function hasErrors() {
return this.$el.find('.error-list').children().length > 0;
};
/**
* @func removeErrorMessage
* @desc Removes an error message from the error container.
*/
ErrorContainer.prototype.removeErrorMessage = function removeErrorMessage(fieldId) {
this.$el.find(`#${fieldId}-top-error`).remove();
// If there are no more error then hide the container.
if (!this.hasErrors()) {
this.hide();
}
};
/**
* @func addStepLink
* @param {object} step - FormStep instance.
* @desc Adds a link to a form step as an error message.
*/
ErrorContainer.prototype.addStepLink = function addStepLink(step) {
const userform = this.$el.closest('.userform').data('inst');
const itemID = `${step.$el.attr('id')}-error-link`;
let $itemElement = this.$el.find(`#${itemID}`);
const stepID = step.$el.attr('id');
const stepTitle = step.$el.data('title');
// If the item already exists we don't need to do anything.
if ($itemElement.length) {
return;
}
$itemElement = $(`<li id="${itemID}"><a href="#${stepID}">${stepTitle}</a></li>`);
$itemElement.on('click', (e) => {
e.preventDefault();
userform.jumpToStep(step.id);
});
this.$el.find('.error-list').append($itemElement);
};
/**
* @func removeStepLink
* @param {object} step - FormStep instance.
* @desc Removes a step link from the error container.
*/
ErrorContainer.prototype.removeStepLink = function removeStepLink(fieldId) {
const stepID = $(`#${fieldId}`).closest('.form-step').attr('id');
this.$el.find(`#${stepID}-error-link`).remove();
// Hide the error container if we've just removed the last error.
if (this.$el.find('.error-list').is(':empty')) {
this.hide();
}
};
/**
* @func ErrorContainer.updateErrorMessage
* @param {object} $input - The jQuery input object which contains the field to validate.
* @param {object} message - The error message to display (html escaped).
* @desc Update an error message (displayed at the top of the form).
*/
ErrorContainer.prototype.updateErrorMessage = function updateErrorMessage($input, message) {
const inputID = $input.attr('id');
let anchor = `#${inputID}`;
const elementID = `${inputID}-top-error`;
let messageElement = $(`#${elementID}`);
let describedBy = $input.attr('aria-describedby');
// The 'message' param will be an empty string if the field is valid.
if (!message) {
// Style issues as fixed if they already exist
messageElement.addClass('fixed');
return;
}
messageElement.removeClass('fixed');
this.show();
if (messageElement.length === 1) {
// Update the existing error message.
messageElement.show().find('a').html(message);
} else {
// Generate better link to field
$input.closest('.field[id]').each(() => {
const anchorID = $(this).attr('id');
if (!anchorID) {
return;
}
anchor = `#${anchorID}`;
});
// Add a new error message
messageElement = $('<li><a></a></li>');
messageElement
.attr('id', elementID)
.find('a')
.attr('href', location.pathname + location.search + anchor)
.html(message);
this.$el.find('ul').append(messageElement);
// Link back to original input via aria
// Respect existing non-error aria-describedby
if (!describedBy) {
describedBy = elementID;
} else if (!describedBy.match(new RegExp(`\\b${elementID}\\b`))) {
// Add to end of list if not already present
describedBy += ` ${elementID}`;
}
$input.attr('aria-describedby', describedBy);
}
};
/**
* @func FormStep
* @constructor
* @param {object} element
* @return {object} - The FormStep instance.
* @desc Creates a form step.
*/
function FormStep(element) {
const self = this;
this.$el = element instanceof $ ? element : $(element);
const userform = this.$el.closest('.userform').data('inst');
// Find button for this step
this.$elButton = $(`.step-button-wrapper[data-for='${this.$el.prop('id')}']`);
// Has the step been viewed by the user?
this.viewed = false;
// Is the form step valid?
// This value is used on form submission, which fails, if any of the steps are invalid.
this.valid = false;
// The internal id of the step. Used for getting the step from the UserForm.steps array.
this.id = null;
this.hide();
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
this.errorContainer = new ErrorContainer(this.$el.find('.error-container'));
// Listen for errors on the UserForm.
userform.$el.on('userform.form.error', (e, validator) => {
// The step only cares about errors if it's currently visible.
if (!self.$el.is(':visible')) {
return;
}
// Add or update each error in the list.
$.each(validator.errorList, (i, error) => {
self.errorContainer.updateErrorMessage($(error.element), error.message);
});
});
// Listen for fields becoming valid
userform.$el.on('userform.form.valid', (e, fieldId) => {
self.errorContainer.removeErrorMessage(fieldId);
});
}
// Ensure that page visibilty updates the step navigation
this
.$elButton
.on('userform.field.hide userform.field.show', () => {
userform.$el.trigger('userform.form.conditionalstep');
});
return this;
}
/**
* Determine if this step is conditionally disabled
*
* @returns {Boolean}
*/
// Because the element itself could be visible but 0 height, so check visibility of button
FormStep.prototype.conditionallyHidden = function conditionallyHidden() {
return !this.$elButton.find('button').is(':visible');
};
/**
* @func ProgressBar
* @constructor
* @param {object} element
* @return {object} - The Progress bar instance.
* @desc Creates a progress bar.
*/
function ProgressBar(element) {
const self = this;
this.$el = element instanceof $ ? element : $(element);
this.$buttons = this.$el.find('.step-button-jump');
this.$jsAlign = this.$el.find('.js-align');
const userform = this.$el.closest('.userform').data('inst');
// Update the progress bar when 'step' buttons are clicked.
this.$buttons.each((i, stepButton) => {
$(stepButton).on('click', (e) => {
e.preventDefault(); e.preventDefault();
const stepNumber = parseInt($(e.target).data('step'), 10); const stepNumber = parseInt(button.getAttribute('data-step'), 10);
self.$el.trigger('userform.progress.changestep', stepNumber); this.userForm.jumpToStep(stepNumber - 1);
return false;
}); });
}); });
this.userForm.dom.addEventListener('userform.form.changestep', (e) => {
// Update the progress bar when 'prev' and 'next' buttons are clicked. this.update(e.detail.stepId);
userform.$el.on('userform.form.changestep', (e, stepID) => {
self.update(stepID);
}); });
this.update(0);
// Listen for steps being conditionally shown / hidden by display rules.
// We need to update step related UI like the number of step buttons
// and any text that shows the total number of steps.
userform.$el.on('userform.form.conditionalstep', () => {
// Update the step numbers on the buttons.
const $visibleButtons = self.$buttons.filter(':visible');
$visibleButtons.each((i, button) => {
$(button).text(i + 1);
});
// Update the actual progress bar.
self.$el.find('.progress-bar').attr('aria-valuemax', $visibleButtons.length);
// Update any text that uses the total number of steps.
self.$el.find('.total-step-number').text($visibleButtons.length);
});
// Spaces out the steps below progress bar evenly
this.$jsAlign.each((index, button) => {
const $button = $(button);
const leftPercent = (100 / (self.$jsAlign.length - 1)) * index;
const leftPercentCssValue = `${leftPercent}%`;
const buttonOffset = -1 * ($button.innerWidth() / 2);
$button.css({
left: leftPercentCssValue,
marginLeft: buttonOffset,
});
// First and last buttons are kept within userform-progress container
if (index === self.$jsAlign.length - 1) {
$button.css({ marginLeft: buttonOffset * 2 });
} else if (index === 0) {
$button.css({ marginLeft: 0 });
}
});
return this;
} }
/** update(stepId) {
* @func ProgressBar.update const stepNumber = this.userForm.getCurrentStepID() + 1;
* @param {number} stepID - Zero based index of the new step. const newStep = this.userForm.getStep(stepId);
* @desc Update the progress element to show a new step. const newStepElement = newStep.step;
*/ let barWidth = (stepId / (this.buttons.length - 1)) * 100;
ProgressBar.prototype.update = function update(stepID) {
const $newStepElement = $(this.$el.parent('.userform').find('.form-step')[stepID]);
let stepNumber = 0;
let barWidth = (stepID / (this.$buttons.length - 1)) * 100;
// Set the current step number. this.currentStepNumber.innerText = stepNumber;
this.$buttons.each((i, button) => {
if (i > stepID) { this.dom.querySelectorAll('[aria-valuenow]').forEach((e) => {
// Break the loop e.setAttribute('aria-valuenow', stepNumber);
return false; });
this.buttons.forEach((button) => {
const btn = button;
const parent = btn.parentNode;
if (parseInt(btn.getAttribute('data-step'), 10) === stepNumber
&& isVisible(btn)) {
parent.classList.add('current');
parent.classList.add('viewed');
btn.disabled = false;
} }
parent.classList.remove('current');
if ($(button).is(':visible')) {
stepNumber += 1;
}
return true;
}); });
// Update elements that contain the current step number. this.progressTitle.innerText = newStepElement.getAttribute('data-title');
this.$el.find('.current-step-number').each((i, element) => {
$(element).text(stepNumber);
});
// Update aria attributes.
this.$el.find('[aria-valuenow]').each((i, element) => {
$(element).attr('aria-valuenow', stepNumber);
});
// Update the CSS classes on step buttons.
this.$buttons.each((i, element) => {
const $element = $(element);
const $item = $element.parent();
if (parseInt($element.data('step'), 10) === stepNumber && $element.is(':visible')) {
$item.addClass('current viewed');
$element.removeAttr('disabled');
return;
}
$item.removeClass('current');
});
// Update the progress bar's title with the new step's title.
this.$el.siblings('.progress-title').text($newStepElement.data('title'));
// Update the width of the progress bar. // Update the width of the progress bar.
barWidth = barWidth ? `${barWidth}%` : ''; barWidth = barWidth ? `${barWidth}%` : '';
this.$el.find('.progress-bar').width(barWidth); this.dom.querySelector('.progress-bar').style.width = barWidth;
}; }
}
/** class FormStep {
* @func FormActions constructor(step, userForm) {
* @constructor this.step = step;
* @param {object} element this.userForm = userForm;
* @desc Creates the navigation and actions (Prev, Next, Submit buttons). this.viewed = false;
*/ this.buttonHolder = null;
function FormActions(element) { this.id = 0;
const self = this;
this.$el = element instanceof $ ? element : $(element); this.init();
const $elFormItself = this.$el.closest('.userform');
this.userformInstance = $elFormItself.data('inst');
this.$prevButton = this.$el.find('.step-button-prev');
this.$nextButton = this.$el.find('.step-button-next');
// Show the buttons.
this.$prevButton.parent().attr('aria-hidden', false).show();
this.$nextButton.parent().attr('aria-hidden', false).show();
// Scroll up to the next page...
const scrollUpFx = function () {
const scrollTop = $elFormItself.offset();
$('html, body').animate({ scrollTop: scrollTop.top }, 'slow');
};
// Bind the step navigation event listeners.
this.$prevButton.on('click', (e) => {
e.preventDefault();
scrollUpFx();
self.$el.trigger('userform.action.prev');
});
this.$nextButton.on('click', (e) => {
e.preventDefault();
scrollUpFx();
self.$el.trigger('userform.action.next');
});
// Listen for changes to the current form step, or conditional pages,
// so we can show hide buttons appropriately.
this.userformInstance.$el.on('userform.form.changestep userform.form.conditionalstep', () => {
self.update();
});
return this;
} }
/** init() {
* @func FormActions.update const id = this.getHTMLId();
* @param {number} stepID - Zero based ID of the current step. this.buttonHolder = document.querySelector(`.step-button-wrapper[data-for='${id}']`);
* @desc Updates the form actions element to reflect the current state of the page. if (this.buttonHolder) {
*/ ['userform.field.hide', 'userform.field.show'].forEach((action) => {
FormActions.prototype.update = function update() { this.buttonHolder.addEventListener(action, () => {
const numberOfSteps = this.userformInstance.steps.length; this.userForm.dom.trigger('userform.form.conditionalstep');
const stepID = this.userformInstance.currentStep ? this.userformInstance.currentStep.id : 0; });
});
}
}
setId(id) {
this.id = id;
}
getHTMLId() {
return this.step.getAttribute('id');
}
show() {
this.step.setAttribute('aria-hidden', false);
this.step.classList.remove('hide');
this.step.classList.add('viewed');
this.viewed = true;
}
hide() {
this.step.setAttribute('aria-hidden', true);
this.step.classList.add('hide');
}
conditionallyHidden() {
const button = this.buttonHolder.querySelector('button');
return !(button.style.display !== 'none' && button.visibility !== 'hidden' && !button.classList.contains('hide'));
}
getValidatorType(input) {
if (input.getAttribute('type') === 'email') {
return 'email';
}
if (input.getAttribute('type') === 'date') {
return 'date';
}
if (input.getAttribute('type') === 'file') {
return 'object';
}
if (input.classList.contains('numeric') || input.getAttribute('type') === 'numeric') {
return 'number';
}
return 'string';
}
getValidatorMessage(input) {
if (input.getAttribute('data-msg-required')) {
return input.getAttribute('data-msg-required');
}
return `${this.getFieldLabel(input)} is required`;
}
getHolderForField(input) {
return window.closest(input, '.field');
}
getFieldLabel(input) {
const holder = this.getHolderForField(input);
if (holder) {
const label = holder.querySelector('label.left, legend.left');
if (label) {
return label.innerText;
}
}
return input.getAttribute('name');
}
isInputNumeric(input) {
return this.getValidatorType(input) === 'number';
}
isInputFile(input) {
return input.getAttribute('type') === 'file';
}
getInputByName(name) {
return this.step.querySelector(`input[name="${name}"]`);
}
getValidationsDescriptors(onlyDirty) {
const descriptors = {};
const fields = this.step.querySelectorAll('input, textarea, select');
fields.forEach((field) => {
if (isVisible(field)
&& (!onlyDirty || (onlyDirty && field.classList.contains(FOCUSED_CLASS)))
) {
const label = this.getFieldLabel(field);
const holder = this.getHolderForField(field);
descriptors[field.getAttribute('name')] = {
title: label,
type: this.getValidatorType(field),
required: holder.classList.contains('requiredField'),
message: this.getValidatorMessage(field)
};
const min = field.getAttribute('data-rule-min');
const max = field.getAttribute('data-rule-max');
if (min !== null || max !== null) {
descriptors[field.getAttribute('name')].asyncValidator = function numericValidator(rule, value) {
return new Promise((resolve, reject) => {
if (min !== null && value < min) {
reject(`${label} cannot be less than ${min}`);
} else if (max !== null && value > max) {
reject(`${label} cannot be greater than ${max}`);
} else {
resolve();
}
});
};
}
const minL = field.getAttribute('data-rule-minlength');
const maxL = field.getAttribute('data-rule-maxlength');
if (minL !== null || maxL !== null) {
descriptors[field.getAttribute('name')].asyncValidator = function lengthValidator(rule, value) {
return new Promise((resolve, reject) => {
if (minL !== null && value.length < minL) {
reject(`${label} cannot be shorter than ${minL}`);
} else if (maxL !== null && value.length > maxL) {
reject(`${label} cannot be longer than ${maxL}`);
} else {
resolve();
}
});
};
}
}
});
return descriptors;
}
validate(onlyDirty) {
const descriptors = this.getValidationsDescriptors(onlyDirty);
if (Object.keys(descriptors).length) {
const validator = new Schema(descriptors);
const formData = new FormData(this.userForm.dom);
const data = {};
formData.forEach((value, key) => {
let sanitised = value;
const input = this.getInputByName(key);
if (sanitised && input && this.isInputNumeric(input)) {
sanitised = parseFloat(sanitised); // because FormData reads all the values as strings
}
data[key] = sanitised;
});
// now check for unselected checkboxes and radio buttons
const selectableFields = this.step.querySelectorAll('input[type="radio"],input[type="checkbox"]');
selectableFields.forEach((selectableField) => {
const fieldName = selectableField.getAttribute('name');
if (typeof data[fieldName] === 'undefined') {
data[fieldName] = '';
}
});
const promise = new Promise((resolve, reject) => {
validator.validate(data, (errors) => {
if (errors && errors.length) {
this.displayErrorMessages(errors);
reject(errors);
} else {
this.displayErrorMessages([]);
resolve();
}
});
});
return promise;
}
const promise = new Promise((resolve) => {
resolve();
});
return promise;
}
enableLiveValidation() {
const fields = this.step.querySelectorAll('input, textarea, select');
fields.forEach((field) => {
field.addEventListener('focusin', () => {
field.classList.add(FOCUSED_CLASS);
});
field.addEventListener('change', () => {
field.classList.add(DIRTY_CLASS);
});
field.addEventListener('focusout', () => {
this.validate(true).then(() => {
}).catch(() => {
});
});
});
}
displayErrorMessages(errors) {
const errorIds = [];
errors.forEach((error) => {
const fieldHolder = this.userForm.dom.querySelector(`#${error.field}`);
if (fieldHolder) {
let errorLabel = fieldHolder.querySelector('span.error');
if (!errorLabel) {
errorLabel = document.createElement('span');
errorLabel.classList.add('error');
errorLabel.setAttribute('data-id', error.field);
}
errorIds.push(error.field);
errorLabel.innerHTML = error.message;
fieldHolder.append(errorLabel);
}
});
// remove any thats not required
const messages = this.step.querySelectorAll('span.error');
messages.forEach((mesasge) => {
const id = mesasge.getAttribute('data-id');
if (errorIds.indexOf(id) === -1) {
mesasge.remove();
}
});
}
}
class FormActions {
constructor(dom, userForm) {
this.dom = dom;
this.userForm = userForm;
this.prevButton = dom.querySelector('.step-button-prev');
this.nextButton = dom.querySelector('.step-button-next');
this.init();
}
init() {
this.prevButton.addEventListener('click', (e) => {
e.preventDefault();
// scrollUpFx();
window.triggerDispatchEvent(this.userForm.dom, 'userform.action.prev');
});
this.nextButton.addEventListener('click', (e) => {
e.preventDefault();
// scrollUpFx();
window.triggerDispatchEvent(this.userForm.dom, 'userform.action.next');
});
this.update();
this.userForm.dom.addEventListener('userform.form.changestep', () => {
this.update();
});
this.userForm.dom.addEventListener('userform.form.conditionalstep', () => {
this.update();
});
}
update() {
const numberOfSteps = this.userForm.getNumberOfSteps();
const stepId = this.userForm.getCurrentStepID();
let i = null; let i = null;
let lastStep = null; let lastStep = null;
// Update the "Prev" button.
this.$el.find('.step-button-prev')[stepID === 0 ? 'hide' : 'show']();
// Find last step, skipping hidden ones
for (i = numberOfSteps - 1; i >= 0; i--) { for (i = numberOfSteps - 1; i >= 0; i--) {
lastStep = this.userformInstance.steps[i]; lastStep = this.userForm.getStep(i);
// Skip if step is hidden
if (!lastStep.conditionallyHidden()) { if (!lastStep.conditionallyHidden()) {
// Update the "Next" button. if (stepId >= i) {
this.$el.find('.step-button-next')[stepID >= i ? 'hide' : 'show'](); this.nextButton.parentNode.classList.add('hide');
} else {
this.nextButton.parentNode.classList.remove('hide');
}
// Update the "Actions". if (stepId > 0 && stepId <= i) {
this.$el.find('.btn-toolbar')[stepID >= i ? 'show' : 'hide'](); this.prevButton.parentNode.classList.remove('hide');
} else {
this.prevButton.parentNode.classList.add('hide');
}
if (stepId >= i) {
this.dom.querySelector('.btn-toolbar').classList.remove('hide');
} else {
this.dom.querySelector('.btn-toolbar').classList.add('hide');
}
// Stop processing last step
break; break;
} }
} }
}; }
}
/** class UserForm {
* @func UserForm constructor(form) {
* @constructor this.dom = form;
* @param {object} element this.CONSTANTS = {}; // Settings that come from the CMS.
* @return {object} - The UserForm instance.
* @desc The form
*/
function UserForm(element) {
const self = this;
this.$el = element instanceof $ ? element : $(element);
this.steps = []; this.steps = [];
this.progressBar = null;
this.actions = null;
this.currentStep = null;
// Add an error container which displays a list of invalid steps on form submission. this.CONSTANTS.ENABLE_LIVE_VALIDATION = this.dom.getAttribute('livevalidation') !== undefined;
this.errorContainer = new ErrorContainer(this.$el.children('.error-container')); this.CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP = this.dom.getAttribute('toperrors') !== undefined;
this.CONSTANTS.ENABLE_ARE_YOU_SURE = this.dom.getAttribute('enableareyousure') !== undefined;
// Listen for events triggered by form steps.
this.$el.on('userform.action.prev', () => {
self.prevStep();
});
this.$el.on('userform.action.next', () => {
self.nextStep();
});
// Listen for events triggered by the progress bar.
this.$el.find('.userform-progress').on('userform.progress.changestep', (e, stepNumber) => {
self.jumpToStep(stepNumber - 1);
});
// When a field becomes valid, remove errors from the error container.
this.$el.on('userform.form.valid', (e, fieldId) => {
self.errorContainer.removeStepLink(fieldId);
});
this.$el.validate(this.validationOptions);
// Ensure checkbox groups are validated correctly
this.$el.find('.optionset.requiredField input').each((a, field) => {
$(field).rules('add', {
required: true,
});
});
return this;
} }
/* init() {
* Default options for step validation. These get extended in main(). this.initialiseFormSteps();
*/
UserForm.prototype.validationOptions = {
ignore: ':hidden,ul',
errorClass: 'error',
errorElement: 'span',
errorPlacement: (error, element) => {
error.addClass('message');
if (element.is(':radio') || element.parents('.checkboxset').length > 0) { if (this.CONSTANTS.ENABLE_ARE_YOU_SURE) {
error.appendTo(element.closest('.middleColumn, .field')); this.initAreYouSure();
} else if (element.parents('.checkbox').length > 0) { }
error.appendTo(element.closest('.field')); }
} else {
error.insertAfter(element); initialiseFormSteps() {
const steps = this.dom.querySelectorAll('.form-step');
steps.forEach((stepDom) => {
const step = new FormStep(stepDom, this);
step.hide();
this.addStep(step);
if (this.CONSTANTS.ENABLE_LIVE_VALIDATION) {
step.enableLiveValidation();
} }
}, });
invalidHandler: (event, validator) => {
// setTimeout 0 so it runs after errorPlacement
setTimeout(() => {
validator.currentElements.filter('.error').first().focus();
}, 0);
},
// Callback for handling the actual submit when the form is valid.
// Submission in the jQuery.validate sence is handled at step level.
// So when the final step is submitted we have to also check all previous steps are valid.
submitHandler: (form) => {
let isValid = true;
const userform = $(form).closest('.userform').data('inst');
// Validate the current step this.setCurrentStep(this.steps[0]);
if (userform.currentStep) {
userform.currentStep.valid = $(form).valid();
}
// Check for invalid previous steps. const progressBarDom = this.dom.querySelector('.userform-progress');
$.each(userform.steps, (i, step) => { if (progressBarDom) {
if (!step.valid && !step.conditionallyHidden()) { this.progressBar = new ProgressBar(progressBarDom, this);
isValid = false; }
userform.errorContainer.addStepLink(step);
const stepNavigation = this.dom.querySelector('.step-navigation');
if (stepNavigation) {
this.formActions = new FormActions(stepNavigation, this);
this.formActions.update();
}
this.setUpPing();
this.dom.addEventListener('userform.action.next', () => {
this.nextStep();
});
this.dom.addEventListener('userform.action.prev', () => {
this.prevStep();
});
this.dom.addEventListener('submit', (e) => {
this.validateForm(e);
});
}
validateForm(e) {
e.preventDefault();
this.currentStep.validate()
.then((errors) => {
if (!errors) {
this.dom.submit();
} }
}); })
.catch(() => {});
}
if (isValid) { setCurrentStep(step) {
// Remove required attributes on hidden fields
const hiddenInputs = $(form).find('.field.requiredField.hide input');
if (hiddenInputs.length > 0) {
hiddenInputs.removeAttr('required aria-required data-rule-required').valid();
}
// When using the "are you sure?" plugin, ensure the form immediately submits.
$(form).removeClass('dirty');
form.submit();
userform.$el.trigger('userform.form.submit');
} else {
userform.errorContainer.show();
}
},
// When a field becomes valid.
success: (error) => {
const userform = $(error).closest('.userform').data('inst');
const errorId = $(error).attr('id');
const fieldId = errorId.substr(0, errorId.indexOf('-error')).replace(/[\\[\\]]/, '');
// Remove square brackets since jQuery.validate.js uses idOrName,
// which breaks further on when using a selector that end with
// square brackets.
error.remove();
// Pass the field's ID with the event
userform.$el.trigger('userform.form.valid', [fieldId]);
},
};
/**
* @func UserForm.addStep
* @param {object} step - An instance of FormStep.
* @desc Adds a step to the UserForm.
*/
UserForm.prototype.addStep = function addStep(step) {
// Make sure we're dealing with a form step. // Make sure we're dealing with a form step.
if (!(step instanceof FormStep)) { if (!(step instanceof FormStep)) {
return; return;
} }
// eslint-disable-next-line no-param-reassign
step.id = this.steps.length;
this.steps.push(step);
};
/**
* @func UserForm.setCurrentStep
* @param {object} step - An instance of FormStep.
* @desc Sets the step the user is currently on.
*/
UserForm.prototype.setCurrentStep = function setCurrentStep(step) {
// Make sure we're dealing with a form step.
if (!(step instanceof FormStep)) {
return;
}
this.currentStep = step; this.currentStep = step;
this.currentStep.show(); this.currentStep.show();
}
// Record the user has viewed the step. addStep(step) {
this.currentStep.viewed = true; if (!(step instanceof FormStep)) {
this.currentStep.$el.addClass('viewed'); return;
}; }
step.setId(this.steps.length);
this.steps.push(step);
}
/** getNumberOfSteps() {
* @func UserForm.jumpToStep return this.steps.length;
* @param {number} stepNumber }
* @param {boolean} [direction] - Defaults to forward (true).
* @desc Jumps to a specific form step. getCurrentStepID() {
*/ return this.currentStep.id ? this.currentStep.id : 0;
UserForm.prototype.jumpToStep = function jumpToStep(stepNumber, direction) { }
getStep(index) {
return this.steps[index];
}
nextStep() {
this.currentStep.validate().then(() => {
this.jumpToStep(this.steps.indexOf(this.currentStep) + 1, true);
}).catch(() => {});
}
prevStep() {
this.jumpToStep(this.steps.indexOf(this.currentStep) - 1, true);
}
jumpToStep(stepNumber, direction) {
const targetStep = this.steps[stepNumber]; const targetStep = this.steps[stepNumber];
let isValid = false;
const forward = direction === undefined ? true : direction; const forward = direction === undefined ? true : direction;
// Make sure the target step exists.
if (targetStep === undefined) { if (targetStep === undefined) {
return; return;
} }
// Make sure the step we're trying to set as current is not
// hidden by custom display rules. If it is then jump to the next step.
if (targetStep.conditionallyHidden()) { if (targetStep.conditionallyHidden()) {
if (forward) { if (forward) {
this.jumpToStep(stepNumber + 1, direction); this.jumpToStep(stepNumber + 1, direction);
} else { } else {
this.jumpToStep(stepNumber - 1, direction); this.jumpToStep(stepNumber - 1, direction);
} }
return; return;
} }
// Validate the form. if (this.currentStep) {
// This well effectivly validate the current step and not the entire form. this.currentStep.hide();
// This is because hidden fields are excluded from validation, and all fields
// on all other steps, are currently hidden.
isValid = this.$el.valid();
// Set the 'valid' property on the current step.
this.currentStep.valid = isValid;
// Users can navigate to step's they've already viewed even if the current step is invalid.
if (isValid === false && targetStep.viewed === false) {
return;
} }
this.currentStep.hide();
this.setCurrentStep(targetStep); this.setCurrentStep(targetStep);
this.$el.trigger('userform.form.changestep', [targetStep.id]); window.triggerDispatchEvent(this.dom, 'userform.form.changestep', {
}; stepId: targetStep.id
/**
* @func UserForm.nextStep
* @desc Advances the form to the next step.
*/
UserForm.prototype.nextStep = function nextStep() {
this.jumpToStep(this.steps.indexOf(this.currentStep) + 1, true);
};
/**
* @func UserForm.prevStep
* @desc Goes back one step (not bound to browser history).
*/
UserForm.prototype.prevStep = function prevStep() {
this.jumpToStep(this.steps.indexOf(this.currentStep) - 1, false);
};
/**
* @func main
* @desc Bootstraps the front-end.
*/
function main(index, userformElement) {
const $userform = $(userformElement);
// If there's no userform, do nothing.
if ($userform.length === 0) {
return;
}
CONSTANTS.ENABLE_LIVE_VALIDATION = $userform.data('livevalidation') !== undefined;
CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP = $userform.data('toperrors') !== undefined;
// Extend the default validation options with conditional options
// that are set by the user in the CMS.
if (CONSTANTS.ENABLE_LIVE_VALIDATION === false) {
$.extend(UserForm.prototype.validationOptions, {
onfocusout: false,
});
}
if (CONSTANTS.DISPLAY_ERROR_MESSAGES_AT_TOP) {
$.extend(UserForm.prototype.validationOptions, {
// Callback for custom code when an invalid form / step is submitted.
invalidHandler: (event, validator) => {
$userform.trigger('userform.form.error', [validator]);
},
onfocusout: false,
});
}
// Display all the things that are hidden when JavaScript is disabled.
$userform.find('.userform-progress, .step-navigation').attr('aria-hidden', false).show();
// Extend classes with common functionality.
$.extend(FormStep.prototype, commonMixin);
$.extend(ErrorContainer.prototype, commonMixin);
const userform = new UserForm($userform);
$userform.data('inst', userform);
// Conditionally hide field labels and use HTML5 placeholder instead.
if (CONSTANTS.HIDE_FIELD_LABELS) {
$userform.find('label.left').each(() => {
const $label = $(this);
$(`[name="${$label.attr('for')}"]`).attr('placeholder', $label.text());
$label.remove();
});
}
// Initialise the form steps.
userform.$el.find('.form-step').each((i, element) => {
const step = new FormStep(element);
userform.addStep(step);
}); });
userform.setCurrentStep(userform.steps[0]);
// Initialise actions and progressbar
const $progressEl = $userform.find('.userform-progress');
if ($progressEl.length) {
const progressBar = new ProgressBar($progressEl);
progressBar.update(0);
}
const $formActionsEl = $userform.find('.step-navigation');
if ($formActionsEl.length) {
const formActions = new FormActions($formActionsEl);
formActions.update();
}
// Enable jQuery UI datepickers
$(document).on('click', 'input.text[data-showcalendar]', () => {
const $element = $(this);
$element.ssDatepicker();
if ($element.data('datepicker')) {
$element.datepicker('show');
}
});
// Make sure the form doesn't expire on the user. Pings every 3 mins.
setInterval(() => {
$.ajax({ url: 'UserDefinedFormController/ping' });
}, 180 * 1000);
// Bind a confirmation message when navigating away from a partially completed form.
if (typeof $userform.areYouSure !== 'undefined') {
$userform.areYouSure({
message: i18n._t('UserForms.LEAVE_CONFIRMATION', 'You have unsaved changes!'),
});
}
} }
$('.userform').each(main); setUpPing() {
// Make sure the form doesn't expire on the user. Pings every 3 mins.
window.setInterval(() => {
fetch('UserDefinedFormController/ping');
}, 180 * 1000);
}
doConfirm(e) {
const dirtyFields = this.dom.querySelectorAll(`.${DIRTY_CLASS}`);
if (dirtyFields.length === 0) {
return true;
}
if (navigator.userAgent.toLowerCase().match(/msie|chrome/)) {
if (window.hasUserFormsPropted) {
return true;
}
window.hasUserFormsPropted = true;
window.setTimeout(
() => {
window.hasUserFormsPropted = false;
},
900
);
}
e.preventDefault();
if (typeof window.i18n !== 'undefined') {
event.returnValue = window.i18n._t('UserForms.LEAVE_CONFIRMATION', 'You have unsaved changes!');
} else {
event.returnValue = 'You have unsaved changes!';
}
return true;
}
initAreYouSure() {
const confirmFunction = this.doConfirm.bind(this);
this.dom.addEventListener('submit', (e) => {
window.removeEventListener('beforeunload', confirmFunction);
})
window.addEventListener('beforeunload', confirmFunction);
}
}
document.addEventListener('DOMContentLoaded', () => {
const forms = document.querySelectorAll('form.userform');
forms.forEach((form) => {
const userForm = new UserForm(form);
userForm.init();
});
}); });

View File

@ -15,6 +15,10 @@
.step-buttons { .step-buttons {
margin-left: 0; margin-left: 0;
padding-left: 0;
width: 100%;
display: flex;
justify-content: space-between;
position: relative; position: relative;
} }
@ -28,8 +32,6 @@
} }
.step-button-jump { .step-button-jump {
position: absolute;
top: 0;
opacity: .7; opacity: .7;
} }
} }

View File

@ -79,22 +79,11 @@ class UserDefinedFormController extends PageController
// load the jquery // load the jquery
if (!$page->config()->get('block_default_userforms_js')) { if (!$page->config()->get('block_default_userforms_js')) {
Requirements::javascript('silverstripe/userforms:client/dist/js/jquery.min.js');
Requirements::javascript(
'silverstripe/userforms:client/dist/js/jquery-validation/jquery.validate.min.js'
);
Requirements::javascript('silverstripe/admin:client/dist/js/i18n.js'); Requirements::javascript('silverstripe/admin:client/dist/js/i18n.js');
Requirements::add_i18n_javascript('silverstripe/userforms:client/lang'); Requirements::add_i18n_javascript('silverstripe/userforms:client/lang');
Requirements::javascript('silverstripe/userforms:client/dist/js/userforms.js'); Requirements::javascript('silverstripe/userforms:client/dist/js/userforms.js');
$this->addUserFormsValidatei18n(); $this->addUserFormsValidatei18n();
// Bind a confirmation message when navigating away from a partially completed form.
if ($page::config()->get('enable_are_you_sure')) {
Requirements::javascript(
'silverstripe/userforms:client/dist/js/jquery.are-you-sure/jquery.are-you-sure.js'
);
}
} }
} }
@ -177,10 +166,16 @@ class UserDefinedFormController extends PageController
*/ */
public function Form() public function Form()
{ {
$page = $this->data();
$form = UserForm::create($this, 'Form_' . $this->ID); $form = UserForm::create($this, 'Form_' . $this->ID);
/** @skipUpgrade */ /** @skipUpgrade */
$form->setFormAction(Controller::join_links($this->Link(), 'Form')); $form->setFormAction(Controller::join_links($this->Link(), 'Form'));
$this->generateConditionalJavascript(); $this->generateConditionalJavascript();
if ($page::config()->get('enable_are_you_sure')) {
$form->setAttribute('enableareyousure', 1);
}
return $form; return $form;
} }
@ -212,14 +207,35 @@ class UserDefinedFormController extends PageController
$rules .= $this->buildWatchJS($watch); $rules .= $this->buildWatchJS($watch);
} }
// add the custom scripts thats used by the steps.
Requirements::customScript(<<<JS
function closest(el, sel) {
while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel)));
return el;
}
window.closest = closest;
function triggerDispatchEvent(element, eventName, arg) {
const event = new CustomEvent(eventName, {
detail: arg
});
element.dispatchEvent(event);
}
window.triggerDispatchEvent = triggerDispatchEvent;
JS
);
// Only add customScript if $default or $rules is defined // Only add customScript if $default or $rules is defined
if ($rules) { if ($rules) {
Requirements::customScript(<<<JS Requirements::customScript(<<<JS
(function($) { document.addEventListener("DOMContentLoaded", function() {
$(document).ready(function() { var form = document.querySelector('form.userform');
if (form) {
{$rules} {$rules}
}); }
})(jQuery); })
JS JS
, 'UserFormsConditional-' . $form->ID); , 'UserFormsConditional-' . $form->ID);
} }
@ -626,10 +642,11 @@ JS
*/ */
protected function buildWatchJS($watch) protected function buildWatchJS($watch)
{ {
$result = ''; $result = '';
foreach ($watch as $key => $rule) { foreach ($watch as $key => $rule) {
$events = implode(' ', $rule['events']); $events = implode(',', $rule['events']);
$selectors = implode(', ', $rule['selectors']); $selectors = implode(',', $rule['selectors']);
$conjunction = $rule['conjunction']; $conjunction = $rule['conjunction'];
$operations = implode(" {$conjunction} ", $rule['operations']); $operations = implode(" {$conjunction} ", $rule['operations']);
$target = $rule['targetFieldID']; $target = $rule['targetFieldID'];
@ -638,16 +655,21 @@ JS
$result .= <<<EOS $result .= <<<EOS
\n \n
$('.userform').on('{$events}', "{$events}".split(',').forEach(function(event) {
"{$selectors}", form.addEventListener(event, function(e) {
function (){
if ({$operations}) { "{$selectors}".split(',').forEach(function(selector) {
$('{$target}').{$rule['view']}; if (e.target.matches(selector)) {
{$holder}.{$rule['view']}.trigger('{$rule['holder_event']}'); if ({$operations}) {
} else { document.querySelector('{$target}').{$rule['view']};
$('{$target}').{$rule['opposite']}; triggerDispatchEvent({$holder}, '{$rule['holder_event']}');
{$holder}.{$rule['opposite']}.trigger('{$rule['holder_event_opposite']}'); } else {
} document.querySelector('{$target}').{$rule['opposite']};
triggerDispatchEvent({$holder}, '{$rule['holder_event_opposite']}');
}
}
})
});
}); });
EOS; EOS;
if ($isFormStep) { if ($isFormStep) {
@ -657,7 +679,7 @@ EOS;
// The HTML for the FormStep buttons is defined in UserFormProgress.ss // The HTML for the FormStep buttons is defined in UserFormProgress.ss
$id = str_replace('#', '', $target ?? ''); $id = str_replace('#', '', $target ?? '');
$result .= <<<EOS $result .= <<<EOS
$('.step-button-wrapper[data-for="{$id}"]').addClass('hide'); document.querySelector('.step-button-wrapper[data-for="{$id}"]').addClass('hide');
EOS; EOS;
} else { } else {
// If a field's initial state is set to be hidden, a '.hide' class will be added to the field as well // If a field's initial state is set to be hidden, a '.hide' class will be added to the field as well
@ -666,7 +688,9 @@ EOS;
// though we need to ensure we don't do this on FormSteps (page breaks) otherwise we'll mistakenly // though we need to ensure we don't do this on FormSteps (page breaks) otherwise we'll mistakenly
// target fields contained within the formstep // target fields contained within the formstep
$result .= <<<EOS $result .= <<<EOS
$("{$target}").find('.hide').removeClass('hide'); if (document.querySelector('{$target}').querySelector('.hide')) {
document.querySelector('{$target}').querySelector('.hide').classList.remove('hide');
}
EOS; EOS;
} }
} }

View File

@ -160,7 +160,7 @@ class EditableCustomRule extends DataObject
// is this field a special option field // is this field a special option field
$checkboxField = $formFieldWatch->isCheckBoxField(); $checkboxField = $formFieldWatch->isCheckBoxField();
$radioField = $formFieldWatch->isRadioField(); $radioField = $formFieldWatch->isRadioField();
$target = sprintf('$("%s")', $formFieldWatch->getSelectorFieldOnly()); $target = $formFieldWatch->getSelectorFieldOnly();
$fieldValue = Convert::raw2js($this->FieldValue); $fieldValue = Convert::raw2js($this->FieldValue);
$conditionOptions = [ $conditionOptions = [
@ -174,7 +174,9 @@ class EditableCustomRule extends DataObject
switch ($this->ConditionOption) { switch ($this->ConditionOption) {
case 'IsNotBlank': case 'IsNotBlank':
case 'IsBlank': case 'IsBlank':
$expression = ($checkboxField || $radioField) ? "!{$target}.is(\":checked\")" : "{$target}.val() == ''"; $expression = ($checkboxField || $radioField)
? sprintf("document.querySelector(\"%s:checked\") !== null", $target)
: "document.querySelector(\"{$target}\").value == ''";
if ((string) $this->ConditionOption === 'IsNotBlank') { if ((string) $this->ConditionOption === 'IsNotBlank') {
//Negate //Negate
$expression = "!({$expression})"; $expression = "!({$expression})";
@ -185,9 +187,9 @@ class EditableCustomRule extends DataObject
if ($checkboxField) { if ($checkboxField) {
if ($formFieldWatch->isCheckBoxGroupField()) { if ($formFieldWatch->isCheckBoxGroupField()) {
$expression = sprintf( $expression = sprintf(
"$.inArray('%s', %s.filter(':checked').map(function(){ return $(this).val();}).get()) > -1", '[...document.querySelectorAll("%s:checked")].map(function(i) { return i ? i.getAttribute("value") : null; }).indexOf(\'%s\') > -1',
$target,
$fieldValue, $fieldValue,
$target
); );
} else { } else {
$expression = "{$target}.prop('checked')"; $expression = "{$target}.prop('checked')";
@ -195,12 +197,12 @@ class EditableCustomRule extends DataObject
} elseif ($radioField) { } elseif ($radioField) {
// We cannot simply get the value of the radio group, we need to find the checked option first. // We cannot simply get the value of the radio group, we need to find the checked option first.
$expression = sprintf( $expression = sprintf(
'%s.closest(".field, .control-group").find("input:checked").val() == "%s"', 'closest(document.querySelector("%s"), ".field, .control-group").querySelector("input:checked").value == "%s"',
$target, $target,
$fieldValue $fieldValue
); );
} else { } else {
$expression = sprintf('%s.val() == "%s"', $target, $fieldValue); $expression = sprintf('document.querySelector("%s").value == "%s"', $target, $fieldValue);
} }
if ((string) $this->ConditionOption === 'ValueNot') { if ((string) $this->ConditionOption === 'ValueNot') {
@ -213,7 +215,7 @@ class EditableCustomRule extends DataObject
case 'ValueGreaterThan': case 'ValueGreaterThan':
case 'ValueGreaterThanEqual': case 'ValueGreaterThanEqual':
$expression = sprintf( $expression = sprintf(
'%s.val() %s parseFloat("%s")', 'document.querySelector("%s").value %s parseFloat("%s")',
$target, $target,
$conditionOptions[$this->ConditionOption], $conditionOptions[$this->ConditionOption],
$fieldValue $fieldValue
@ -299,11 +301,11 @@ class EditableCustomRule extends DataObject
*/ */
public function toggleDisplayText($initialState, $invert = false) public function toggleDisplayText($initialState, $invert = false)
{ {
$action = strtolower($initialState ?? '') === 'hide' ? 'removeClass' : 'addClass'; $action = strtolower($initialState ?? '') === 'hide' ? 'remove' : 'add';
if ($invert) { if ($invert) {
$action = $action === 'removeClass' ? 'addClass' : 'removeClass'; $action = $action === 'remove' ? 'add' : 'remove';
} }
return sprintf('%s("hide")', $action); return sprintf('classList.%s("hide")', $action);
} }
/** /**

View File

@ -868,7 +868,7 @@ class EditableFormField extends DataObject
*/ */
public function getSelectorHolder() public function getSelectorHolder()
{ {
return sprintf('$("%s")', $this->getSelectorOnly()); return sprintf('document.querySelector("%s")', $this->getSelectorOnly());
} }
/** /**
@ -891,7 +891,7 @@ class EditableFormField extends DataObject
*/ */
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
{ {
return sprintf("$(%s)", $this->getSelectorFieldOnly()); return sprintf("document.querySelector(%s)", $this->getSelectorFieldOnly());
} }
/** /**

View File

@ -48,7 +48,7 @@ class EditableFieldGroup extends EditableFormField
public function getCMSFields() public function getCMSFields()
{ {
$this->beforeUpdateCMSFields(function (FieldList $fields) { $this->beforeUpdateCMSFields(function (FieldList $fields) {
$fields->removeByName(['MergeField', 'Default', 'Validation', 'DisplayRules']); $fields->removeByName(['MergeField', 'Default', 'Validation']);
}); });
return parent::getCMSFields(); return parent::getCMSFields();
@ -98,5 +98,10 @@ class EditableFieldGroup extends EditableFormField
if ($this->ExtraClass) { if ($this->ExtraClass) {
$field->addExtraClass($this->ExtraClass); $field->addExtraClass($this->ExtraClass);
} }
// if ShowOnLoad is false hide the field
if (!$this->ShowOnLoad) {
$field->addExtraClass($this->ShowOnLoadNice());
}
} }
} }

View File

@ -37,10 +37,10 @@
"node-dir": "^0.1.17" "node-dir": "^0.1.17"
}, },
"dependencies": { "dependencies": {
"babel-preset-es2016": "^6.24.1",
"jquery": "^3.5.0", "jquery": "^3.5.0",
"jquery-validation": "^1.19.5", "babel-preset-es2016": "^6.24.1",
"jquery.are-you-sure": "^1.9.0", "validator": "13.9.0",
"async-validator": "4.2.5",
"mime": "^1.4.1", "mime": "^1.4.1",
"qs": "^6.9.4", "qs": "^6.9.4",
"react": "^16.13.1", "react": "^16.13.1",

View File

@ -1,19 +1,21 @@
<% if $Steps.Count > 1 %> <% if $Steps.Count > 1 %>
<div id="userform-progress" class="userform-progress" aria-hidden="true" style="display:none;"> <div id="userform-progress" class="userform-progress" aria-hidden="true" style="display:none;">
<p>Page <span class="current-step-number">1</span> of <span class="total-step-number">$Steps.Count</span></p> <p>Page <span class="current-step-number">1</span> of <span class="total-step-number">$Steps.Count</span></p>
<div class="progress"> <div class="userform-progress-navigator">
<div class="progress-bar" role="progressbar" aria-valuenow="1" aria-valuemin="1" aria-valuemax="$Steps.Count"></div> <div class="progress">
</div> <div class="progress-bar" role="progressbar" aria-valuenow="1" aria-valuemin="1" aria-valuemax="$Steps.Count"></div>
<nav aria-label="Pages in this form"> </div>
<ul class="step-buttons"> <nav aria-label="Pages in this form">
<% loop $Steps %> <ul class="step-buttons">
<li class="step-button-wrapper<% if $IsFirst %> current<% end_if %>" data-for="$Name"> <% loop $Steps %>
<%-- Remove js-align class to remove javascript positioning --%> <li class="step-button-wrapper<% if $IsFirst %> current<% end_if %>" data-for="$Name">
<button class="step-button-jump js-align" disabled="disabled" data-step="$Pos"><% if $Top.ButtonText %>$Top.ButtonText <% end_if %>$Pos</button> <%-- Remove js-align class to remove javascript positioning --%>
</li> <button class="step-button-jump js-align" disabled="disabled" data-step="$Pos"><% if $Top.ButtonText %>$Top.ButtonText <% end_if %>$Pos</button>
<% end_loop %> </li>
</ul> <% end_loop %>
</nav> </ul>
</nav>
</div>
</div> </div>
<h2 class="progress-title"></h2> <h2 class="progress-title"></h2>
<% end_if %> <% end_if %>

View File

@ -4,12 +4,12 @@
If JavaScript is disabled multi-step forms are displayed as a single page If JavaScript is disabled multi-step forms are displayed as a single page
so the 'prev' and 'next' button are not used. These buttons are made visible via JavaScript. so the 'prev' and 'next' button are not used. These buttons are made visible via JavaScript.
--%> --%>
<li class="step-button-wrapper" aria-hidden="true" style="display:none;"> <li class="step-button-wrapper hide" aria-hidden="true">
<button class="step-button-prev"> <button class="step-button-prev">
<%t SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFormStep.STEP_PREV "Prev" %> <%t SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFormStep.STEP_PREV "Prev" %>
</button> </button>
</li> </li>
<li class="step-button-wrapper" aria-hidden="true" style="display:none;"> <li class="step-button-wrapper hide" aria-hidden="true">
<button class="step-button-next"> <button class="step-button-next">
<%t SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFormStep.STEP_NEXT "Next" %> <%t SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFormStep.STEP_NEXT "Next" %>
</button> </button>

View File

@ -1,4 +1,6 @@
<$Tag class="CompositeField $extraClass<% if $ColumnCount %> multicolumn<% end_if %>"<% if $Tag == 'fieldset' && $RightTitle %> aria-describedby="{$Name}_right_title"<% end_if %>> <$Tag id="{$Name}"
class="CompositeField $extraClass<% if $ColumnCount %> multicolumn<% end_if %>"<% if $Tag == 'fieldset' && $RightTitle %>
aria-describedby="{$Name}_right_title"<% end_if %>>
<% if $Tag == 'fieldset' && $Legend %> <% if $Tag == 'fieldset' && $Legend %>
<legend>$Legend</legend> <legend>$Legend</legend>
<% end_if %> <% end_if %>

View File

@ -28,15 +28,7 @@ const copyData = [
{ {
from: 'client/src/images', from: 'client/src/images',
to: 'images' to: 'images'
}, }
{
from: PATHS.MODULES + '/jquery/dist/jquery.min.js',
to: PATHS.DIST_JS
},
{
from: PATHS.MODULES + '/jquery.are-you-sure/jquery.are-you-sure.js',
to: PATHS.DIST_JS + '/jquery.are-you-sure/jquery.are-you-sure.js'
},
]; ];
/** /**
@ -60,7 +52,6 @@ const addMinFiles = (from, to) => {
}); });
}; };
addMinFiles('/jquery-validation/dist', '/jquery-validation');
const config = [ const config = [

View File

@ -463,6 +463,11 @@ async-limiter@~1.0.0:
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async-validator@4.2.5:
version "4.2.5"
resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339"
integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
async@^2.1.2: async@^2.1.2:
version "2.6.4" version "2.6.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
@ -4313,22 +4318,10 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jquery-validation@^1.19.5: jquery@^3.5.0:
version "1.19.5" version "3.6.4"
resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.19.5.tgz#557495b7cad79716897057c4447ad3cd76fda811" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f"
integrity sha512-X2SmnPq1mRiDecVYL8edWx+yTBZDyC8ohWXFhXdtqFHgU9Wd4KHkvcbCoIZ0JaSaumzS8s2gXSkP8F7ivg/8ZQ== integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==
jquery.are-you-sure@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/jquery.are-you-sure/-/jquery.are-you-sure-1.9.0.tgz#16aaa2c6080fa214f64027239ae3a34ad6a6265e"
integrity sha512-2r0uFx8CyAopjeHGOdvvwpFP921TnW1+v1uJXcAWQYHYGB1tryTDhQY+5u6HsVeMwbWiRTKVZFWnLaFpDvIqZQ==
dependencies:
jquery ">=1.4.2"
jquery@>=1.4.2, jquery@^3.5.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
js-base64@^2.1.8, js-base64@^2.1.9: js-base64@^2.1.8, js-base64@^2.1.9:
version "2.6.4" version "2.6.4"
@ -8447,6 +8440,11 @@ validate-npm-package-name@^3.0.0, validate-npm-package-name@~3.0.0:
dependencies: dependencies:
builtins "^1.0.3" builtins "^1.0.3"
validator@13.9.0:
version "13.9.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855"
integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==
vary@~1.1.2: vary@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"