UNPKG

octobercms

Version:

AJAX Framework for October CMS

350 lines (290 loc) 11.7 kB
import { Options } from "./options"; import { Actions } from "./actions"; import { Data } from "./data"; import { HttpRequest } from "../util/http-request"; import { Deferred } from "../util/deferred"; import { ProgressBar } from "../extras/progress-bar"; import { dispatch } from "../util"; export class Request { constructor(element, handler, options) { this.el = element; this.handler = handler; this.options = { ...this.constructor.DEFAULTS, ...(options || {}) }; this.context = { el: element, handler: handler, options: this.options }; this.progressBar = new ProgressBar; this.showProgressBar = () => { this.progressBar.show({ cssClass: 'is-ajax' }); }; } static get DEFAULTS() { return { handler: null, update: {}, files: false, bulk: false, download: false, browserValidate: false, browserTarget: null, progressBarDelay: 500, progressBar: null } } start() { // Setup this.notifyApplicationAjaxSetup(); this.initOtherElements(); this.preprocessOptions(); // Prepare actions this.actions = new Actions(this, this.context, this.options); if (!this.validateClientSideForm() || !this.applicationAllowsRequest()) { return; } // Prepare data const dataObj = new Data(this.options.data, this.el, this.formEl); let data; if (this.options.files) { data = dataObj.getAsFormData(); } else if (this.options.bulk) { data = dataObj.getAsJsonData(); } else { data = dataObj.getAsQueryString(); } // Prepare query if (this.options.query) { this.actions.invoke('applyQueryToUrl', [this.options.query]); } // Prepare request const { url, headers, method, responseType } = Options.fetch(this.handler, this.options); this.request = new HttpRequest(this, url, { method, headers, responseType, data, trackAbort: true }); this.promise = new Deferred({ delegate: this.request }); this.isRedirect = this.options.redirect && this.options.redirect.length > 0; // Confirm before sending if (this.options.confirm && !this.actions.invoke('handleConfirmMessage', [this.options.confirm])) { return; } // Send request this.sendInternal(); return this.promise; } sendInternal() { var self = this; this.notifyApplicationBeforeSend(); this.notifyApplicationAjaxPromise(); this.promise .fail(function(data, responseCode, xhr) { if (!self.isRedirect) { self.notifyApplicationAjaxFail(data, responseCode, xhr); } }) .done(function(data, responseCode, xhr) { if (!self.isRedirect) { self.notifyApplicationAjaxDone(data, responseCode, xhr); } }) .always(function(data, responseCode, xhr) { self.notifyApplicationAjaxAlways(data, responseCode, xhr); }); this.request.send(); } static send(handler, options) { return (new Request(document, handler, options)).start(); } static sendElement(element, handler, options) { if (typeof element === 'string') { element = document.querySelector(element); } return (new Request(element, handler, options)).start(); } toggleRedirect(redirectUrl) { if (!redirectUrl) { this.options.redirect = null; this.isRedirect = false; } else { this.options.redirect = redirectUrl; this.isRedirect = true; } } applicationAllowsRequest() { const event = this.notifyApplicationBeforeRequest(); return !event.defaultPrevented; } applicationAllowsUpdate(data, responseCode, xhr) { const event = this.notifyApplicationBeforeUpdate(data, responseCode, xhr); return !event.defaultPrevented; } applicationAllowsError(message, responseCode, xhr) { const event = this.notifyApplicationRequestError(message, responseCode, xhr); return !event.defaultPrevented; } // Application events notifyApplicationAjaxSetup() { return dispatch('ajax:setup', { target: this.el, detail: { context: this.context } }); } notifyApplicationAjaxPromise() { return dispatch('ajax:promise', { target: this.el, detail: { context: this.context } }); } notifyApplicationAjaxFail(data, responseCode, xhr) { return dispatch('ajax:fail', { target: this.el, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationAjaxDone(data, responseCode, xhr) { return dispatch('ajax:done', { target: this.el, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationAjaxAlways(data, responseCode, xhr) { return dispatch('ajax:always', { target: this.el, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationAjaxUpdate(target, data, responseCode, xhr) { return dispatch('ajax:update', { target, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationBeforeRedirect() { return dispatch('ajax:before-redirect', { target: this.el }); } notifyApplicationBeforeRequest() { return dispatch('ajax:before-request', { target: this.triggerEl, detail: { context: this.context } }); } notifyApplicationBeforeUpdate(data, responseCode, xhr) { return dispatch('ajax:before-update', { target: this.triggerEl, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationRequestSuccess(data, responseCode, xhr) { return dispatch('ajax:request-success', { target: this.triggerEl, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationRequestError(message, responseCode, xhr) { return dispatch('ajax:request-error', { target: this.triggerEl, detail: { context: this.context, message, responseCode, xhr } }); } notifyApplicationRequestComplete(data, responseCode, xhr) { return dispatch('ajax:request-complete', { target: this.triggerEl, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationBeforeValidate(message, fields) { return dispatch('ajax:before-validate', { target: this.triggerEl, detail: { context: this.context, message, fields } }); } notifyApplicationBeforeReplace(target) { return dispatch('ajax:before-replace', { target }); } // Window-based events notifyApplicationBeforeSend() { return dispatch('ajax:before-send', { target: window, detail: { context: this.context } }); } notifyApplicationUpdateComplete(data, responseCode, xhr) { return dispatch('ajax:update-complete', { target: window, detail: { context: this.context, data, responseCode, xhr } }); } notifyApplicationFieldInvalid(element, fieldName, errorMsg, isFirst) { return dispatch('ajax:invalid-field', { target: window, detail: { element, fieldName, errorMsg, isFirst } }); } notifyApplicationConfirmMessage(message, promise) { return dispatch('ajax:confirm-message', { target: window, detail: { message, promise } }); } notifyApplicationErrorMessage(message) { return dispatch('ajax:error-message', { target: window, detail: { message } }); } // HTTP request delegate requestStarted() { this.markAsProgress(true); this.toggleLoadingElement(true); if (this.options.progressBar) { this.showProgressBarAfterDelay(); } this.actions.invoke('start', [this.request.xhr]); } requestProgressed(progress) { this.promise.notify(progress); } requestCompletedWithResponse(response, statusCode) { this.actions.invoke('success', [response, statusCode, this.request.xhr]); this.actions.invoke('complete', [response, statusCode, this.request.xhr]); this.promise.resolve(response, statusCode, this.request.xhr); } requestFailedWithStatusCode(statusCode, response) { this.actions.invoke('error', [response, statusCode, this.request.xhr]); this.actions.invoke('complete', [response, statusCode, this.request.xhr]); this.promise.reject(response, statusCode, this.request.xhr); } requestFinished() { this.markAsProgress(false); this.toggleLoadingElement(false); if (this.options.progressBar) { this.hideProgressBar(); } } // Private initOtherElements() { if (typeof this.options.form === 'string') { this.formEl = document.querySelector(this.options.form); } else if (this.options.form) { this.formEl = this.options.form; } else { this.formEl = this.el && this.el !== document ? this.el.closest('form') : null; } this.triggerEl = this.formEl ? this.formEl : this.el; this.partialEl = this.el && this.el !== document ? this.el.closest('[data-ajax-partial]') : null; this.loadingEl = typeof this.options.loading === 'string' ? document.querySelector(this.options.loading) : this.options.loading; } preprocessOptions() { // Partial mode if (this.options.partial === undefined && this.partialEl && this.partialEl.dataset.ajaxPartial !== undefined) { this.options.partial = this.partialEl.dataset.ajaxPartial || true; } } validateClientSideForm() { if ( this.options.browserValidate && typeof document.createElement('input').reportValidity === 'function' && this.formEl && !this.formEl.checkValidity() ) { this.formEl.reportValidity(); return false; } return true; } toggleLoadingElement(isLoading) { if (!this.loadingEl) { return; } if ( typeof this.loadingEl.show !== 'function' || typeof this.loadingEl.hide !== 'function' ) { this.loadingEl.style.display = isLoading ? 'block' : 'none'; return; } if (isLoading) { this.loadingEl.show(); } else { this.loadingEl.hide(); } } showProgressBarAfterDelay() { this.progressBar.setValue(0); this.progressBarTimeout = window.setTimeout(this.showProgressBar, this.options.progressBarDelay); } hideProgressBar() { this.progressBar.setValue(100); this.progressBar.hide(); if (this.progressBarTimeout != null) { window.clearTimeout(this.progressBarTimeout); delete this.progressBarTimeout; } } markAsProgress(isLoading) { if (isLoading) { document.documentElement.setAttribute('data-ajax-progress', ''); if (this.formEl) { this.formEl.setAttribute('data-ajax-progress', this.handler); } } else { document.documentElement.removeAttribute('data-ajax-progress'); if (this.formEl) { this.formEl.removeAttribute('data-ajax-progress'); } } } }