From 94335c70e086edc1f7be36ceac259e7e5d000cc4 Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Tue, 21 May 2019 21:53:13 +0200 Subject: [PATCH 1/3] add missing checks for already initialized js utils in form.js --- static/js/utils/form.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/static/js/utils/form.js b/static/js/utils/form.js index da02abd5d..e610a7c0b 100644 --- a/static/js/utils/form.js +++ b/static/js/utils/form.js @@ -270,6 +270,10 @@ throw new Error('Auto Submit Button utility needs to be passed an element!'); } + if (element.classList.contains(AUTO_SUBMIT_BUTTON_INITIALIZED_CLASS)) { + return false; + } + // hide and mark initialized element.classList.add(AUTO_SUBMIT_BUTTON_HIDDEN_CLASS, AUTO_SUBMIT_BUTTON_INITIALIZED_CLASS); @@ -315,6 +319,10 @@ throw new Error('Auto Submit Input utility needs to be passed an element!'); } + if (element.classList.contains(AUTO_SUBMIT_INPUT_INITIALIZED_CLASS)) { + return false; + } + form = element.form; if (!form) { throw new Error('Could not determine associated form for auto submit input'); @@ -374,6 +382,10 @@ throw new Error('Form Error Remover utility needs to be passed an element!'); } + if (element.classList.contains(FORM_ERROR_REMOVER_INITIALIZED_CLASS)) { + return false; + } + // find form groups formGroups = Array.from(element.querySelectorAll(FORM_GROUP_SELECTOR)); @@ -428,6 +440,8 @@ var DATEPICKER_UTIL_NAME = 'datepicker'; var DATEPICKER_UTIL_SELECTOR = 'input[type="date"], input[type="time"], input[type="datetime-local"]'; + var DATEPICKER_INITIALIZED_CLASS = 'datepicker--initialized'; + var DATEPICKER_CONFIG = { "datetime-local": { enableTime: true, @@ -459,6 +473,10 @@ throw new Error('Datepicker utility needs to be passed an element!'); } + if (element.classList.contains(DATEPICKER_INITIALIZED_CLASS)) { + return false; + } + var flatpickrConfig = DATEPICKER_CONFIG[element.getAttribute("type")]; if (!flatpickrConfig) { @@ -467,6 +485,9 @@ flatpickrInstance = flatpickr(element, flatpickrConfig); + // mark initialized + element.classList.add(DATEPICKER_INITIALIZED_CLASS); + return { name: DATEPICKER_UTIL_NAME, element: element, From 9d49de4382d8c67e69bfcb9e2569243dd57430c5 Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Tue, 21 May 2019 21:54:32 +0200 Subject: [PATCH 2/3] add navigate away prompt js utility --- static/js/utils/form.js | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/static/js/utils/form.js b/static/js/utils/form.js index e610a7c0b..3cbbbd852 100644 --- a/static/js/utils/form.js +++ b/static/js/utils/form.js @@ -247,6 +247,86 @@ setup: interactiveFieldsetUtil, }); + /** + * + * Navigate Away Prompt Utility + * This utility asks the user if (s)he really wants to navigate away + * from a page containing a form if (s)he already touched an input. + * Form-Submits will not trigger the prompt. + * Utility will ignore forms that contain auto submit elements (buttons, inputs). + * + * Attribute: [none] + * (automatically setup on all form tags that dont automatically submit, see AutoSubmitButtonUtil) + * + * Example usage: + * (any page with a form) + */ + + var NAVIGATE_AWAY_PROMPT_UTIL_NAME = 'navigateAwayPrompt'; + var NAVIGATE_AWAY_PROMPT_UTIL_SELECTOR = 'form'; + + var NAVIGATE_AWAY_PROMPT_INITIALIZED_CLASS = 'navigate-away-prompt--initialized'; + + var navigateAwayPromptUtil = function(element) { + var touched = false; + var unloadDueToSubmit = false; + + function init() { + if (!element) { + throw new Error('Navigate Away Prompt utility needs to be passed an element!'); + } + + if (element.classList.contains(NAVIGATE_AWAY_PROMPT_INITIALIZED_CLASS)) { + return false; + } + + // ignore forms that get submitted automatically + if (element.querySelector(AUTO_SUBMIT_BUTTON_UTIL_SELECTOR) || element.querySelector(AUTO_SUBMIT_INPUT_UTIL_SELECTOR)) { + return false; + } + + window.addEventListener('beforeunload', beforeUnloadHandler); + + element.addEventListener('submit', function() { + unloadDueToSubmit = true; + }); + element.addEventListener('change', function() { + touched = true; + unloadDueToSubmit = false; + }); + + // mark initialized + element.classList.add(NAVIGATE_AWAY_PROMPT_INITIALIZED_CLASS); + + return { + name: NAVIGATE_AWAY_PROMPT_UTIL_NAME, + element: element, + destroy: function() { + window.removeEventListener('beforeunload', beforeUnloadHandler); + }, + }; + } + + function beforeUnloadHandler(event) { + if (!touched || unloadDueToSubmit) { + return false; + } + + // cancel the unload event. This is the standard to force the prompt to appear. + event.preventDefault(); + // for all non standard compliant browsers we return a truthy value to activate the prompt. + return true; + } + + return init(); + }; + + formUtilities.push({ + name: NAVIGATE_AWAY_PROMPT_UTIL_NAME, + selector: NAVIGATE_AWAY_PROMPT_UTIL_SELECTOR, + setup: navigateAwayPromptUtil, + }); + /** * * Auto Submit Button Utility From 8b4bdf9489fbe8fc40357003ed2f10d7e974c90d Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Tue, 21 May 2019 21:57:34 +0200 Subject: [PATCH 3/3] more documentation for navigate away pompt js util closes #377 --- static/js/utils/form.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/js/utils/form.js b/static/js/utils/form.js index 3cbbbd852..ed8b0fa9a 100644 --- a/static/js/utils/form.js +++ b/static/js/utils/form.js @@ -308,6 +308,8 @@ } function beforeUnloadHandler(event) { + // allow the event to happen if the form was not touched by the + // user or the unload event was initiated by a form submit if (!touched || unloadDueToSubmit) { return false; }