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.
315 lines
8.4 KiB
315 lines
8.4 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.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Represents DOM node for notification list. Stores the collection of ZBX_Notification objects.
|
|
*/
|
|
function ZBX_NotificationCollection() {
|
|
this._dangling_nodes = [];
|
|
|
|
this._list_sequence = [];
|
|
this._list_obj = {};
|
|
|
|
this.makeNodes();
|
|
|
|
this.node.style.right = '10px';
|
|
this.node.style.top = '0px';
|
|
}
|
|
|
|
/**
|
|
* @return {array}
|
|
*/
|
|
ZBX_NotificationCollection.prototype.getIds = function() {
|
|
return this._list_sequence;
|
|
};
|
|
|
|
/**
|
|
* @param {callable} callback
|
|
*/
|
|
ZBX_NotificationCollection.prototype.map = function(callback) {
|
|
var len = this._list_sequence.length;
|
|
|
|
while (--len > -1) {
|
|
callback(this.getById(this._list_sequence[len]), len);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {callable} callback
|
|
*
|
|
* @return {array}
|
|
*/
|
|
ZBX_NotificationCollection.prototype.filterList = function(callback) {
|
|
var list = [],
|
|
len = this._list_sequence.length;
|
|
|
|
while (--len > -1) {
|
|
var ret = callback(this.getById(this._list_sequence[len]));
|
|
(ret !== false) && list.push(ret);
|
|
}
|
|
|
|
return list;
|
|
};
|
|
|
|
/**
|
|
* @return {array} List of raw notification objects.
|
|
*/
|
|
ZBX_NotificationCollection.prototype.getRawList = function() {
|
|
return this.filterList(function(notif) {
|
|
return notif.getRaw();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Merges current with new list. Updates changes for ZBX_Notification if possible, or creates new ZBX_Notification.
|
|
* Recoverable notification is just partial of raw with one mandatory field - `eventid`. New iterator sequence reflects
|
|
* the order of list given to this method. During merge, nodes that are not present in list will be removed.
|
|
*
|
|
* @param {array} list List of raw/recoverable notification objects.
|
|
*/
|
|
ZBX_NotificationCollection.prototype.consumeList = function(list) {
|
|
var new_list_sequence = [];
|
|
|
|
while (raw = list.pop()) {
|
|
/*
|
|
* Case if server returns with "recovered" notification type, that cannot be recovered by client.
|
|
* This should never happen.
|
|
*/
|
|
if (!raw.body && !this._list_obj[raw.eventid]) {
|
|
continue;
|
|
}
|
|
|
|
if (this._list_obj[raw.eventid]) {
|
|
this._list_obj[raw.eventid].updateRaw(raw);
|
|
}
|
|
else {
|
|
this._list_obj[raw.eventid] = new ZBX_Notification(raw);
|
|
}
|
|
|
|
new_list_sequence.push(raw.eventid);
|
|
}
|
|
|
|
for (var id in this._list_obj) {
|
|
if (new_list_sequence.indexOf(id) == -1) {
|
|
this._dangling_nodes.push(this._list_obj[id].node);
|
|
delete this._list_obj[id];
|
|
}
|
|
}
|
|
|
|
this._list_sequence = new_list_sequence;
|
|
};
|
|
|
|
/**
|
|
* Creates detached DOM nodes.
|
|
*/
|
|
ZBX_NotificationCollection.prototype.makeNodes = function() {
|
|
var header = document.createElement('div'),
|
|
controls = document.createElement('ul');
|
|
|
|
this.node = document.createElement('div');
|
|
this.node.style.display = 'none';
|
|
this.node.hidden = true;
|
|
this.node.className = 'overlay-dialogue notif';
|
|
|
|
this.btn_close = document.createElement('button');
|
|
this.btn_close.setAttribute('title', locale['S_CLOSE']);
|
|
this.btn_close.setAttribute('type', 'button');
|
|
this.btn_close.className = 'btn-overlay-close';
|
|
|
|
header.className = 'dashboard-widget-head cursor-move';
|
|
this.node.appendChild(header);
|
|
|
|
header.appendChild(controls);
|
|
header.appendChild(this.btn_close);
|
|
|
|
this.btn_snooze = this.makeToggleBtn(
|
|
{class: [ZBX_STYLE_BTN_ICON + ' ' + ZBX_ICON_BELL]},
|
|
{class: [ZBX_STYLE_BTN_ICON + ' ' + ZBX_ICON_BELL_OFF]}
|
|
);
|
|
this.btn_snooze.setAttribute('title', locale['S_SNOOZE']);
|
|
|
|
const li_btn_snooze = document.createElement('li');
|
|
li_btn_snooze.appendChild(this.btn_snooze);
|
|
|
|
this.btn_mute = this.makeToggleBtn(
|
|
{class: ZBX_STYLE_BTN_ICON + ' ' + ZBX_ICON_SPEAKER, title: locale['S_MUTE']},
|
|
{class: ZBX_STYLE_BTN_ICON + ' ' + ZBX_ICON_SPEAKER_OFF, title: locale['S_UNMUTE']}
|
|
);
|
|
|
|
const li_btn_mute = document.createElement('li');
|
|
li_btn_mute.appendChild(this.btn_mute);
|
|
|
|
controls.appendChild(li_btn_snooze);
|
|
controls.appendChild(li_btn_mute);
|
|
|
|
this.list_node = document.createElement('ul');
|
|
this.list_node.className = 'notif-body';
|
|
|
|
this.node.appendChild(this.list_node);
|
|
};
|
|
|
|
/**
|
|
* Creates a button node with a method `renderState(bool)`.
|
|
*
|
|
* @param {object} attrs_inactive Attribute key-value object to be mapped on renderState(true).
|
|
* @param {object} attrs_active Attribute key-value object to be mapped on renderState(false).
|
|
*
|
|
* @return {HTMLElement} DOM button element.
|
|
*/
|
|
ZBX_NotificationCollection.prototype.makeToggleBtn = function(attrs_inactive, attrs_active) {
|
|
var button = document.createElement('button');
|
|
button.renderState = function(is_active) {
|
|
var attrs = is_active ? attrs_active : attrs_inactive,
|
|
attr_name;
|
|
|
|
for (attr_name in attrs) {
|
|
this.setAttribute(attr_name, attrs[attr_name]);
|
|
}
|
|
};
|
|
|
|
return button;
|
|
};
|
|
|
|
/**
|
|
* Iterator property will be updated and reference to DOM node kept to be gracefully removed at render. No reference
|
|
* to notification object would exist after this call - notification is removed and will not cycle back into LS.
|
|
*
|
|
* @param {string} id
|
|
*/
|
|
ZBX_NotificationCollection.prototype.removeById = function(id) {
|
|
var index = this._list_sequence.indexOf(id);
|
|
|
|
if (index === -1) {
|
|
return;
|
|
}
|
|
|
|
this._list_sequence.splice(index, 1);
|
|
|
|
if (this._list_obj[id]) {
|
|
this._list_obj[id].display_timeoutid && clearTimeout(this._list_obj[id].display_timeoutid);
|
|
this._dangling_nodes.push(this._list_obj[id].node);
|
|
delete this._list_obj[id];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {string} id
|
|
*
|
|
* @return {ZBX_Notification}
|
|
*/
|
|
ZBX_NotificationCollection.prototype.getById = function(id) {
|
|
return this._list_obj[id];
|
|
};
|
|
|
|
/**
|
|
* Shows list of notifications.
|
|
*
|
|
* @return {Promise}
|
|
*/
|
|
ZBX_NotificationCollection.prototype.show = function() {
|
|
return ZBX_Notifications.util.fadeIn(this.node);
|
|
};
|
|
|
|
/**
|
|
* Hides list of notifications.
|
|
*
|
|
* @return {Promise}
|
|
*/
|
|
ZBX_NotificationCollection.prototype.hide = function() {
|
|
return ZBX_Notifications.util.fadeOut(this.node);
|
|
};
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
ZBX_NotificationCollection.prototype.isEmpty = function() {
|
|
return !this._list_sequence.length;
|
|
};
|
|
|
|
/**
|
|
* Animates slide-up-remove on dangling nodes one by one.
|
|
*/
|
|
ZBX_NotificationCollection.prototype.removeDanglingNodes = function() {
|
|
var duration = this._dangling_nodes.length > 4 ? 200 : 500;
|
|
var first = true;
|
|
|
|
while (node = this._dangling_nodes.pop()) {
|
|
ZBX_Notifications.util.slideUp(node, first && 500 || duration, this._dangling_nodes.length * duration)
|
|
.then(function(node) {
|
|
node.parentNode && node.remove();
|
|
});
|
|
first = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Notification sequence is maintained in DOM, in server response notifications must be ordered.
|
|
* Shows or hides list node, updates and appends notification nodes, then deligates to remove dangling nodes.
|
|
*
|
|
* @param {object} severity_styles
|
|
* @param {ZBX_NotificationsAlarm} alarm_state
|
|
*/
|
|
ZBX_NotificationCollection.prototype.render = function(severity_styles, alarm_state) {
|
|
this.btn_snooze.renderState(alarm_state.isSnoozed(this.getRawList()));
|
|
if (alarm_state.supported) {
|
|
this.btn_mute.renderState(alarm_state.muted);
|
|
}
|
|
else {
|
|
this.btn_mute.renderState(true);
|
|
this.btn_mute.disabled = true;
|
|
this.btn_mute.title = locale['S_CANNOT_SUPPORT_NOTIFICATION_AUDIO'];
|
|
}
|
|
|
|
var list_node = this.list_node,
|
|
prev_notif_node = null;
|
|
|
|
if (this.isEmpty()) {
|
|
return this.hide().then(function() {
|
|
list_node.innerHTML = '';
|
|
this._dangling_nodes = [];
|
|
}.bind(this));
|
|
}
|
|
|
|
var slide_down = list_node.children.length != 0;
|
|
|
|
this.map(function(notif, index) {
|
|
notif.render(severity_styles);
|
|
|
|
if (notif.isNodeConnected()) {
|
|
prev_notif_node = notif.node;
|
|
return;
|
|
}
|
|
|
|
if (prev_notif_node) {
|
|
prev_notif_node.insertAdjacentElement('afterend', notif.node);
|
|
}
|
|
else {
|
|
list_node.insertAdjacentElement('afterbegin', notif.node);
|
|
}
|
|
|
|
slide_down && ZBX_Notifications.util.slideDown(notif.node, 200);
|
|
prev_notif_node = notif.node;
|
|
});
|
|
|
|
this.removeDanglingNodes();
|
|
|
|
(this.node.style.display === 'none') && this.show();
|
|
};
|