js: Properly initialize modules and don't trigger `rendered` events twice

Cleans up how our JS initializes module javascript. Previously the `rendered`
event only got fired upon page load if a module got loaded as well. This is
now decoupled and `rendered` events fire for all containers and the entire
layout upon page load.

Notable changes:
* A `load` event initializes modules and triggers `rendered` on `.container` and `#layout` elements
* Module javascript (module.js) is still lazy loaded but with a `beforerender` event, not `rendered`
* Previously `#layout` received a `rendered` event during bootstrapping, this is not the case anymore
* Initial search value preservation now got its own handler `events.onRenderedMenu()`
* Initial dashlet loading now git its own handler `events.loadDashlets()`
This commit is contained in:
Johannes Meyer 2019-11-20 16:11:01 +01:00
parent 47c2a8bdc1
commit 7f78c1a8a8
1 changed files with 77 additions and 78 deletions

View File

@ -14,7 +14,6 @@
this.searchValue = '';
this.searchTimer = null;
this.initializeModules = true;
};
Icinga.Events.prototype = {
@ -24,87 +23,16 @@
*/
initialize: function () {
this.applyGlobalDefaults();
$('#layout').trigger('rendered');
//$('.container').trigger('rendered');
$('.container').each(function(idx, el) {
icinga.ui.initializeControls($(el));
});
},
// TODO: What's this?
applyHandlers: function (event) {
var $target = $(event.target);
var _this = event.data.self;
var icinga = _this.icinga;
if (! icinga) {
// Attempt to catch a rare error, race condition, whatever
console.log('Got no icinga in applyHandlers');
return;
}
if (_this.initializeModules) {
var loaded = false;
var moduleName = $target.data('icingaModule');
if (moduleName) {
if (icinga.hasModule(moduleName) && !icinga.isLoadedModule(moduleName)) {
loaded |= icinga.loadModule(moduleName);
}
}
$('.icinga-module', $target).each(function(idx, mod) {
moduleName = $(mod).data('icingaModule');
if (icinga.hasModule(moduleName) && !icinga.isLoadedModule(moduleName)) {
loaded |= icinga.loadModule(moduleName);
}
});
if (loaded) {
// Modules may register their own handler for the 'renderend' event
// so we need to ensure that it is called the first time they are
// initialized
event.stopImmediatePropagation();
_this.initializeModules = false;
var $container = $target.closest('.container');
if (! $container.length) {
// The page obviously got loaded for the first time,
// so we'll trigger the event for all containers
$container = $('.container');
}
$container.trigger('rendered');
// But since we're listening on this event by ourself, we'll have
// to abort our own processing as we'll process it twice otherwise
return false;
}
} else {
_this.initializeModules = true;
}
$('.dashboard > div', $target).each(function(idx, el) {
var $element = $(el);
var $url = $element.data('icingaUrl');
if (typeof $url !== 'undefined') {
icinga.loader.loadUrl($url, $element).autorefresh = true;
}
});
var $searchField = $('#menu input.search', $target);
// Remember initial search field value if any
if ($searchField.length && $searchField.val().length) {
_this.searchValue = $searchField.val();
}
},
/**
* Global default event handlers
*/
applyGlobalDefaults: function () {
// Apply element-specific behavior whenever the layout is rendered
// Note: It is important that this is the first handler for this event!
$(document).on('rendered', { self: this }, this.applyHandlers);
$(document).on('beforerender', { self: this }, this.initializeModules);
$(document).on('visibilitychange', { self: this }, this.onVisibilityChange);
@ -141,18 +69,52 @@
$(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('keyup', '#menu input.search', {self: this}, this.autoSubmitSearch);
$(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);
},
/**
* Lazy load module javascript (Applies only to module.js code)
*
* @param {Event} event
*/
initializeModules: function (event) {
var _this, $target;
if (typeof event === 'undefined') {
_this = this;
$target = $('#col1');
} else {
_this = event.data.self;
$target = $(event.target);
}
var moduleName = $target.data('icingaModule');
if (moduleName) {
if (_this.icinga.hasModule(moduleName) && ! _this.icinga.isLoadedModule(moduleName)) {
_this.icinga.loadModule(moduleName);
}
}
$target.find('.icinga-module').each(function () {
moduleName = $(this).data('icingaModule');
if (_this.icinga.hasModule(moduleName) && !_this.icinga.isLoadedModule(moduleName)) {
_this.icinga.loadModule(moduleName);
}
});
},
treeNodeToggle: function () {
var $parent = $(this).closest('li');
if ($parent.hasClass('collapsed')) {
@ -167,7 +129,16 @@
onLoad: function (event) {
$('body').removeClass('loading');
//$('.container').trigger('rendered');
// First initialize already included modules
event.data.self.initializeModules();
// Then 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) {
@ -197,12 +168,25 @@
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;
if ($('#menu input.search').val() === _this.searchValue) {
var $searchField = $(event.target);
if ($searchField.val() === _this.searchValue) {
return;
}
_this.searchValue = $('#menu input.search').val();
_this.searchValue = $searchField.val();
if (_this.searchTimer !== null) {
clearTimeout(_this.searchTimer);
@ -210,11 +194,26 @@
}
var _event = $.extend({}, event); // event seems gc'd once the timeout is over
_this.searchTimer = setTimeout(function () {
_this.submitForm(_event, true);
_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');