/*! 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('mousedown', '#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, false); _this.modalOpener = event.currentTarget; // Disable pointer events to block further function calls _this.modalOpener.style.pointerEvents = 'none'; // Add showCompact, we don't want controls in a modal url = _this.icinga.utils.addUrlFlag(url, 'showCompact'); // 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 $button; var $rememberedSubmittButton = $form.data('submitButton'); if (typeof $rememberedSubmittButton != 'undefined') { if ($form.has($rememberedSubmittButton)) { $button = $rememberedSubmittButton; } $form.removeData('submitButton'); } var req = _this.icinga.loader.submitForm($form, $autoSubmittedBy, $button); req.addToHistory = false; req.$redirectTarget = $modal.data('redirectTarget'); req.done(function (data, textStatus, req) { var title = req.getResponseHeader('X-Icinga-Title'); if (!! title) { _this.setTitle($modal, decodeURIComponent(title).replace(/\s::\s.*/, '')); } if (req.getResponseHeader('X-Icinga-Redirect')) { _this.hide($modal); } }); if (typeof $autoSubmittedBy === 'undefined') { // otherwise the form is submitted several times by clicking the "Submit" button several times $form.find('input[type=submit],button[type=submit],button:not([type])').prop('disabled', true); } 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.isDefaultPrevented() && event.key === 'Escape') { let $modal = _this.$layout.children('#modal'); if ($modal.length) { _this.hide($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) { // Remove pointerEvent none style to make the button clickable again this.modalOpener.style.pointerEvents = ''; this.modalOpener = null; $modal.removeClass('active'); // Using `setTimeout` here to let the transition finish setTimeout(function () { $modal.find('#modal-content').trigger('close-modal'); $modal.remove(); }, 200); }; Icinga.Behaviors.Modal = Modal; })(Icinga, jQuery);