ntopng/httpdocs/templates/pages/components/range-picker.template
2021-05-27 15:18:10 +02:00

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>&nbsp;` : ``}
${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>`}&nbsp;
<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>