summaryrefslogtreecommitdiffstats
path: root/public/js/icinga/events.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/js/icinga/events.js')
-rw-r--r--public/js/icinga/events.js425
1 files changed, 425 insertions, 0 deletions
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));