@curveball/browser
Version:
Automatic API browser generator. A middleware that turns your JSON responses into HTML if accessed by a browser.
117 lines (116 loc) • 4.16 kB
JavaScript
/* eslint no-console: 0 */
import { serializeJsonForm } from './serialize-json-form.js';
export function enhanceForm(elem) {
elem.addEventListener('submit', (ev) => {
ev.preventDefault();
processSubmit(elem);
});
}
export function autoEnhanceForms(doc) {
var _a, _b;
const forms = doc.getElementsByTagName('form');
for (const form of forms) {
const method = ((_a = form.getAttribute('method')) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'GET';
const encType = (_b = form.getAttribute('enctype')) === null || _b === void 0 ? void 0 : _b.toLowerCase();
if (!['POST', 'GET'].includes(method) ||
encType === 'application/json') {
enhanceForm(form);
}
}
}
autoEnhanceForms(document);
async function processSubmit(elem) {
var _a, _b, _c;
const method = ((_a = elem.getAttribute('method')) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'GET';
const encType = ((_b = elem.getAttribute('enctype')) === null || _b === void 0 ? void 0 : _b.toLowerCase()) || 'application/x-www-form-urlencoded';
let action = (_c = elem.getAttribute('action')) !== null && _c !== void 0 ? _c : '';
let body;
if (method === 'GET' || method === 'DELETE') {
const tUrl = new URL(action);
for (const [key, value] of new FormData(elem)) {
if (typeof value === 'string') {
tUrl.searchParams.append(key, value);
}
else {
console.warn('Form field %s is ignored', key);
}
}
action = tUrl.toString();
body = undefined;
}
else {
if (!encType || encType === 'application/x-www-form-urlencoded') {
body = new URLSearchParams(Object.fromEntries(new FormData(elem).entries()));
}
else if (encType === 'application/json' || encType.match(/^application\/(.*)\+json$/)) {
body = JSON.stringify(serializeJsonForm(elem));
}
}
let response;
try {
response = await fetch(action, {
method,
headers: {
'Content-Type': encType,
'Accept': 'text/html',
},
body,
});
}
catch (err) {
console.error('[html-form-enhancer] HTTP error while submitting form: ' + err);
return;
}
if (!response.ok) {
console.error('[html-form-enhancer] HTTP error while submitting form: ' + response.status);
await replaceBody(response);
return;
}
/*
* As far as we know there's currently no way we can receive a 3xx response
*
* Leaving this code snippet for future archeologists, or in case browsers have a means
* to intercept 3xx responses in the future.
if (response.status >= 300 && response.status <= 399) {
const location = response.headers.get('Location');
if (location) {
document.location.href = location;
} else {
console.warn('Got a 3xx response from a form submission, but no Location header');
}
return;
}
*/
switch (response.status) {
case 201: {
const location = response.headers.get('Location');
if (location) {
document.location.href = location;
elem.action = location;
elem.method = 'PUT';
}
else {
console.warn('Got a 201 response from a form submission, but no Location header');
}
break;
}
case 204:
// Do nothing
break;
case 205:
elem.reset();
break;
default:
replaceBody(response);
break;
}
}
async function replaceBody(response) {
const responseBody = await response.text();
const domParser = new DOMParser();
const newDom = domParser.parseFromString(responseBody, 'text/html');
autoEnhanceForms(newDom);
document.body.innerHTML = newDom.body.innerHTML;
history.pushState(null, newDom.title, response.url);
}
//# sourceMappingURL=html-form-enhancer.js.map