2020-12-10 15:18:02 +01:00
|
|
|
class AJAXApi {
|
2020-12-15 10:40:49 +01:00
|
|
|
static genericRequestHeaders = {
|
2020-12-10 15:18:02 +01:00
|
|
|
'X-Requested-With': 'XMLHttpRequest'
|
2020-12-15 10:40:49 +01:00
|
|
|
};
|
2020-12-10 15:18:02 +01:00
|
|
|
static genericRequestConfigGET = {
|
2020-12-15 10:40:49 +01:00
|
|
|
headers: new Headers(Object.assign({}, AJAXApi.genericRequestHeaders))
|
2020-12-10 15:18:02 +01:00
|
|
|
}
|
|
|
|
static genericRequestConfigPOST = {
|
2020-12-15 10:40:49 +01:00
|
|
|
headers: new Headers(Object.assign({}, AJAXApi.genericRequestHeaders)),
|
2020-12-10 15:18:02 +01:00
|
|
|
redirect: 'manual',
|
|
|
|
method: 'POST',
|
|
|
|
}
|
2020-12-15 10:40:49 +01:00
|
|
|
static renderHTMLOnFailureHeader = {
|
|
|
|
name: 'X-Request-HTML-On-Failure',
|
|
|
|
value: '1'
|
|
|
|
}
|
2020-12-10 15:18:02 +01:00
|
|
|
|
|
|
|
static defaultOptions = {
|
2020-12-11 10:38:43 +01:00
|
|
|
provideFeedback: true,
|
|
|
|
statusNode: false,
|
2020-12-15 10:40:49 +01:00
|
|
|
renderedHTMLOnFailureRequested: false,
|
|
|
|
errorToast: {
|
|
|
|
delay: 10000
|
|
|
|
}
|
2020-12-10 15:18:02 +01:00
|
|
|
}
|
|
|
|
options = {}
|
|
|
|
loadingOverlay = false
|
|
|
|
|
|
|
|
constructor(options) {
|
|
|
|
this.mergeOptions(AJAXApi.defaultOptions)
|
|
|
|
this.mergeOptions(options)
|
|
|
|
}
|
|
|
|
|
2020-12-15 10:40:49 +01:00
|
|
|
provideFeedback(toastOptions, isError=false, skip=false) {
|
|
|
|
const alteredToastOptions = isError ? Object.assign({}, AJAXApi.defaultOptions.errorToast, toastOptions) : toastOptions
|
|
|
|
if (!skip) {
|
|
|
|
if (this.options.provideFeedback) {
|
|
|
|
UI.toast(alteredToastOptions)
|
|
|
|
} else {
|
|
|
|
if (isError) {
|
|
|
|
console.error(alteredToastOptions.body)
|
|
|
|
}
|
2020-12-11 10:38:43 +01:00
|
|
|
}
|
2020-12-10 15:18:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mergeOptions(newOptions) {
|
|
|
|
this.options = Object.assign({}, this.options, newOptions)
|
|
|
|
}
|
|
|
|
|
|
|
|
static mergeFormData(formData, dataToMerge) {
|
|
|
|
for (const [fieldName, value] of Object.entries(dataToMerge)) {
|
|
|
|
formData.set(fieldName, value)
|
|
|
|
}
|
|
|
|
return formData
|
|
|
|
}
|
|
|
|
|
2020-12-11 10:38:43 +01:00
|
|
|
static async quickFetchURL(url, options={}) {
|
|
|
|
const constAlteredOptions = Object.assign({}, {provideFeedback: false}, options)
|
|
|
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
|
|
|
return tmpApi.fetchURL(url, constAlteredOptions.skipRequestHooks)
|
|
|
|
}
|
|
|
|
|
|
|
|
static async quickFetchForm(url, options={}) {
|
|
|
|
const constAlteredOptions = Object.assign({}, {provideFeedback: false}, options)
|
|
|
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
|
|
|
return tmpApi.fetchForm(url, constAlteredOptions.skipRequestHooks)
|
|
|
|
}
|
|
|
|
|
2020-12-15 10:40:49 +01:00
|
|
|
static async quickPostForm(form, dataToMerge={}, options={}) {
|
|
|
|
const constAlteredOptions = Object.assign({}, {}, options)
|
|
|
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
|
|
|
return tmpApi.postForm(form, dataToMerge, constAlteredOptions.skipRequestHooks)
|
|
|
|
}
|
|
|
|
|
2020-12-11 10:38:43 +01:00
|
|
|
static async quickFetchAndPostForm(url, dataToMerge={}, options={}) {
|
|
|
|
const constAlteredOptions = Object.assign({}, {}, options)
|
|
|
|
const tmpApi = new AJAXApi(constAlteredOptions)
|
|
|
|
return tmpApi.fetchAndPostForm(url, dataToMerge, constAlteredOptions.skipRequestHooks)
|
|
|
|
}
|
|
|
|
|
2020-12-15 10:40:49 +01:00
|
|
|
async fetchURL(url, skipRequestHooks=false, skipFeedback=false) {
|
2020-12-10 15:18:02 +01:00
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.beforeRequest()
|
|
|
|
}
|
|
|
|
let toReturn
|
|
|
|
try {
|
|
|
|
const response = await fetch(url, AJAXApi.genericRequestConfigGET);
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error('Network response was not ok')
|
|
|
|
}
|
|
|
|
const data = await response.text();
|
2020-12-11 10:38:43 +01:00
|
|
|
this.provideFeedback({
|
|
|
|
variant: 'success',
|
|
|
|
title: 'URL fetched',
|
2020-12-15 10:40:49 +01:00
|
|
|
}, false, skipFeedback);
|
2020-12-10 15:18:02 +01:00
|
|
|
toReturn = data;
|
|
|
|
} catch (error) {
|
|
|
|
this.provideFeedback({
|
|
|
|
variant: 'danger',
|
|
|
|
title: 'There has been a problem with the operation',
|
|
|
|
body: error
|
2020-12-15 10:40:49 +01:00
|
|
|
}, true, skipFeedback);
|
2020-12-10 15:18:02 +01:00
|
|
|
toReturn = Promise.reject(error);
|
|
|
|
} finally {
|
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.afterRequest()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return toReturn
|
|
|
|
}
|
|
|
|
|
2020-12-15 10:40:49 +01:00
|
|
|
async fetchForm(url, skipRequestHooks=false, skipFeedback=false) {
|
2020-12-10 15:18:02 +01:00
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.beforeRequest()
|
|
|
|
}
|
|
|
|
let toReturn
|
|
|
|
try {
|
|
|
|
const response = await fetch(url, AJAXApi.genericRequestConfigGET);
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error('Network response was not ok')
|
|
|
|
}
|
|
|
|
const formHtml = await response.text();
|
|
|
|
let tmpNode = document.createElement("div");
|
|
|
|
tmpNode.innerHTML = formHtml;
|
|
|
|
let form = tmpNode.getElementsByTagName('form');
|
|
|
|
if (form.length == 0) {
|
|
|
|
throw new Error('The server did not return a form element')
|
|
|
|
}
|
|
|
|
toReturn = form[0];
|
|
|
|
} catch (error) {
|
|
|
|
this.provideFeedback({
|
|
|
|
variant: 'danger',
|
|
|
|
title: 'There has been a problem with the operation',
|
|
|
|
body: error
|
2020-12-15 10:40:49 +01:00
|
|
|
}, true, skipFeedback);
|
2020-12-10 15:18:02 +01:00
|
|
|
toReturn = Promise.reject(error);
|
|
|
|
} finally {
|
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.afterRequest()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return toReturn
|
|
|
|
}
|
2020-12-15 10:40:49 +01:00
|
|
|
|
|
|
|
async postForm(form, dataToMerge={}, skipRequestHooks=false, skipFeedback=false) {
|
2020-12-10 15:18:02 +01:00
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.beforeRequest()
|
|
|
|
}
|
|
|
|
let toReturn
|
2020-12-15 10:40:49 +01:00
|
|
|
let feedbackShown = false
|
2020-12-10 15:18:02 +01:00
|
|
|
try {
|
|
|
|
try {
|
|
|
|
let formData = new FormData(form)
|
|
|
|
formData = AJAXApi.mergeFormData(formData, dataToMerge)
|
2020-12-15 10:40:49 +01:00
|
|
|
let requestConfig = AJAXApi.genericRequestConfigPOST
|
|
|
|
if (this.options.renderedHTMLOnFailureRequested) {
|
|
|
|
requestConfig.headers.append(AJAXApi.renderHTMLOnFailureHeader.name, AJAXApi.renderHTMLOnFailureHeader.value)
|
|
|
|
}
|
2020-12-10 15:18:02 +01:00
|
|
|
let options = {
|
2020-12-15 10:40:49 +01:00
|
|
|
...requestConfig,
|
2020-12-10 15:18:02 +01:00
|
|
|
body: formData,
|
|
|
|
};
|
|
|
|
const response = await fetch(form.action, options);
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error('Network response was not ok')
|
|
|
|
}
|
2020-12-15 10:40:49 +01:00
|
|
|
const clonedResponse = response.clone()
|
|
|
|
try {
|
|
|
|
const data = await response.json()
|
|
|
|
if (data.success) {
|
|
|
|
this.provideFeedback({
|
|
|
|
variant: 'success',
|
|
|
|
body: data.message
|
|
|
|
}, false, skipFeedback);
|
|
|
|
toReturn = data;
|
|
|
|
} else {
|
|
|
|
this.provideFeedback({
|
|
|
|
variant: 'danger',
|
|
|
|
title: 'There has been a problem with the operation',
|
|
|
|
body: data.errors
|
|
|
|
}, true, skipFeedback);
|
|
|
|
feedbackShown = true
|
|
|
|
toReturn = Promise.reject(data.errors);
|
|
|
|
}
|
|
|
|
} catch (error) { // could not parse JSON
|
|
|
|
if (this.options.renderedHTMLOnFailureRequested) {
|
|
|
|
const data = await clonedResponse.text();
|
|
|
|
toReturn = {
|
|
|
|
success: 0,
|
|
|
|
html: data,
|
|
|
|
}
|
|
|
|
}
|
2020-12-10 15:18:02 +01:00
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
this.provideFeedback({
|
|
|
|
variant: 'danger',
|
|
|
|
title: 'There has been a problem with the operation',
|
|
|
|
body: error
|
2020-12-15 10:40:49 +01:00
|
|
|
}, true, feedbackShown);
|
2020-12-10 15:18:02 +01:00
|
|
|
toReturn = Promise.reject(error);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
toReturn = Promise.reject(error);
|
|
|
|
} finally {
|
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.afterRequest()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return toReturn
|
|
|
|
}
|
2020-12-15 10:40:49 +01:00
|
|
|
|
|
|
|
async fetchAndPostForm(url, dataToMerge={}, skipRequestHooks=false) {
|
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.beforeRequest()
|
|
|
|
}
|
|
|
|
let toReturn
|
|
|
|
try {
|
|
|
|
const form = await this.fetchForm(url, true, true);
|
|
|
|
toReturn = await this.postForm(form, dataToMerge, true, true)
|
|
|
|
} catch (error) {
|
|
|
|
toReturn = Promise.reject(error);
|
|
|
|
} finally {
|
|
|
|
if (!skipRequestHooks) {
|
|
|
|
this.afterRequest()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return toReturn
|
|
|
|
}
|
2020-12-10 15:18:02 +01:00
|
|
|
|
|
|
|
beforeRequest() {
|
|
|
|
if (this.options.statusNode !== false) {
|
|
|
|
this.toggleLoading(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
afterRequest() {
|
|
|
|
if (this.options.statusNode !== false) {
|
|
|
|
this.toggleLoading(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleLoading(loading) {
|
|
|
|
if (this.loadingOverlay === false) {
|
2020-12-15 15:00:21 +01:00
|
|
|
this.loadingOverlay = new OverlayFactory(this.options.statusNode);
|
2020-12-10 15:18:02 +01:00
|
|
|
}
|
|
|
|
if (loading) {
|
|
|
|
this.loadingOverlay.show()
|
|
|
|
} else {
|
|
|
|
this.loadingOverlay.hide()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|