/*
** Zabbix
** Copyright (C) 2001-2019 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.
*/
var old_addPopupValues = null;
if (typeof addPopupValues === 'undefined') {
var addPopupValues = null;
}
if (typeof (zbx_widget_navtree_trigger) !== typeof (Function)) {
function zbx_widget_navtree_trigger(action, grid) {
var $navtree = jQuery('.navtree', grid['widget']['content_body']);
$navtree.zbx_navtree(action);
}
}
(function($) {
$.widget('zbx.sortable_tree', $.extend({}, $.ui.sortable.prototype, {
options: {
// jQuery UI sortable options:
cursor: IE ? 'move' : '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) {
var o = this.options,
prev_offset_top,
scrolled;
// 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) {
scrolled = false;
if (this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
if ((this.overflowOffset.top + this.scrollParent[0].offsetHeight)
- event.pageY < o.scrollSensitivity) {
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
}
else if (event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
}
if ((this.overflowOffset.left + this.scrollParent[0].offsetWidth)
- event.pageX < o.scrollSensitivity) {
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
}
else if (event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
}
}
else {
if (event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
}
else if ($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
}
if (event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
}
else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
}
}
if (scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
$.ui.ddmanager.prepareOffsets(this, event);
}
}
// Regenerate the absolute position used for position checks.
this.positionAbs = this._convertPositionTo('absolute');
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 (var i = this.items.length - 1; i >= 0; i--) {
// Cache variables and intersection, continue if no intersection.
var item = this.items[i], itemElement = item.item[0], 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')) {
var uiObj = this;
$(itemElement).addClass('hovering');
this.hovering = setTimeout(function() {
$(itemElement)
.removeClass('closed')
.addClass('opened');
uiObj.refreshPositions();
}, o.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;
}
}
var parent_item = $(this.placeholder.parent()).closest('.tree-item'),
level = +$(this.placeholder.parent()).attr('data-depth'),
prev_item = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null,
next_item = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null,
child_levels = this._levelsUnder(this.currentItem[0]),
direction_moved = null,
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 <= o.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 + o.indent_size) {
direction_moved = 'right';
}
if (direction_moved) {
levels_moved = Math.floor(Math.abs(parent_item.offset().left - this.positionAbs.left) / o.indent_size);
}
$('.highlighted-parent').removeClass('highlighted-parent');
if (direction_moved === 'right' && levels_moved) {
var drop_to = prev_item,
uiObj = this;
this._isAllowed(prev_item, level, level + child_levels);
this.changing_parent = setTimeout(function() {
$(drop_to)
.addClass('highlighted-parent opened')
.removeClass('closed');
if (prev_offset_top && (prev_offset_top <= prev_item.offset().top)) {
$('>.tree-list', drop_to).prepend(uiObj.placeholder);
}
else {
$('>.tree-list', drop_to).append(uiObj.placeholder);
}
uiObj.refreshPositions();
}, o.parent_change_delay);
}
else if (direction_moved === 'left' && levels_moved) {
var drop_to = $(this.currentItem[0]).closest('.tree-item'),
one_before = null,
uiObj = this;
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(function() {
if (one_before && one_before.length) {
$(uiObj.placeholder).insertAfter(one_before);
}
else {
$('>.tree-list', drop_to).append(uiObj.placeholder);
}
if (drop_to.children('.tree-list').children('li:visible:not(.ui-sortable-helper)').length < 1) {
drop_to.removeClass('opened');
}
uiObj.refreshPositions();
}, o.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);
// Interconnect with droppables.
if ($.ui.ddmanager) {
$.ui.ddmanager.drag(this, 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 {
// If we are using droppables, inform the manager about the drop.
if ($.ui.ddmanager && !this.options.dropBehaviour) {
$.ui.ddmanager.drop(this, event);
var parent_id = this.placeholder.parent().closest('.tree-item').data('id'),
item_id = $(this.currentItem[0]).data('id');
$('[name="map.parent.' + item_id + '"]').val(parent_id);
}
if (this.options.revert) {
var self = this,
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 != 0 && (this.options.max_depth < levels
|| +this.placeholder.closest('[data-depth]').attr('data-depth') > this.options.max_depth)
) {
this.placeholder.addClass('sortable-error');
this.beyondMaxLevels = levels - this.options.max_depth;
}
else {
this.placeholder.removeClass('sortable-error');
this.beyondMaxLevels = 0;
}
},
_levelsUnder: function(item) {
var depths = [], 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);
jQuery(function($) {
/**
* Create Navigation Tree Widget.
*
* @return object
*/
if (typeof($.fn.zbx_navtree) === 'undefined') {
$.fn.zbx_navtree = function(input) {
var getNextId = function($obj) {
var widget_data = $obj.data('widgetData');
widget_data.lastId++;
while ($('[name="map.name.' + widget_data.lastId + '"]').length) {
widget_data.lastId++;
}
return widget_data.lastId;
};
var makeSortable = function($obj) {
var widget_data = $obj.data('widgetData');
$('.root-item>.tree-list')
.sortable_tree({
max_depth: widget_data['max_depth'],
stop: function(event, ui) {
setTreeHandlers($obj);
}
})
.disableSelection();
};
/*
* Find and fix Circular Dependencies in parent - child (id) relations.
* Once the circular dependency is found, an item parent is set to be 0.
*
* @param {array} tree_items - array of tree items.
*/
var fixCircularDependencies = function($obj, tree_items) {
var tree_items = tree_items || [],
item_to_test,
parents;
$.each(tree_items, function(i, item) {
if (item['parent'] != 0) {
item_to_test = item;
while (item_to_test['parent'] != 0) {
if (item_to_test['parent'] == item['id']) {
tree_items[i]['parent'] = 0;
break;
}
parents = tree_items.filter(function(item) {
return item['id'] == item_to_test['parent'];
});
if (parents.length) {
item_to_test = parents[0];
}
else {
break;
}
}
}
});
return tree_items;
};
var drawTree = function($obj, isEditMode) {
var root = createTreeBranch($obj, 'root', null),
widget_data = $obj.data('widgetData'),
prefix = widget_data['uniqueid'] + '_',
tree_items = getTreeWidgetItems($obj),
tree_items = fixCircularDependencies($obj, tree_items),
tree = buildTree($obj, tree_items, 0);
$('.root', $obj).remove();
$('.tree', $obj).append(root);
if (isEditMode) {
var edit_mode_tree = createTreeItem($obj, {name: t('root'), id: 0}, 0, false, true);
root.appendChild(edit_mode_tree);
if (tree.length) {
var new_class = edit_mode_tree.getAttribute('class').replace('closed', 'opened');
edit_mode_tree.setAttribute('class', new_class);
}
root = document.getElementById(prefix + 'children-of-0');
}
$.each(tree, function(i, item) {
if (typeof item === 'object') {
root.appendChild(createTreeItem($obj, item, 1, true, isEditMode));
}
});
setTreeHandlers($obj);
};
var parseProblems = function($obj) {
var widget_data = $obj.data('widgetData'),
empty_tmpl = {};
if (typeof widget_data.severity_levels === 'undefined') {
return false;
}
$.each(widget_data.severity_levels, function(sev, conf) {
empty_tmpl[sev] = 0;
});
$.each(widget_data.problems, function(itemid, problems) {
problems = problems ? problems : empty_tmpl;
$.each(problems, function(sev, numb) {
if (numb) {
$('.tree-item[data-id=' + itemid + ']').attr('data-problems' + sev, numb);
}
});
});
$.each(widget_data.severity_levels, function(sev, conf) {
$('[data-problems' + sev + ']', $obj).each(function() {
var obj = $(this);
$('>.tree-row>.problems', this).append($('', {
'style': 'background: #' + conf['color'],
'class': 'problems-per-item',
'title': conf['name']
})
.html(obj.attr('data-problems' + sev))
);
});
});
};
var createTreeBranch = function($obj, className, parentId) {
var className = className || '',
widget_data = $obj.data('widgetData'),
prefix = widget_data['uniqueid'] + '_',
ul = document.createElement('UL');
if (parentId !== null) {
ul.setAttribute('id', prefix + 'children-of-' + parentId);
}
className += ' tree-list';
ul.setAttribute('class', className);
return ul;
};
/*
* Dialog to create new or edit existing Tree item.
*
* @param {numeric} id - widget field ID or 0 when creating new item.
* @param {numeric} parent - ID of parent item under which new item is created.
* @param {numeric} depth - a depth of parent item under which new item is created.
* @param {object} trigger_elmnt - UI element clicked to open dialog.
*/
var itemEditDialog = function($obj, id, parent, depth, trigger_elmnt) {
var url = new Curl('zabbix.php'),
item_edit = !!id,
ajax_data = {
map_name: '',
map_mapid: 0,
depth: depth || 1,
map_id: +id
};
if (id) {
ajax_data['map_name'] = $('[name="map.name.' + id + '"]', $obj).val();
ajax_data['map_mapid'] = $('[name="mapid.' + id + '"]', $obj).val();
}
else {
ajax_data['map_id'] = getNextId($obj);
}
url.setArgument('action', 'widget.navtree.item.edit');
jQuery.ajax({
url: url.getUrl(),
method: 'POST',
data: ajax_data,
dataType: 'json',
success: function(resp) {
var id = +ajax_data['map_id'];
overlayDialogue({
'title': t('Edit tree element'),
'content': resp.body,
'buttons': [
{
'title': item_edit ? t('Apply') : t('Add'),
'class': 'dialogue-widget-save',
'action': function() {
var form = $('#widget_dialogue_form'),
url = new Curl('zabbix.php'),
ajax_data = {
add_submaps: $('[name="add_submaps"]', form).is(':checked') ? 1 : 0,
map_name: $('[name="map.name.' + id + '"]', form).val(),
map_mapid: $('[name="linked_map_id"]', form).val(),
depth: depth || 1,
mapid: id
};
url.setArgument('action', 'widget.navtree.item.update');
jQuery.ajax({
url: url.getUrl(),
method: 'POST',
data: ajax_data,
dataType: 'json',
success: function(resp) {
var new_item,
root;
$('.msg-bad', form).remove();
if (typeof resp.errors === 'object' && resp.errors.length > 0) {
form.prepend(resp.errors);
return false;
}
else {
if ($('[name="map.name.' + id + '"]', $obj).length) {
$('[name="map.name.' + id + '"]', $obj).val(resp['map_name']);
$('[name="mapid.' + id + '"]', $obj).val(resp['map_mapid']);
$('[data-id=' + id + '] > .tree-row > .content > .item-name',
$obj
)
.empty()
.attr('title', resp['map_name'])
.append($('').text(resp['map_name']));
}
else {
root = $('.tree-item[data-id=' + parent + ']>ul.tree-list',
$obj
).get(0),
id = +resp['map_id'];
new_item = {
name: resp['map_name'],
mapid: resp['map_mapid'],
id: id,
parent: +parent
};
root.appendChild(createTreeItem($obj, new_item, 1, true, true));
$(root).closest('.tree-item')
.removeClass('closed')
.addClass('opened is-parent');
}
if (typeof resp.hierarchy !== 'undefined') {
var add_child_levels = function($obj, mapid, itemid) {
if (typeof resp.hierarchy[mapid] !== 'undefined') {
var root = $('.tree-item[data-id=' + itemid +
']>ul.tree-list', $obj
).get(0);
$.each(resp.hierarchy[mapid], function(i, submapid) {
if (typeof resp.submaps[submapid] !== 'undefined') {
var submap_item = resp.submaps[submapid],
submap_itemid = getNextId($obj),
new_item = {
name: submap_item['name'],
mapid: submap_item['sysmapid'],
id: submap_itemid,
parent: +itemid
};
root.appendChild(createTreeItem($obj, new_item,
1, true, true
));
add_child_levels($obj, submapid, submap_itemid);
}
});
}
};
add_child_levels($obj, resp['map_mapid'], id);
$(root).closest('.tree-item')
.addClass('is-parent opened')
.removeClass('closed');
}
overlayDialogueDestroy('navtreeitem');
setTreeHandlers($obj);
}
}
});
return false;
}
},
{
'title': t('Cancel'),
'class': 'btn-alt',
'action': function() {}
}
],
'dialogueid': 'navtreeitem'
}, trigger_elmnt);
}
});
};
/**
* Create Tree LI item with everything needed inside it.
* Native javascript element building is used to improve performance.
*
* @param {object} item
* @param {numeric} depth
* @param {bool} editable Eeither item in edit-mode will be editable. Root item is not editable.
* @param {booln} isEditMode Indicates either dashboard is in edit mode.
*
* @returns {object}
*/
var createTreeItem = function($obj, item, depth, editable, isEditMode) {
var widget_data = $obj.data('widgetData'),
prefix = widget_data['uniqueid'] + '_',
ul = createTreeBranch($obj, null, item.id),
item_clases = 'tree-item';
if (!editable || widget_data['navtree_items_opened'].indexOf(item.id.toString()) !== -1) {
item_clases += ' opened';
}
else {
item_clases += ' closed';
}
if (!editable) {
item_clases += ' root-item';
}
if (isEditMode && item.mapid == 0) {
item_clases += ' no-map';
}
if (typeof item.children !== 'undefined' && widget_data.max_depth > depth) {
var child_items_visible = 0;
$.each(item.children, function(i, item) {
if (typeof item === 'object') {
ul.appendChild(createTreeItem($obj, item, depth + 1, true, isEditMode));
if (item.id > widget_data.lastId) {
widget_data.lastId = item.id;
}
if (item.item_visible === true) {
child_items_visible++;
}
}
});
if (item.children.length && child_items_visible > 0) {
item_clases += ' is-parent';
}
}
if (item.item_active === false && !isEditMode && item.mapid > 0) {
item_clases += ' inaccessible';
}
if (!isEditMode && item.mapid > 0 && item.item_active === true) {
var link = document.createElement('A');
link.setAttribute('data-mapid', item.mapid);
link.setAttribute('href', '#');
link.addEventListener('click', function(event) {
var data_to_share = {mapid: $(this).data('mapid')},
itemid = $(this).closest('.tree-item').data('id'),
step_in_path = $(this).closest('.tree-item'),
widget = getWidgetData($obj);
if ($('.dashbrd-grid-container').dashboardGrid('widgetDataShare', widget,
'selected_mapid', data_to_share)
) {
$('.selected', $obj).removeClass('selected');
while ($(step_in_path).length) {
$(step_in_path).addClass('selected');
step_in_path = $(step_in_path).parent().closest('.tree-item');
}
$(this).closest('.tree-item').addClass('selected');
}
event.preventDefault();
updateUserProfile('web.dashbrd.navtree.item.selected', itemid, [widget['widgetid']]);
});
}
else {
var link = document.createElement('SPAN');
}
link.setAttribute('class', 'item-name');
link.setAttribute('title', item.name);
link.innerText = item.name;
var li_item = document.createElement('LI');
li_item.setAttribute('class', item_clases);
li_item.setAttribute('data-id', item.id);
li_item.setAttribute('id', prefix + 'tree-item-' + item.id);
if (item.mapid) {
li_item.setAttribute('data-mapid', item.mapid);
}
if (item.item_visible === false) {
li_item.style.display = 'none';
}
var tree_row = document.createElement('DIV');
tree_row.setAttribute('class', 'tree-row');
li_item.appendChild(tree_row);
if (isEditMode) {
var tools = document.createElement('DIV');
tools.setAttribute('class', 'tools');
tree_row.appendChild(tools);
}
else {
var problems = document.createElement('DIV');
problems.setAttribute('class', 'problems');
tree_row.appendChild(problems);
}
var content = document.createElement('DIV');
content.setAttribute('class', 'content');
tree_row.appendChild(content);
var margin_lvl = document.createElement('DIV');
margin_lvl.setAttribute('class', 'margin-lvl');
content.appendChild(margin_lvl);
if (isEditMode) {
var btn1 = document.createElement('INPUT');
btn1.setAttribute('type', 'button');
btn1.setAttribute('data-id', item.id);
btn1.setAttribute('class', 'add-child-btn');
btn1.setAttribute('title', t('Add child element'));
btn1.addEventListener('click', function(event) {
var parentId = +$(this).data('id'),
widget_data = $obj.data('widgetData'),
depth = $(this).closest('.tree-list').attr('data-depth'),
branch = $('.tree-item[data-id=' + parentId + ']>ul', $obj);
if (typeof depth === 'undefined') {
depth = 0;
}
if (widget_data.max_depth > +depth) {
itemEditDialog($obj, 0, parentId, +depth + 1, event.target);
}
});
tools.appendChild(btn1);
var btn2 = document.createElement('INPUT');
btn2.setAttribute('type', 'button');
btn2.setAttribute('data-id', item.id);
btn2.setAttribute('class', 'import-items-btn');
btn2.setAttribute('title', t('Add multiple maps'));
btn2.addEventListener('click', function(event) {
var id = +$(this).data('id');
if (typeof addPopupValues === 'function') {
old_addPopupValues = addPopupValues;
}
addPopupValues = function(data) {
var root = $('.tree-item[data-id=' + id + ']>ul.tree-list', $obj).get(0),
new_item;
$.each(data.values, function() {
new_item = {
name: this['name'],
mapid: this['sysmapid'],
id: getNextId($obj),
parent: id
};
root.appendChild(createTreeItem($obj, new_item, 1, true, isEditMode));
});
$(root)
.closest('.tree-item')
.removeClass('closed')
.addClass('opened');
setTreeHandlers($obj);
if (typeof old_addPopupValues === 'function') {
addPopupValues = old_addPopupValues;
old_addPopupValues = null;
}
};
return PopUp('popup.generic', {
srctbl: 'sysmaps',
srcfld1: 'sysmapid',
srcfld2: 'name',
multiselect: '1'
}, null, event.target);
});
tools.appendChild(btn2);
if (editable) {
var btn3 = document.createElement('INPUT');
btn3.setAttribute('type', 'button');
btn3.setAttribute('data-id', item.id);
btn3.setAttribute('class', 'edit-item-btn');
btn3.setAttribute('title', t('Edit'));
btn3.addEventListener('click', function(event) {
var id = +$(this).data('id'),
parent = +$('input[name="map.parent.' + id + '"]', $obj).val(),
depth = +$(this).closest('[data-depth]').attr('data-depth');
itemEditDialog($obj, id, parent, depth, event.target);
});
tools.appendChild(btn3);
var btn4 = document.createElement('BUTTON');
btn4.setAttribute('type', 'button');
btn4.setAttribute('data-id', item.id);
btn4.setAttribute('class', 'remove-btn');
btn4.setAttribute('title', t('Remove'));
btn4.addEventListener('click', function() {
removeItem($obj, [$(this).data('id')]);
});
tools.appendChild(btn4);
}
}
if (isEditMode && editable) {
var drag = document.createElement('DIV');
drag.setAttribute('class', 'drag-icon');
content.appendChild(drag);
}
var arrow = document.createElement('DIV');
arrow.setAttribute('class', 'arrow');
content.appendChild(arrow);
if (editable) {
var arrow_btn = document.createElement('BUTTON'),
arrow_span = document.createElement('SPAN');
arrow_btn.setAttribute('type', 'button');
arrow_btn.setAttribute('class', 'treeview');
arrow_span.setAttribute('class',
(item_clases.indexOf('opened') !== -1) ? 'arrow-right' : 'arrow-down'
);
arrow_btn.appendChild(arrow_span);
arrow.appendChild(arrow_btn);
arrow_btn.addEventListener('click', function(event) {
var widget_data = getWidgetData($obj),
widget_options = $obj.data('widgetData'),
branch = $(this).closest('[data-id]'),
button = $(this),
closed_state = '1';
if (branch.hasClass('opened')) {
$('span', button)
.addClass('arrow-right')
.removeClass('arrow-down');
branch.removeClass('opened').addClass('closed');
}
else {prefix
$('span', button)
.addClass('arrow-down')
.removeClass('arrow-right');
branch.removeClass('closed').addClass('opened');
closed_state = '0';
}
if (widget_data['widgetid'].length) {
updateUserProfile(
'web.dashbrd.navtree-' + branch.data('id') + '.toggle',
closed_state, [widget_data['widgetid']]
);
var index = widget_options['navtree_items_opened'].indexOf(branch.data('id').toString());
if (index > -1) {
if (closed_state === '1') {
widget_options['navtree_items_opened'].splice(index, 1);
}
else {
widget_options['navtree_items_opened'].push(branch.data('id').toString());
}
}
else if (closed_state === '0' && index == -1) {
widget_options['navtree_items_opened'].push(branch.data('id').toString());
}
}
});
}
content.appendChild(link);
li_item.appendChild(ul);
if (isEditMode && editable) {
var name_fld = document.createElement('INPUT');
name_fld.setAttribute('type', 'hidden');
name_fld.setAttribute('name', 'map.name.' + item.id);
name_fld.setAttribute('id', prefix + 'map.name.' + item.id);
name_fld.value = item.name;
li_item.appendChild(name_fld);
var parent_fld = document.createElement('INPUT');
parent_fld.setAttribute('type', 'hidden');
parent_fld.setAttribute('name', 'map.parent.' + item.id);
parent_fld.setAttribute('id', prefix + 'map.parent.' + item.id);
parent_fld.value = +item.parent || 0;
li_item.appendChild(parent_fld);
var mapid_fld = document.createElement('INPUT');
mapid_fld.setAttribute('type', 'hidden');
mapid_fld.setAttribute('name', 'mapid.' + item.id);
mapid_fld.setAttribute('id', prefix + 'mapid.' + item.id);
mapid_fld.value = item.mapid;
li_item.appendChild(mapid_fld);
}
return li_item;
};
var setTreeHandlers = function($obj) {
var widget_data = $obj.data('widgetData'),
tree_list_depth;
// Add .is-parent class for branches with sub-items.
$('.tree-list', $obj).not('.ui-sortable, .root').each(function() {
if ($('>li', this).not('.inaccessible').length) {
$(this).closest('.tree-item').addClass('is-parent');
}
else {
$(this).closest('.tree-item').removeClass('is-parent');
}
});
// Set [data-depth] for list and each sublist.
$('.tree-list', $obj).not('.root').each(function() {
tree_list_depth = $(this).parents('.tree-list').not('.root').length + 1;
$(this).attr('data-depth', tree_list_depth);
}).promise().done(function() {
// Show/hide 'add new items' buttons.
$('.tree-list', $obj).filter(function() {
return +$(this).attr('data-depth') >= widget_data.max_depth;
}).each(function() {
$('.import-items-btn', $(this)).css('visibility', 'hidden');
$('.add-child-btn', $(this)).css('visibility', 'hidden');
});
// Show/hide buttons in deepest levels.
$('.tree-list', $obj).filter(function() {
return widget_data.max_depth > +$(this).attr('data-depth');
}).each(function() {
$('>.tree-item>.tree-row>.tools>.import-items-btn', $(this)).css('visibility', 'visible');
$('>.tree-item>.tree-row>.tools>.add-child-btn', $(this)).css('visibility', 'visible');
});
});
// Change arrow style.
$('.is-parent', $obj).each(function() {
var arrow = $('> .tree-row > .content > .arrow > .treeview > span', $(this));
if ($(this).hasClass('opened')) {
arrow.removeClass('arrow-right').addClass('arrow-down');
}
else {
$(arrow).removeClass('arrow-down a1').addClass('arrow-right');
}
});
};
var getWidgetData = function($obj) {
var widget_data = $obj.data('widgetData'),
response = $(".dashbrd-grid-container")
.dashboardGrid('getWidgetsBy', 'uniqueid', widget_data['uniqueid']);
if (response.length) {
return response[0];
}
else {
return null;
}
};
/**
* Detects either the dashboard is in edit mode.
*
* @returns {boolean}
*/
var isEditMode = function() {
return $(".dashbrd-grid-container").dashboardGrid('isEditMode');
};
/*
* Grouping a separate widget fields into objects of items. Each items consists of its name, parent, id etc.
*
* @returns {Array} - an array of item objects.
*/
var getTreeWidgetItems = function($obj) {
var widget_data = getWidgetData($obj),
tree_items = [];
$.each(widget_data['fields'], function(field_name, value) {
var det = /^map\.name\.(\d+)$/.exec(field_name);
if (det) {
var item = {
name: value,
parent: 0,
order: 1,
mapid: 0,
id: +det[1]
};
if (typeof widget_data['fields']['map.parent.' + item.id] !== 'undefined') {
item.parent = +widget_data['fields']['map.parent.' + item.id];
}
if (typeof widget_data['fields']['mapid.' + item.id] !== 'undefined') {
item.mapid = widget_data['fields']['mapid.' + item.id];
}
if (typeof widget_data['fields']['map.order.' + item.id] !== 'undefined') {
item.order = +widget_data['fields']['map.order.' + item.id];
}
tree_items.push(item);
}
});
return tree_items;
};
// Create multi-level array that represents real child-parent dependencies in tree.
var buildTree = function($obj, rows, parent_id) {
var widget_data = $obj.data('widgetData'),
rows = rows || [],
tree = [];
if (!rows.length) {
return [];
}
$.each(rows, function(i, item) {
if (typeof item === 'object') {
if (item['id'] > widget_data.lastId) {
widget_data.lastId = item['id'];
}
if (item['parent'] == parent_id) {
var children = buildTree($obj, rows, item['id']),
item_visible = true,
item_active = true;
if (children.length) {
item['children'] = children;
}
if (widget_data.show_unavailable && item.mapid
&& widget_data['maps_accessible'].indexOf(item.mapid) === -1) {
item_active = false;
}
else {
if (item.mapid) {
item_active = widget_data['maps_accessible'].indexOf(item.mapid) !== -1;
if (!widget_data.show_unavailable && !item_active) {
item_visible = false;
}
}
else {
item_active = false;
}
}
item['item_visible'] = item_visible;
item['item_active'] = item_active;
tree.push(item);
}
}
});
tree.sort(function(a, b) {
if (a['order'] < b['order']) {
return -1;
}
if (a['order'] > b['order']) {
return 1;
}
return 0;
});
return tree;
};
// Remove item from tree.
var removeItem = function($obj, id) {
var item = $('[data-id=' + id + ']', $obj),
widget_data = $obj.data('widgetData'),
prefix = widget_data['uniqueid'] + '_',
parent = +$('#' + prefix + 'map.parent.' + id, item).val();
if ($('#' + prefix + 'children-of-' + parent + '>.tree-item', $obj).length == 1) {
$('#' + prefix + 'tree-item-' + parent).removeClass('is-parent');
}
$(item).remove();
setTreeHandlers($obj);
};
// Records data from DOM to dashboard widget[fields] array.
var updateWidgetFields = function($obj) {
var dashboard_widget = getWidgetData($obj),
prefix = dashboard_widget['uniqueid'] + '_',
widget_fields = {};
if (!dashboard_widget || !isEditMode()) {
return false;
}
for (var field_name in dashboard_widget['fields']) {
if (!/map\.?(?:id|parent|name|order)\.\d+/.test(field_name)) {
widget_fields[field_name] = dashboard_widget['fields'][field_name];
}
}
$('input[name^="map.name."]', dashboard_widget['content_body']).each(function(index, field) {
var id = +field.getAttribute('name').substr(9);
if (id) {
var parent = +document.getElementById(prefix + 'map.parent.' + id).value,
mapid = document.getElementById(prefix + 'mapid.' + id).value,
sibl = document.getElementById(prefix + 'children-of-' + parent).childNodes,
order = 0;
while (typeof sibl[order] !== 'undefined' && +sibl[order].getAttribute('data-id') !== id) {
order++;
}
widget_fields['map.name.' + id] = field.value;
widget_fields['map.parent.' + id] = parent || 0;
widget_fields['map.order.' + id] = order + 1;
if (mapid) {
widget_fields['mapid.' + id] = mapid;
}
}
});
dashboard_widget['fields'] = widget_fields;
};
var openBranch = function($obj, id) {
if (!$('.tree-item[data-id=' + id + ']').is(':visible')) {
var selector = '> .tree-row > .content > .arrow > .treeview > span',
branch_to_open = $('.tree-item[data-id=' + id + ']').closest('.tree-list').not('.root');
while (branch_to_open.length) {
branch_to_open.closest('.tree-item.is-parent')
.removeClass('closed')
.addClass('opened');
$(selector, branch_to_open.closest('.tree-item.is-parent'))
.removeClass('arrow-right')
.addClass('arrow-down');
branch_to_open = branch_to_open.closest('.tree-item.is-parent')
.closest('.tree-list').not('.root');
}
}
};
var switchToNavigationMode = function($obj) {
drawTree($obj, isEditMode());
parseProblems($obj);
};
var switchToEditMode = function($obj) {
var dashboard_widget = getWidgetData($obj);
if (!dashboard_widget) {
return false;
}
drawTree($obj, isEditMode());
makeSortable($obj);
};
var markTreeItemSelected = function($obj, item_id, send_data) {
var widget = getWidgetData($obj),
prefix = widget['uniqueid'] + '_',
selected_item = $('#' + prefix + 'tree-item-' + item_id),
step_in_path = selected_item;
/**
* If 'send_data' is set to be 'false', use an unexisting 'data_name', just to check if widget has
* linked widgets, but avoid real data sharing.
*/
if (item_id && $('.dashbrd-grid-container').dashboardGrid('widgetDataShare', widget,
send_data ? 'selected_mapid' : '', {mapid: $(selected_item).data('mapid')})) {
$('.selected', $obj).removeClass('selected');
while ($(step_in_path).length) {
$(step_in_path).addClass('selected');
step_in_path = $(step_in_path).parent().closest('.tree-item');
}
}
};
var methods = {
// beforeConfigLoad trigger method
beforeConfigLoad: function() {
var $this = $(this);
return this.each(function() {
updateWidgetFields($this);
});
},
// beforeDashboardSave trigger method
beforeDashboardSave: function() {
var $this = $(this);
return this.each(function() {
updateWidgetFields($this);
});
},
// onEditStart trigger method
onEditStart: function() {
var $this = $(this);
return this.each(function() {
switchToEditMode($this);
});
},
// onDashboardReady trigger method
onDashboardReady: function() {
var $this = $(this);
return this.each(function() {
var widget = getWidgetData($this),
widget_data = $this.data('widgetData');
if (!widget_data.navtree_item_selected
|| !$('.tree-item[data-id=' + widget_data.navtree_item_selected + ']').is(':visible')) {
widget_data.navtree_item_selected = $('.tree-item:visible', $this)
.not('[data-mapid="0"]')
.first()
.data('id');
}
markTreeItemSelected($this, widget_data.navtree_item_selected, true);
});
},
// initialization of widget
init: function(options) {
options = $.extend({}, options);
return this.each(function() {
var $this = $(this);
$this.data('widgetData', {
uniqueid: options.uniqueid,
severity_levels: options.severity_levels || [],
navtree_items_opened: options.navtree_items_opened.toString().split(',') || [],
navtree_item_selected: +options.navtree_item_selected || null,
maps_accessible: options.maps_accessible || [],
show_unavailable: options.show_unavailable == 1 || false,
problems: options.problems || [],
max_depth: options.max_depth || 10,
lastId: 0
});
var widget_data = getWidgetData($this),
triggers = ['onEditStart', 'beforeDashboardSave','beforeConfigLoad', 'onDashboardReady'];
$.each(triggers, function(index, trigger) {
$(".dashbrd-grid-container").dashboardGrid("addAction", trigger,
'zbx_widget_navtree_trigger', options.uniqueid, {
'parameters': [trigger],
'grid': {'widget': 1},
'priority': 5,
'trigger_name': 'maptree_' + options.uniqueid
}
);
});
if (isEditMode()) {
switchToEditMode($this);
}
else {
$('.dashbrd-grid-container').dashboardGrid('registerDataExchange', {
uniqueid: widget_data['uniqueid'],
data_name: 'current_sysmapid',
callback: function(widget, data) {
var item,
selector = '',
mapid_selector = '',
prev_map_selector = '';
mapid_selector = '.tree-item[data-mapid=' + data[0]['submapid'] + ']';
if (data[0]['previous_maps'].length) {
var prev_maps = data[0]['previous_maps'].split(','),
prev_maps = prev_maps.length
? prev_maps[prev_maps.length-1]
: null;
if (prev_maps) {
var sc = '.selected',
mapid = '[data-mapid=' + prev_maps + ']',
prev_map_selectors = [
Array(4).join(sc + ' ') + '.tree-item' + sc + mapid,
Array(3).join(sc + ' ') + '.tree-item' + sc + mapid,
Array(2).join(sc + ' ') + '.tree-item' + sc + mapid,
sc + ' .tree-item' + sc + mapid,
'.tree-item' + sc + mapid,
'.tree-item' + mapid
],
indx = 0;
while (!prev_map_selector.length
&& typeof prev_map_selectors[indx] !== 'undefined') {
if ($(prev_map_selectors[indx], $this).length) {
prev_map_selector = prev_map_selectors[indx] + ' ';
}
indx++;
}
}
}
if (prev_map_selector.length && mapid_selector.length) {
selector = prev_map_selector + ' > .tree-list > ' + mapid_selector;
if (!data[0]['moving_upward']) {
selector = selector + ':first';
}
item = $(selector.trim(selector), $this);
}
else {
item = $('.selected', $this).closest(mapid_selector);
}
if (item.length) {
item = item.first();
var step_in_path = $(item).closest('.tree-item');
$('.selected', $this).removeClass('selected');
$(item).addClass('selected');
while ($(step_in_path).length) {
$(step_in_path).addClass('selected');
step_in_path = $(step_in_path).parent().closest('.tree-item');
}
openBranch($this, $(item).data('id'));
updateUserProfile('web.dashbrd.navtree.item.selected', $(item).data('id'),
[widget['widgetid']]
);
}
}
});
switchToNavigationMode($this);
if (!options['initial_load']) {
markTreeItemSelected($this, options.navtree_item_selected, false);
}
}
});
}
};
if (methods[input]) {
return methods[input].apply(this, Array.prototype.slice.call(arguments, 1));
}
else if (typeof input === 'object') {
return methods.init.apply(this, arguments);
}
else {
return null;
}
};
}
});