mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-05 19:15:03 +00:00
372 lines
15 KiB
Text
372 lines
15 KiB
Text
{#
|
|
(C) 2021 - ntop.org
|
|
#}
|
|
|
|
{%
|
|
-- if there are set the _GET["epoch_begin"] && _GET["epoch_end"]
|
|
-- then select the `Custom` Label
|
|
local presetSelected = (isEmptyString(_GET["epoch_begin"]) and isEmptyString(_GET["epoch_end"]))
|
|
%}
|
|
|
|
{% if tags.enabled then %}
|
|
<link rel="stylesheet" href="{{ ntop.getHttpPrefix }}/css/tagify.css" />
|
|
<style>
|
|
.tagify__input {
|
|
min-width: 175px;
|
|
}
|
|
|
|
.tagify__tag {
|
|
white-space: nowrap;
|
|
margin: 3px 0px 5px 5px;
|
|
}
|
|
.tagify__tag select.operator {
|
|
margin: 0px 4px;
|
|
border: 1px solid #c4c4c4;
|
|
border-radius: 4px;
|
|
}
|
|
.tagify__tag b.operator {
|
|
margin: 0px 4px;
|
|
background-color: white;
|
|
border: 1px solid #c4c4c4;
|
|
border-radius: 4px;
|
|
padding: 0.05em 0.2em;
|
|
}
|
|
.tagify__tag > div {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
</style>
|
|
{% end %}
|
|
|
|
<div class="w-100">
|
|
|
|
<div class="range-container d-flex flex-wrap">
|
|
|
|
{% if not isEmptyString(extra_range_buttons) then %}
|
|
{* extra_range_buttons *}
|
|
{% end %}
|
|
|
|
<div class="range-picker d-flex my-auto flex-wrap">
|
|
<div class="form-group my-auto mx-1">
|
|
<select class="form-select" id="select-presets">
|
|
{% if (presets.five_mins) or (presets.five_mins ~= false) then %}
|
|
<option {{ ((presetSelected and default == '5min') and 'selected' or '') }} value="5,minutes">{{ i18n('show_alerts.presets.5_min') }}</option>
|
|
{% end %}
|
|
|
|
<option {{ ((presetSelected and default == '30min') and 'selected' or '') }} value="30,minutes">{{ i18n('show_alerts.presets.30_min') }}</option>
|
|
|
|
<option {{ ((presetSelected and default == '1hour') and 'selected' or '') }} value="1,hours">{{ i18n('show_alerts.presets.hour') }}</option>
|
|
|
|
{% if (presets.day == nil) or presets.day then %}
|
|
<option {{ ((presetSelected and default == '1day') and 'selected' or '') }} value="1,days">{{ i18n('show_alerts.presets.day') }}</option>
|
|
{% end %}
|
|
|
|
{% if (presets.week == nil) or presets.week then %}
|
|
<option {{ ((presetSelected and default == '1week') and 'selected' or '') }} value="1,weeks">{{ i18n('show_alerts.presets.week') }}</option>
|
|
{% end %}
|
|
|
|
{% if (presets.month == nil) or presets.month then %}
|
|
<option value="1,months">{{ i18n('show_alerts.presets.month') }}</option>
|
|
{% end %}
|
|
|
|
{% if (presets.year == nil) or presets.year then %}
|
|
<option value="1,years">{{ i18n('show_alerts.presets.year') }}</option>
|
|
{% end %}
|
|
|
|
<option value="custom" disabled {{ not presetSelected and 'selected' or '' }}>{{ i18n("graphs.custom") }}</option>
|
|
</select>
|
|
</div>
|
|
|
|
|
|
<div style="width: 30rem;" class="input-group mx-1">
|
|
<span class="input-group-text">
|
|
<i class="fas fa-calendar-alt"></i>
|
|
</span>
|
|
<input id='begin-epoch' type="text" aria-label="Begin" data-toggle="datetimepicker" class="form-control time-input datetimepicker-input border-right-0" data-target="#begin-epoch">
|
|
<span class="input-group-text">
|
|
<i class="fas fa-long-arrow-alt-right"></i>
|
|
</span>
|
|
<input id='end-epoch' type="text" aria-label="End" data-toggle="datetimepicker" class="form-control time-input datetimepicker-input border-left-0" data-target="#end-epoch">
|
|
</div>
|
|
|
|
<span class="wrong-ranger invalid-feedback mx-1 my-auto" data-bs-toggle="tooltip" data-placement="bottom" title="{{ i18n('wrong_date_range') }}" style="width: 1rem; display: none;">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
</span>
|
|
|
|
<div class="controls d-flex flex-wrap">
|
|
<div class="btn-group me-auto btn-group-sm">
|
|
<button class="btn btn-link" id="btn-jump-time-back">
|
|
<i class="fas fa-arrow-left"></i>
|
|
</button>
|
|
<button class="btn btn-link me-2" disabled id="btn-jump-time-ahead">
|
|
<i class="fas fa-arrow-right"></i>
|
|
</button>
|
|
<button class="btn btn-link" id="btn-zoom-in">
|
|
<i class="fas fa-search-plus"></i>
|
|
</button>
|
|
<button class="btn btn-link" id="btn-zoom-out">
|
|
<i class="fas fa-search-minus"></i>
|
|
</button>
|
|
</div>
|
|
<button id="btn-apply" class="btn btn-sm btn-primary m-auto">{{ i18n("apply") }}</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
{% if tags.enabled then %}
|
|
<div class="d-flex mt-1">
|
|
<input class="w-100 form-control h-auto" name="tags" placeholder="{{ i18n('show_alerts.filters') }}">
|
|
|
|
{% if not isEmptyString(extra_tags_buttons) then %}
|
|
{* extra_tags_buttons *}
|
|
{% end %}
|
|
|
|
<button data-bs-toggle="tooltip" data-placement="bottom" title="{{ i18n('show_alerts.remove_filters') }}" class="btn ms-1 my-auto btn-sm btn-remove-tags">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
{% end %}
|
|
</div>
|
|
|
|
|
|
|
|
{% if tags.enabled then %}
|
|
<script type="text/javascript" src="{{ ntop.getHttpPrefix }}/js/tagify.min.js"></script>
|
|
<script type="text/javascript">
|
|
|
|
i18n.tags = {* json.encode(tags.localization) *};
|
|
|
|
const TAG_OPERATORS = {* json.encode(tags.tag_operators) *};
|
|
const DEFINED_TAGS = {* json.encode(tags.defined_tags) *};
|
|
const initialTags = {* json.encode(tags.values) *};
|
|
const input = $(`[name='tags']`)[0];
|
|
|
|
const tagify = new Tagify(input, {
|
|
duplicate: false,
|
|
delimiters : null,
|
|
dropdown : {
|
|
enabled: 1, // suggest tags after a single character input
|
|
classname : 'extra-properties' // custom class for the suggestions dropdown
|
|
},
|
|
autoComplete: { enabled: false },
|
|
templates : {
|
|
tag : function(tagData){
|
|
try{
|
|
return `<tag title='${tagData.value}' contenteditable='false' spellcheck="false" class='tagify__tag ${tagData.class ? tagData.class : ""}' ${this.getAttributes(tagData)}>
|
|
<x title='remove tag' class='tagify__tag__removeBtn'></x>
|
|
<div>
|
|
${tagData.label ? `<b>${tagData.label}</b> ` : ``}
|
|
${tagData.operators ? `<select class='operator'>${tagData.operators.map(op => `<option ${tagData.selectedOperator === op ? 'selected' : ''} value='${op}'>${TAG_OPERATORS[op]}</option>`).join()}</select>` : `<b class='operator'>=</b>`}
|
|
<span class='tagify__tag-text'>${tagData.value}</span>
|
|
</div>
|
|
</tag>`
|
|
}
|
|
catch(err){
|
|
console.error(`An error occured when creating a new tag: ${err}`);
|
|
}
|
|
},
|
|
}
|
|
});
|
|
|
|
$(document).ready(function() {
|
|
// add existing tags
|
|
tagify.addTags(initialTags);
|
|
// intialize tooltip
|
|
$(`.btn-remove-tags`).tooltip();
|
|
});
|
|
</script>
|
|
{% end %}
|
|
|
|
|
|
<script type="text/javascript">
|
|
const beginEpoch = {{ (_GET['epoch_begin'] or _GET['epoch_begin'] or 'undefined') }};
|
|
const endEpoch = {{ (_GET['epoch_end'] or _GET['epoch_end'] or 'undefined') }};
|
|
</script>
|
|
<script type='text/javascript'>
|
|
$(document).ready(function() {
|
|
|
|
const rangepickerEvent = new Event('change');
|
|
|
|
const MAX_ZOOM_IN = {{ (max_delta_in) }}; // 5 minutes
|
|
const MAX_ZOOM_OUT = {{ (max_delta_out) }}; // 12 hours
|
|
|
|
const now = (endEpoch === undefined) ? moment() : moment(endEpoch, 'X');
|
|
const prev = (beginEpoch === undefined) ? moment().subtract({{ (default == '30min' and '30' or '10080') }}, 'minutes') : moment(beginEpoch, 'X');
|
|
|
|
const $rangePicker = $(`.range-picker`)[0];
|
|
const $inputBeginEpoch = $(`#begin-epoch`);
|
|
const $inputEndEpoch = $(`#end-epoch`);
|
|
|
|
const $btnApply = $(`#btn-apply`);
|
|
const $btnJumpTimeBack = $(`#btn-jump-time-back`);
|
|
const $btnJumpTimeAhead = $(`#btn-jump-time-ahead`);
|
|
const $btnZoomIn = $(`#btn-zoom-in`);
|
|
const $btnZoomOut = $(`#btn-zoom-out`);
|
|
|
|
const $selectPresets = $(`#select-presets`);
|
|
|
|
function calculateHalfZoomIn() {
|
|
const begin = $inputBeginEpoch.datetimepicker('date');
|
|
const end = $inputEndEpoch.datetimepicker('date');
|
|
|
|
const delta = end.unix() - begin.unix();
|
|
const zoomIn = delta / 3;
|
|
const halfZoomIn = (zoomIn) / 2;
|
|
return [begin, end, delta, halfZoomIn];
|
|
}
|
|
|
|
function highlightCustomPresetButton() {
|
|
$(`#select-presets`).val('custom');
|
|
}
|
|
|
|
function disableRangePickerButtons() {
|
|
$('.controls button').attr('disabled', 'disabled');
|
|
}
|
|
|
|
function enableRangePickerButtons() {
|
|
$('.controls button').removeAttr('disabled');
|
|
}
|
|
|
|
function highlightWrongDate() {
|
|
$(`.range-picker .input-group`).addClass('border').addClass('rounded').addClass('border-danger');
|
|
$(`.wrong-ranger`).show();
|
|
}
|
|
|
|
function disableWrongDate() {
|
|
$(`.range-picker .input-group`).removeClass('border').removeClass('rounded').removeClass('border-danger');
|
|
$(`.wrong-ranger`).hide();
|
|
}
|
|
|
|
function updateRangePicker(operation, maxDate) {
|
|
|
|
// get the selected preset or the first prest available
|
|
const [delta, measure] = ($selectPresets.val() || $(`#select-presets option:first`).val()).split(',');
|
|
const begin = $inputBeginEpoch.datetimepicker('date');
|
|
const end = $inputEndEpoch.datetimepicker('date');
|
|
|
|
$inputEndEpoch.datetimepicker('date', end[operation](parseInt(delta), measure));
|
|
$inputBeginEpoch.datetimepicker('date', begin[operation](parseInt(delta), measure));
|
|
|
|
// if the new end time is less than the max current date then
|
|
// enable the jump time ahead button, otherwise disable it
|
|
if (end < maxDate) {
|
|
$btnJumpTimeAhead.removeClass('disabled');
|
|
}
|
|
else {
|
|
$btnJumpTimeAhead.addClass('disabled');
|
|
}
|
|
|
|
$rangePicker.dispatchEvent(rangepickerEvent);
|
|
}
|
|
|
|
$('[data-bs-toggle="tooltip"]').tooltip();
|
|
|
|
// initialize timepickers
|
|
$inputBeginEpoch.datetimepicker({format: 'DD/MM/YYYY HH:mm:ss', useCurrent: false});
|
|
$inputEndEpoch.datetimepicker({format: 'DD/MM/YYYY HH:mm:ss', useCurrent: false});
|
|
$inputBeginEpoch.datetimepicker('date', prev);
|
|
$inputEndEpoch.datetimepicker('date', now);
|
|
|
|
$selectPresets.on('change', function(e) {
|
|
|
|
// remove the active class
|
|
const [subtract, measure] = $(this).val().split(',');
|
|
|
|
const now = moment();
|
|
const diff = moment().subtract(parseInt(subtract), measure);
|
|
|
|
$inputEndEpoch.datetimepicker('minDate', diff);
|
|
$inputEndEpoch.datetimepicker('maxDate', now);
|
|
|
|
$inputEndEpoch.datetimepicker('date', now);
|
|
$inputBeginEpoch.datetimepicker('date', diff);
|
|
|
|
});
|
|
|
|
// travel back in the past by the selected preset
|
|
$btnJumpTimeBack.on('click', () => {updateRangePicker('subtract', now)});
|
|
// travel ahead in the future by the selected preset
|
|
$btnJumpTimeAhead.on('click', () => {updateRangePicker('add', now)});
|
|
|
|
$btnZoomIn.on('click', function() {
|
|
|
|
const [begin, end, delta, halfZoomIn] = calculateHalfZoomIn();
|
|
|
|
const newBegin = moment(begin.unix() + halfZoomIn, 'X');
|
|
const newEnd = moment(end.unix() - halfZoomIn, 'X');
|
|
|
|
$inputBeginEpoch.datetimepicker('date', newBegin);
|
|
$inputEndEpoch.datetimepicker('date', newEnd);
|
|
|
|
// if delta is lower than 5 minutes then disable the zoom in button
|
|
if (delta < MAX_ZOOM_IN) {
|
|
$(this).attr("disabled", "disabled");
|
|
}
|
|
|
|
$btnZoomOut.removeAttr("disabled");
|
|
$rangePicker.dispatchEvent(rangepickerEvent);
|
|
});
|
|
|
|
$btnZoomOut.on('click', function() {
|
|
|
|
const [begin, end, delta, halfZoomIn] = calculateHalfZoomIn();
|
|
|
|
const newBegin = moment(begin.unix() - halfZoomIn, 'X');
|
|
const newEnd = moment(end.unix() + halfZoomIn, 'X');
|
|
|
|
$inputBeginEpoch.datetimepicker('date', newBegin);
|
|
$inputEndEpoch.datetimepicker('date', newEnd);
|
|
|
|
// if delta is lower than 5 minutes then disable the zoom in button
|
|
if (delta > MAX_ZOOM_OUT) {
|
|
$(this).attr("disabled", "disabled");
|
|
}
|
|
|
|
$btnZoomIn.removeAttr("disabled");
|
|
$rangePicker.dispatchEvent(rangepickerEvent);
|
|
});
|
|
|
|
/* Autoselect the 'custom' dropdown entry when clicking any of the buttons */
|
|
$.each([$btnApply, $btnJumpTimeBack, $btnJumpTimeAhead], function(idx, btn) {
|
|
$(btn).on('click', function() {
|
|
highlightCustomPresetButton();
|
|
$rangePicker.dispatchEvent(rangepickerEvent);
|
|
});
|
|
});
|
|
|
|
$inputBeginEpoch.on("change.datetimepicker", function (e) {
|
|
|
|
const newBegin = e.date;
|
|
const currentEnd = $inputEndEpoch.datetimepicker('date');
|
|
|
|
// if the begin date is wrong disable the range picker buttons
|
|
if (newBegin.isAfter(currentEnd)) {
|
|
disableRangePickerButtons();
|
|
highlightWrongDate();
|
|
}
|
|
else {
|
|
enableRangePickerButtons();
|
|
disableWrongDate();
|
|
}
|
|
});
|
|
|
|
$inputEndEpoch.on("change.datetimepicker", function (e) {
|
|
|
|
const newEnd = e.date;
|
|
const currentBegin = $inputBeginEpoch.datetimepicker('date');
|
|
|
|
// if the end date is wrong disable the range picker buttons
|
|
if (newEnd.isBefore(currentBegin)) {
|
|
disableRangePickerButtons();
|
|
highlightWrongDate();
|
|
}
|
|
else {
|
|
enableRangePickerButtons();
|
|
disableWrongDate();
|
|
}
|
|
});
|
|
|
|
});
|
|
</script>
|