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.

414 lines
12 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.
**/
/*
* Since function addPopupValues can be defined by several dashboard widgets, the variable addPopupValues should be
* defined in global scope and always re-written with function right before usage. Do this in all widgets where it is
* needed.
*/
let old_addPopupValues = null;
if (typeof addPopupValues === 'undefined') {
window.addPopupValues = null;
}
(function($) {
$.widget('zbx.sortable_tree', $.extend({}, $.ui.sortable.prototype, {
options: {
// jQuery UI sortable options:
cursor: 'grabbing',
placeholder: 'placeholder',
forcePlaceholderSize: true,
toleranceElement: '> div',
forceHelperSize: true,
tolerance: 'intersect',
handle: '.drag-icon',
items: '.tree-item',
helper: 'clone',
revert: 10,
opacity: .75,
scrollSpeed: 20,
// Custom options:
parent_change_delay: 0,
parent_expand_delay: 600,
indent_size: 15,
max_depth: 10
},
_create: function() {
$.ui.sortable.prototype._create.apply(this, arguments);
},
_mouseDrag: function(event) {
const opt = this.options;
// Compute the helpers position.
this.position = this._generatePosition(event);
this.positionAbs = this._convertPositionTo('absolute');
if (!this.lastPositionAbs) {
this.lastPositionAbs = this.positionAbs;
}
// Do scrolling.
if (this.options.scroll) {
if (this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
if ((this.overflowOffset.top + this.scrollParent[0].offsetHeight)
- event.pageY < opt.scrollSensitivity) {
this.scrollParent[0].scrollTop = this.scrollParent[0].scrollTop + opt.scrollSpeed;
}
else if (event.pageY - this.overflowOffset.top < opt.scrollSensitivity) {
this.scrollParent[0].scrollTop = this.scrollParent[0].scrollTop - opt.scrollSpeed;
}
if ((this.overflowOffset.left + this.scrollParent[0].offsetWidth)
- event.pageX < opt.scrollSensitivity) {
this.scrollParent[0].scrollLeft = this.scrollParent[0].scrollLeft + opt.scrollSpeed;
}
else if (event.pageX - this.overflowOffset.left < opt.scrollSensitivity) {
this.scrollParent[0].scrollLeft = this.scrollParent[0].scrollLeft - opt.scrollSpeed;
}
}
else {
if (event.pageY - $(document).scrollTop() < opt.scrollSensitivity) {
$(document).scrollTop($(document).scrollTop() - opt.scrollSpeed);
}
else if ($(window).height() - (event.pageY - $(document).scrollTop()) < opt.scrollSensitivity) {
$(document).scrollTop($(document).scrollTop() + opt.scrollSpeed);
}
if (event.pageX - $(document).scrollLeft() < opt.scrollSensitivity) {
$(document).scrollLeft($(document).scrollLeft() - opt.scrollSpeed);
}
else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < opt.scrollSensitivity) {
$(document).scrollLeft($(document).scrollLeft() + opt.scrollSpeed);
}
}
}
// Regenerate the absolute position used for position checks.
this.positionAbs = this._convertPositionTo('absolute');
const prev_offset_top = this.placeholder.offset().top;
// Set the helper position.
if (!this.options.axis || this.options.axis !== 'y') {
this.helper[0].style.left = this.position.left + 'px';
}
if (!this.options.axis || this.options.axis !== 'x') {
this.helper[0].style.top = this.position.top + 'px';
}
this.hovering = this.hovering ? this.hovering : null;
this.changing_parent = this.changing_parent ? this.changing_parent : null;
this.mouseentered = this.mouseentered ? this.mouseentered : false;
if (this.changing_parent) {
clearTimeout(this.changing_parent);
}
// re-arrange
for (let i = this.items.length - 1; i >= 0; i--) {
// Cache variables and intersection, continue if no intersection.
const item = this.items[i];
const itemElement = item.item[0];
const intersection = this._intersectsWithPointer(item);
if (!intersection) {
continue;
}
// Cannot intersect with itself.
if (itemElement != this.currentItem[0]
&& this.placeholder[(intersection == 1) ? 'next' : 'prev']()[0] != itemElement
&& !$.contains(this.placeholder[0], itemElement)
&& (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)) {
if (!this.hovering && !$(itemElement).hasClass('opened')) {
$(itemElement).addClass('hovering');
this.hovering = setTimeout(() => {
$(itemElement)
.removeClass('closed')
.addClass('opened');
this.refreshPositions();
}, opt.parent_expand_delay);
}
if (!this.mouseentered) {
$(itemElement).mouseenter();
this.mouseentered = true;
}
this.direction = (intersection == 1) ? 'down' : 'up';
if (this._intersectsWithSides(item)) {
$(itemElement).removeClass('hovering').mouseleave();
this.mouseentered = false;
if (this.hovering) {
clearTimeout(this.hovering);
this.hovering = null;
}
this._rearrange(event, item);
}
else {
break;
}
this._trigger('change', event, this._uiHash());
break;
}
}
const parent_item = $(this.placeholder.parent()).closest('.tree-item');
const level = +$(this.placeholder.parent()).attr('data-depth');
const child_levels = this._levelsUnder(this.currentItem[0]) + 1;
let prev_item = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
let next_item = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
let direction_moved = null;
let levels_moved = 0;
if (prev_item !== null) {
while (prev_item[0] === this.currentItem[0] || prev_item[0] === this.helper[0]
|| prev_item[0].className.indexOf('tree-item') == -1) {
if (prev_item[0].previousSibling) {
prev_item = $(prev_item[0].previousSibling);
}
else {
prev_item = null;
break;
}
}
}
if (next_item !== null) {
while (next_item[0] === this.currentItem[0] || next_item[0] === this.helper[0]
|| next_item[0].className.indexOf('tree-item') == -1) {
if (next_item[0].nextSibling) {
next_item = $(next_item[0].nextSibling);
}
else {
next_item = null;
break;
}
}
}
if (parent_item.get(0) === this.currentItem[0]) {
$(this.element[0]).append(this.placeholder[0]);
this._trigger('stop', event, this._uiHash());
return false;
}
this.beyondMaxLevels = 0;
/*
* If item is moved to the left and it is last element of the list, add it as a child element to the
* element before.
*/
if (parent_item !== null && next_item === null
&& (this.positionAbs.left <= parent_item.offset().left
|| this.positionAbs.left <= opt.indent_size * -0.6)) {
direction_moved = 'left';
}
// If item is moved to the right and there is sibling element before, put it as a child of it.
else if (prev_item !== null && this.positionAbs.left >= prev_item.offset().left + opt.indent_size) {
direction_moved = 'right';
}
if (direction_moved) {
levels_moved = Math.floor(
Math.abs(parent_item.offset().left - this.positionAbs.left) / opt.indent_size
);
}
$('.highlighted-parent').removeClass('highlighted-parent');
if (direction_moved === 'right' && levels_moved) {
const drop_to = prev_item;
const hovered_branch_depth = drop_to[0].getElementsByClassName('tree-list')[0].dataset.depth;
this._isAllowed(prev_item, level, level + child_levels);
if (hovered_branch_depth < this.options.max_depth + 1) {
this.changing_parent = setTimeout(() => {
$(drop_to)
.addClass('highlighted-parent opened')
.removeClass('closed');
if (prev_offset_top && (prev_offset_top <= prev_item.offset().top)) {
$('>.tree-list', drop_to).prepend(this.placeholder);
}
else {
$('>.tree-list', drop_to).append(this.placeholder);
}
this.refreshPositions();
}, opt.parent_change_delay);
}
}
else if (direction_moved === 'left' && levels_moved) {
let drop_to = $(this.currentItem[0]).closest('.tree-item');
let one_before = null;
while (levels_moved > 0) {
if ($(drop_to).parent().closest('.tree-item').length) {
one_before = drop_to;
drop_to = $(drop_to).parent().closest('.tree-item');
}
levels_moved--;
}
$(drop_to).addClass('highlighted-parent');
this.changing_parent = setTimeout(() => {
if (one_before && one_before.length) {
$(this.placeholder).insertAfter(one_before);
}
else {
$('>.tree-list', drop_to).append(this.placeholder);
}
if (drop_to.children('.tree-list').children('li:visible:not(.ui-sortable-helper)').length < 1) {
drop_to.removeClass('opened');
}
this.refreshPositions();
}, opt.parent_change_delay);
this._isAllowed(prev_item, level, level + child_levels);
}
else {
$(this.placeholder.parent().closest('.tree-item')).addClass('highlighted-parent');
this._isAllowed(prev_item, level, level + child_levels);
}
// Post events to containers.
this._contactContainers(event);
// Call callbacks.
this._trigger('sort', event, this._uiHash());
this.lastPositionAbs = this.positionAbs;
return false;
},
_mouseStop: function(event, noPropagation) {
if (!event) {
return;
}
$('.highlighted-parent').removeClass('highlighted-parent');
this.placeholder.removeClass('sortable-error');
if (this.changing_parent) {
clearTimeout(this.changing_parent);
}
if (this.beyondMaxLevels > 0) {
this.reverting = true;
if (this.domPosition.prev) {
$(this.domPosition.prev).after(this.placeholder);
}
else {
$(this.domPosition.parent).prepend(this.placeholder);
}
this._trigger('revert', event, this._uiHash());
this.refreshPositions();
this._clear(event, noPropagation);
}
else {
const parent_id = this.placeholder.parent().closest('.tree-item').data('id');
const item_id = $(this.currentItem[0]).data('id');
$(`[name="navtree.parent.${item_id}"]`).val(parent_id);
if (this.options.revert) {
const self = this;
const cur = self.placeholder.offset();
self.reverting = true;
$(this.helper).animate({
left: cur.left - this.offset.parent.left - self.margins.left
+ ((this.offsetParent[0] == document.body) ? 0 : this.offsetParent[0].scrollLeft),
top: cur.top - this.offset.parent.top - self.margins.top
+ ((this.offsetParent[0] == document.body) ? 0 : this.offsetParent[0].scrollTop)
}, parseInt(this.options.revert, 10) || 500, function() {
self._clear(event);
});
}
else {
this._clear(event, noPropagation);
}
}
return false;
},
_isAllowed: function(parentItem, level, levels) {
if (this.options.max_depth + 1 > 0 && (this.options.max_depth + 1 < levels
|| +this.placeholder.closest('[data-depth]').attr('data-depth') > this.options.max_depth + 1)) {
this.placeholder.addClass('sortable-error');
this.beyondMaxLevels = levels - this.options.max_depth + 1;
}
else {
this.placeholder.removeClass('sortable-error');
this.beyondMaxLevels = 0;
}
},
_levelsUnder: function(item) {
const depths = [];
let levels;
$('.tree-list', item).not(':empty').each(function(i, item) {
levels = 0;
while ($('.tree-list', item).length) {
item = $('.tree-list', item).not(':empty');
levels++;
}
depths.push(levels);
});
return depths.length ? Math.max.apply(null, depths) : 0;
}
}));
$.zbx.sortable_tree.prototype.options = $.extend({}, $.ui.sortable.prototype.options,
$.zbx.sortable_tree.prototype.options
);
})(jQuery);