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.
zabbix/ui/js/flickerfreescreen.js

477 lines
13 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.
**/
(function($) {
window.flickerfreeScreen = {
screens: [],
responsiveness: 10000,
/**
* Set or reset UI in progress state for element with id.
*
* @param {boolean} in_progress
* @param {string} id
*/
setElementProgressState: function(id, in_progress) {
var elm = $('#flickerfreescreen_'+id);
if (in_progress) {
elm.addClass('is-loading is-loading-fadein delayed-15s');
}
else {
elm.removeClass('is-loading is-loading-fadein delayed-15s');
}
},
add: function(screen) {
// switch off time control refreshing using full page refresh
timeControl.refreshPage = false;
// init screen item
this.screens[screen.id] = screen;
this.screens[screen.id].interval = (screen.interval > 0) ? screen.interval * 1000 : 0;
this.screens[screen.id].timestamp = 0;
this.screens[screen.id].timestampResponsiveness = 0;
this.screens[screen.id].timestampActual = 0;
this.screens[screen.id].isRefreshing = false;
this.screens[screen.id].isReRefreshRequire = false;
this.screens[screen.id].error = 0;
// SCREEN_RESOURCE_MAP
if (screen.resourcetype == 2) {
this.screens[screen.id].data = new SVGMap(this.screens[screen.id].data);
$(screen.data.container).attr({'aria-label': screen.data.options.aria_label, 'tabindex': 0})
.find('svg').attr('aria-hidden', 'true');
}
// init refresh plan
if (screen.isFlickerfree && screen.interval > 0) {
this.screens[screen.id].timeoutHandler = window.setTimeout(
function() {
window.flickerfreeScreen.refresh(screen.id);
},
this.screens[screen.id].interval
);
}
},
remove: function(screen) {
if (typeof screen.id !== 'undefined' && typeof this.screens[screen.id] !== 'undefined') {
if (typeof this.screens[screen.id].timeoutHandler !== 'undefined') {
window.clearTimeout(this.screens[screen.id].timeoutHandler);
}
delete this.screens[screen.id];
}
},
refresh: function(id) {
var screen = this.screens[id];
if (empty(screen.id)) {
return;
}
// Do not update screen if displaying static hintbox.
if ($('#flickerfreescreen_' + id + ' [data-expanded="true"]').length) {
if (screen.isFlickerfree && screen.interval > 0) {
clearTimeout(screen.timeoutHandler);
screen.timeoutHandler = setTimeout(() => flickerfreeScreen.refresh(id), 1000);
}
return;
}
/**
* 17 SCREEN_RESOURCE_HISTORY
* 21 SCREEN_RESOURCE_HTTPTEST_DETAILS
* 22 SCREEN_RESOURCE_DISCOVERY
* 23 SCREEN_RESOURCE_HTTPTEST
*/
var type_params = {
'17': ['mode', 'resourcetype', 'pageFile', 'page'],
'21': ['mode', 'resourcetype', 'profileIdx2'],
'22': ['mode', 'resourcetype', 'data'],
'23': ['mode', 'resourcetype', 'data', 'page'],
'default': ['mode', 'screenid', 'groupid', 'hostid', 'pageFile', 'profileIdx', 'profileIdx2',
'screenitemid'
]
},
params_index = type_params[screen.resourcetype] ? screen.resourcetype : 'default',
self = this;
const ajax_url = new Curl('jsrpc.php');
const post_data = {
type: 9, // PAGE_TYPE_TEXT
method: 'screen.get',
// TODO: remove, do not use timestamp passing to server and back to ensure newest content will be shown.
timestamp: screen.timestampActual
};
$.each(type_params[params_index], function (i, name) {
if (!empty(screen[name])) {
post_data[name] = screen[name];
}
});
// set actual timestamp
screen.timestampActual = new CDate().getTime();
// timeline params
// SCREEN_RESOURCE_HTTPTEST_DETAILS, SCREEN_RESOURCE_DISCOVERY, SCREEN_RESOURCE_HTTPTEST
if ($.inArray(screen.resourcetype, [21, 22, 23]) === -1) {
post_data.from = screen.timeline.from;
post_data.to = screen.timeline.to;
}
switch (parseInt(screen.resourcetype, 10)) {
// SCREEN_RESOURCE_GRAPH
// SCREEN_RESOURCE_SIMPLE_GRAPH
case 0:
case 1:
self.refreshImg(id, function() {
$('a', '#flickerfreescreen_' + id).each(function() {
var obj = $(this),
url = new Curl(obj.attr('href'));
url.setArgument('from', screen.timeline.from);
url.setArgument('to', screen.timeline.to);
obj.attr('href', url.getUrl());
});
});
break;
// SCREEN_RESOURCE_MAP
case 2:
self.refreshMap(id);
break;
// SCREEN_RESOURCE_HISTORY
case 17:
if (screen.data.action == 'showgraph') {
self.refreshImg(id);
}
else {
if ('itemids' in screen.data) {
$.each(screen.data.itemids, function (i, value) {
if (!empty(value)) {
post_data['itemids[' + value + ']'] = value;
}
});
}
else {
post_data['graphid'] = screen.data.graphid;
}
$.each({
'filter': screen.data.filter,
'filter_task': screen.data.filterTask,
'mark_color': screen.data.markColor,
'action': screen.data.action
}, function (ajax_key, value) {
if (!empty(value)) {
post_data[ajax_key] = value;
}
});
self.refreshHtml(id, ajax_url, post_data);
}
break;
default:
self.refreshHtml(id, ajax_url, post_data);
break;
}
// set next refresh execution time
if (screen.isFlickerfree && screen.interval > 0) {
clearTimeout(screen.timeoutHandler);
screen.timeoutHandler = setTimeout(() => flickerfreeScreen.refresh(id), screen.interval);
}
},
refreshAll: function(time_object) {
for (var id in this.screens) {
var screen = this.screens[id];
if (!empty(screen.id) && typeof screen.timeline !== 'undefined') {
screen.timeline = $.extend(screen.timeline, {
from: time_object.from,
to: time_object.to,
from_ts: time_object.from_ts,
to_ts: time_object.to_ts
});
// Reset pager on time range update (SCREEN_RESOURCE_HISTORY).
if (screen.resourcetype == 17) {
screen.page = 1;
}
// restart refresh execution starting from Now
clearTimeout(screen.timeoutHandler);
this.refresh(id);
}
}
},
refreshHtml: function(id, ajaxUrl, post_data = {}) {
var screen = this.screens[id],
request_start = new CDate().getTime();
if (screen.isRefreshing) {
this.calculateReRefresh(id);
}
else {
screen.isRefreshing = true;
screen.timestampResponsiveness = new CDate().getTime();
this.setElementProgressState(id, true);
var ajaxRequest = $.ajax({
url: ajaxUrl.getUrl(),
type: 'post',
cache: false,
data: post_data,
dataType: 'html',
success: function(html) {
var html = $(html);
// Replace existing markup with server response.
if (request_start > screen.timestamp) {
screen.timestamp = request_start;
screen.isRefreshing = false;
$('.wrapper > .msg-bad').remove();
$('#flickerfreescreen_' + id).replaceWith(html);
html.filter('.msg-bad').insertBefore('.wrapper main');
window.flickerfreeScreen.setElementProgressState(id, false);
}
else if (!html.length) {
$('#flickerfreescreen_' + id).remove();
}
chkbxRange.init();
},
error: function() {
window.flickerfreeScreen.calculateReRefresh(id);
}
});
$.when(ajaxRequest).always(function() {
if (screen.isReRefreshRequire) {
screen.isReRefreshRequire = false;
window.flickerfreeScreen.refresh(id);
}
});
}
},
refreshMap: function(id) {
var screen = this.screens[id];
if (screen.isRefreshing) {
this.calculateReRefresh(id);
}
else {
screen.isRefreshing = true;
screen.error = 0;
screen.timestampResponsiveness = new CDate().getTime();
this.setElementProgressState(id, true);
var url = new Curl(screen.data.options.refresh);
url.setArgument('curtime', new CDate().getTime());
$.ajax({
'url': url.getUrl()
})
.fail(function() {
screen.error++;
window.flickerfreeScreen.calculateReRefresh(id);
})
.done(function(data) {
data.show_timestamp = screen.data.options.show_timestamp;
screen.isRefreshing = false;
screen.data.update(data);
$(screen.data.container).attr('aria-label', data.aria_label);
screen.timestamp = screen.timestampActual;
window.flickerfreeScreen.setElementProgressState(id, false);
});
}
},
refreshImg: function(id, successAction) {
var screen = this.screens[id],
request_start = new CDate().getTime();
if (screen.isRefreshing) {
this.calculateReRefresh(id);
}
else {
screen.isRefreshing = true;
screen.error = 0;
screen.timestampResponsiveness = new CDate().getTime();
this.setElementProgressState(id, true);
$('img', '#flickerfreescreen_' + id).each(function() {
var domImg = $(this),
url = new Curl(domImg.attr('src')),
zbx_sbox = domImg.data('zbx_sbox');
if (zbx_sbox && zbx_sbox.prevent_refresh) {
screen.isRefreshing = false;
window.flickerfreeScreen.setElementProgressState(id, false);
return;
}
url.setArgument('screenid', empty(screen.screenid) ? null : screen.screenid);
url.setArgument('from', screen.timeline.from);
url.setArgument('to', screen.timeline.to);
// Prevent image caching.
url.setArgument('_', request_start.toString(34));
// Create temp image in buffer.
var img = $('<img>', {
class: domImg.attr('class'),
id: domImg.attr('id'),
name: domImg.attr('name'),
border: domImg.attr('border'),
usemap: domImg.attr('usemap'),
alt: domImg.attr('alt')
})
.on('error', function() {
screen.error++;
window.flickerfreeScreen.calculateReRefresh(id);
})
.on('load', function() {
if (screen.error > 0) {
return;
}
screen.isRefreshing = false;
window.flickerfreeScreen.setElementProgressState(id, false);
if (request_start > screen.timestamp) {
screen.timestamp = request_start;
domImg.replaceWith(img);
// Callback function on success.
if (!empty(successAction)) {
successAction();
}
}
if (screen.isReRefreshRequire) {
screen.isReRefreshRequire = false;
window.flickerfreeScreen.refresh(id);
}
});
var async = flickerfreeScreen.getImageSboxHeight(url, function(height) {
zbx_sbox.height = parseInt(height, 10);
// 'src' should be added only here to trigger load event after new height is received.
img.data('zbx_sbox', zbx_sbox)
.attr('src', url.getUrl());
});
if (async === null) {
img.attr('src', url.getUrl());
}
if (zbx_sbox) {
img.data('zbx_sbox', $.extend(zbx_sbox, {
from: screen.timeline.from,
from_ts: screen.timeline.from_ts,
to: screen.timeline.to,
to_ts: screen.timeline.to_ts
}));
}
});
}
},
/**
* Getting shadow box height of graph image, asynchronous. Only for line graphs on dashboard.
* Will return xhr request for line graphs.
*
* @param {Curl} url Curl object for image request.
* Endpoint should support returning height via HTTP header.
* @param {function} cb Callable, will be called with value of shadow box height.
*
* @return {object|null}
*/
getImageSboxHeight: function (url, cb) {
if (['chart.php', 'chart2.php', 'chart3.php'].indexOf(url.getPath()) > -1
&& url.getArgument('outer') === '1') {
// Prevent request caching.
url.setArgument('_', (new Date).getTime().toString(34));
return $.get(url.getUrl(), {'onlyHeight': 1}, 'json')
.done(function(response, status, xhr) {
cb(xhr.getResponseHeader('X-ZBX-SBOX-HEIGHT'))
});
}
return null;
},
calculateReRefresh: function(id) {
var screen = this.screens[id],
time = new CDate().getTime();
if (screen.timestamp + this.responsiveness < time
&& screen.timestampResponsiveness + this.responsiveness < time) {
// take of busy flags
screen.isRefreshing = false;
screen.isReRefreshRequire = false;
// refresh anyway
window.flickerfreeScreen.refresh(id);
}
else {
screen.isReRefreshRequire = true;
}
},
cleanAll: function() {
for (var id in this.screens) {
var screen = this.screens[id];
if (!empty(screen.id)) {
clearTimeout(screen.timeoutHandler);
}
}
this.screens = [];
for (var id in timeControl.objectList) {
if (timeControl.objectList.hasOwnProperty(id)) {
timeControl.removeObject(id);
}
}
}
};
}(jQuery));