class UIFactory { /* Display a toast based on provided options */ toast(options) { const theToast = new Toaster(options); theToast.makeToast() theToast.show() return theToast } /* Display a modal based on provided options */ modal (options) { const theModal = new ModalFactory(options); theModal.makeModal() theModal.show() return theModal } /* Display a modal based on provided options */ modalFromURL (url, successCallback, failCallback) { return AJAXApi.quickFetchURL(url).then((modalHTML) => { const theModal = new ModalFactory({ rawHTML: modalHTML, replaceFormSubmissionByAjax: true, successCallback: successCallback !== undefined ? successCallback : () => {}, failCallback: failCallback !== undefined ? failCallback : (errorMessage) => {}, }); theModal.makeModal(modalHTML) theModal.show() theModal.$modal.data('modalObject', theModal) return theModal }) } /* Fetch HTML from the provided URL and override content of $container. $statusNode allows to specify another HTML node to display the loading */ reload (url, $container, $statusNode=null) { $container = $($container) $statusNode = $($statusNode) if (!$statusNode) { $statusNode = $container } AJAXApi.quickFetchURL(url, { statusNode: $statusNode[0] }).then((data) => { $container.replaceWith(data) }) } } class Toaster { constructor(options) { this.options = Object.assign({}, Toaster.defaultOptions, options) this.bsToastOptions = { autohide: this.options.autohide, delay: this.options.delay, } } static defaultOptions = { id: false, title: false, muted: false, body: false, variant: 'default', autohide: true, delay: 5000, titleHtml: false, mutedHtml: false, bodyHtml: false, closeButton: true, } makeToast() { if (this.isValid()) { this.$toast = Toaster.buildToast(this.options) $('#mainToastContainer').append(this.$toast) } } show() { if (this.isValid()) { var that = this this.$toast.toast(this.bsToastOptions) .toast('show') .on('hidden.bs.toast', function () { that.removeToast() }) } } removeToast() { this.$toast.remove(); } isValid() { return this.options.title !== false || this.options.muted !== false || this.options.body !== false || this.options.titleHtml !== false || this.options.mutedHtml !== false || this.options.bodyHtml !== false } static buildToast(options) { var $toast = $('
') if (options.id !== false) { $toast.attr('id', options.id) } $toast.addClass('toast-' + options.variant) if (options.title !== false || options.titleHtml !== false || options.muted !== false || options.mutedHtml !== false) { var $toastHeader = $('') $toastHeader.addClass('toast-' + options.variant) if (options.title !== false || options.titleHtml !== false) { var $toastHeaderText if (options.titleHtml !== false) { $toastHeaderText = $('').html(options.titleHtml); } else { $toastHeaderText = $('').text(options.title) } $toastHeader.append($toastHeaderText) } if (options.muted !== false || options.mutedHtml !== false) { var $toastHeaderMuted if (options.mutedHtml !== false) { $toastHeaderMuted = $('').html(options.mutedHtml) } else { $toastHeaderMuted = $('').text(options.muted) } $toastHeader.append($toastHeaderMuted) } if (options.closeButton) { var $closeButton = $('') $toastHeader.append($closeButton) } $toast.append($toastHeader) } if (options.body !== false || options.bodyHtml !== false) { var $toastBody if (options.bodyHtml !== false) { $toastBody = $('').html(options.mutedHtml) } else { $toastBody = $('').text(options.body) } $toast.append($toastBody) } return $toast } } class ModalFactory { constructor(options) { this.options = Object.assign({}, ModalFactory.defaultOptions, options) this.bsModalOptions = { show: true } } static defaultOptions = { id: false, size: 'md', centered: false, scrollable: false, title: '', titleHtml: false, body: false, bodyHtml: false, rawHTML: false, variant: '', modalClass: [], headerClass: [], bodyClass: [], footerClass: [], buttons: [], type: 'ok-only', confirmText: 'Confirm', cancelText: 'Cancel', closeManually: false, closeOnSuccess: true, confirm: function() {}, APIConfirm: null, cancel: function() {}, error: function() {}, shownCallback: function() {}, hiddenCallback: function() {}, successCallback: function() {}, replaceFormSubmissionByAjax: false } static availableType = [ 'ok-only', 'confirm', 'confirm-success', 'confirm-warning', 'confirm-danger', ] static closeButtonHtml = '' static spinnerHtml = 'Loading...' makeModal() { if (this.isValid()) { this.$modal = this.buildModal() $('#mainModalContainer').append(this.$modal) } } show() { if (this.isValid()) { var that = this this.$modal.modal(this.bsModalOptions) .on('hidden.bs.modal', function () { that.removeModal() that.options.hiddenCallback() }) .on('shown.bs.modal', function () { that.options.shownCallback() if (that.options.replaceFormSubmissionByAjax) { that.replaceFormSubmissionByAjax() } }) } } hide() { this.$modal.modal('hide') } removeModal() { this.$modal.remove(); } isValid() { return this.options.title !== false || this.options.body !== false || this.options.titleHtml !== false || this.options.bodyHtml !== false || this.options.rawHTML !== false } buildModal() { const $modal = $('') if (this.options.id !== false) { $modal.attr('id', this.options.id) $modal.attr('aria-labelledby', this.options.id) } if (this.options.modalClass !== false) { $modal.addClass(this.options.modalClass) } let $modalDialog if (this.options.rawHTML) { $modalDialog = $(this.options.rawHTML) } else { $modalDialog = $('') const $modalContent = $('') if (this.options.title !== false || this.options.titleHtml !== false) { const $modalHeader = $('') let $modalHeaderText if (this.options.titleHtml !== false) { $modalHeaderText = $('').html(this.options.titleHtml); } else { $modalHeaderText = $('').text(this.options.title) } $modalHeader.append($modalHeaderText, ModalFactory.getCloseButton()) $modalContent.append($modalHeader) } if (this.options.body !== false || this.options.bodyHtml !== false) { const $modalBody = $('') let $modalBodyText if (this.options.bodyHtml !== false) { $modalBodyText = $('').html(this.options.bodyHtml); } else { $modalBodyText = $('').text(this.options.body) } $modalBody.append($modalBodyText) $modalContent.append($modalBody) } const $modalFooter = $('') $modalFooter.append(this.getFooterBasedOnType()) $modalContent.append($modalFooter) $modalDialog.append($modalContent) } $modal.append($modalDialog) return $modal } getFooterBasedOnType() { if (this.options.type == 'ok-only') { return this.getFooterOkOnly() } else if (this.options.type.includes('confirm')) { return this.getFooterConfirm() } else { return this.getFooterOkOnly() } } getFooterOkOnly() { return [ $('') .attr('data-dismiss', 'modal'), ] } getFooterConfirm() { let variant = this.options.type.split('-')[1] variant = variant !== undefined ? variant : 'primary' const $buttonCancel = $('') .text(this.options.cancelText) .click( (evt) => { this.options.cancel(() => { this.hide() }, this, evt) } ) .attr('data-dismiss', (this.options.closeManually || !this.options.closeOnSuccess) ? '' : 'modal') const $buttonConfirm = $('') .addClass('btn-' + variant) .text(this.options.confirmText) .click(this.getConfirmationHandlerFunction()) .attr('data-dismiss', (this.options.closeManually || this.options.closeOnSuccess) ? '' : 'modal') return [$buttonCancel, $buttonConfirm] } static getCloseButton() { return $(ModalFactory.closeButtonHtml) } getConfirmationHandlerFunction() { return (evt) => { let confirmFunction = this.options.confirm if (this.options.APIConfirm) { const tmpApi = new AJAXApi({ statusNode: evt.target }) confirmFunction = () => { return this.options.APIConfirm(tmpApi) } } let confirmResult = confirmFunction(() => { this.hide() }, this, evt) if (confirmResult === undefined) { this.hide() } else { confirmResult.then((data) => { if (this.options.closeOnSuccess) { this.hide() } }) .catch(() => { this.options.error(() => { this.hide() }, this, evt) }) } } } replaceFormSubmissionByAjax() { const $submitButton = this.$modal.find('.modal-footer #submitButton') const formID = $submitButton.data('form-id') let $form if (formID) { $form = $(formID) } else { $form = this.$modal.find('form') } this.options.APIConfirm = (tmpApi) => { tmpApi.mergeOptions({renderedHTMLOnFailureRequested: true}) return tmpApi.postForm($form[0]) .then((data) => { if (data.success) { this.options.successCallback(data) } else { // Validation error, replace modal content with new html this.$modal.html(data.html) this.replaceFormSubmissionByAjax() return Promise.reject('Validation error'); } }) .catch((errorMessage, response) => { this.options.failCallback(errorMessage) return Promise.reject(errorMessage); }) } $submitButton.click(this.getConfirmationHandlerFunction()) } } class OverlayFactory { constructor(options) { this.options = Object.assign({}, OverlayFactory.defaultOptions, options) if (this.options.spinnerAuto) { this.adjustSpinnerOptionsBasedOnNode() } } static defaultOptions = { node: false, variant: 'light', opacity: 0.85, blur: '2px', rounded: false, spinnerVariant: '', spinnerSmall: false, spinnerAuto: true } static overlayWrapper = '' static overlayContainer = '' static overlayBg = '' static overlaySpinner = '