/*
** 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.
**/
// Time range selector.
jQuery(function($) {
var $container = $('.filter-space').first(),
xhr = null,
endpoint = new Curl('zabbix.php'),
element = {
from: $container.find('[id=from]'),
to: $container.find('[id=to]'),
from_clndr: $container.find('[name=from_calendar]'),
to_clndr: $container.find('[name=to_calendar]'),
apply: $container.find('[name=apply]'),
increment: $container.find('.js-btn-time-right'),
decrement: $container.find('.js-btn-time-left'),
zoomout: $container.find('.btn-time-zoomout'),
quickranges: $container.find('.time-quick a'),
label: $container.find('.btn-time')
},
request_data = {
idx: $container.data('profileIdx'),
idx2: $container.data('profileIdx2'),
from: element.from.val(),
to: element.to.val()
},
ui_accessible = ($container.data('accessible') == 1),
ui_disabled = false;
endpoint.setArgument('action', 'timeselector.update');
endpoint.setArgument('type', 11); // PAGE_TYPE_TEXT_RETURN_JSON
$.subscribe('timeselector.rangechange timeselector.decrement timeselector.increment timeselector.zoomout' +
' timeselector.rangeoffset',
timeSelectorEventHandler
);
element.from.keydown(submitChangeHandler);
element.to.keydown(submitChangeHandler);
// Time selector DOM elements event triggerers initialization.
$container.on('click', function(event) {
var action = '',
data = {},
$target = $(event.target);
if (ui_disabled) {
return cancelEvent(event);
}
else if ($target.is(element.increment)) {
action = 'timeselector.increment';
}
else if ($target.is(element.decrement)) {
action = 'timeselector.decrement';
}
else if ($target.is(element.zoomout)) {
action = 'timeselector.zoomout';
}
else if ($target.is(element.apply)) {
action = 'timeselector.rangechange';
data = {
from: element.from.val(),
to: element.to.val()
}
}
else if (element.quickranges.index($target) != -1) {
action = 'timeselector.rangechange';
data = $target.data();
element.quickranges.removeClass('selected');
$target.addClass('selected');
}
if (action !== '') {
$.publish(action, data);
}
});
/**
* Trigger timeselector.rangechange event on 'enter' key press in 'from' or 'to' input field.
*
* @param {object} event jQuery event object.
*/
function submitChangeHandler(event) {
if (event.which == 13) { // Enter
$.publish('timeselector.rangechange', {
from: element.from.val(),
to: element.to.val()
});
return cancelEvent(event);
}
}
/**
* Time selector UI update.
*
* @param {object} data Server response on 'timeselector.rangechange' request.
*/
function updateTimeSelectorUI(data) {
if (!ui_accessible) {
return;
}
if ('error' in data === false) {
element.from.val(data.from);
element.to.val(data.to);
element.label.text(data.label);
}
$([element.from[0], element.to[0], element.apply[0]]).prop('disabled', false);
$.each({
decrement: data.can_decrement,
increment: data.can_increment,
zoomout: data.can_zoomout
}, function(elm, state) {
if (typeof state !== 'undefined') {
element[elm].prop('disabled', !state);
}
element[elm].removeClass('disabled');
});
element.quickranges.removeClass('selected');
element.quickranges
.filter('[data-label="' + data.label + '"]')
.addClass('selected');
element.apply
.closest('.ui-tabs-panel')
.removeClass('is-loading is-loading-fadein');
ui_disabled = false;
}
/**
* Disable time selector UI.
*/
function disableTimeSelectorUI() {
if (!ui_accessible) {
return;
}
element.apply
.closest('.ui-tabs-panel')
.addClass('is-loading is-loading-fadein');
$([element.from[0], element.to[0], element.apply[0]]).prop('disabled', true);
$([element.decrement[0], element.zoomout[0], element.increment[0]]).addClass('disabled');
ui_disabled = true;
}
/**
* Time selector events handler. Any of current time selector interval changes will publish event
* 'timeselector.rangeupdate'.
*
* Handled events:
* timeselector.rangechange Event to apply new time selector from and to values.
* timeselector.decrement Event to decrement current time selector interval.
* timeselector.increment Event to increment current time selector interval.
* timeselector.zoomout Event to zoomout current time selector interval.
* timeselector.rangeoffset Event to apply offset to from and to values.
*
* @param {object} event jQuery event object.
* @param {object} data Object with published data for event.
*/
function timeSelectorEventHandler(event, data) {
var args = {
'idx': request_data.idx,
'idx2': request_data.idx2,
'from': (event.namespace === 'rangechange') ? data.from : request_data.from,
'to': (event.namespace === 'rangechange') ? data.to : request_data.to
};
switch (event.namespace) {
case 'rangeoffset':
args.from_offset = data.from_offset;
args.to_offset = data.to_offset;
break;
case 'zoomout':
if (request_data.can_zoomout === false) {
return;
}
break;
}
endpoint.setArgument('method', event.namespace);
if (xhr && xhr.abort) {
return;
}
disableTimeSelectorUI();
xhr = $.ajax({
url: endpoint.getUrl(),
type: 'post',
cache: false,
data: args,
success: function(json) {
request_data = $.extend(data, request_data, json, {event: event.namespace});
updateTimeSelectorUI(request_data);
if (json.error) {
if (typeof json.error === 'string') {
// Error message originates from CControllerTimeSelectorUpdate::checkInput().
alert(json.error);
}
$container.find('.time-input-error').each(function(i, elm) {
var $node = $(elm),
field = $node.attr('data-error-for');
if (json.error[field]) {
$node
.show()
.find('.red')
.text(json.error[field]);
}
else {
$node.hide();
}
});
delete request_data.error;
}
else {
updateUrlArguments(json.from, json.to);
$container
.find('.time-input-error')
.hide();
$.publish('timeselector.rangeupdate', request_data);
}
xhr = null;
},
error: function(request, status, error) {
/*
* In case there is something very wrong with the code like "echo '
'" in the middle where there is
* supposed to be JSON, show error. Otherwise it could've been just a temporary connection issue
* like 404, for example, so just retry.
*/
if (request.status != 200) {
var request = this,
retry = function() {
$.ajax(request);
};
// Retry with 2s interval.
setTimeout(retry, 2000);
}
else {
alert(error);
}
}
});
}
/**
* Update from/to URL arguments and remove page URL argument from browser history.
*
* @param {string} from Value for 'from' argument.
* @param {string} to Value for 'to' argument.
*/
function updateUrlArguments(from, to) {
var url = new Curl(),
args = url.getArguments();
if (('from' in args) || ('to' in args) || ('page' in args)) {
if ('from' in args) {
url.setArgument('from', from);
}
if ('to' in args) {
url.setArgument('to', to);
}
if ('page' in args) {
url.unsetArgument('page');
}
history.replaceState(history.state, '', url.getUrl());
}
}
// Time selection box for graphs.
var selection = null,
noclick_area = null,
was_dragged = false,
prevent_click = false;
$(document)
.on('mousedown', 'img', selectionHandlerDragStart)
.on('dblclick', 'img', function(event) {
if (typeof $(event.target).data('zbx_sbox') !== 'undefined') {
$.publish('timeselector.zoomout', {
from: element.from.val(),
to: element.to.val()
});
return cancelEvent(event);
}
})
.on('click', 'a', function(event) {
// Prevent click on graph image parent element when clicked inside graph selectable area.
if ($(event.target).is('img') && typeof $(event.target).data('zbx_sbox') !== 'undefined' && prevent_click
&& $(this).hasClass('dashboard-widget-graph-link')) {
return cancelEvent(event);
}
});
/**
* Handle selection box drag start event.
*
* @param {object} event jQuery event object.
*/
function selectionHandlerDragStart(event) {
if (event.which !== 1) {
return;
}
var target = $(event.target),
data = target.data();
if (typeof data.zbx_sbox === 'undefined') {
return;
}
was_dragged = false;
/**
* @prop {object} data
* @prop {integer} data.height Height of selection box.
* @prop {integer} data.left Left margin of selection box.
* @prop {integer} data.right Right margin of selection box.
* @prop {integer} data.top Top margin of selection box.
* @prop {integer} data.from_ts Timestamp for start time of selection box.
* @prop {integer} data.to_ts Timestamp for end time of selection box.
* @prop {integer} data.prevent_refresh Mark image as non updateable during selection.
*/
data = data.zbx_sbox;
data.prevent_refresh = true;
target.data('zbx_sbox', data);
var offset = target.offset(),
left = data.left,
right = target.outerWidth() - data.right,
xpos = Math.min(Math.max(left, event.pageX - offset.left), right),
parent = target.parent();
offset.top += data.top;
if ((event.pageY < offset.top) || event.pageY > offset.top + data.height) {
prevent_click = false;
return;
}
prevent_click = true;
noclick_area = $('