diff --git a/library/Icingadb/Common/DetailActions.php b/library/Icingadb/Common/DetailActions.php
index b182b1f26..3358458d3 100644
--- a/library/Icingadb/Common/DetailActions.php
+++ b/library/Icingadb/Common/DetailActions.php
@@ -46,6 +46,9 @@ public function getDetailActionsDisabled(): bool
public function initializeDetailActions(): self
{
$this->getAttributes()
+ ->registerAttributeCallback('data-interactable-action-list', function () {
+ return $this->getDetailActionsDisabled() ? null : true;
+ })
->registerAttributeCallback('class', function () {
return $this->getDetailActionsDisabled() ? null : 'action-list';
})
diff --git a/public/js/action-list.js b/public/js/action-list.js
deleted file mode 100644
index c176a0db7..000000000
--- a/public/js/action-list.js
+++ /dev/null
@@ -1,789 +0,0 @@
-/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
-
-;(function (Icinga) {
-
- "use strict";
-
- try {
- var notjQuery = require('icinga/icinga-php-library/notjQuery');
- } catch (e) {
- console.warn('Unable to provide input enrichments. Libraries not available:', e);
- return;
- }
-
- Icinga.Behaviors = Icinga.Behaviors || {};
-
- class ActionList extends Icinga.EventListener {
- constructor(icinga) {
- super(icinga);
-
- this.on('click', '.action-list [data-action-item]:not(.page-separator), .action-list [data-action-item] a[href]', this.onClick, this);
- this.on('close-column', '#main > #col2', this.onColumnClose, this);
- this.on('column-moved', this.onColumnMoved, this);
-
- this.on('rendered', '#main .container', this.onRendered, this);
- this.on('keydown', '#body', this.onKeyDown, this);
-
- this.on('click', '.load-more[data-no-icinga-ajax] a', this.onLoadMoreClick, this);
- this.on('keypress', '.load-more[data-no-icinga-ajax] a', this.onKeyPress, this);
-
- this.lastActivatedItemUrl = null;
- this.lastTimeoutId = null;
- this.isProcessingLoadMore = false;
- this.activeRequests = {};
- }
-
- /**
- * Parse the filter query contained in the given URL query string
- *
- * @param {string} queryString
- *
- * @returns {array}
- */
- parseSelectionQuery(queryString) {
- return queryString.split('|');
- }
-
- /**
- * Suspend auto refresh for the given item's container
- *
- * @param {Element} item
- *
- * @return {string} The container's id
- */
- suspendAutoRefresh(item) {
- const container = item.closest('.container');
- container.dataset.suspendAutorefresh = '';
-
- return container.id;
- }
-
- /**
- * Enable auto refresh on the given container
- *
- * @param {string} containerId
- */
- enableAutoRefresh(containerId) {
- delete document.getElementById(containerId).dataset.suspendAutorefresh;
- }
-
- onClick(event) {
- let _this = event.data.self;
- let target = event.currentTarget;
-
- if (target.matches('a') && (! target.matches('.subject') || event.ctrlKey || event.metaKey)) {
- return true;
- }
-
- event.preventDefault();
- event.stopImmediatePropagation();
- event.stopPropagation();
-
- let item = target.closest('[data-action-item]');
- let list = target.closest('.action-list');
- let activeItems = _this.getActiveItems(list);
- let toActiveItems = [],
- toDeactivateItems = [];
-
- const isBeingMultiSelected = list.matches('[data-icinga-multiselect-url]')
- && (event.ctrlKey || event.metaKey || event.shiftKey);
-
- if (isBeingMultiSelected) {
- if (event.ctrlKey || event.metaKey) {
- if (item.classList.contains('active')) {
- toDeactivateItems.push(item);
- } else {
- toActiveItems.push(item);
- }
- } else {
- document.getSelection().removeAllRanges();
-
- let allItems = _this.getAllItems(list);
-
- let startIndex = allItems.indexOf(item);
- if(startIndex < 0) {
- startIndex = 0;
- }
-
- let endIndex = activeItems.length ? allItems.indexOf(activeItems[0]) : 0;
- if (startIndex > endIndex) {
- toActiveItems = allItems.slice(endIndex, startIndex + 1);
- } else {
- endIndex = activeItems.length ? allItems.indexOf(activeItems[activeItems.length - 1]) : 0;
- toActiveItems = allItems.slice(startIndex, endIndex + 1);
- }
-
- toDeactivateItems = activeItems.filter(item => ! toActiveItems.includes(item));
- toActiveItems = toActiveItems.filter(item => ! activeItems.includes(item));
- }
- } else {
- toDeactivateItems = activeItems;
- toActiveItems.push(item);
- }
-
- if (activeItems.length === 1
- && toActiveItems.length === 0
- && _this.icinga.loader.getLinkTargetFor($(target)).attr('id') === 'col2'
- ) {
- _this.icinga.ui.layout1col();
- _this.icinga.history.pushCurrentState();
- _this.enableAutoRefresh('col1');
- return;
- }
-
- let dashboard = list.closest('.dashboard');
- if (dashboard) {
- dashboard.querySelectorAll('.action-list').forEach(otherList => {
- if (otherList !== list) {
- toDeactivateItems.push(..._this.getAllItems(otherList));
- }
- })
- }
-
- let lastActivatedUrl = null;
- if (toActiveItems.includes(item)) {
- lastActivatedUrl = item.dataset.icingaDetailFilter;
- } else if (activeItems.length > 1) {
- lastActivatedUrl = activeItems[activeItems.length - 1] === item
- ? activeItems[activeItems.length - 2].dataset.icingaDetailFilter
- : activeItems[activeItems.length - 1].dataset.icingaDetailFilter;
- }
-
- _this.clearSelection(toDeactivateItems);
- _this.setActive(toActiveItems);
-
- if (! dashboard) {
- _this.addSelectionCountToFooter(list);
- }
-
- _this.setLastActivatedItemUrl(lastActivatedUrl);
- _this.loadDetailUrl(list, target.matches('a') ? target.getAttribute('href') : null);
- }
-
- /**
- * Add the selection count to footer if list allow multi selection
- *
- * @param list
- */
- addSelectionCountToFooter(list) {
- if (! list.matches('[data-icinga-multiselect-url]')) {
- return;
- }
-
- let activeItemCount = this.getActiveItems(list).length;
- let footer = list.closest('.container').querySelector('.footer');
-
- // For items that do not have a bottom status bar like Downtimes, Comments...
- if (footer === null) {
- footer = notjQuery.render(
- '
'
- )
-
- list.closest('.container').appendChild(footer);
- }
-
- let selectionCount = footer.querySelector('.selection-count');
- if (selectionCount === null) {
- selectionCount = notjQuery.render(
- '
'
- );
-
- footer.prepend(selectionCount);
- }
-
- let selectedItems = selectionCount.querySelector('.selected-items');
- selectedItems.innerText = activeItemCount
- ? list.dataset.icingaMultiselectCountLabel.replace('%d', activeItemCount)
- : list.dataset.icingaMultiselectHintLabel;
-
- if (activeItemCount === 0) {
- selectedItems.classList.add('hint');
- } else {
- selectedItems.classList.remove('hint');
- }
- }
-
- /**
- * Key navigation for .action-list
- *
- * - `Shift + ArrowUp|ArrowDown` = Multiselect
- * - `ArrowUp|ArrowDown` = Select next/previous
- * - `Ctrl|cmd + A` = Select all on currect page
- *
- * @param event
- */
- onKeyDown(event) {
- let _this = event.data.self;
- let list = null;
- let pressedArrowDownKey = event.key === 'ArrowDown';
- let pressedArrowUpKey = event.key === 'ArrowUp';
- let focusedElement = document.activeElement;
-
- if (
- _this.isProcessingLoadMore
- || ! event.key // input auto-completion is triggered
- || (event.key.toLowerCase() !== 'a' && ! pressedArrowDownKey && ! pressedArrowUpKey)
- ) {
- return;
- }
-
- if (focusedElement && (
- focusedElement.matches('#main > :scope')
- || focusedElement.matches('#body'))
- ) {
- let activeItem = document.querySelector(
- '#main > .container > .content > .action-list [data-action-item].active'
- );
- if (activeItem) {
- list = activeItem.closest('.action-list');
- } else {
- list = focusedElement.querySelector('#main > .container > .content > .action-list');
- }
- } else if (focusedElement) {
- list = focusedElement.closest('.content > .action-list');
- }
-
- if (! list) {
- return;
- }
-
- let isMultiSelectableList = list.matches('[data-icinga-multiselect-url]');
-
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'a') {
- if (! isMultiSelectableList) {
- return;
- }
-
- event.preventDefault();
- _this.selectAll(list);
- return;
- }
-
- event.preventDefault();
-
- let allItems = _this.getAllItems(list);
- let firstListItem = allItems[0];
- let lastListItem = allItems[allItems.length -1];
- let activeItems = _this.getActiveItems(list);
- let markAsLastActive = null; // initialized only if it is different from toActiveItem
- let toActiveItem = null;
- let wasAllSelected = activeItems.length === allItems.length;
- let lastActivatedItem = list.querySelector(
- `[data-icinga-detail-filter="${ _this.lastActivatedItemUrl }"]`
- );
-
- if (! lastActivatedItem && activeItems.length) {
- lastActivatedItem = activeItems[activeItems.length - 1];
- }
-
- let directionalNextItem = _this.getDirectionalNext(lastActivatedItem, event.key);
-
- if (activeItems.length === 0) {
- toActiveItem = pressedArrowDownKey ? firstListItem : lastListItem;
- // reset all on manual page refresh
- _this.clearSelection(activeItems);
- if (toActiveItem.classList.contains('load-more')) {
- toActiveItem = toActiveItem.previousElementSibling;
- }
- } else if (isMultiSelectableList && event.shiftKey) {
- if (activeItems.length === 1) {
- toActiveItem = directionalNextItem;
- } else if (wasAllSelected && (
- (lastActivatedItem !== firstListItem && pressedArrowDownKey)
- || (lastActivatedItem !== lastListItem && pressedArrowUpKey)
- )) {
- if (pressedArrowDownKey) {
- toActiveItem = lastActivatedItem === lastListItem ? null : lastListItem;
- } else {
- toActiveItem = lastActivatedItem === firstListItem ? null : lastListItem;
- }
-
- } else if (directionalNextItem && directionalNextItem.classList.contains('active')) {
- // deactivate last activated by down to up select
- _this.clearSelection([lastActivatedItem]);
- if (wasAllSelected) {
- _this.scrollItemIntoView(lastActivatedItem, event.key);
- }
-
- toActiveItem = directionalNextItem;
- } else {
- [toActiveItem, markAsLastActive] = _this.findToActiveItem(lastActivatedItem, event.key);
- }
- } else {
- toActiveItem = directionalNextItem ?? lastActivatedItem;
-
- if (toActiveItem) {
- if (toActiveItem.classList.contains('load-more')) {
- clearTimeout(_this.lastTimeoutId);
- _this.handleLoadMoreNavigate(toActiveItem, lastActivatedItem, event.key);
- return;
- }
-
- _this.clearSelection(activeItems);
- if (toActiveItem.classList.contains('page-separator')) {
- toActiveItem = _this.getDirectionalNext(toActiveItem, event.key);
- }
- }
- }
-
- if (! toActiveItem) {
- return;
- }
-
- _this.setActive(toActiveItem);
- _this.setLastActivatedItemUrl(
- markAsLastActive ? markAsLastActive.dataset.icingaDetailFilter : toActiveItem.dataset.icingaDetailFilter
- );
- _this.scrollItemIntoView(toActiveItem, event.key);
- _this.addSelectionCountToFooter(list);
- _this.loadDetailUrl(list);
- }
-
- /**
- * Get the next list item according to the pressed key (`ArrowUp` or `ArrowDown`)
- *
- * @param item The list item from which we want the next item
- * @param eventKey Pressed key (`ArrowUp` or `ArrowDown`)
- *
- * @returns {Element|null}
- */
- getDirectionalNext(item, eventKey) {
- if (! item) {
- return null;
- }
-
- return eventKey === 'ArrowUp' ? item.previousElementSibling : item.nextElementSibling;
- }
-
- /**
- * Find the list item that should be activated next
- *
- * @param lastActivatedItem
- * @param eventKey Pressed key (`ArrowUp` or `ArrowDown`)
- *
- * @returns {Element[]}
- */
- findToActiveItem(lastActivatedItem, eventKey) {
- let toActiveItem;
- let markAsLastActive;
-
- toActiveItem = this.getDirectionalNext(lastActivatedItem, eventKey);
-
- while (toActiveItem) {
- if (! toActiveItem.classList.contains('active')) {
- break;
- }
-
- toActiveItem = this.getDirectionalNext(toActiveItem, eventKey);
- }
-
- markAsLastActive = toActiveItem;
- // if the next/previous sibling element is already active,
- // mark the last/first active element in list as last active
- while (markAsLastActive && this.getDirectionalNext(markAsLastActive, eventKey)) {
- if (! this.getDirectionalNext(markAsLastActive, eventKey).classList.contains('active')) {
- break;
- }
-
- markAsLastActive = this.getDirectionalNext(markAsLastActive, eventKey);
- }
-
- return [toActiveItem, markAsLastActive];
- }
-
- /**
- * Select All list items
- *
- * @param list The action list
- */
- selectAll(list) {
- let allItems = this.getAllItems(list);
- let activeItems = this.getActiveItems(list);
- this.setActive(allItems.filter(item => ! activeItems.includes(item)));
- this.setLastActivatedItemUrl(allItems[allItems.length -1].dataset.icingaDetailFilter);
- this.addSelectionCountToFooter(list);
- this.loadDetailUrl(list);
- }
-
- /**
- * Clear the selection by removing .active class
- *
- * @param selectedItems The items with class active
- */
- clearSelection(selectedItems) {
- selectedItems.forEach(item => item.classList.remove('active'));
- }
-
- /**
- * Set the last activated item Url
- *
- * @param url
- */
- setLastActivatedItemUrl (url) {
- this.lastActivatedItemUrl = url;
- }
-
- /**
- * Scroll the given item into view
- *
- * @param item Item to scroll into view
- * @param pressedKey Pressed key (`ArrowUp` or `ArrowDown`)
- */
- scrollItemIntoView(item, pressedKey) {
- item.scrollIntoView({block: "nearest"});
- let directionalNext = this.getDirectionalNext(item, pressedKey);
-
- if (directionalNext) {
- directionalNext.scrollIntoView({block: "nearest"});
- }
- }
-
- /**
- * Load the detail url with selected items
- *
- * @param list The action list
- * @param anchorUrl If any anchor is clicked (e.g. host in service list)
- */
- loadDetailUrl(list, anchorUrl = null) {
- let url = anchorUrl;
- let activeItems = this.getActiveItems(list);
-
- if (url === null) {
- if (activeItems.length > 1) {
- url = this.createMultiSelectUrl(activeItems);
- } else {
- let anchor = activeItems[0].querySelector('[href]');
- url = anchor ? anchor.getAttribute('href') : null;
- }
- }
-
- if (url === null) {
- return;
- }
-
- const suspendedContainer = this.suspendAutoRefresh(list);
-
- clearTimeout(this.lastTimeoutId);
- this.lastTimeoutId = setTimeout(() => {
- const requestNo = this.lastTimeoutId;
- this.activeRequests[requestNo] = suspendedContainer;
- this.lastTimeoutId = null;
-
- let req = this.icinga.loader.loadUrl(
- url,
- this.icinga.loader.getLinkTargetFor($(activeItems[0]))
- );
-
- req.always((_, __, errorThrown) => {
- if (errorThrown !== 'abort') {
- this.enableAutoRefresh(this.activeRequests[requestNo]);
- }
-
- delete this.activeRequests[requestNo];
- });
- }, 250);
- }
-
- /**
- * Add .active class to given list item
- *
- * @param toActiveItem The list item(s)
- */
- setActive(toActiveItem) {
- if (toActiveItem instanceof HTMLElement) {
- toActiveItem.classList.add('active');
- } else {
- toActiveItem.forEach(item => item.classList.add('active'));
- }
- }
-
- /**
- * Get the active items from given list
- *
- * @param list The action list
- *
- * @return array
- */
- getActiveItems(list)
- {
- let items;
- if (list.tagName.toLowerCase() === 'table') {
- items = list.querySelectorAll(':scope > tbody > [data-action-item].active');
- } else {
- items = list.querySelectorAll(':scope > [data-action-item].active');
- }
-
- return Array.from(items);
- }
-
- /**
- * Get all available items from given list
- *
- * @param list The action list
- *
- * @return array
- */
- getAllItems(list)
- {
- let items;
- if (list.tagName.toLowerCase() === 'table') {
- items = list.querySelectorAll(':scope > tbody > [data-action-item]');
- } else {
- items = list.querySelectorAll(':scope > [data-action-item]');
- }
-
- return Array.from(items);
- }
-
- /**
- * Handle the navigation on load-more button
- *
- * @param loadMoreElement
- * @param lastActivatedItem
- * @param pressedKey Pressed key (`ArrowUp` or `ArrowDown`)
- */
- handleLoadMoreNavigate(loadMoreElement, lastActivatedItem, pressedKey) {
- let req = this.loadMore(loadMoreElement.firstChild);
- this.isProcessingLoadMore = true;
- req.done(() => {
- this.isProcessingLoadMore = false;
- // list has now new items, so select the lastActivatedItem and then move forward
- let toActiveItem = lastActivatedItem.nextElementSibling;
- while (toActiveItem) {
- if (toActiveItem.hasAttribute('data-action-item')) {
- this.clearSelection([lastActivatedItem]);
- this.setActive(toActiveItem);
- this.setLastActivatedItemUrl(toActiveItem.dataset.icingaDetailFilter);
- this.scrollItemIntoView(toActiveItem, pressedKey);
- this.addSelectionCountToFooter(toActiveItem.parentElement);
- this.loadDetailUrl(toActiveItem.parentElement);
- return;
- }
-
- toActiveItem = toActiveItem.nextElementSibling;
- }
- });
- }
-
- /**
- * Click on load-more button
- *
- * @param event
- *
- * @returns {boolean}
- */
- onLoadMoreClick(event) {
- event.stopPropagation();
- event.preventDefault();
-
- event.data.self.loadMore(event.target);
-
- return false;
- }
-
- onKeyPress(event) {
- if (event.key === ' ') { // space
- event.data.self.onLoadMoreClick(event);
- }
- }
-
- /**
- * Load more list items based on the given anchor
- *
- * @param anchor
- *
- * @returns {*|{getAllResponseHeaders: function(): *|null, abort: function(*): this, setRequestHeader: function(*, *): this, readyState: number, getResponseHeader: function(*): null|*, overrideMimeType: function(*): this, statusCode: function(*): this}|jQuery|boolean}
- */
- loadMore(anchor) {
- let showMore = anchor.parentElement;
- var progressTimer = this.icinga.timer.register(function () {
- var label = anchor.innerText;
-
- var dots = label.substr(-3);
- if (dots.slice(0, 1) !== '.') {
- dots = '. ';
- } else {
- label = label.slice(0, -3);
- if (dots === '...') {
- dots = '. ';
- } else if (dots === '.. ') {
- dots = '...';
- } else if (dots === '. ') {
- dots = '.. ';
- }
- }
-
- anchor.innerText = label + dots;
- }, null, 250);
-
- let url = anchor.getAttribute('href');
- let req = this.icinga.loader.loadUrl(
- // Add showCompact, we don't want controls in paged results
- this.icinga.utils.addUrlFlag(url, 'showCompact'),
- $(showMore.parentElement),
- undefined,
- undefined,
- 'append',
- false,
- progressTimer
- );
- req.addToHistory = false;
- req.done(function () {
- showMore.remove();
-
- // Set data-icinga-url to make it available for Icinga.History.getCurrentState()
- req.$target.closest('.container').data('icingaUrl', url);
-
- this.icinga.history.replaceCurrentState();
- });
-
- return req;
- }
-
- /**
- * Create the detail url for multi selectable list
- *
- * @param items List items
- * @param withBaseUrl Default to true
- *
- * @returns {string} The url
- */
- createMultiSelectUrl(items, withBaseUrl = true) {
- let filters = [];
- items.forEach(item => {
- filters.push(item.getAttribute('data-icinga-multiselect-filter'));
- });
-
- let url = '?' + filters.join('|');
-
- if (withBaseUrl) {
- return items[0].closest('.action-list').getAttribute('data-icinga-multiselect-url') + url;
- }
-
- return url;
- }
-
- onColumnClose(event) {
- let _this = event.data.self;
- let list = _this.findDetailUrlActionList(document.getElementById('col1'));
- if (list && list.matches('[data-icinga-multiselect-url], [data-icinga-detail-url]')) {
- _this.clearSelection(_this.getActiveItems(list));
- _this.addSelectionCountToFooter(list);
- }
- }
-
- /**
- * Find the action list using the detail url
- *
- * @param {Element} container
- *
- * @return Element|null
- */
- findDetailUrlActionList(container) {
- let detailUrl = this.icinga.utils.parseUrl(
- this.icinga.history.getCol2State().replace(/^#!/, '')
- );
-
- let detailItem = container.querySelector(
- '[data-icinga-detail-filter="'
- + detailUrl.query.replace('?', '') + '"],' +
- '[data-icinga-multiselect-filter="'
- + detailUrl.query.split('|', 1).toString().replace('?', '') + '"]'
- );
-
- return detailItem ? detailItem.parentElement : null;
- }
-
- /**
- * Triggers when column is moved to left or right
- *
- * @param event
- * @param sourceId The content is moved from
- */
- onColumnMoved(event, sourceId) {
- let _this = event.data.self;
-
- if (event.target.id === 'col2' && sourceId === 'col1') { // only for browser-back (col1 shifted to col2)
- _this.clearSelection(event.target.querySelectorAll('.action-list .active'));
- } else if (event.target.id === 'col1' && sourceId === 'col2') {
- for (const requestNo of Object.keys(_this.activeRequests)) {
- if (_this.activeRequests[requestNo] === sourceId) {
- _this.enableAutoRefresh(_this.activeRequests[requestNo]);
- _this.activeRequests[requestNo] = _this.suspendAutoRefresh(event.target);
- }
- }
- }
- }
-
- onRendered(event, isAutoRefresh) {
- let _this = event.data.self;
- let container = event.target;
- let isTopLevelContainer = container.matches('#main > :scope');
-
- let list;
- if (event.currentTarget !== container || Object.keys(_this.activeRequests).length) {
- // Nested containers are not processed multiple times || still processing selection/navigation request
- return;
- } else if (isTopLevelContainer && container.id !== 'col1') {
- if (isAutoRefresh) {
- return;
- }
-
- // only for browser back/forward navigation
- list = _this.findDetailUrlActionList(document.getElementById('col1'));
- } else {
- list = _this.findDetailUrlActionList(container);
- }
-
- if (list && list.matches('[data-icinga-multiselect-url], [data-icinga-detail-url]')) {
- let detailUrl = _this.icinga.utils.parseUrl(
- _this.icinga.history.getCol2State().replace(/^#!/, '')
- );
- let toActiveItems = [];
- if (list.dataset.icingaMultiselectUrl === detailUrl.path) {
- for (const filter of _this.parseSelectionQuery(detailUrl.query.slice(1))) {
- let item = list.querySelector(
- '[data-icinga-multiselect-filter="' + filter + '"]'
- );
-
- if (item) {
- toActiveItems.push(item);
- }
- }
- } else if (_this.matchesDetailUrl(list.dataset.icingaDetailUrl, detailUrl.path)) {
- let item = list.querySelector(
- '[data-icinga-detail-filter="' + detailUrl.query.slice(1) + '"]'
- );
-
- if (item) {
- toActiveItems.push(item);
- }
- }
-
- _this.clearSelection(_this.getAllItems(list).filter(item => !toActiveItems.includes(item)));
- _this.setActive(toActiveItems);
- }
-
- if (isTopLevelContainer) {
- let footerList = list ?? container.querySelector('.content > .action-list');
- if (footerList) {
- _this.addSelectionCountToFooter(footerList);
- }
- }
- }
-
- matchesDetailUrl(itemUrl, detailUrl) {
- if (itemUrl === detailUrl) {
- return true;
- }
-
- // The slash is used to avoid false positives (e.g. icingadb/hostgroup and icingadb/host)
- return detailUrl.startsWith(itemUrl + '/');
- }
- }
-
- Icinga.Behaviors.ActionList = ActionList;
-
-}(Icinga));
diff --git a/public/js/module.js b/public/js/module.js
new file mode 100644
index 000000000..f920ee444
--- /dev/null
+++ b/public/js/module.js
@@ -0,0 +1,36 @@
+/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
+
+(function (Icinga, $) {
+
+ "use strict";
+
+ class IcingaDB {
+ /**
+ * Constructor
+ *
+ * @param {Icinga.Module} module
+ */
+ constructor(module) {
+ try {
+ let Class = require("icinga/icinga-php-library/compat/ActionListBehavior");
+ let behavior = new Class(module.icinga);
+ module.icinga.behaviors[Class.name.toLowerCase()] = behavior;
+ behavior.bind($(document));
+
+ Class = require("icinga/icinga-php-library/compat/LoadMoreBehavior");
+ behavior = new Class(module.icinga);
+ module.icinga.behaviors[Class.name.toLowerCase()] = behavior;
+ behavior.bind($(document));
+
+ } catch (e) {
+ console.warn('Unable to provide behaviors. Libraries not available:', e);
+ return;
+ }
+
+ this.icinga = module.icinga;
+ }
+ }
+
+ Icinga.availableModules.icingadb = IcingaDB;
+
+})(Icinga, jQuery);