225 lines
6.8 KiB
JavaScript
225 lines
6.8 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
/**
|
|
*
|
|
* Mass Input Utility
|
|
* allows form shapes to be manipulated asynchronously:
|
|
* will asynchronously submit the containing form and replace the contents
|
|
* of the mass input element with the one from the BE response
|
|
* The utility will only trigger an AJAX request if the mass input element has
|
|
* an active/focused element whilst the form is being submitted.
|
|
*
|
|
* Attribute: uw-mass-input
|
|
*
|
|
* Example usage:
|
|
* <form method="POST" action="...">
|
|
* <input type="text">
|
|
* <div uw-mass-input>
|
|
* <input type="text">
|
|
* <button type="submit">
|
|
*/
|
|
|
|
var MASS_INPUT_UTIL_NAME = 'massInput';
|
|
var MASS_INPUT_UTIL_SELECTOR = '[uw-mass-input]';
|
|
|
|
var MASS_INPUT_CELL_SELECTOR = '.massinput__cell';
|
|
var MASS_INPUT_ADD_CELL_SELECTOR = '.massinput__cell--add';
|
|
var MASS_INPUT_SUBMIT_BUTTON_CLASS = 'massinput__submit-button';
|
|
var MASS_INPUT_INITIALIZED_CLASS = 'mass-input--initialized';
|
|
|
|
var massInputUtil = function(element) {
|
|
var massInputId;
|
|
var massInputFormSubmitHandler;
|
|
var massInputForm;
|
|
|
|
function init() {
|
|
if (!element) {
|
|
throw new Error('Mass Input utility cannot be setup without an element!');
|
|
}
|
|
|
|
massInputId = element.id;
|
|
massInputForm = element.closest('form');
|
|
|
|
if (!massInputForm) {
|
|
throw new Error('Mass Input utility cannot be setup without being wrapped in a <form>!');
|
|
}
|
|
|
|
massInputFormSubmitHandler = makeSubmitHandler();
|
|
|
|
// "unarm" submit buttons inside this massinput so browser
|
|
// uses correct submit button for form submission.
|
|
// contents of the massinput will be replaced either way,
|
|
// so unarming is no problem
|
|
unarmSubmitButtons(massInputFormSubmitHandler);
|
|
|
|
massInputForm.addEventListener('submit', massInputFormSubmitHandler);
|
|
|
|
// mark initialized
|
|
element.classList.add(MASS_INPUT_INITIALIZED_CLASS);
|
|
|
|
return {
|
|
name: MASS_INPUT_UTIL_NAME,
|
|
element: element,
|
|
destroy: function() {
|
|
reset();
|
|
},
|
|
};
|
|
}
|
|
|
|
function makeSubmitHandler() {
|
|
if (!HttpClient) {
|
|
throw new Error('HttpClient not found!');
|
|
}
|
|
|
|
var method = massInputForm.getAttribute('method') || 'POST';
|
|
var url = massInputForm.getAttribute('action') || window.location.href;
|
|
var enctype = massInputForm.getAttribute('enctype') || 'application/json';
|
|
|
|
var requestFn;
|
|
if (HttpClient[method.toLowerCase()]) {
|
|
requestFn = HttpClient[method.toLowerCase()];
|
|
}
|
|
|
|
return function(event) {
|
|
var activeElement;
|
|
|
|
// check if event occured from either a mass input add/delete button or
|
|
// from inside one of massinput's inputs (i.e. they are focused/active)
|
|
if (event.type === 'click') {
|
|
activeElement = event.target;
|
|
} else {
|
|
activeElement = element.querySelector(':focus, :active');
|
|
}
|
|
|
|
if (!activeElement) {
|
|
return false;
|
|
}
|
|
|
|
// find the according massinput cell thats hosts the element that triggered the submit
|
|
var massInputCell = activeElement.closest(MASS_INPUT_CELL_SELECTOR);
|
|
if (!massInputCell) {
|
|
return false;
|
|
}
|
|
|
|
var submitButton = massInputCell.querySelector('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS);
|
|
if (!submitButton) {
|
|
return false;
|
|
}
|
|
|
|
var isAddCell = massInputCell.matches(MASS_INPUT_ADD_CELL_SELECTOR);
|
|
var submitButtonIsActive = submitButton.matches(':focus, :active');
|
|
// if the cell is not an add cell the active element must at least be the cells submit button
|
|
if (!isAddCell && !submitButtonIsActive) {
|
|
return false;
|
|
}
|
|
|
|
event.preventDefault();
|
|
var requestBody = serializeForm(submitButton, enctype);
|
|
|
|
if (requestFn && requestBody) {
|
|
requestFn(
|
|
url,
|
|
{
|
|
'Content-Type': enctype,
|
|
'Mass-Input-Shortcircuit': massInputId,
|
|
},
|
|
requestBody,
|
|
).then(function(response) {
|
|
return response.text();
|
|
}).then(function(response) {
|
|
processResponse(response);
|
|
if (isAddCell) {
|
|
reFocusAddCell();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
function unarmSubmitButtons(submitHandler) {
|
|
var buttons = Array.from(element.querySelectorAll('button[type="submit"][name][value]'));
|
|
buttons.forEach(function(button) {
|
|
button.setAttribute('type', 'button');
|
|
button.classList.add(MASS_INPUT_SUBMIT_BUTTON_CLASS);
|
|
|
|
button.addEventListener('click', submitHandler);
|
|
});
|
|
}
|
|
|
|
function processResponse(response) {
|
|
element.innerHTML = response;
|
|
|
|
prefixInputIds();
|
|
reset()
|
|
|
|
if (UtilRegistry) {
|
|
UtilRegistry.setupAll(element);
|
|
}
|
|
}
|
|
|
|
function prefixInputIds() {
|
|
var idAttrs = ['id', 'for', 'data-conditional-input'];
|
|
idAttrs.forEach(function(attr) {
|
|
Array.from(element.querySelectorAll('[' + attr + ']')).forEach(function(input) {
|
|
var value = element.id + '__' + input.getAttribute(attr);
|
|
input.setAttribute(attr, value);
|
|
});
|
|
});
|
|
}
|
|
|
|
function serializeForm(submitButton, enctype) {
|
|
var formData = new FormData(massInputForm);
|
|
|
|
// manually add name and value of submit button to formData
|
|
formData.append(submitButton.name, submitButton.value);
|
|
|
|
if (enctype === 'application/x-www-form-urlencoded') {
|
|
return new URLSearchParams(formData);
|
|
} else if (enctype === 'multipart/form-data') {
|
|
return formData;
|
|
} else {
|
|
throw new Error('Unsupported form enctype: ' + enctype);
|
|
}
|
|
}
|
|
|
|
function reFocusAddCell() {
|
|
var addCell = element.querySelector(MASS_INPUT_ADD_CELL_SELECTOR);
|
|
if (!addCell) {
|
|
return false;
|
|
}
|
|
|
|
var addCellInput = addCell.querySelector('input:not([type="hidden"])');
|
|
if (addCellInput) {
|
|
// clear the inputs value
|
|
// TBD: make this work for checkboxes and radioboxes
|
|
// where the value should not be cleared
|
|
addCellInput.value = '';
|
|
addCellInput.focus();
|
|
}
|
|
}
|
|
|
|
function reset() {
|
|
element.classList.remove(MASS_INPUT_INITIALIZED_CLASS);
|
|
massInputForm.removeEventListener('submit', massInputFormSubmitHandler)
|
|
|
|
Array.from(element.querySelectorAll('.' + MASS_INPUT_SUBMIT_BUTTON_CLASS)).forEach(function(button) {
|
|
button.removeEventListener('click', massInputFormSubmitHandler);
|
|
button.classList.remove(MASS_INPUT_SUBMIT_BUTTON_CLASS);
|
|
button.setAttribute('type', 'submit');
|
|
});
|
|
}
|
|
|
|
return init();
|
|
};
|
|
|
|
// register mass input util
|
|
if (UtilRegistry) {
|
|
UtilRegistry.register({
|
|
name: MASS_INPUT_UTIL_NAME,
|
|
selector: MASS_INPUT_UTIL_SELECTOR,
|
|
setup: massInputUtil
|
|
});
|
|
}
|
|
})();
|