From 3e02d5aff85babc3ffbfcf52313f2108e313aa23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:46:43 +0200 Subject: Adding upstream version 2.12.1. Signed-off-by: Daniel Baumann --- public/js/icinga/events.js | 425 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+) create mode 100644 public/js/icinga/events.js (limited to 'public/js/icinga/events.js') diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js new file mode 100644 index 0000000..1878d8f --- /dev/null +++ b/public/js/icinga/events.js @@ -0,0 +1,425 @@ +/*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ + +/** + * Icinga.Events + * + * Event handlers + */ +(function (Icinga, $) { + + 'use strict'; + + Icinga.Events = function (icinga) { + this.icinga = icinga; + + this.searchValue = ''; + this.searchTimer = null; + }; + + Icinga.Events.prototype = { + + /** + * Icinga will call our initialize() function once it's ready + */ + initialize: function () { + this.applyGlobalDefaults(); + }, + + /** + * Global default event handlers + */ + applyGlobalDefaults: function () { + $(document).on('visibilitychange', { self: this }, this.onVisibilityChange); + + $.each(this.icinga.behaviors, function (name, behavior) { + behavior.bind($(document)); + }); + + // Initialize module javascript (Applies only to module.js code) + this.icinga.ensureSubModules(document); + + // We catch resize events + $(window).on('resize', { self: this.icinga.ui }, this.icinga.ui.onWindowResize); + + // Trigger 'rendered' event also on page loads + $(document).on('icinga-init', { self: this }, this.onInit); + + // Destroy Icinga, clean up and interrupt pending requests on unload + $( window ).on('unload', { self: this }, this.onUnload); + $( window ).on('beforeunload', { self: this }, this.onUnload); + + // Remove notifications on click + $(document).on('click', '#notifications li', function () { $(this).remove(); }); + + // We want to catch each link click + $(document).on('click', 'a', { self: this }, this.linkClicked); + $(document).on('click', 'tr[href]', { self: this }, this.linkClicked); + + $(document).on('click', 'input[type="submit"], button[type="submit"]', this.rememberSubmitButton); + // We catch all form submit events + $(document).on('submit', 'form', { self: this }, this.submitForm); + + // We support an 'autosubmit' class on dropdown form elements + $(document).on('change', 'form select.autosubmit', { self: this }, this.autoSubmitForm); + $(document).on('change', 'form input.autosubmit', { self: this }, this.autoSubmitForm); + + // Automatically check a radio button once a specific input is focused + $(document).on('focus', 'form select[data-related-radiobtn]', { self: this }, this.autoCheckRadioButton); + $(document).on('focus', 'form input[data-related-radiobtn]', { self: this }, this.autoCheckRadioButton); + + $(document).on('rendered', '#menu', { self: this }, this.onRenderedMenu); + $(document).on('keyup', '#search', { self: this }, this.autoSubmitSearch); + + $(document).on('click', '.tree .handle', { self: this }, this.treeNodeToggle); + + $(document).on('click', '#search + .search-reset', this.clearSearch); + + $(document).on('rendered', '.container', { self: this }, this.loadDashlets); + + // TBD: a global autocompletion handler + // $(document).on('keyup', 'form.auto input', this.formChangeDelayed); + // $(document).on('change', 'form.auto input', this.formChanged); + // $(document).on('change', 'form.auto select', this.submitForm); + }, + + treeNodeToggle: function () { + var $parent = $(this).closest('li'); + if ($parent.hasClass('collapsed')) { + $('li', $parent).addClass('collapsed'); + $parent.removeClass('collapsed'); + } else { + $parent.addClass('collapsed'); + } + }, + + onInit: function (event) { + $('body').removeClass('loading'); + + // Trigger the initial `rendered` events + $('.container').trigger('rendered'); + + // Additionally trigger a `rendered` event on the layout, some behaviors may + // want to differentiate whether a container or the entire layout is rendered + $('#layout').trigger('rendered'); + }, + + onUnload: function (event) { + var icinga = event.data.self.icinga; + icinga.logger.info('Unloading Icinga'); + icinga.destroy(); + }, + + onVisibilityChange: function (event) { + var icinga = event.data.self.icinga; + + if (document.visibilityState === undefined || document.visibilityState === 'visible') { + icinga.loader.autorefreshSuspended = false; + icinga.logger.debug('Page visible, enabling auto-refresh'); + } else { + icinga.loader.autorefreshSuspended = true; + icinga.logger.debug('Page invisible, disabling auto-refresh'); + } + }, + + autoCheckRadioButton: function (event) { + var $input = $(event.currentTarget); + var $radio = $('#' + $input.attr('data-related-radiobtn')); + if ($radio.length) { + $radio.prop('checked', true); + } + return true; + }, + + onRenderedMenu: function(event) { + var _this = event.data.self; + var $target = $(event.target); + + var $searchField = $target.find('input.search'); + // Remember initial search field value if any + if ($searchField.length && $searchField.val().length) { + _this.searchValue = $searchField.val(); + } + }, + + autoSubmitSearch: function(event) { + var _this = event.data.self; + var $searchField = $(event.target); + + if ($searchField.val() === _this.searchValue) { + return; + } + _this.searchValue = $searchField.val(); + + if (_this.searchTimer !== null) { + clearTimeout(_this.searchTimer); + _this.searchTimer = null; + } + var _event = $.extend({}, event); // event seems gc'd once the timeout is over + _this.searchTimer = setTimeout(function () { + _this.submitForm(_event, $searchField); + _this.searchTimer = null; + }, 500); + }, + + loadDashlets: function(event) { + var _this = event.data.self; + var $target = $(event.target); + + if ($target.children('.dashboard').length) { + $target.find('.dashboard > .container').each(function () { + var $dashlet = $(this); + var url = $dashlet.data('icingaUrl'); + if (typeof url !== 'undefined') { + _this.icinga.loader.loadUrl(url, $dashlet).autorefresh = true; + } + }); + } + }, + + rememberSubmitButton: function(e) { + var $button = $(this); + var $form = $button.closest('form'); + $form.data('submitButton', $button); + }, + + autoSubmitForm: function (event) { + let form = event.currentTarget.form; + + if (form.closest('[data-no-icinga-ajax]')) { + return; + } + + form.dispatchEvent(new CustomEvent('submit', { + cancelable: true, + bubbles: true, + detail: { submittedBy: event.currentTarget } + })); + }, + + /** + * + */ + submitForm: function (event, $autoSubmittedBy) { + var _this = event.data.self; + + // .closest is not required unless subelements to trigger this + var $form = $(event.currentTarget).closest('form'); + + if ($form.closest('[data-no-icinga-ajax]').length > 0) { + return true; + } + + var $button; + var $rememberedSubmittButton = $form.data('submitButton'); + if (typeof $rememberedSubmittButton != 'undefined') { + if ($form.has($rememberedSubmittButton)) { + $button = $rememberedSubmittButton; + } + $form.removeData('submitButton'); + } + + if (typeof $button === 'undefined') { + var $el; + + if (typeof event.originalEvent !== 'undefined' + && typeof event.originalEvent.explicitOriginalTarget === 'object') { // Firefox + $el = $(event.originalEvent.explicitOriginalTarget); + _this.icinga.logger.debug('events/submitForm: Button is event.originalEvent.explicitOriginalTarget'); + } else { + $el = $(event.currentTarget); + _this.icinga.logger.debug('events/submitForm: Button is event.currentTarget'); + } + + if ($el && ($el.is('input[type=submit]') || $el.is('button[type=submit]'))) { + $button = $el; + } else { + _this.icinga.logger.debug( + 'events/submitForm: Can not determine submit button, using the first one in form' + ); + } + } + + if (! $autoSubmittedBy && event.detail && event.detail.submittedBy) { + $autoSubmittedBy = $(event.detail.submittedBy); + } + + _this.icinga.loader.submitForm($form, $autoSubmittedBy, $button); + + event.stopPropagation(); + event.preventDefault(); + return false; + }, + + handleExternalTarget: function($node) { + var linkTarget = $node.attr('target'); + + // TODO: Let remote links pass through. Right now they only work + // combined with target="_blank" or target="_self" + // window.open is used as return true; didn't work reliable + if (linkTarget === '_blank' || linkTarget === '_self') { + window.open($node.attr('href'), linkTarget); + return true; + } + return false; + }, + + /** + * Someone clicked a link or tr[href] + */ + linkClicked: function (event) { + var _this = event.data.self; + var icinga = _this.icinga; + var $a = $(this); + var $eventTarget = $(event.target); + var href = $a.attr('href'); + var linkTarget = $a.attr('target'); + var $target; + var formerUrl; + + if (! href) { + return; + } + + if (href.match(/^(?:(?:mailto|javascript|data):|[a-z]+:\/\/)/)) { + event.stopPropagation(); + return true; + } + + if ($a.closest('[data-no-icinga-ajax]').length > 0) { + return true; + } + + // Check for ctrl or cmd click to open new tab unless clicking on a multiselect row + if ((event.ctrlKey || event.metaKey) && href !== '#' && $a.is('a')) { + window.open(href, linkTarget); + return false; + } + + // Special checks for link clicks in action tables + if (! $a.is('tr[href]') && $a.closest('table.action').length > 0) { + + // ignore clicks to ANY link with special key pressed + if ($a.closest('table.multiselect').length > 0 && (event.ctrlKey || event.metaKey || event.shiftKey)) { + return true; + } + + // ignore inner links matching the row URL + if ($a.attr('href') === $a.closest('tr[href]').attr('href')) { + return true; + } + } + + // window.open is used as return true; didn't work reliable + if (linkTarget === '_blank' || linkTarget === '_self') { + window.open(href, linkTarget); + return false; + } + + if (! $eventTarget.is($a)) { + if ($eventTarget.is('input') || $eventTarget.is('button')) { + // Ignore form elements in action rows + return; + } else { + var $button = $('input[type=submit]:focus').add('button[type=submit]:focus'); + if ($button.length > 0 && $.contains($button[0], $eventTarget[0])) { + // Ignore any descendant of form elements + return; + } + } + } + + // ignore multiselect table row clicks + if ($a.is('tr') && $a.closest('table.multiselect').length > 0) { + return; + } + + // Handle all other links as XHR requests + event.stopPropagation(); + event.preventDefault(); + + // This is an anchor only + if (href.substr(0, 1) === '#' && href.length > 1 + && href.substr(1, 1) !== '!' + ) { + icinga.ui.focusElement(href.substr(1), $a.closest('.container')); + return; + } + + // activate spinner indicator + if ($a.hasClass('spinner')) { + $a.addClass('active'); + } + + // If link has hash tag... + if (href.match(/#/)) { + if (href === '#') { + if ($a.hasClass('close-container-control')) { + if (! icinga.ui.isOneColLayout()) { + var $cont = $a.closest('.container').first(); + if ($cont.attr('id') === 'col1') { + icinga.ui.moveToLeft(); + icinga.ui.layout1col(); + } else { + icinga.ui.layout1col(); + } + icinga.history.pushCurrentState(); + } + } + return false; + } + $target = icinga.loader.getLinkTargetFor($a); + + formerUrl = $target.data('icingaUrl'); + if (typeof formerUrl !== 'undefined' && formerUrl.split(/#/)[0] === href.split(/#/)[0]) { + icinga.ui.focusElement(href.split(/#/)[1], $target); + $target.data('icingaUrl', href); + if (formerUrl !== href) { + icinga.history.pushCurrentState(); + } + return false; + } + } else { + $target = icinga.loader.getLinkTargetFor($a); + } + + // Load link URL + icinga.loader.loadUrl(href, $target); + + if ($a.closest('#menu').length > 0) { + // Menu links should remove all but the first layout column + icinga.ui.layout1col(); + } + + return false; + }, + + clearSearch: function (event) { + $(event.target).parent().find('#search').attr('value', ''); + }, + + unbindGlobalHandlers: function () { + $.each(this.icinga.behaviors, function (name, behavior) { + behavior.unbind($(document)); + }); + $(window).off('resize', this.onWindowResize); + $(window).off('load', this.onLoad); + $(window).off('unload', this.onUnload); + $(window).off('beforeunload', this.onUnload); + $(document).off('scroll', '.container', this.onContainerScroll); + $(document).off('click', 'a', this.linkClicked); + $(document).off('submit', 'form', this.submitForm); + $(document).off('change', 'form select.autosubmit', this.submitForm); + $(document).off('change', 'form input.autosubmit', this.submitForm); + $(document).off('focus', 'form select[data-related-radiobtn]', this.autoCheckRadioButton); + $(document).off('focus', 'form input[data-related-radiobtn]', this.autoCheckRadioButton); + $(document).off('visibilitychange', this.onVisibilityChange); + }, + + destroy: function() { + // This is gonna be hard, clean up the mess + this.unbindGlobalHandlers(); + this.icinga = null; + } + }; + +}(Icinga, jQuery)); -- cgit v1.2.3