fradrive/static/js/utils/massInput.js

233 lines
7.0 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!');
}
if (element.classList.contains(MASS_INPUT_INITIALIZED_CLASS)) {
return false;
}
massInputId = element.dataset.massInputIdent || '_';
massInputForm = element.closest('form');
if (!massInputForm) {
throw new Error('Mass Input utility cannot be setup without being wrapped in a <form>!');
}
massInputFormSubmitHandler = makeSubmitHandler();
// setup submit buttons inside this massinput so browser
// uses correct submit button for form submission.
var buttons = getMassInputSubmitButtons();
buttons.forEach(function(button) {
setupSubmitButton(button);
});
massInputForm.addEventListener('submit', massInputFormSubmitHandler);
massInputForm.addEventListener('keypress', keypressHandler);
// 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. a child is focused/active)
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) {
var headers = {'Mass-Input-Shortcircuit': massInputId};
if (enctype !== 'multipart/form-data')
headers['Content-Type'] = enctype;
requestFn({
url: url,
headers: headers,
body: requestBody,
accept: HttpClient.ACCEPT.TEXT_HTML,
}).then(function(response) {
return HtmlHelpers.parseResponse(response);
}).then(function(response) {
processResponse(response.element);
if (isAddCell) {
reFocusAddCell();
}
});
}
};
}
function keypressHandler(event) {
if (event.keyCode !== 13) {
return false;
}
if (massInputFormSubmitHandler) {
return massInputFormSubmitHandler(event);
}
}
function getMassInputSubmitButtons() {
return Array.from(element.querySelectorAll('button[type="submit"][name][value], .' + MASS_INPUT_SUBMIT_BUTTON_CLASS));
}
function setupSubmitButton(button) {
button.setAttribute('type', 'button');
button.classList.add(MASS_INPUT_SUBMIT_BUTTON_CLASS);
button.addEventListener('click', massInputFormSubmitHandler);
}
function resetSubmitButton(button) {
button.setAttribute('type', 'submit');
button.classList.remove(MASS_INPUT_SUBMIT_BUTTON_CLASS);
button.removeEventListener('click', massInputFormSubmitHandler);
}
function processResponse(responseElement) {
element.innerHTML = "";
element.appendChild(responseElement);
reset();
if (UtilRegistry) {
UtilRegistry.setupAll(element);
}
}
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) {
// Clearing of add-inputs is done in the backend
addCellInput.focus();
}
}
function reset() {
element.classList.remove(MASS_INPUT_INITIALIZED_CLASS);
massInputForm.removeEventListener('submit', massInputFormSubmitHandler)
massInputForm.removeEventListener('keypress', keypressHandler);
var buttons = getMassInputSubmitButtons();
buttons.forEach(function(button) {
resetSubmitButton(button);
});
}
return init();
};
// register mass input util
if (UtilRegistry) {
UtilRegistry.register({
name: MASS_INPUT_UTIL_NAME,
selector: MASS_INPUT_UTIL_SELECTOR,
setup: massInputUtil
});
}
})();