You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2242 lines
59 KiB

/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
const ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES = 'btn-dashboard-page-properties';
const ZBX_STYLE_DASHBOARD_IS_MULTIPAGE = 'dashboard-is-multipage';
const ZBX_STYLE_DASHBOARD_IS_EDIT_MODE = 'dashboard-is-edit-mode';
const ZBX_STYLE_DASHBOARD_NAVIGATION_IS_SCROLLABLE = 'is-scrollable';
const ZBX_STYLE_DASHBOARD_SELECTED_TAB = 'selected-tab';
const DASHBOARD_STATE_INITIAL = 'initial';
const DASHBOARD_STATE_ACTIVE = 'active';
const DASHBOARD_CLIPBOARD_TYPE_WIDGET = 'widget';
const DASHBOARD_CLIPBOARD_TYPE_DASHBOARD_PAGE = 'dashboard-page';
const DASHBOARD_EVENT_BUSY = 'dashboard-busy';
const DASHBOARD_EVENT_IDLE = 'dashboard-idle';
const DASHBOARD_EVENT_EDIT = 'dashboard-edit';
const DASHBOARD_EVENT_APPLY_PROPERTIES = 'dashboard-apply-properties';
const DASHBOARD_EVENT_CONFIGURATION_OUTDATED = 'dashboard-configuration-outdated';
class CDashboard {
constructor(target, {
containers,
buttons,
data,
max_dashboard_pages,
cell_width,
cell_height,
max_columns,
max_rows,
widget_min_rows,
widget_max_rows,
widget_defaults,
widget_last_type = null,
configuration_hash = null,
is_editable,
is_edit_mode,
can_edit_dashboards,
is_kiosk_mode,
time_period,
dynamic_hostid,
csrf_token = null
}) {
this._target = target;
this._containers = {
grid: containers.grid,
navigation: containers.navigation,
navigation_tabs: containers.navigation_tabs
}
this._buttons = {
previous_page: buttons.previous_page,
next_page: buttons.next_page,
slideshow: buttons.slideshow
};
this._data = {
dashboardid: data.dashboardid,
name: data.name,
userid: data.userid,
templateid: data.templateid,
display_period: data.display_period,
auto_start: data.auto_start
};
this._max_dashboard_pages = max_dashboard_pages;
this._cell_width = cell_width;
this._cell_height = cell_height;
this._max_columns = max_columns;
this._max_rows = max_rows;
this._widget_min_rows = widget_min_rows;
this._widget_max_rows = widget_max_rows;
this._widget_defaults = {...widget_defaults};
this._widget_last_type = widget_last_type;
this._configuration_hash = configuration_hash;
this._is_editable = is_editable;
this._is_edit_mode = is_edit_mode;
this._can_edit_dashboards = can_edit_dashboards;
this._is_kiosk_mode = is_kiosk_mode;
this._time_period = time_period;
this._dynamic_hostid = dynamic_hostid;
this._csrf_token = csrf_token;
this._init();
this._registerEvents();
}
_init() {
this._state = DASHBOARD_STATE_INITIAL;
this._dashboard_pages = new Map();
this._selected_dashboard_page = null;
this._busy_conditions = new Set();
this._async_timeout_ms = 50;
this._unique_id_index = 0;
this._new_widget_dashboard_page = null;
this._new_widget_pos = null;
this._new_widget_pos_reserved = null;
this._warning_message_box = null;
this._reserve_header_lines = 0;
this._reserve_header_lines_timeout_id = null;
this._is_edit_widget_properties_cancel_subscribed = false;
this._header_lines_steady_period = 2000;
this._slideshow_steady_period = 5000;
this._slideshow_switch_time = null;
this._slideshow_timeout_id = null;
this._configuration_check_period = 60000;
this._configuration_check_steady_period = 2000;
this._configuration_check_time = null;
this._configuration_check_timeout_id = null;
this._is_unsaved = false;
if (!this._is_kiosk_mode) {
const sortable = document.createElement('div');
this._containers.navigation_tabs.appendChild(sortable);
this._tabs = new CSortable(sortable, {
is_vertical: false,
is_sorting_enabled: this._is_edit_mode
});
this._tabs_dashboard_pages = new Map();
}
}
// Logical state control methods.
activate() {
if (this._dashboard_pages.size == 0) {
throw new Error('Cannot activate dashboard without dashboard pages.');
}
this._state = DASHBOARD_STATE_ACTIVE;
this._activateEvents();
this._announceWidgets();
const dashboard_page = this._getInitialDashboardPage();
this._selectDashboardPage(dashboard_page);
if (this._is_edit_mode) {
this._target.classList.add(ZBX_STYLE_DASHBOARD_IS_EDIT_MODE);
}
if (!this._is_edit_mode) {
this._startConfigurationChecker();
if (this._data.auto_start == 1 && this._dashboard_pages.size > 1) {
this._startSlideshow();
}
}
}
// External events management methods.
isEditMode() {
return this._is_edit_mode;
}
setEditMode({is_internal_call = false} = {}) {
this._is_edit_mode = true;
for (const dashboard_page of this._dashboard_pages.keys()) {
if (!dashboard_page.isEditMode()) {
dashboard_page.setEditMode();
}
}
if (!this._is_kiosk_mode) {
this._tabs.enableSorting();
}
this._stopConfigurationChecker();
this._stopSlideshow();
this._target.classList.add(ZBX_STYLE_DASHBOARD_IS_EDIT_MODE);
if (is_internal_call) {
this.fire(DASHBOARD_EVENT_EDIT);
}
}
setDynamicHost(dynamic_hostid) {
this._dynamic_hostid = dynamic_hostid;
for (const dashboard_page of this._dashboard_pages.keys()) {
dashboard_page.setDynamicHost(this._dynamic_hostid);
}
}
_startSlideshow() {
if (this._slideshow_timeout_id !== null) {
clearTimeout(this._slideshow_timeout_id);
}
if (this._buttons.slideshow !== null) {
if (this._is_kiosk_mode) {
this._buttons.slideshow.classList.remove(ZBX_ICON_PLAY);
this._buttons.slideshow.classList.add(ZBX_ICON_PAUSE);
}
this._buttons.slideshow.classList.remove('slideshow-state-stopped');
this._buttons.slideshow.classList.add('slideshow-state-started');
if (this._buttons.slideshow.title !== '') {
this._buttons.slideshow.title = t('Stop slideshow');
}
}
let timeout_ms = this._selected_dashboard_page.getDisplayPeriod() * 1000;
if (timeout_ms == 0) {
timeout_ms = this._data.display_period * 1000;
}
this._slideshow_switch_time = Date.now() + timeout_ms;
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(), timeout_ms);
}
_stopSlideshow() {
if (this._slideshow_timeout_id === null) {
return;
}
if (this._buttons.slideshow !== null) {
if (this._is_kiosk_mode) {
this._buttons.slideshow.classList.remove(ZBX_ICON_PAUSE);
this._buttons.slideshow.classList.add(ZBX_ICON_PLAY);
}
this._buttons.slideshow.classList.remove('slideshow-state-started');
this._buttons.slideshow.classList.add('slideshow-state-stopped');
if (this._buttons.slideshow.title !== '') {
this._buttons.slideshow.title = t('Start slideshow');
}
}
clearTimeout(this._slideshow_timeout_id);
this._slideshow_switch_time = null;
this._slideshow_timeout_id = null;
}
_switchSlideshow() {
this._slideshow_timeout_id = null;
if (this._isUserInteracting()) {
this._slideshow_switch_time = Date.now() + this._slideshow_steady_period;
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(), this._slideshow_steady_period);
return;
}
const dashboard_pages = [...this._dashboard_pages.keys()];
const dashboard_page_index = dashboard_pages.indexOf(this._selected_dashboard_page);
this._selectDashboardPage(
dashboard_pages[dashboard_page_index < dashboard_pages.length - 1 ? dashboard_page_index + 1 : 0]
)
let timeout_ms = this._selected_dashboard_page.getDisplayPeriod() * 1000;
if (timeout_ms == 0) {
timeout_ms = this._data.display_period * 1000;
}
this._slideshow_switch_time = Math.max(Date.now() + this._slideshow_steady_period,
this._slideshow_switch_time + timeout_ms
);
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(),
this._slideshow_switch_time - Date.now()
);
}
_isSlideshowRunning() {
return this._slideshow_timeout_id !== null;
}
_keepSteadySlideshow() {
if (this._slideshow_timeout_id === null) {
return;
}
if (this._slideshow_switch_time - Date.now() < this._slideshow_steady_period) {
clearTimeout(this._slideshow_timeout_id);
this._slideshow_switch_time = Date.now() + this._slideshow_steady_period;
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(),
this._slideshow_switch_time - Date.now()
);
}
}
_startConfigurationChecker() {
if (this._configuration_check_timeout_id !== null) {
clearTimeout(this._configuration_check_timeout_id);
}
this._configuration_check_time = Date.now() + this._configuration_check_period;
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
this._configuration_check_period
);
}
_stopConfigurationChecker() {
if (this._configuration_check_timeout_id === null) {
return;
}
clearTimeout(this._configuration_check_timeout_id);
this._configuration_check_time = null;
this._configuration_check_timeout_id = null;
}
_checkConfiguration() {
this._configuration_check_timeout_id = null;
if (this._isUserInteracting()) {
this._configuration_check_time = Date.now() + this._configuration_check_steady_period;
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
this._configuration_check_steady_period
);
return;
}
const busy_condition = this._createBusyCondition();
Promise.resolve()
.then(() => this._promiseCheckConfiguration())
.catch((exception) => {
console.log('Could not check the dashboard configuration', exception);
})
.finally(() => {
this._configuration_check_time = Math.max(Date.now() + this._configuration_check_steady_period,
this._configuration_check_time + this._configuration_check_period
);
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
this._configuration_check_time - Date.now()
);
this._deleteBusyCondition(busy_condition);
});
}
_promiseCheckConfiguration() {
const curl = new Curl('zabbix.php');
curl.setArgument('action', 'dashboard.config.hash');
return fetch(curl.getUrl(), {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
templateid: this._data.templateid ?? undefined,
dashboardid: this._data.dashboardid
})
})
.then((response) => response.json())
.then((response) => {
if ('error' in response) {
throw {error: response.error};
}
if (response.configuration_hash !== null && this._configuration_hash !== response.configuration_hash) {
this.fire(DASHBOARD_EVENT_CONFIGURATION_OUTDATED);
}
});
}
_keepSteadyConfigurationChecker() {
if (this._configuration_check_timeout_id === null) {
return;
}
if (this._configuration_check_time - Date.now() < this._configuration_check_steady_period) {
clearTimeout(this._configuration_check_timeout_id);
this._configuration_check_time = Date.now() + this._configuration_check_steady_period;
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
this._configuration_check_time - Date.now()
);
}
}
_announceWidgets() {
const dashboard_pages = Array.from(this._dashboard_pages.keys());
for (const dashboard_page of dashboard_pages) {
dashboard_page.announceWidgets(dashboard_pages);
}
}
_createBusyCondition() {
if (this._busy_conditions.size == 0) {
this.fire(DASHBOARD_EVENT_BUSY);
}
const busy_condition = {};
this._busy_conditions.add(busy_condition);
return busy_condition;
}
_deleteBusyCondition(busy_condition) {
this._busy_conditions.delete(busy_condition);
if (this._busy_conditions.size == 0) {
this.fire(DASHBOARD_EVENT_IDLE);
}
}
isUnsaved() {
if (this._is_unsaved) {
return true;
}
for (const dashboard_page of this._dashboard_pages.keys()) {
if (dashboard_page.isUnsaved()) {
return true;
}
}
return false;
}
// Data interface methods.
getData() {
return this._data;
}
addNewDashboardPage() {
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
this._warnDashboardExhausted();
return;
}
this.editDashboardPageProperties();
}
addNewWidget() {
this.editWidgetProperties();
}
addDashboardPage({dashboard_pageid, name, display_period, widgets}) {
const dashboard_page = new CDashboardPage(this._containers.grid, {
data: {
dashboard_pageid,
name,
display_period
},
dashboard: {
templateid: this._data.templateid,
dashboardid: this._data.dashboardid
},
cell_width: this._cell_width,
cell_height: this._cell_height,
max_columns: this._max_columns,
max_rows: this._max_rows,
widget_min_rows: this._widget_min_rows,
widget_max_rows: this._widget_max_rows,
widget_defaults: this._widget_defaults,
is_editable: this._is_editable,
is_edit_mode: this._is_edit_mode,
can_edit_dashboards: this._can_edit_dashboards,
time_period: this._time_period,
dynamic_hostid: this._dynamic_hostid,
csrf_token: this._csrf_token,
unique_id: this._createUniqueId()
});
this._dashboard_pages.set(dashboard_page, {});
for (const widget_data of widgets) {
dashboard_page.addWidget({
...widget_data,
is_new: false,
unique_id: this._createUniqueId()
});
}
if (this._state === DASHBOARD_STATE_ACTIVE) {
this._announceWidgets();
}
if (!this._is_kiosk_mode) {
this._addTab(dashboard_page);
}
this._target.classList.toggle(ZBX_STYLE_DASHBOARD_IS_MULTIPAGE, this._dashboard_pages.size > 1);
if (dashboard_pageid === null) {
this._is_unsaved = true;
}
return dashboard_page;
}
deleteDashboardPage(dashboard_page) {
if (this._dashboard_pages.size == 1) {
throw new Error('Cannot delete the last dashboard page.');
}
if (dashboard_page === this._selected_dashboard_page) {
if (this._is_kiosk_mode) {
for (const select_dashboard_page of this._dashboard_pages.keys()) {
if (select_dashboard_page !== dashboard_page) {
this._selectDashboardPage(select_dashboard_page);
break;
}
}
}
else {
const tabs = [...this._tabs.getList().children];
const tab_index = tabs.indexOf(this._dashboard_pages.get(dashboard_page).tab);
this._selectDashboardPage(
this._tabs_dashboard_pages.get(tabs[tab_index > 0 ? tab_index - 1 : tab_index + 1])
);
}
}
if (dashboard_page.getState() !== DASHBOARD_PAGE_STATE_INITIAL) {
dashboard_page.destroy();
}
if (!this._is_kiosk_mode) {
this._deleteTab(dashboard_page);
}
this._dashboard_pages.delete(dashboard_page);
this._announceWidgets();
this._target.classList.toggle(ZBX_STYLE_DASHBOARD_IS_MULTIPAGE, this._dashboard_pages.size > 1);
this._is_unsaved = true;
}
getDashboardPage(unique_id) {
for (const dashboard_page of this._dashboard_pages.keys()) {
if (dashboard_page.getUniqueId() === unique_id) {
return dashboard_page;
}
}
return null;
}
getDashboardPages() {
return [...this._dashboard_pages.keys()];
}
pasteDashboardPage(new_dashboard_page_data) {
this._clearWarnings();
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
this._warnDashboardExhausted();
return;
}
const widgets = [];
for (const widget of new_dashboard_page_data.widgets) {
if (widget.type in this._widget_defaults) {
widgets.push(widget);
}
}
const busy_condition = this._createBusyCondition();
return Promise.resolve()
.then(() => this._promiseDashboardWidgetsSanitize(widgets))
.then((response) => {
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
this._warnDashboardExhausted();
return;
}
if (response.widgets.length < new_dashboard_page_data.widgets.length) {
this._warn(t('Inaccessible widgets were not pasted.'));
}
const sane_widgets = [];
for (let i = 0; i < response.widgets.length; i++) {
if (response.widgets[i] !== null) {
sane_widgets.push({
...widgets[i],
fields: response.widgets[i].fields
});
}
}
const used_references = this._getUsedReferences();
const reference_substitution = new Map();
for (const widget of sane_widgets) {
const widget_class = eval(this._widget_defaults[widget.type].js_class);
if (widget_class.hasReferenceField()) {
const old_reference = widget.fields.reference;
const new_reference = this._createReference({used_references});
widget.fields.reference = new_reference;
used_references.add(new_reference);
reference_substitution.set(old_reference, new_reference);
}
}
for (const widget of sane_widgets) {
const widget_class = eval(this._widget_defaults[widget.type].js_class);
for (const reference_field of widget_class.getForeignReferenceFields()) {
const old_reference = widget.fields[reference_field];
if (reference_substitution.has(old_reference)) {
widget.fields[reference_field] = reference_substitution.get(old_reference);
}
}
}
const dashboard_page = this.addDashboardPage({
dashboard_pageid: null,
name: new_dashboard_page_data.name,
display_period: new_dashboard_page_data.display_period,
widgets: sane_widgets
});
this._selectDashboardPage(dashboard_page, {is_async: true});
})
.catch((exception) => {
clearMessages();
let title;
let messages = [];
if (typeof exception === 'object' && 'error' in exception) {
title = exception.error.title;
messages = exception.error.messages;
}
else {
title = t('Failed to paste dashboard page.');
}
const message_box = makeMessageBox('bad', messages, title);
addMessage(message_box);
})
.finally(() => this._deleteBusyCondition(busy_condition))
}
pasteWidget(new_widget_data, {widget = null, new_widget_pos = null} = {}) {
this._clearWarnings();
if (!(new_widget_data.type in this._widget_defaults)) {
this._warn(t('Cannot paste inaccessible widget.'));
return;
}
const dashboard_page = this._selected_dashboard_page;
if (widget !== null) {
new_widget_pos = widget.getPos();
}
else if (new_widget_pos !== null) {
new_widget_pos = {
...new_widget_data.pos,
...new_widget_pos
};
new_widget_pos.width = Math.min(new_widget_pos.width, this._max_columns - new_widget_pos.x);
new_widget_pos.height = Math.min(new_widget_pos.height, this._max_rows - new_widget_pos.y);
new_widget_pos = dashboard_page.accommodatePos(new_widget_pos);
}
else {
new_widget_pos = dashboard_page.findFreePos(new_widget_data.pos);
}
if (new_widget_pos === null) {
this._warnDashboardPageExhausted();
return;
}
let old_widget_data = null;
if (widget !== null) {
old_widget_data = widget.getDataCopy({is_single_copy: false});
dashboard_page.deleteWidget(widget, {is_batch_mode: true});
}
const new_widget_class = eval(this._widget_defaults[new_widget_data.type].js_class);
if (new_widget_class.hasReferenceField()) {
new_widget_data.fields.reference = this._createReference();
}
let references = [];
for (const widget of dashboard_page.getWidgets()) {
if (widget.constructor.hasReferenceField()) {
references.push(widget.getFields()['reference']);
}
}
for (const reference_field of new_widget_class.getForeignReferenceFields()) {
if (reference_field in new_widget_data.fields
&& !references.includes(new_widget_data.fields[reference_field])) {
new_widget_data.fields[reference_field] = '';
}
}
const paste_placeholder_widget = dashboard_page.addPastePlaceholderWidget({
type: new_widget_data.type,
name: new_widget_data.name,
view_mode: new_widget_data.view_mode,
pos: new_widget_pos,
unique_id: this._createUniqueId()
});
dashboard_page.resetWidgetPlaceholder();
const busy_condition = this._createBusyCondition();
dashboard_page.promiseScrollIntoView(new_widget_pos)
.then(() => this._promiseDashboardWidgetsSanitize([new_widget_data]))
.then((response) => {
if (dashboard_page.getState() === DASHBOARD_PAGE_STATE_DESTROYED) {
return;
}
if (response.widgets[0] === null) {
if (widget !== null) {
dashboard_page.replaceWidget(paste_placeholder_widget, {
...old_widget_data,
widgetid: widget.getWidgetId(),
is_new: false,
unique_id: widget.getUniqueId()
});
}
else {
dashboard_page.deleteWidget(paste_placeholder_widget);
}
this._warn(t('Cannot paste inaccessible widget.'));
return;
}
dashboard_page.replaceWidget(paste_placeholder_widget, {
...new_widget_data,
fields: response.widgets[0].fields,
widgetid: null,
pos: new_widget_pos,
is_new: true,
unique_id: this._createUniqueId()
});
})
.catch((exception) => {
dashboard_page.deleteWidget(paste_placeholder_widget);
clearMessages();
let title, messages;
if (typeof exception === 'object' && 'error' in exception) {
title = exception.error.title;
messages = exception.error.messages;
}
else {
title = t('Failed to paste widget.');
}
const message_box = makeMessageBox('bad', messages, title);
addMessage(message_box);
})
.finally(() => this._deleteBusyCondition(busy_condition));
}
_promiseDashboardWidgetsSanitize(widgets_data) {
let request_widgets_data = [];
for (const widget_data of widgets_data) {
request_widgets_data.push({
type: widget_data.type,
fields: widget_data.fields
});
}
const curl = new Curl('zabbix.php');
curl.setArgument('action', 'dashboard.widgets.sanitize');
return fetch(curl.getUrl(), {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({widgets: request_widgets_data})
})
.then((response) => response.json())
.then((response) => {
if ('error' in response) {
throw {error: response.error};
}
return response;
});
}
_storeDashboardObject(data) {
localStorage.setItem('dashboard.clipboard', JSON.stringify({
zbx_session_name: window.ZBX_SESSION_NAME,
data
}));
}
_getStoredDashboardObject() {
let clipboard = localStorage.getItem('dashboard.clipboard');
if (clipboard === null) {
return null;
}
clipboard = JSON.parse(clipboard);
if (clipboard.zbx_session_name !== window.ZBX_SESSION_NAME) {
return null;
}
return clipboard.data;
}
_storeWidgetDataCopy(widget_data) {
this._storeDashboardObject({
type: DASHBOARD_CLIPBOARD_TYPE_WIDGET,
data: widget_data
});
}
getStoredWidgetDataCopy() {
const data = this._getStoredDashboardObject();
if (data === null || data.type !== DASHBOARD_CLIPBOARD_TYPE_WIDGET) {
return null;
}
const widget_data = data.data;
if (widget_data.dashboard.templateid !== this._data.templateid) {
return null;
}
return widget_data;
}
_storeDashboardPageDataCopy(dashboard_page_data) {
this._storeDashboardObject({
type: DASHBOARD_CLIPBOARD_TYPE_DASHBOARD_PAGE,
data: dashboard_page_data
});
}
getStoredDashboardPageDataCopy() {
const data = this._getStoredDashboardObject();
if (data === null || data.type !== DASHBOARD_CLIPBOARD_TYPE_DASHBOARD_PAGE) {
return null;
}
const dashboard_page_data = data.data;
if (dashboard_page_data.dashboard.templateid !== this._data.templateid) {
return null;
}
return dashboard_page_data;
}
_selectDashboardPage(dashboard_page, {is_async = false} = {}) {
if (this._data.templateid === null || !this._is_edit_mode) {
this._setInitialDashboardPage(dashboard_page);
}
if (!this._is_edit_mode) {
this._keepSteadyConfigurationChecker();
if (this._isSlideshowRunning()) {
this._keepSteadySlideshow();
}
}
this._promiseSelectDashboardPage(dashboard_page, {is_async})
.then(() => {
if (!this._is_edit_mode) {
this._keepSteadyConfigurationChecker();
if (this._isSlideshowRunning()) {
this._startSlideshow();
}
}
});
}
_promiseSelectDashboardPage(dashboard_page, {is_async = false} = {}) {
return new Promise((resolve) => {
if (this._is_kiosk_mode) {
this._doSelectDashboardPage(dashboard_page);
resolve();
}
else {
this._selectTab(dashboard_page);
if (is_async) {
setTimeout(() => {
this._doSelectDashboardPage(dashboard_page);
resolve();
}, this._async_timeout_ms);
}
else {
this._doSelectDashboardPage(dashboard_page);
resolve();
}
}
});
}
_doSelectDashboardPage(dashboard_page) {
if (this._selected_dashboard_page !== null) {
this._deactivatePage(this._selected_dashboard_page);
}
this._selected_dashboard_page = dashboard_page;
if (this._selected_dashboard_page.getState() === DASHBOARD_PAGE_STATE_INITIAL) {
this._selected_dashboard_page.start();
}
this._activatePage(this._selected_dashboard_page);
if (this._is_kiosk_mode) {
this._resetHeaderLines();
}
}
_activatePage(dashboard_page) {
dashboard_page.activate();
dashboard_page
.on(DASHBOARD_PAGE_EVENT_EDIT, this._events.dashboardPageEdit)
.on(DASHBOARD_PAGE_EVENT_WIDGET_ADD, this._events.dashboardPageWidgetAdd)
.on(DASHBOARD_PAGE_EVENT_WIDGET_ADD_NEW, this._events.dashboardPageWidgetAddNew)
.on(DASHBOARD_PAGE_EVENT_WIDGET_DELETE, this._events.dashboardPageWidgetDelete)
.on(DASHBOARD_PAGE_EVENT_WIDGET_POSITION, this._events.dashboardPageWidgetPosition)
.on(DASHBOARD_PAGE_EVENT_WIDGET_ACTIONS, this._events.dashboardPageWidgetActions)
.on(DASHBOARD_PAGE_EVENT_WIDGET_EDIT, this._events.dashboardPageWidgetEdit)
.on(DASHBOARD_PAGE_EVENT_WIDGET_COPY, this._events.dashboardPageWidgetCopy)
.on(DASHBOARD_PAGE_EVENT_WIDGET_PASTE, this._events.dashboardPageWidgetPaste)
.on(DASHBOARD_PAGE_EVENT_ANNOUNCE_WIDGETS, this._events.dashboardPageAnnounceWidgets);
if (this._is_kiosk_mode) {
dashboard_page.on(DASHBOARD_PAGE_EVENT_RESERVE_HEADER_LINES, this._events.dashboardPageReserveHeaderLines);
}
}
_deactivatePage(dashboard_page) {
dashboard_page.deactivate();
dashboard_page
.off(DASHBOARD_PAGE_EVENT_EDIT, this._events.dashboardPageEdit)
.off(DASHBOARD_PAGE_EVENT_WIDGET_ADD, this._events.dashboardPageWidgetAdd)
.off(DASHBOARD_PAGE_EVENT_WIDGET_ADD_NEW, this._events.dashboardPageWidgetAddNew)
.off(DASHBOARD_PAGE_EVENT_WIDGET_DELETE, this._events.dashboardPageWidgetDelete)
.off(DASHBOARD_PAGE_EVENT_WIDGET_POSITION, this._events.dashboardPageWidgetPosition)
.off(DASHBOARD_PAGE_EVENT_WIDGET_ACTIONS, this._events.dashboardPageWidgetActions)
.off(DASHBOARD_PAGE_EVENT_WIDGET_EDIT, this._events.dashboardPageWidgetEdit)
.off(DASHBOARD_PAGE_EVENT_WIDGET_COPY, this._events.dashboardPageWidgetCopy)
.off(DASHBOARD_PAGE_EVENT_WIDGET_PASTE, this._events.dashboardPageWidgetPaste)
.off(DASHBOARD_PAGE_EVENT_ANNOUNCE_WIDGETS, this._events.dashboardPageAnnounceWidgets);
if (this._is_kiosk_mode) {
dashboard_page.off(DASHBOARD_PAGE_EVENT_RESERVE_HEADER_LINES, this._events.dashboardPageReserveHeaderLines);
}
}
_setInitialDashboardPage(dashboard_page) {
const dashboard_page_index = this.getDashboardPageIndex(dashboard_page);
const url = new URL(location.href);
if (dashboard_page_index > 0) {
url.searchParams.set('page', dashboard_page_index + 1);
}
else {
url.searchParams.delete('page');
}
history.replaceState(null, null, url);
}
_getInitialDashboardPage() {
const url = new URL(location.href);
if (url.searchParams.has('page')) {
const dashboard_pages = [...this._dashboard_pages.keys()];
const dashboard_page_index = parseInt(url.searchParams.get('page')) - 1;
if (dashboard_page_index in dashboard_pages) {
return dashboard_pages[dashboard_page_index];
}
}
return this._dashboard_pages.keys().next().value;
}
getSelectedDashboardPage() {
return this._selected_dashboard_page;
}
getDashboardPageIndex(dashboard_page) {
if (this._is_kiosk_mode) {
const dashboard_pages = [...this._dashboard_pages.keys()];
return dashboard_pages.indexOf(dashboard_page);
}
const tabs = [...this._tabs.getList().children];
const data = this._dashboard_pages.get(dashboard_page);
return tabs.indexOf(data.tab);
}
save() {
const data = {
dashboardid: this._data.dashboardid ?? undefined,
name: this._data.name,
userid: this._data.userid ?? undefined,
templateid: this._data.templateid ?? undefined,
display_period: this._data.display_period,
auto_start: this._data.auto_start,
pages: []
};
let dashboard_pages = [];
if (this._is_kiosk_mode) {
for (const dashboard_page of this._dashboard_pages.keys()) {
dashboard_pages.push(dashboard_page);
}
}
else {
for (const tab of this._tabs.getList().children) {
dashboard_pages.push(this._tabs_dashboard_pages.get(tab));
}
}
for (const dashboard_page of dashboard_pages) {
data.pages.push(dashboard_page.save());
}
return data;
}
editProperties() {
const properties = {
template: this._data.templateid !== null ? 1 : undefined,
userid: this._data.templateid === null ? this._data.userid : undefined,
name: this._data.name,
display_period: this._data.display_period,
auto_start: this._data.auto_start
};
PopUp('dashboard.properties.edit', properties, {
dialogueid: 'dashboard_properties',
dialogue_class: 'modal-popup-generic'
});
}
applyProperties() {
const overlay = overlays_stack.getById('dashboard_properties');
const form = overlay.$dialogue.$body[0].querySelector('form');
const properties = getFormFields(form);
overlay.setLoading();
const busy_condition = this._createBusyCondition();
return new Promise((resolve) => resolve(this._promiseApplyProperties(properties)))
.then(() => {
this._is_unsaved = true;
overlayDialogueDestroy(overlay.dialogueid);
this.fire(DASHBOARD_EVENT_APPLY_PROPERTIES);
})
.catch((exception) => {
for (const element of form.parentNode.children) {
if (element.matches('.msg-good, .msg-bad, .msg-warning')) {
element.parentNode.removeChild(element);
}
}
let title, messages;
if (typeof exception === 'object' && 'error' in exception) {
title = exception.error.title;
messages = exception.error.messages;
}
else {
messages = [t('Failed to update dashboard properties.')];
}
const message_box = makeMessageBox('bad', messages, title)[0];
form.parentNode.insertBefore(message_box, form);
})
.finally(() => {
overlay.unsetLoading();
this._deleteBusyCondition(busy_condition);
});
}
_promiseApplyProperties(properties) {
properties.template = this._data.templateid !== null ? 1 : undefined;
properties.name = properties.name.trim();
const curl = new Curl('zabbix.php');
curl.setArgument('action', 'dashboard.properties.check');
return fetch(curl.getUrl(), {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(properties)
})
.then((response) => response.json())
.then((response) => {
if ('error' in response) {
throw {error: response.error};
}
this._data.name = properties.name;
this._data.userid = this._data.templateid === null ? properties.userid : null;
this._data.display_period = properties.display_period;
this._data.auto_start = properties.auto_start === '1' ? '1' : '0';
});
}
editDashboardPageProperties(properties = {}) {
properties.dashboard_display_period = this._data.display_period;
PopUp('dashboard.page.properties.edit', properties, {
dialogueid: 'dashboard_page_properties',
dialogue_class: 'modal-popup-generic'
});
}
applyDashboardPageProperties() {
const overlay = overlays_stack.getById('dashboard_page_properties');
const form = overlay.$dialogue.$body[0].querySelector('form');
const properties = getFormFields(form);
overlay.setLoading();
const busy_condition = this._createBusyCondition();
return Promise.resolve()
.then(() => this._promiseApplyDashboardPageProperties(properties, overlay.data))
.then(() => {
this._is_unsaved = true;
overlayDialogueDestroy(overlay.dialogueid);
})
.catch((exception) => {
for (const element of form.parentNode.children) {
if (element.matches('.msg-good, .msg-bad, .msg-warning')) {
element.parentNode.removeChild(element);
}
}
let title, messages;
if (typeof exception === 'object' && 'error' in exception) {
title = exception.error.title;
messages = exception.error.messages;
}
else {
messages = [t('Failed to update dashboard page properties.')];
}
const message_box = makeMessageBox('bad', messages, title)[0];
form.parentNode.insertBefore(message_box, form);
})
.finally(() => {
overlay.unsetLoading();
this._deleteBusyCondition(busy_condition);
});
}
_promiseApplyDashboardPageProperties(properties, data) {
properties.name = properties.name.trim();
const curl = new Curl('zabbix.php');
curl.setArgument('action', 'dashboard.page.properties.check');
return fetch(curl.getUrl(), {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(properties)
})
.then((response) => response.json())
.then((response) => {
if ('error' in response) {
throw {error: response.error};
}
if (data.unique_id !== null) {
const dashboard_page = this.getDashboardPage(data.unique_id);
if (dashboard_page === null) {
return;
}
const dashboard_page_name = dashboard_page.getName();
dashboard_page.setName(properties.name);
dashboard_page.setDisplayPeriod(properties.display_period);
if (properties.name !== dashboard_page_name && !this._is_kiosk_mode) {
this._updateTab(dashboard_page);
}
}
else {
const dashboard_page = this.addDashboardPage({
dashboard_pageid: null,
name: properties.name,
display_period: properties.display_period,
widgets: []
});
this._selectDashboardPage(dashboard_page);
}
});
}
editWidgetProperties(properties = {}, {new_widget_pos = null} = {}) {
this._clearWarnings();
if (properties.type === undefined) {
properties.type = this._widget_last_type;
if (properties.type === null) {
this._warn(t('Cannot add widget: no widgets available.'));
return;
}
}
const overlay = PopUp(`widget.${properties.type}.edit`, {
templateid: this._data.templateid ?? undefined,
...properties
}, {
dialogueid: 'widget_properties',
dialogue_class: 'modal-widget-configuration'
});
overlay.xhr.then(() => {
const form = overlay.$dialogue.$body[0].querySelector('form');
const original_properties = overlay.data.original_properties;
if (original_properties.unique_id === null) {
this._new_widget_dashboard_page = this._selected_dashboard_page;
this._new_widget_pos = new_widget_pos;
const default_widget_size = this._widget_defaults[original_properties.type].size;
if (this._new_widget_pos === null) {
this._new_widget_pos_reserved = this._new_widget_dashboard_page.findFreePos(default_widget_size);
}
else {
this._new_widget_pos_reserved = {
...default_widget_size,
...this._new_widget_pos
};
this._new_widget_pos_reserved.width = Math.min(this._new_widget_pos_reserved.width,
this._max_columns - this._new_widget_pos_reserved.x
);
this._new_widget_pos_reserved.height = Math.min(this._new_widget_pos_reserved.height,
this._max_rows - this._new_widget_pos_reserved.y
);
this._new_widget_pos_reserved = this._new_widget_dashboard_page.accommodatePos(
this._new_widget_pos_reserved
);
}
if (this._new_widget_pos_reserved === null) {
for (const el of form.parentNode.children) {
if (el.matches('.msg-warning')) {
el.parentNode.removeChild(el);
}
}
const message_box = makeMessageBox('warning', [],
t('Cannot add widget: not enough free space on the dashboard.')
)[0];
form.parentNode.insertBefore(message_box, form);
overlay.$btn_submit[0].disabled = true;
}
}
document.getElementById('type').addEventListener('change', () => this.reloadWidgetProperties());
form.addEventListener('change', (e) => {
const do_trim = e.target.matches(
'input[type="text"]:not([data-no-trim="1"]), textarea:not([data-no-trim="1"])'
);
if (do_trim) {
e.target.value = e.target.value.trim();
}
}, {capture: true});
for (const fieldset of
form.querySelectorAll(`fieldset.${CFormFieldsetCollapsible.ZBX_STYLE_COLLAPSIBLE}`)) {
new CFormFieldsetCollapsible(fieldset);
}
try {
new TabIndicators();
}
catch (error) {
}
});
if (!this._is_edit_widget_properties_cancel_subscribed) {
this._is_edit_widget_properties_cancel_subscribed = true;
overlay.$dialogue[0].addEventListener('dialogue.close', this._events.editWidgetPropertiesCancel,
{once: true}
);
}
}
reloadWidgetProperties() {
const overlay = overlays_stack.getById('widget_properties');
const form = overlay.$dialogue.$body[0].querySelector('form');
const fields = getFormFields(form);
const properties = {
type: fields.type,
unique_id: overlay.data.original_properties.unique_id ?? undefined,
dashboard_page_unique_id: overlay.data.original_properties.dashboard_page_unique_id ?? undefined
};
if (properties.type === overlay.data.original_properties.type) {
properties.name = fields.name;
properties.view_mode = fields.show_header == 1
? ZBX_WIDGET_VIEW_MODE_NORMAL
: ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER;
delete fields.type;
delete fields.name;
delete fields.show_header;
properties.fields = fields;
}
overlay.$dialogue[0].dispatchEvent(new CustomEvent('overlay.reload'));
this.editWidgetProperties(properties, {new_widget_pos: this._new_widget_pos});
}
applyWidgetProperties() {
const overlay = overlays_stack.getById('widget_properties');
const form = overlay.$dialogue.$body[0].querySelector('form');
const fields = getFormFields(form);
const templateid = this._data.templateid ?? undefined;
const type = fields.type;
const name = fields.name;
const view_mode = fields.show_header == 1
? ZBX_WIDGET_VIEW_MODE_NORMAL
: ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER;
delete fields.type;
delete fields.name;
delete fields.show_header;
const dashboard_page = overlay.data.original_properties.dashboard_page_unique_id !== null
? this.getDashboardPage(overlay.data.original_properties.dashboard_page_unique_id)
: null;
const widget = dashboard_page !== null
? dashboard_page.getWidget(overlay.data.original_properties.unique_id)
: null;
const busy_condition = this._createBusyCondition();
return Promise.resolve()
.then(() => this._promiseDashboardWidgetCheck({templateid, type, name, view_mode, fields}))
.then(() => {
this._is_unsaved = true;
overlayDialogueDestroy(overlay.dialogueid);
if (widget !== null && widget.getType() === type) {
widget.updateProperties({name, view_mode, fields});
return;
}
if (type !== this._widget_last_type) {
this._widget_last_type = type;
updateUserProfile('web.dashboard.last_widget_type', type, [], PROFILE_TYPE_STR);
}
const widget_class = eval(this._widget_defaults[type].js_class);
if (widget_class.hasReferenceField()) {
fields.reference = this._createReference();
}
const widget_data = {
type,
name,
view_mode,
fields,
widgetid: null,
pos: widget === null ? this._new_widget_pos_reserved : widget.getPos(),
is_new: widget === null,
rf_rate: 0,
unique_id: this._createUniqueId()
};
if (widget === null) {
this._new_widget_dashboard_page.promiseScrollIntoView(widget_data.pos)
.then(() => {
this._new_widget_dashboard_page.addWidget(widget_data);
this._new_widget_dashboard_page.resetWidgetPlaceholder();
this._new_widget_dashboard_page = null;
this._new_widget_pos = null;
this._new_widget_pos_reserved = null;
});
}
else {
if (dashboard_page.getState() === DASHBOARD_PAGE_STATE_DESTROYED) {
return;
}
dashboard_page.promiseScrollIntoView(widget_data.pos)
.then(() => {
dashboard_page.replaceWidget(widget, widget_data);
dashboard_page.resetWidgetPlaceholder();
});
}
})
.catch((exception) => {
for (const element of form.parentNode.children) {
if (element.matches('.msg-good, .msg-bad, .msg-warning')) {
element.parentNode.removeChild(element);
}
}
let title, messages;
if (typeof exception === 'object' && 'error' in exception) {
title = exception.error.title;
messages = exception.error.messages;
}
else {
messages = [t('Failed to update widget properties.')];
}
const message_box = makeMessageBox('bad', messages, title)[0];
form.parentNode.insertBefore(message_box, form);
})
.finally(() => {
overlay.unsetLoading();
this._deleteBusyCondition(busy_condition);
});
}
_isEditingWidgetProperties() {
return this._is_edit_widget_properties_cancel_subscribed;
}
_promiseDashboardWidgetCheck({templateid, type, name, view_mode, fields}) {
const curl = new Curl('zabbix.php');
curl.setArgument('action', 'dashboard.widget.check');
return fetch(curl.getUrl(), {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({templateid, type, name, view_mode, fields})
})
.then((response) => response.json())
.then((response) => {
if ('error' in response) {
throw {error: response.error};
}
});
}
_cancelEditingWidgetProperties() {
this._selected_dashboard_page.resetWidgetPlaceholder();
}
_getDashboardPageActionsContextMenu(dashboard_page) {
let menu = [];
let menu_actions = [];
if (this._can_edit_dashboards) {
menu_actions.push({
label: t('Copy'),
clickCallback: () => {
this._clearWarnings();
const data_copy = dashboard_page.getDataCopy();
const data_copy_widgets = data_copy.widgets;
data_copy.widgets = [];
for (const widget of data_copy_widgets) {
if (widget.type in this._widget_defaults) {
data_copy.widgets.push(widget);
}
}
this._storeDashboardPageDataCopy(data_copy);
if (data_copy.widgets.length < data_copy_widgets.length) {
this._warn(t('Inaccessible widgets were not copied.'));
}
}
});
}
if (this._is_edit_mode) {
menu_actions.push({
label: t('Delete'),
disabled: this._dashboard_pages.size == 1,
clickCallback: () => this.deleteDashboardPage(dashboard_page)
});
}
if (menu_actions.length > 0) {
menu.push({
label: t('Actions'),
items: menu_actions
});
}
if (this._is_editable) {
menu.push({
items: [
{
label: t('Properties'),
clickCallback: () => {
if (!this._is_edit_mode) {
this.setEditMode({is_internal_call: true});
}
this.editDashboardPageProperties({
name: dashboard_page.getName(),
display_period: dashboard_page.getDisplayPeriod(),
unique_id: dashboard_page.getUniqueId()
});
}
}
]
});
}
return menu;
}
// Dashboard view methods.
_warn(warning) {
this._clearWarnings();
this._warning_message_box = makeMessageBox('warning', [], warning);
addMessage(this._warning_message_box);
}
_warnDashboardExhausted() {
this._warn(sprintf(
t('Cannot add dashboard page: maximum number of %1$d dashboard pages has been added.'),
this._max_dashboard_pages
));
}
_warnDashboardPageExhausted() {
this._warn(t('Cannot add widget: not enough free space on the dashboard.'));
}
_clearWarnings() {
if (this._warning_message_box !== null) {
this._warning_message_box.remove();
this._warning_message_box = null;
}
}
_isUserInteracting() {
if (this._selected_dashboard_page.isUserInteracting()) {
return true;
}
if (!this._is_kiosk_mode) {
const has_aria_expanded = this._tabs
.getList()
.querySelector(`.${ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES}[aria-expanded="true"]`) !== null;
if (has_aria_expanded) {
return true;
}
}
return false;
}
_addTab(dashboard_page) {
const tab = document.createElement('li');
const tab_contents = document.createElement('div');
const tab_contents_name = document.createElement('span');
tab.appendChild(tab_contents);
tab_contents.appendChild(tab_contents_name);
const data = this._dashboard_pages.get(dashboard_page);
const name = dashboard_page.getName();
data.tab = tab;
if (name !== '') {
data.index = null;
tab_contents_name.textContent = name;
tab_contents_name.title = name;
}
else {
let max_index = this._dashboard_pages.size - 1;
for (const dashboard_page_data of this._dashboard_pages.values()) {
if (dashboard_page_data.index !== null && dashboard_page_data.index > max_index) {
max_index = dashboard_page_data.index;
}
}
data.index = max_index + 1;
const name = sprintf(t('Page %1$d'), data.index);
tab_contents_name.textContent = name;
tab_contents_name.title = name;
}
if (this._getDashboardPageActionsContextMenu(dashboard_page).length > 0) {
const properties_button = document.createElement('button');
properties_button.type = 'button';
properties_button.title = t('Actions');
properties_button.setAttribute('aria-expanded', 'false');
properties_button.setAttribute('aria-haspopup', 'true');
properties_button.classList.add(ZBX_STYLE_BTN_ICON, ZBX_ICON_MORE, ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES);
tab_contents.append(properties_button);
}
this._tabs.insertItemBefore(tab);
this._tabs_dashboard_pages.set(tab, dashboard_page);
}
_updateTab(dashboard_page) {
const name = dashboard_page.getName();
const data = this._dashboard_pages.get(dashboard_page);
const tab_contents_name = data.tab.firstElementChild.firstElementChild;
data.index = null;
if (name !== '') {
tab_contents_name.textContent = name;
tab_contents_name.title = name;
}
else {
const tab_index = [...this._tabs.getList().children].indexOf(data.tab) + 1;
let max_index = this._dashboard_pages.size - 1;
let is_tab_index_available = true;
for (const dashboard_page_data of this._dashboard_pages.values()) {
if (dashboard_page_data.index !== null) {
if (dashboard_page_data.index === tab_index) {
is_tab_index_available = false;
}
if (dashboard_page_data.index > max_index) {
max_index = dashboard_page_data.index;
}
}
}
data.index = is_tab_index_available ? tab_index : max_index + 1;
const name = sprintf(t('Page %1$d'), data.index);
tab_contents_name.textContent = name;
tab_contents_name.title = name;
}
}
_deleteTab(dashboard_page) {
const data = this._dashboard_pages.get(dashboard_page);
this._tabs.removeItem(data.tab);
this._tabs_dashboard_pages.delete(data.tab);
}
_selectTab(dashboard_page) {
this._tabs.getList().querySelectorAll(`.${ZBX_STYLE_DASHBOARD_SELECTED_TAB}`).forEach((el) => {
el.classList.remove(ZBX_STYLE_DASHBOARD_SELECTED_TAB);
})
const data = this._dashboard_pages.get(dashboard_page);
data.tab.firstElementChild.classList.add(ZBX_STYLE_DASHBOARD_SELECTED_TAB);
this._updateNavigationButtons(dashboard_page);
this._tabs.scrollItemIntoView(data.tab);
}
_updateNavigationButtons(dashboard_page = null) {
this._containers.navigation.classList.toggle(ZBX_STYLE_DASHBOARD_NAVIGATION_IS_SCROLLABLE,
this._tabs.isScrollable()
);
if (dashboard_page !== null) {
const tab = this._dashboard_pages.get(dashboard_page).tab;
this._buttons.previous_page.disabled = tab.previousElementSibling === null;
this._buttons.next_page.disabled = tab.nextElementSibling === null;
}
}
_reserveHeaderLines(num_lines) {
this._reserve_header_lines = num_lines;
if (this._reserve_header_lines_timeout_id !== null) {
clearTimeout(this._reserve_header_lines_timeout_id);
this._reserve_header_lines_timeout_id = null;
}
let old_num_lines = 0;
for (let i = 2; i > 0; i--) {
if (this._containers.grid.classList.contains(`reserve-header-lines-${i}`)) {
old_num_lines = i;
break;
}
}
if (num_lines > old_num_lines) {
if (old_num_lines > 0) {
this._containers.grid.classList.remove(`reserve-header-lines-${old_num_lines}`);
}
this._containers.grid.classList.add(`reserve-header-lines-${num_lines}`);
}
else if (num_lines < old_num_lines) {
this._reserve_header_lines_timeout_id = setTimeout(() => {
this._reserve_header_lines_timeout_id = null;
this._containers.grid.classList.remove(`reserve-header-lines-${old_num_lines}`);
if (num_lines > 0) {
this._containers.grid.classList.add(`reserve-header-lines-${num_lines}`);
}
}, this._header_lines_steady_period);
}
}
_keepSteadyHeaderLines() {
if (this._reserve_header_lines_timeout_id !== null) {
this._reserveHeaderLines(this._reserve_header_lines);
}
}
_resetHeaderLines() {
if (this._reserve_header_lines_timeout_id !== null) {
clearTimeout(this._reserve_header_lines_timeout_id);
this._reserve_header_lines_timeout_id = null;
}
this._containers.grid.classList.remove('reserve-header-lines-1', 'reserve-header-lines-2');
}
_createUniqueId() {
return 'U' + (this._unique_id_index++).toString(36).toUpperCase().padStart(6, '0');
}
_createReference({used_references = null} = {}) {
if (used_references === null) {
used_references = this._getUsedReferences();
}
let reference;
do {
reference = '';
for (let i = 0; i < 5; i++) {
reference += String.fromCharCode(65 + Math.floor(Math.random() * 26));
}
}
while (used_references.has(reference));
return reference;
}
_getUsedReferences() {
const used_references = new Set();
for (const dashboard_page of this._dashboard_pages.keys()) {
for (const widget of dashboard_page.getWidgets()) {
const fields = widget.getFields();
if (widget.constructor.hasReferenceField()) {
used_references.add(fields.reference);
}
for (const reference_field of widget.constructor.getForeignReferenceFields()) {
used_references.add(fields[reference_field]);
}
}
}
return used_references;
}
// Internal events management methods.
_registerEvents() {
let wrapper_scrollbar_width = 0;
let user_interaction_animation_frame = null;
this._events = {
dashboardPageEdit: () => {
this.setEditMode({is_internal_call: true});
},
dashboardPageWidgetAdd: (e) => {
const dashboard_page = this._selected_dashboard_page;
const new_widget_data = this.getStoredWidgetDataCopy();
const new_widget_pos = e.detail.new_widget_pos;
if (new_widget_data !== null) {
const menu = [
{
label: t('Actions'),
items: [
{
label: t('Add widget'),
clickCallback: () => this.editWidgetProperties({}, {new_widget_pos})
},
{
label: t('Paste widget'),
clickCallback: () => this.pasteWidget(new_widget_data, {new_widget_pos})
}
]
}
];
const placeholder = e.detail.placeholder;
const placeholder_event = new jQuery.Event(e.detail.mouse_event);
placeholder_event.target = placeholder;
jQuery(placeholder).menuPopup(menu, placeholder_event, {
closeCallback: () => {
if (!this._isEditingWidgetProperties()) {
dashboard_page.resetWidgetPlaceholder();
}
}
});
}
else {
this.editWidgetProperties({}, {new_widget_pos});
if (!this._isEditingWidgetProperties()) {
dashboard_page.resetWidgetPlaceholder();
}
}
},
dashboardPageWidgetAddNew: () => {
this.editWidgetProperties();
},
dashboardPageWidgetDelete: () => {
this._clearWarnings();
},
dashboardPageWidgetPosition: () => {
this._clearWarnings();
},
dashboardPageWidgetActions: (e) => {
const menu = e.detail.widget.getActionsContextMenu({
can_paste_widget: this.getStoredWidgetDataCopy() !== null
});
jQuery(e.detail.mouse_event.target).menuPopup(menu, new jQuery.Event(e.detail.mouse_event));
},
dashboardPageWidgetEdit: (e) => {
const dashboard_page = e.detail.target;
const widget = e.detail.widget;
this.editWidgetProperties({
type: widget.getType(),
name: widget.getName(),
view_mode: widget.getViewMode(),
fields: widget.getFields(),
unique_id: widget.getUniqueId(),
dashboard_page_unique_id: dashboard_page.getUniqueId()
});
},
dashboardPageWidgetCopy: (e) => {
const widget = e.detail.widget;
this._storeWidgetDataCopy(widget.getDataCopy({is_single_copy: true}));
},
dashboardPageWidgetPaste: (e) => {
const widget = e.detail.widget;
this.pasteWidget(this.getStoredWidgetDataCopy(), {widget});
},
dashboardPageAnnounceWidgets: () => {
this._announceWidgets();
},
dashboardPageReserveHeaderLines: (e) => {
this._reserveHeaderLines(e.detail.num_lines);
},
gridResize: () => {
const wrapper = document.querySelector('.wrapper');
if (wrapper.offsetWidth > wrapper.clientWidth) {
wrapper_scrollbar_width = wrapper.offsetWidth - wrapper.clientWidth;
this._buttons.next_page.style.marginRight = '0';
}
else {
this._buttons.next_page.style.marginRight = `${wrapper_scrollbar_width}px`;
}
},
tabsResize: () => {
this._updateNavigationButtons();
},
tabsDragStart: () => {
this._selected_dashboard_page.blockInteraction();
},
tabsDragEnd: () => {
this._selected_dashboard_page.unblockInteraction();
this._updateNavigationButtons();
},
tabsSort: () => {
this._setInitialDashboardPage(this._selected_dashboard_page);
this._is_unsaved = true;
},
tabsClick: (e) => {
const tab = e.target.closest(`.${ZBX_STYLE_SORTABLE_ITEM}`);
if (tab !== null && tab.parentNode.classList.contains(ZBX_STYLE_SORTABLE_LIST)) {
const dashboard_page = this._tabs_dashboard_pages.get(tab);
if (dashboard_page !== this._selected_dashboard_page) {
this._selectDashboardPage(dashboard_page, {is_async: true});
}
else if (e.target.classList.contains(ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES)) {
jQuery(e.target).menuPopup(this._getDashboardPageActionsContextMenu(dashboard_page),
new jQuery.Event(e)
);
}
}
},
tabsKeyDown: (e) => {
if (e.key === 'Enter') {
const tab = e.target.closest(`.${ZBX_STYLE_SORTABLE_ITEM}`);
if (tab !== null) {
const dashboard_page = this._tabs_dashboard_pages.get(tab);
if (dashboard_page !== this._selected_dashboard_page) {
this._selectDashboardPage(dashboard_page, {is_async: true});
}
else if (e.target.classList.contains(ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES)) {
jQuery(e.target).menuPopup(this._getDashboardPageActionsContextMenu(dashboard_page),
new jQuery.Event(e)
);
}
}
}
},
tabsPreviousPageClick: () => {
const tab = this._dashboard_pages.get(this._selected_dashboard_page).tab;
this._selectDashboardPage(this._tabs_dashboard_pages.get(tab.previousElementSibling), {is_async: true});
},
tabsNextPageClick: () => {
const tab = this._dashboard_pages.get(this._selected_dashboard_page).tab;
this._selectDashboardPage(this._tabs_dashboard_pages.get(tab.nextElementSibling), {is_async: true});
},
slideshowToggle: () => {
if (this._is_edit_mode) {
return;
}
if (this._isSlideshowRunning()) {
this._stopSlideshow();
}
else {
this._startSlideshow();
}
},
userInteract: () => {
if (user_interaction_animation_frame !== null) {
cancelAnimationFrame(user_interaction_animation_frame);
}
user_interaction_animation_frame = requestAnimationFrame(() => {
user_interaction_animation_frame = null;
if (this._is_kiosk_mode) {
this._keepSteadyHeaderLines();
}
if (!this._is_edit_mode) {
this._keepSteadyConfigurationChecker();
if (this._isSlideshowRunning()) {
this._keepSteadySlideshow();
}
}
});
},
kioskModePreviousPageClick: () => {
const dashboard_pages = [...this._dashboard_pages.keys()];
const dashboard_page_index = dashboard_pages.indexOf(this._selected_dashboard_page);
this._selectDashboardPage(
dashboard_pages[dashboard_page_index > 0 ? dashboard_page_index - 1 : dashboard_pages.length - 1]
);
},
kioskModeNextPageClick: () => {
const dashboard_pages = [...this._dashboard_pages.keys()];
const dashboard_page_index = dashboard_pages.indexOf(this._selected_dashboard_page);
this._selectDashboardPage(
dashboard_pages[dashboard_page_index < dashboard_pages.length - 1 ? dashboard_page_index + 1 : 0]
);
},
timeSelectorRangeUpdate: (e, time_period) => {
this._time_period = {
from: time_period.from,
from_ts: time_period.from_ts,
to: time_period.to,
to_ts: time_period.to_ts
};
for (const dashboard_page of this._dashboard_pages.keys()) {
dashboard_page.setTimePeriod(this._time_period);
}
},
editWidgetPropertiesCancel: () => {
this._cancelEditingWidgetProperties();
this._is_edit_widget_properties_cancel_subscribed = false;
}
};
}
_activateEvents() {
if (!this._is_kiosk_mode) {
new ResizeObserver(this._events.gridResize).observe(this._containers.grid);
new ResizeObserver(this._events.tabsResize).observe(this._containers.navigation_tabs);
this._tabs.on(SORTABLE_EVENT_DRAG_START, this._events.tabsDragStart);
this._tabs.on(SORTABLE_EVENT_DRAG_END, this._events.tabsDragEnd);
this._tabs.on(SORTABLE_EVENT_SORT, this._events.tabsSort);
this._containers.navigation_tabs.addEventListener('click', this._events.tabsClick);
this._containers.navigation_tabs.addEventListener('keydown', this._events.tabsKeyDown);
this._buttons.previous_page.addEventListener('click', this._events.tabsPreviousPageClick);
this._buttons.next_page.addEventListener('click', this._events.tabsNextPageClick);
}
if (this._buttons.slideshow !== null && !this._is_edit_mode && this._dashboard_pages.size > 1) {
this._buttons.slideshow.addEventListener('click', this._events.slideshowToggle);
}
for (const event_name of ['mousemove', 'mousedown', 'keydown', 'wheel']) {
window.addEventListener(event_name, this._events.userInteract);
}
if (this._is_kiosk_mode) {
if (this._buttons.previous_page !== null) {
this._buttons.previous_page.addEventListener('click', this._events.kioskModePreviousPageClick);
}
if (this._buttons.next_page !== null) {
this._buttons.next_page.addEventListener('click', this._events.kioskModeNextPageClick);
}
}
if (this._time_period !== null) {
jQuery.subscribe('timeselector.rangeupdate', this._events.timeSelectorRangeUpdate);
}
}
/**
* Attach event listener to dashboard events.
*
* @param {string} type
* @param {function} listener
* @param {Object|false} options
*
* @returns {CDashboard}
*/
on(type, listener, options = false) {
this._target.addEventListener(type, listener, options);
return this;
}
/**
* Detach event listener from dashboard events.
*
* @param {string} type
* @param {function} listener
* @param {Object|false} options
*
* @returns {CDashboard}
*/
off(type, listener, options = false) {
this._target.removeEventListener(type, listener, options);
return this;
}
/**
* Dispatch dashboard event.
*
* @param {string} type
* @param {Object} detail
* @param {Object} options
*
* @returns {boolean}
*/
fire(type, detail = {}, options = {}) {
return this._target.dispatchEvent(new CustomEvent(type, {...options, detail: {target: this, ...detail}}));
}
}