From a13d2b1e4804ebb2b6d9a351e033766c59225fa2 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 24 Oct 2019 14:37:59 +0200 Subject: [PATCH] js: Introduce behavior modal.js --- public/js/icinga/behavior/modal.js | 198 +++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 public/js/icinga/behavior/modal.js diff --git a/public/js/icinga/behavior/modal.js b/public/js/icinga/behavior/modal.js new file mode 100644 index 000000000..7a616e8e0 --- /dev/null +++ b/public/js/icinga/behavior/modal.js @@ -0,0 +1,198 @@ +/*! Icinga Web 2 | (c) 2019 Icinga GmbH | GPLv2+ */ + +;(function(Icinga, $) { + + 'use strict'; + + Icinga.Behaviors = Icinga.Behaviors || {}; + + /** + * Behavior for modal dialogs. + * + * @param icinga {Icinga} The current Icinga Object + */ + var Modal = function(icinga) { + Icinga.EventListener.call(this, icinga); + + this.icinga = icinga; + this.$layout = $('#layout'); + this.$ghost = $('#modal-ghost'); + + this.on('submit', '#modal form', this.onFormSubmit, this); + this.on('change', '#modal form select.autosubmit', this.onFormAutoSubmit, this); + this.on('change', '#modal form input.autosubmit', this.onFormAutoSubmit, this); + this.on('click', '[data-icinga-modal]', this.onModalToggleClick, this); + this.on('click', '#layout > #modal', this.onModalLeave, this); + this.on('click', '.modal-header > button', this.onModalClose, this); + this.on('keydown', this.onKeyDown, this); + }; + + Modal.prototype = new Icinga.EventListener(); + + /** + * Event handler for toggling modals. Shows the link target in a modal dialog. + * + * @param event {Event} The `onClick` event triggered by the clicked modal-toggle element + * @returns {boolean} + */ + Modal.prototype.onModalToggleClick = function(event) { + var _this = event.data.self; + var $a = $(event.currentTarget); + var url = $a.attr('href'); + var $modal = _this.$ghost.clone(); + var $urlTarget = _this.icinga.loader.getLinkTargetFor($a); + + // Add view=compact, we don't want controls in a modal + url = _this.icinga.utils.addUrlParams(url, { 'view': 'compact' }); + + // Set the toggle's base target on the modal to use it as redirect target + $modal.data('redirectTarget', $urlTarget); + + // Final preparations, the id is required so that it's not `display:none` anymore + $modal.attr('id', 'modal'); + _this.$layout.append($modal); + + var req = _this.icinga.loader.loadUrl(url, $modal.find('#modal-content')); + req.addToHistory = false; + req.done(function () { + _this.setTitle($modal, req.$target.data('icingaTitle').replace(/\s::\s.*/, '')); + _this.show($modal); + _this.focus($modal); + }); + req.fail(function (req, _, errorThrown) { + if (req.status >= 500) { + // Yes, that's done twice (by us and by the base fail handler), + // but `renderContentToContainer` does too many useful things.. + _this.icinga.loader.renderContentToContainer(req.responseText, $urlTarget, req.action); + } else if (req.status > 0) { + var msg = $(req.responseText).find('.error-message').text(); + if (msg && msg !== errorThrown) { + errorThrown += ': ' + msg; + } + + _this.icinga.loader.createNotice('error', errorThrown); + } + + _this.hide($modal); + }); + + return false; + }; + + /** + * Event handler for form submits within a modal. + * + * @param event {Event} The `submit` event triggered by a form within the modal + * @param $autoSubmittedBy {jQuery} The element triggering the auto submit, if any + * @returns {boolean} + */ + Modal.prototype.onFormSubmit = function(event, $autoSubmittedBy) { + var _this = event.data.self; + var $form = $(event.currentTarget).closest('form'); + var $modal = $form.closest('#modal'); + + var req = _this.icinga.loader.submitForm($form, $autoSubmittedBy); + req.$redirectTarget = $modal.data('redirectTarget'); + req.done(function (data, textStatus, req) { + if (req.getResponseHeader('X-Icinga-Redirect')) { + _this.hide($modal); + } + }); + + event.stopPropagation(); + event.preventDefault(); + return false; + }; + + /** + * Event handler for form auto submits within a modal. + * + * @param event {Event} The `change` event triggered by a form input within the modal + * @returns {boolean} + */ + Modal.prototype.onFormAutoSubmit = function(event) { + return event.data.self.onFormSubmit(event, $(event.currentTarget)); + }; + + /** + * Event handler for closing the modal. Closes it when the user clicks on the overlay. + * + * @param event {Event} The `click` event triggered by clicking on the overlay + */ + Modal.prototype.onModalLeave = function(event) { + var _this = event.data.self; + var $target = $(event.target); + + if ($target.is('#modal')) { + _this.hide($target); + } + }; + + /** + * Event handler for closing the modal. Closes it when the user clicks on the close button. + * + * @param event {Event} The `click` event triggered by clicking on the close button + */ + Modal.prototype.onModalClose = function(event) { + var _this = event.data.self; + + _this.hide($(event.currentTarget).closest('#modal')); + }; + + /** + * Event handler for closing the modal. Closes it when the user pushed ESC. + * + * @param event {Event} The `keydown` event triggered by pushing a key + */ + Modal.prototype.onKeyDown = function(event) { + var _this = event.data.self; + + if (event.which === 27) { + _this.hide(_this.$layout.children('#modal')); + } + }; + + /** + * Make final preparations and add the modal to the DOM + * + * @param $modal {jQuery} The modal element + */ + Modal.prototype.show = function($modal) { + $modal.addClass('active'); + }; + + /** + * Set a title for the modal + * + * @param $modal {jQuery} The modal element + * @param title {string} The title + */ + Modal.prototype.setTitle = function($modal, title) { + $modal.find('.modal-header > h1').html(title); + }; + + /** + * Focus the modal + * + * @param $modal {jQuery} The modal element + */ + Modal.prototype.focus = function($modal) { + this.icinga.ui.focusElement($modal.find('.modal-window')); + }; + + /** + * Hide the modal and remove it from the DOM + * + * @param $modal {jQuery} The modal element + */ + Modal.prototype.hide = function($modal) { + $modal.removeClass('active'); + // Using `setTimeout` here to let the transition finish + setTimeout(function () { + $modal.remove(); + }, 200); + }; + + Icinga.Behaviors.Modal = Modal; + +})(Icinga, jQuery);