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.

4109 lines
107 KiB

1 year ago
/*
** 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.
**/
ZABBIX.namespace('classes.Observer');
ZABBIX.classes.Observer = (function() {
var Observer = function() {
this.listeners = {};
};
Observer.prototype = {
constructor: ZABBIX.classes.Observer,
bind: function(event, callback) {
var i;
if (typeof callback === 'function') {
event = ('' + event).toLowerCase().split(/\s+/);
for (i = 0; i < event.length; i++) {
if (this.listeners[event[i]] === void(0)) {
this.listeners[event[i]] = [];
}
this.listeners[event[i]].push(callback);
}
}
return this;
},
trigger: function(event, target) {
event = event.toLowerCase();
var handlers = this.listeners[event] || [],
i;
if (handlers.length) {
event = jQuery.Event(event);
for (i = 0; i < handlers.length; i++) {
try {
if (handlers[i](event, target) === false || event.isDefaultPrevented()) {
break;
}
}
catch(ex) {
window.console && window.console.log && window.console.log(ex);
}
}
}
return this;
}
};
Observer.makeObserver = function(object) {
var i;
for (i in Observer.prototype) {
if (Observer.prototype.hasOwnProperty(i) && typeof Observer.prototype[i] === 'function') {
object[i] = Observer.prototype[i];
}
}
object.listeners = {};
};
return Observer;
}());
ZABBIX.namespace('apps.map');
ZABBIX.apps.map = (function($) {
// dependencies
var Observer = ZABBIX.classes.Observer;
const ZBX_STYLE_DEFAULT_OPTION = 'default-option';
function createMap(containerId, mapData) {
var CMap = function(containerId, mapData) {
var selementid,
shapeid,
linkid;
this.reupdateImage = false; // if image should be updated again after last update is finished
this.imageUpdating = false; // if ajax request for image updating is processing
this.selements = {}; // element objects
this.shapes = {}; // shape objects
this.links = {}; // map links array
this.selection = {
count: { // number of selected items
selements: 0,
shapes: 0
},
selements: {}, // selected elements
shapes: {} // selected shapes
};
this.currentLinkId = '0'; // linkid of currently edited link
this.allLinkTriggerIds = {};
this.sysmapid = mapData.sysmap.sysmapid;
this.data = mapData.sysmap;
this.background = null;
this.iconList = mapData.iconList;
this.defaultAutoIconId = mapData.defaultAutoIconId;
this.defaultIconId = mapData.defaultIconId;
this.defaultIconName = mapData.defaultIconName;
this.csrf_token = mapData.csrf_token;
this.container = $('#' + containerId);
if (this.container.length === 0) {
this.container = $(document.body);
}
this.images = {};
Object.keys(this.iconList).forEach(function (id) {
var item = this.iconList[id];
this.images[item.imageid] = item;
}, this);
this.map = new SVGMap({
theme: mapData.theme,
canvas: {
width: this.data.width,
height: this.data.height,
mask: true
},
container: this.container[0]
});
this.container.css({
width: this.data.width + 'px',
height: this.data.height + 'px',
overflow: 'hidden'
});
this.container.css('position', 'relative');
this.base64image = true;
$('#sysmap_img').remove();
for (selementid in this.data.selements) {
if (this.data.selements.hasOwnProperty(selementid)) {
this.selements[selementid] = new Selement(this, this.data.selements[selementid]);
}
}
var shapes = [];
for (shapeid in this.data.shapes) {
if (this.data.shapes.hasOwnProperty(shapeid)) {
shapes.push(this.data.shapes[shapeid]);
}
}
shapes = shapes.sort(function (a,b) {
return a.zindex - b.zindex;
});
shapes.forEach(function (shape) {
this.shapes[shape.sysmap_shapeid] = new Shape(this, shape);
}, this);
for (linkid in this.data.links) {
if (this.data.selements.hasOwnProperty(selementid)) {
this.links[linkid] = new Link(this, this.data.links[linkid]);
}
}
// create container for forms
this.formContainer = $('<div>', {
id: 'map-window',
class: 'overlay-dialogue',
style: 'display: none; top: 0; left: 0; padding-top: 13px;'
})
.appendTo('.wrapper')
.draggable({
containment: [0, 0, 3200, 3200]
});
this.updateImage();
this.form = new SelementForm(this.formContainer, this);
this.massForm = new MassForm(this.formContainer, this);
this.linkForm = new LinkForm(this.formContainer, this);
this.shapeForm = new ShapeForm(this.formContainer, this);
this.massShapeForm = new MassShapeForm(this.formContainer, this);
this.bindActions();
// initialize selectable
this.container.selectable({
start: $.proxy(function(event) {
if (!event.ctrlKey && !event.metaKey) {
this.clearSelection();
}
}, this),
stop: $.proxy(function(event) {
var selected = this.container.children('.ui-selected'),
ids = [],
i,
ln;
for (i = 0, ln = selected.length; i < ln; i++) {
ids.push({
id: $(selected[i]).data('id'),
type: $(selected[i]).data('type')
});
// remove ui-selected class, to not confuse next selection
selected.removeClass('ui-selected');
}
this.selectElements(ids, event.ctrlKey || event.metaKey);
}, this)
});
};
CMap.LABEL_TYPE_LABEL = 0; // MAP_LABEL_TYPE_LABEL
CMap.LABEL_TYPE_IP = 1; // MAP_LABEL_TYPE_IP
CMap.LABEL_TYPE_NAME = 2; // MAP_LABEL_TYPE_NAME
CMap.LABEL_TYPE_STATUS = 3; // MAP_LABEL_TYPE_STATUS
CMap.LABEL_TYPE_NOTHING = 4; // MAP_LABEL_TYPE_NOTHING
CMap.LABEL_TYPE_CUSTOM = 5; // MAP_LABEL_TYPE_CUSTOM
CMap.prototype = {
copypaste_buffer: [],
buffered_expand: false,
expand_sources: [],
save: function() {
var url = new Curl();
$.ajax({
url: url.getPath() + '?output=ajax',
type: 'post',
data: {
favobj: 'sysmap',
action: 'update',
_csrf_token: this.csrf_token,
sysmapid: this.sysmapid,
sysmap: JSON.stringify(this.data)
},
error: function() {
throw new Error('Cannot update map.');
}
});
},
setExpandedLabels: function (elements, labels) {
for (var i = 0, ln = elements.length; i < ln; i++) {
if (labels !== null) {
elements[i].expanded = labels[i];
}
else {
elements[i].expanded = null;
}
}
this.updateImage();
if (labels === null) {
alert(t('S_MACRO_EXPAND_ERROR'));
}
},
expandMacros: function(source) {
var url = new Curl();
if (source !== null) {
if (/\{.+\}/.test(source.getLabel(false))) {
this.expand_sources.push(source);
}
else {
source.expanded = null;
}
}
if (this.buffered_expand === false && this.expand_sources.length > 0) {
var sources = this.expand_sources,
post = [],
map = this;
this.expand_sources = [];
for (var i = 0, ln = sources.length; i < ln; i++) {
post.push(sources[i].data);
}
$.ajax({
url: url.getPath() + '?output=ajax',
type: 'post',
dataType: 'html',
data: {
favobj: 'sysmap',
action: 'expand',
_csrf_token: this.csrf_token,
sysmapid: this.sysmapid,
name: this.data.name,
source: JSON.stringify(post)
},
success: function(data) {
try {
data = JSON.parse(data);
}
catch (e) {
data = null;
}
map.setExpandedLabels(sources, data);
},
error: function() {
map.setExpandedLabels(sources, null);
}
});
}
else if (this.buffered_expand === false) {
this.updateImage();
}
},
updateImage: function() {
var shapes = [],
links = [],
elements = [],
grid_size = (this.data.grid_show === '1') ? parseInt(this.data.grid_size, 10) : 0;
if (grid_size !== this.data.last_grid_size) {
this.map.setGrid(grid_size);
this.data.last_grid_size = grid_size;
}
Object.keys(this.selements).forEach(function(key) {
var element = {},
data = this.selements[key].data;
['selementid', 'x', 'y', 'label_location'].forEach(function (name) {
element[name] = data[name];
}, this);
element['label'] = this.selements[key].getLabel();
// host group elements
if (data.elementtype === '3' && data.elementsubtype === '1') {
element.width = (data.areatype === '0') ? this.data.width : data.width;
element.height = (data.areatype === '0') ? this.data.height : data.height;
}
if ((data.use_iconmap === '1' && this.data.iconmapid !== '0')
&& (data.elementtype === '0'
|| (data.elementtype === '3' && data.elementsubtype === '1'))) {
element.icon = this.defaultAutoIconId;
}
else {
element.icon = data.iconid_off;
}
elements.push(element);
}, this);
Object.keys(this.links).forEach(function(key) {
var link = {};
['linkid', 'selementid1', 'selementid2', 'drawtype', 'color'].forEach(function (name) {
link[name] = this.links[key].data[name];
}, this);
link['label'] = this.links[key].getLabel();
links.push(link);
}, this);
Object.keys(this.shapes).forEach(function(key) {
var shape = {};
Object.keys(this.shapes[key].data).forEach(function (name) {
shape[name] = this.shapes[key].data[name];
}, this);
shape['text'] = this.shapes[key].getLabel();
shapes.push(shape);
}, this);
this.map.update({
'background': this.data.backgroundid,
'elements': elements,
'links': links,
'shapes': shapes,
'label_location': this.data.label_location
});
},
// elements
deleteSelectedElements: function() {
var selementid;
if (this.selection.count.selements && confirm(t('S_DELETE_SELECTED_ELEMENTS_Q'))) {
for (selementid in this.selection.selements) {
this.selements[selementid].remove();
this.removeLinksBySelementId(selementid);
}
this.toggleForm();
this.updateImage();
}
},
// shapes
deleteSelectedShapes: function() {
var shapeid;
if (this.selection.count.shapes && confirm(t('S_DELETE_SELECTED_SHAPES_Q'))) {
for (shapeid in this.selection.shapes) {
this.shapes[shapeid].remove();
}
this.toggleForm();
this.updateImage();
}
},
removeLinksBySelementId: function(selementid) {
var selementIds = {},
linkids,
i,
ln;
selementIds[selementid] = selementid;
linkids = this.getLinksBySelementIds(selementIds);
for (i = 0, ln = linkids.length; i < ln; i++) {
this.links[linkids[i]].remove();
}
},
/**
* Returns the links between the given elements.
*
* @param selementIds
*
* @return {Array} an array of link ids
*/
getLinksBySelementIds: function(selementIds) {
var linkIds = [],
link,
linkid;
for (linkid in this.data.links) {
link = this.data.links[linkid];
if (!!selementIds[link.selementid1] && !!selementIds[link.selementid2]
|| (objectSize(selementIds) === 1 && (!!selementIds[link.selementid1] || !!selementIds[link.selementid2]))) {
linkIds.push(linkid);
}
}
return linkIds;
},
bindActions: function() {
var that = this;
/*
* Map panel events
*/
// toggle expand macros
$('#expand_macros').click(function() {
that.data.expand_macros = (that.data.expand_macros === '1') ? '0' : '1';
$(this).html((that.data.expand_macros === '1') ? t('S_ON') : t('S_OFF'));
that.updateImage();
});
// change grid size
$('#gridsize').change(function() {
var value = $(this).val();
if (that.data.grid_size !== value) {
that.data.grid_size = value;
that.updateImage();
}
});
// toggle autoalign
$('#gridautoalign').click(function() {
that.data.grid_align = (that.data.grid_align === '1') ? '0' : '1';
$(this).html((that.data.grid_align === '1') ? t('S_ON') : t('S_OFF'));
});
// toggle grid visibility
$('#gridshow').click(function() {
that.data.grid_show = (that.data.grid_show === '1') ? '0' : '1';
$(this).html((that.data.grid_show === '1') ? t('S_SHOWN') : t('S_HIDDEN'));
that.updateImage();
});
// perform align all
$('#gridalignall').click(function() {
var selementid;
for (selementid in that.selements) {
that.selements[selementid].align(true);
}
that.updateImage();
});
// save map
$('#sysmap_update').click(function() {
that.save();
});
// add element
$('#selementAdd').click(function() {
if (typeof(that.iconList[0]) === 'undefined') {
alert(t('S_NO_IMAGES'));
return;
}
var selement = new Selement(that);
that.selements[selement.id] = selement;
that.updateImage();
});
// remove element
$('#selementRemove').click($.proxy(this.deleteSelectedElements, this));
// add shape
$('#shapeAdd').click(function() {
var shape = new Shape(that);
that.shapes[shape.id] = shape;
that.updateImage();
});
// remove shapes
$('#shapeRemove, #shapesRemove, #shapeMassRemove').click($.proxy(this.deleteSelectedShapes, this));
// add link
$('#linkAdd').click(function() {
var link;
if (that.selection.count.selements !== 2) {
alert(t('S_TWO_MAP_ELEMENTS_SHOULD_BE_SELECTED'));
return false;
}
link = new Link(that);
that.links[link.id] = link;
that.updateImage();
that.linkForm.updateList(that.selection.selements);
});
// removes all of the links between the selected elements
$('#linkRemove').click(function() {
var linkids;
if (that.selection.count.selements !== 2) {
alert(t('S_PLEASE_SELECT_TWO_ELEMENTS'));
return false;
}
linkids = that.getLinksBySelementIds(that.selection.selements);
if (linkids.length && confirm(t('S_DELETE_LINKS_BETWEEN_SELECTED_ELEMENTS_Q'))) {
for (var i = 0, ln = linkids.length; i < ln; i++) {
that.links[linkids[i]].remove();
}
that.linkForm.hide();
that.linkForm.updateList({});
that.updateImage();
}
});
/*
* Selements events
*/
// Delegate selements icons clicks.
$(this.container).on('click', '.sysmap_element, .sysmap_shape', function(event) {
that.selectElements([{
id: $(this).attr('data-id'),
type: $(this).attr('data-type')
}], event.ctrlKey || event.metaKey);
});
$(this.container).on('contextmenu', function(event) {
var target = $(event.target),
item_data = {
id: target.attr('data-id'),
type: target.attr('data-type')
},
can_copy = false,
can_paste = (that.copypaste_buffer.items && that.copypaste_buffer.items.length > 0),
can_remove = false,
can_reorder = false;
if (typeof item_data.id === 'undefined') {
that.clearSelection();
}
else if (item_data.type && typeof that.selection[item_data.type][item_data.id] === 'undefined') {
that.selectElements([item_data], false, true);
}
can_copy = (that.selection.count.shapes > 0 || that.selection.count.selements > 0);
can_remove = can_copy;
can_reorder = (that.selection.count.shapes > 0);
event.preventDefault();
event.stopPropagation();
const overlay = overlays_stack.end();
if (typeof overlay !== 'undefined' && 'element' in overlay && overlay.element !== event.target) {
$('.menu-popup-top').menuPopup('close', null, false);
}
if (!(can_copy || can_paste || can_remove || can_reorder)) {
return false;
}
var items = [
{
'items': [
{
label: t('S_BRING_TO_FRONT'),
disabled: !can_reorder,
clickCallback: function() {
that.reorderShapes(that.selection.shapes, 'last');
}
},
{
label: t('S_BRING_FORWARD'),
disabled: !can_reorder,
clickCallback: function() {
that.reorderShapes(that.selection.shapes, 'next');
}
},
{
label: t('S_SEND_BACKWARD'),
disabled: !can_reorder,
clickCallback: function() {
that.reorderShapes(that.selection.shapes, 'previous');
}
},
{
label: t('S_SEND_TO_BACK'),
disabled: !can_reorder,
clickCallback: function() {
that.reorderShapes(that.selection.shapes, 'first');
}
}
]
},
{
'items': [
{
label: t('S_COPY'),
disabled: !can_copy,
clickCallback: function() {
that.copypaste_buffer = that.getSelectionBuffer(that);
}
},
{
label: t('S_PASTE'),
disabled: !can_paste,
clickCallback: function() {
var offset = $(that.container).offset(),
delta_x = event.pageX - offset.left - that.copypaste_buffer.left,
delta_y = event.pageY - offset.top - that.copypaste_buffer.top,
selectedids;
delta_x = Math.min(delta_x,
parseInt(that.data.width, 10) - that.copypaste_buffer.right
);
delta_y = Math.min(delta_y,
parseInt(that.data.height, 10) - that.copypaste_buffer.bottom
);
selectedids = that.pasteSelectionBuffer(delta_x, delta_y, that, true);
that.selectElements(selectedids, false);
that.updateImage();
that.linkForm.updateList(that.selection.selements);
}
},
{
label: t('S_PASTE_SIMPLE'),
disabled: !can_paste,
clickCallback: function() {
var offset = $(that.container).offset(),
delta_x = event.pageX - offset.left - that.copypaste_buffer.left,
delta_y = event.pageY - offset.top - that.copypaste_buffer.top,
selectedids;
delta_x = Math.min(delta_x,
parseInt(that.data.width, 10) - that.copypaste_buffer.right
);
delta_y = Math.min(delta_y,
parseInt(that.data.height, 10) - that.copypaste_buffer.bottom
);
selectedids = that.pasteSelectionBuffer(delta_x, delta_y, that, false);
that.selectElements(selectedids, false);
that.updateImage();
that.linkForm.updateList(that.selection.selements);
}
},
{
label: t('S_REMOVE'),
disabled: !can_remove,
clickCallback: function() {
if (that.selection.count.selements || that.selection.count.shapes) {
for (selementid in that.selection.selements) {
that.selements[selementid].remove();
that.removeLinksBySelementId(selementid);
}
for (shapeid in that.selection.shapes) {
that.shapes[shapeid].remove();
}
}
that.toggleForm();
that.updateImage();
}
}
]
}
];
$(event.target).menuPopup(items, event, {
position: {
of: event,
my: 'left top',
at: 'left bottom',
using: (pos, data) => {
let max_left = (data.horizontal === 'left')
? document.getElementById(containerId).clientWidth
: document.getElementById(containerId).clientWidth - data.element.width;
pos.top = Math.max(0, pos.top);
pos.left = Math.max(0, Math.min(max_left, pos.left));
data.element.element[0].style.top = `${pos.top}px`;
data.element.element[0].style.left = `${pos.left}px`;
}
},
background_layer: false
});
});
/*
* Form events
*/
$('#elementType').change(function() {
var obj = $(this);
switch (obj.val()) {
// host
case '0':
$('#elementNameHost').multiSelect('clean');
$('#triggerContainer tbody').html('');
break;
// map
case '1':
$('#elementNameMap').multiSelect('clean');
$('#triggerContainer tbody').html('');
break;
// triggers
case '2':
$('#elementNameTriggers').multiSelect('clean');
$('#triggerContainer tbody').html('');
break;
// host group
case '3':
$('#elementNameHostGroup').multiSelect('clean');
$('#triggerContainer tbody').html('');
break;
// others types
default:
$('input[name=elementName]').val('');
$('#triggerContainer tbody').html('');
}
});
$('#elementClose').click(function() {
that.clearSelection();
that.toggleForm();
});
$('#elementRemove').click($.proxy(this.deleteSelectedElements, this));
$('#elementApply').click($.proxy(function() {
if (this.selection.count.selements !== 1) {
throw 'Try to single update element, when more than one selected.';
}
var values = this.form.getValues();
if (values) {
for (var selementid in this.selection.selements) {
this.selements[selementid].update(values, true);
}
}
}, this));
$('#shapeApply').click($.proxy(function() {
if (this.selection.count.shapes !== 1) {
throw 'Trying to single update shape, when more than one selected.';
}
var values = this.shapeForm.getValues();
if (values) {
for (var id in this.selection.shapes) {
this.shapes[id].update(values);
}
}
}, this));
$('#shapeClose, #shapeMassClose').click(function() {
that.clearSelection();
that.toggleForm();
});
$('#shapeMassApply').click($.proxy(function() {
var values = this.massShapeForm.getValues();
if (values) {
this.buffered_expand = true;
for (var shapeid in this.selection.shapes) {
this.shapes[shapeid].update(values);
}
this.buffered_expand = false;
this.expandMacros(null);
}
}, this));
$('#newSelementUrl').click($.proxy(function() {
this.form.addUrls();
}, this));
$('#newSelementTriggers').click($.proxy(function() {
this.form.addTriggers();
}, this));
$('#x, #y', this.form.domNode).change(function() {
var value = parseInt(this.value, 10);
this.value = isNaN(value) || (value < 0) ? 0 : value;
});
$('#areaSizeWidth, #areaSizeHeight', this.form.domNode).change(function() {
var value = parseInt(this.value, 10);
this.value = isNaN(value) || (value < 10) ? 10 : value;
});
// Init tag fields.
$('#selement-tags')
.dynamicRows({template: '#tag-row-tmpl', counter: 0, allow_empty: true})
.on('beforeadd.dynamicRows', function() {
var options = $('#selement-tags').data('dynamicRows');
options.counter = ++options.counter;
})
.on('afteradd.dynamicRows', function() {
var rows = this.querySelectorAll('.form_row');
new CTagFilterItem(rows[rows.length - 1]);
});
// mass update form
$('#massClose').click(function() {
that.clearSelection();
that.toggleForm();
});
$('#massRemove').click($.proxy(this.deleteSelectedElements, this));
$('#massApply').click($.proxy(function() {
var values = this.massForm.getValues();
if (values) {
this.buffered_expand = true;
for (var selementid in this.selection.selements) {
this.selements[selementid].update(values);
}
this.buffered_expand = false;
this.expandMacros(null);
}
}, this));
// open link form
$('.element-links').on('click', '.openlink', function() {
that.currentLinkId = $(this).attr('data-linkid');
var linkData = that.links[that.currentLinkId].getData();
that.linkForm.setValues(linkData);
that.linkForm.show();
});
// link form
$('#formLinkRemove').click(function() {
that.links[that.currentLinkId].remove();
that.linkForm.updateList(that.selection.selements);
that.linkForm.hide();
that.updateImage();
});
$('#formLinkApply').click(function() {
try {
var linkData = that.linkForm.getValues();
that.links[that.currentLinkId].update(linkData);
that.linkForm.updateList(that.selection.selements);
}
catch (err) {
alert(err);
}
});
$('#formLinkClose').click(function() {
that.linkForm.hide();
});
this.linkForm.domNode.on('click', '.triggerRemove', function() {
var triggerid,
tid = $(this).attr('data-linktriggerid').toString();
$('#linktrigger_' + tid).remove();
for (triggerid in that.linkForm.triggerids) {
if (that.linkForm.triggerids[triggerid] === tid) {
delete that.linkForm.triggerids[triggerid];
}
}
});
$('#border_type').on('change', function() {
$(this).parent().find('input').prop("disabled", this.value === '0');
});
$('#mass_border_type, #chkboxBorderType').on('change', function() {
var disable = ($('#mass_border_type').val() === '0' && $('#chkboxBorderType').is(":checked"));
$('#chkboxBorderWidth, #chkboxBorderColor').prop("disabled", disable);
$('#mass_border_width').prop("disabled", disable || !$('#chkboxBorderWidth').is(":checked"));
$('#mass_border_color').prop("disabled", disable || !$('#chkboxBorderColor').is(":checked"));
});
$('#shapeForm input[type=radio][name=type]').on('change', function() {
var value = parseInt(this.value, 10),
last_value = parseInt($('#shapeForm #last_shape_type').val(), 10);
$('#shape-text-row, #shape-background-row').toggle(value !== SVGMapShape.TYPE_LINE);
$('.switchable-content').each(function (i, element) {
element.textContent = element.hasAttribute('data-value-' + value) ?
element.getAttribute('data-value-' + value) :
element.getAttribute('data-value');
});
if ((last_value === SVGMapShape.TYPE_LINE) !== (value === SVGMapShape.TYPE_LINE)) {
var x = parseInt($('#shapeX').val(), 10),
y = parseInt($('#shapeY').val(), 10),
width = parseInt($('#shapeAreaSizeWidth').val(), 10),
height = parseInt($('#shapeAreaSizeHeight').val(), 10);
if (value === SVGMapShape.TYPE_LINE) {
// Switching from figures to line.
$('#shapeAreaSizeWidth').val(x + width);
$('#shapeAreaSizeHeight').val(y + height);
}
else {
// Switching from line to figures.
var mx = Math.min(x, width),
my = Math.min(y, height);
$('#shapeX').val(mx);
$('#shapeY').val(my);
$('#shapeAreaSizeWidth').val(Math.max(x, width) - mx);
$('#shapeAreaSizeHeight').val(Math.max(y, height) - my);
}
}
$('#last_shape_type').val(value);
});
$(this.container).parents('.sysmap-scroll-container').eq(0)
.on('scroll', (e) => {
if (!e.target.dataset.last_scroll_at || Date.now() - e.target.dataset.last_scroll_at > 1000) {
$('.menu-popup-top').menuPopup('close', null, false);
e.target.dataset.last_scroll_at = Date.now();
}
});
$('input[type=radio][name=type]:checked').change();
},
/**
* Buffer for draggable elements and elements group bounds.
*/
draggable_buffer: null,
/**
* Returns virtual DOM element used by draggable.
*
* @return {object}
*/
dragGroupPlaceholder: function() {
return $('<div>').css({
width: $(this.domNode).width(),
height: $(this.domNode).height()
});
},
/**
* Recalculate x and y position of moved elements.
*
* @param {int} delta_x Shift between old and new x position.
* @param {int} delta_y Shift between old and new y position.
*
* @return {object} Object of elements with recalculated positions.
*/
dragGroupRecalculate: function(cmap, delta_x, delta_y) {
var dragged = cmap.draggable_buffer;
dragged.items.forEach(function(item) {
var node = cmap[item.type][item.id];
if ('updatePosition' in node) {
var dimensions = node.getDimensions();
node.updatePosition({
x: dimensions.x + delta_x,
y: dimensions.y + delta_y
}, false);
}
});
},
/**
* Initializes multiple elements dragging.
*
* @param {object} draggable Draggable DOM element where drag event was started.
*/
dragGroupInit: function(draggable) {
var buffer,
draggable_node = $(draggable.domNode),
dimensions = draggable.getDimensions();
if (draggable.selected) {
buffer = draggable.sysmap.getSelectionBuffer(draggable.sysmap);
}
else {
draggable_node = $(draggable.domNode);
// Create getSelectionBuffer structure if drag event was started on unselected element.
buffer = {
items: [{
type: draggable_node.attr('data-type'),
id: draggable.id
}],
left: dimensions.x,
right: dimensions.x + draggable_node.width(),
top: dimensions.y,
bottom: dimensions.y + draggable_node.height()
};
}
buffer.xaxis = {
min: dimensions.x - buffer.left,
max: (draggable.sysmap.container).width() - (buffer.right - dimensions.x)
};
buffer.yaxis = {
min: dimensions.y - buffer.top,
max: (draggable.sysmap.container).height() - (buffer.bottom - dimensions.y)
};
draggable.sysmap.draggable_buffer = buffer;
},
/**
* Handler for drag event.
*
* @param {object} data jQuery UI draggable data.
* @param {object} draggable Element where drag event occurred.
*/
dragGroupDrag: function(data, draggable) {
var cmap = draggable.sysmap,
dimensions = draggable.getDimensions(),
delta_x = data.position.left - dimensions.x,
delta_y = data.position.top - dimensions.y;
if (data.position.left < cmap.draggable_buffer.xaxis.min) {
delta_x = dimensions.x < cmap.draggable_buffer.xaxis.min
? 0
: cmap.draggable_buffer.xaxis.min - dimensions.x;
}
else if (data.position.left > cmap.draggable_buffer.xaxis.max) {
delta_x = dimensions.x > cmap.draggable_buffer.xaxis.max
? 0
: cmap.draggable_buffer.xaxis.max - dimensions.x;
}
if (data.position.top < cmap.draggable_buffer.yaxis.min) {
delta_y = dimensions.y < cmap.draggable_buffer.yaxis.min
? 0
: cmap.draggable_buffer.yaxis.min - dimensions.y;
}
else if (data.position.top > cmap.draggable_buffer.yaxis.max) {
delta_y = dimensions.y > cmap.draggable_buffer.yaxis.max
? 0
: cmap.draggable_buffer.yaxis.max - dimensions.y;
}
if (delta_x != 0 || delta_y != 0) {
cmap.dragGroupRecalculate(cmap, Math.round(delta_x), Math.round(delta_y));
cmap.updateImage();
}
},
/**
* Final tasks for dragged element on drag stop event.
*
* @param {object} draggable Element where drag stop event occurred.
*/
dragGroupStop: function(draggable) {
var cmap = draggable.sysmap,
should_align = (cmap.data.grid_align === '1');
if (should_align) {
cmap.draggable_buffer.items.forEach(function(item) {
var node = cmap[item.type][item.id];
if ('updatePosition' in node) {
var dimensions = node.getDimensions();
node.updatePosition({
x: dimensions.x,
y: dimensions.y
});
}
});
}
},
/**
* Paste that.copypaste_buffer content at new location.
*
* @param {number} delta_x Shift between desired and actual x position.
* @param {number} delta_y Shift between desired and actual y position.
* @param {object} that CMap object
* @param {bool} keep_external_links Should be links to non selected elements copied or not.
*
* @return {array}
*/
pasteSelectionBuffer: function(delta_x, delta_y, that, keep_external_links) {
var selectedids = [],
source_cloneids = {};
that.copypaste_buffer.items.forEach(function(element_data) {
var data = $.extend({}, element_data.data, false),
type = element_data.type,
element;
switch (type) {
case 'selements':
element = new Selement(that);
delete data.selementid;
break;
case 'shapes':
element = new Shape(that);
delete data.sysmap_shapeid;
break;
default:
throw 'Unsupported element type found in copy buffer!';
}
if (element) {
data.x = parseInt(data.x, 10) + delta_x;
data.y = parseInt(data.y, 10) + delta_y;
element.expanded = element_data.expanded;
if (type === 'shapes' && data.type == SVGMapShape.TYPE_LINE) {
// Additional shift for line shape.
data.width = parseInt(data.width, 10) + delta_x;
data.height = parseInt(data.height, 10) + delta_y;
}
element.update(data);
that[type][element.id] = element;
selectedids.push({
id: element.id,
type: type
});
source_cloneids[element_data.id] = element.id;
if (that.data.grid_align === '1') {
element.align(true);
}
}
});
var link,
fromid,
toid,
data;
that.copypaste_buffer.links.forEach(function(link_data) {
data = $.extend({}, link_data.data, false);
if (!keep_external_links && (data.selementid1 in source_cloneids === false
|| data.selementid2 in source_cloneids === false)) {
return;
}
link = new Link(that);
delete data.linkid;
fromid = (data.selementid1 in source_cloneids)
? source_cloneids[data.selementid1]
: data.selementid1;
toid = (data.selementid2 in source_cloneids) ? source_cloneids[data.selementid2] : data.selementid2;
data.selementid1 = fromid;
data.selementid2 = toid;
link.update(data);
that.links[link.id] = link;
});
return selectedids;
},
/**
* Return object with selected elements data and links.
*
* @param {object} that CMap object
*
* @return {object}
*/
getSelectionBuffer: function(that) {
var items = [],
left = null,
top = null,
right = null,
bottom = null;
for (var type in that.selection) {
if (type in that === false || typeof that[type] !== 'object') {
continue;
}
var data,
dimensions,
dom_node,
x,
y;
for (var id in that.selection[type]) {
if ('getData' in that[type][id] === false) {
continue;
}
// Get current data without observers.
data = $.extend({}, that[type][id].getData(), false);
dimensions = that[type][id].getDimensions();
dom_node = that[type][id].domNode;
x = dimensions.x;
y = dimensions.y;
left = Math.min(x, (left === null) ? x : left);
top = Math.min(y, (top === null) ? y : top);
right = Math.max(x + dom_node.outerWidth(true), (right === null) ? 0 : right);
bottom = Math.max(y + dom_node.outerHeight(true), (bottom === null) ? 0 : bottom);
items.push({
id: id,
type: type,
data: data
});
}
}
// Sort items array according to item.data.zindex value.
items = items.sort(function(a, b) {
var aindex = parseInt(a.data.zindex, 10) || 0,
bindex = parseInt(b.data.zindex, 10) || 0;
return aindex - bindex;
});
var links = [];
for (var id in that.links) {
// Get current data without observers.
var data = $.extend({}, that.links[id].getData(), false);
if (data.selementid1 in that.selection.selements || data.selementid2 in that.selection.selements) {
links.push({
id: id,
data: data
})
}
}
return {
items: items,
links: links,
top: top,
left: left,
right: right,
bottom: bottom
};
},
clearSelection: function() {
var id;
['selements', 'shapes'].forEach(function (type) {
for (id in this.selection[type]) {
this.selection.count[type]--;
this[type][id].toggleSelect(false);
delete this.selection[type][id];
}
}, this);
// Clean trigger selement.
if ($('#elementType').val() == 2) {
$('#elementNameTriggers').multiSelect('clean');
$('#triggerContainer tbody').html('');
}
this.form.cleanTagsField();
},
reorderShapes: function(ids, position) {
var shapes = [],
target,
temp,
ignore = [],
selection = [];
Object.keys(this.shapes).forEach(function(key) {
shapes.push(this.shapes[key]);
}, this);
shapes = shapes.sort(function (a,b) {
return a.data.zindex - b.data.zindex;
});
shapes.forEach(function(value, index) {
if (typeof ids[value.id] !== 'undefined') {
selection.push(index);
}
});
// All shapes are selected, no need to update order.
if (shapes.length === selection.length)
{
return;
}
switch (position.toLowerCase()) {
case 'first':
target = [];
for (var i = selection.length - 1; i >= 0; i--) {
target.unshift(shapes.splice(selection[i], 1)[0]);
}
for (var i = 0; i < target.length; i++) {
$(target[i].domNode).insertBefore(shapes[0].domNode);
}
shapes = target.concat(shapes);
shapes.forEach(function(shape, index) {
shape.data.zindex = index;
});
break;
case 'last':
target = [];
for (var i = selection.length - 1; i >= 0; i--) {
target.unshift(shapes.splice(selection[i], 1)[0]);
}
for (var i = target.length - 1; i >= 0 ; i--) {
$(target[i].domNode).insertAfter(shapes[shapes.length-1].domNode);
}
shapes = shapes.concat(target);
shapes.forEach(function(shape, index) {
shape.data.zindex = index;
});
break;
case 'next':
ignore.push(shapes.length - 1);
for (var i = selection.length - 1; i >= 0; i--) {
target = selection[i];
// No need to update.
if (ignore.indexOf(target) !== -1) {
ignore.push(target - 1);
continue;
}
$(shapes[target].domNode).insertAfter(shapes[target + 1].domNode);
shapes[target + 1].data.zindex--;
shapes[target].data.zindex++;
temp = shapes[target + 1];
shapes[target + 1] = shapes[target];
shapes[target] = temp;
}
break;
case 'previous':
ignore.push(0);
for (var i = 0; i < selection.length; i++) {
target = selection[i];
// No need to update.
if (ignore.indexOf(target) !== -1) {
ignore.push(target + 1);
continue;
}
$(shapes[target].domNode).insertBefore(shapes[target - 1].domNode);
shapes[target - 1].data.zindex++;
shapes[target].data.zindex--;
temp = shapes[target - 1];
shapes[target - 1] = shapes[target];
shapes[target] = temp;
}
break;
}
this.map.invalidate('shapes');
this.updateImage();
},
selectElements: function(ids, addSelection, prevent_form_open) {
var i, ln;
$('.menu-popup-top').menuPopup('close', null, false);
if (!addSelection) {
this.clearSelection();
}
for (i = 0, ln = ids.length; i < ln; i++) {
var id = ids[i].id,
type = ids[i].type;
if (typeof id === 'undefined' || typeof type === 'undefined') {
continue;
}
if (this[type][id].toggleSelect()) {
this.selection.count[type]++;
this.selection[type][id] = id;
}
else {
this.selection.count[type]--;
delete this.selection[type][id];
}
}
if (typeof prevent_form_open === 'undefined' || !prevent_form_open) {
this.toggleForm();
}
},
toggleForm: function() {
var id;
this.shapeForm.hide();
this.linkForm.hide();
if (this.selection.count.selements + this.selection.count.shapes === 0
|| (this.selection.count.selements > 0 && this.selection.count.shapes > 0)) {
$('#map-window').hide();
}
else {
this.linkForm.updateList(this.selection.selements);
// only one element selected
if (this.selection.count.selements === 1) {
for (id in this.selection.selements) {
this.form.setValues(this.selements[id].getData());
}
this.massForm.hide();
this.massShapeForm.hide();
$('#link-connect-to').show();
this.form.show();
}
// only one shape is selected
else if (this.selection.count.shapes === 1) {
this.form.hide();
this.massForm.hide();
this.massShapeForm.hide();
for (id in this.selection.shapes) {
this.shapeForm.setValues(this.shapes[id].getData());
}
this.shapeForm.show();
}
// multiple elements selected
else if (this.selection.count.selements > 1) {
this.form.hide();
this.massShapeForm.hide();
$('#link-connect-to').hide();
this.massForm.show();
}
// multiple shapes selected
else {
var figures = null;
for (id in this.selection.shapes) {
if (figures === null) {
figures = (this.shapes[id].data.type != SVGMapShape.TYPE_LINE);
}
else if (figures !== (this.shapes[id].data.type != SVGMapShape.TYPE_LINE)) {
// Different shape types are selected (lines and figures).
$('#map-window').hide();
this.massShapeForm.hide();
return;
}
}
this.form.hide();
this.massForm.hide();
$('#link-connect-to').hide();
this.massShapeForm.show(figures);
}
}
}
};
/**
* Creates a new Link.
*
* @class represents connector between two Elements
*
* @property {object} sysmap Reference to Map object.
* @property {object} data Link db values.
* @property {string} id Link ID (linkid).
*
* @param {object} sysmap Map object.
* @param {object} linkData Link data from DB.
*/
function Link(sysmap, linkData) {
var selementid;
this.sysmap = sysmap;
if (!linkData) {
linkData = {
label: '',
selementid1: null,
selementid2: null,
linktriggers: {},
drawtype: 0,
color: '00CC00'
};
for (selementid in this.sysmap.selection.selements) {
if (linkData.selementid1 === null) {
linkData.selementid1 = selementid;
}
else {
linkData.selementid2 = selementid;
}
}
// generate unique linkid
linkData.linkid = getUniqueId();
}
else {
if ($.isArray(linkData.linktriggers)) {
linkData.linktriggers = {};
}
}
this.data = linkData;
this.id = this.data.linkid;
this.expanded = this.data.expanded;
delete this.data.expanded;
for (var linktrigger in this.data.linktriggers) {
this.sysmap.allLinkTriggerIds[linktrigger.triggerid] = true;
}
// assign by reference
this.sysmap.data.links[this.id] = this.data;
}
Link.prototype = {
/**
* Return label based on map constructor configuration.
*
* @param {boolean} return label with expanded macros.
*
* @returns {string}
*/
getLabel: function (expand) {
var label = this.data.label;
if (typeof(expand) === 'undefined') {
expand = true;
}
if (expand && typeof(this.expanded) === 'string' && this.sysmap.data.expand_macros === '1') {
label = this.expanded;
}
return label;
},
/**
* Updades values in property data.
*
* @param {object} data
*/
update: function(data) {
var key,
invalidate = (this.data.label !== data.label);
for (key in data) {
this.data[key] = data[key];
}
if (invalidate) {
sysmap.expandMacros(this);
}
else {
sysmap.updateImage();
}
},
/**
* Removes Link object, delete all reference to it.
*/
remove: function() {
delete this.sysmap.data.links[this.id];
delete this.sysmap.links[this.id];
if (sysmap.form.active) {
sysmap.linkForm.updateList(sysmap.selection.selements);
}
sysmap.linkForm.hide();
},
/**
* Gets Link data.
*
* @returns {Object}
*/
getData: function() {
return this.data;
}
};
Observer.makeObserver(Link.prototype);
/**
* Creates a new Shape.
*
* @class represents shape (static) element
*
* @property {object} sysmap Reference to Map object
* @property {object} data Shape values from DB.
* @property {string} id Shape ID (shapeid).
*
* @param {object} sysmap Map object
* @param {object} [shape_data] shape data from db
*/
function Shape(sysmap, shape_data) {
var default_data = {
type: SVGMapShape.TYPE_RECTANGLE,
x: 10,
y: 10,
width: 50,
height: 50,
border_color: '000000',
background_color: '',
border_width: 2,
font: 9, // Helvetica
font_size: 11,
font_color: '000000',
text_valign: 0,
text_halign: 0,
text: '',
border_type: 1
};
this.sysmap = sysmap;
if (!shape_data) {
shape_data = default_data;
// generate unique sysmap_shapeid
shape_data.sysmap_shapeid = getUniqueId();
shape_data.zindex = Object.keys(sysmap.shapes).length;
}
else {
for (var field in default_data) {
if (typeof shape_data[field] === 'undefined') {
shape_data[field] = default_data[field];
}
}
}
this.data = shape_data;
this.id = this.data.sysmap_shapeid;
this.expanded = this.data.expanded;
delete this.data.expanded;
// assign by reference
this.sysmap.data.shapes[this.id] = this.data;
// create dom
this.domNode = $('<div>', {
style: 'position: absolute; z-index: 1; background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat',
})
.appendTo(this.sysmap.container)
.addClass('cursor-pointer sysmap_shape')
.attr('data-id', this.id)
.attr('data-type', 'shapes');
this.makeDraggable(true);
this.makeResizable(this.data.type != SVGMapShape.TYPE_LINE);
var dimensions = this.getDimensions();
this.domNode.css({
top: dimensions.y + 'px',
left: dimensions.x + 'px',
width: dimensions.width + 'px',
height: dimensions.height + 'px'
});
}
Shape.prototype = {
/**
* Updades values in property data.
*
* @param {object} data
*/
update: function(data) {
var key,
dimensions,
invalidate = (data.type != SVGMapShape.TYPE_LINE && typeof(data.text) !== 'undefined'
&& this.data.text !== data.text);
if (typeof data['type'] !== 'undefined' && /^[0-9]+$/.test(this.data.sysmap_shapeid) === true
&& (data['type'] == SVGMapShape.TYPE_LINE) != (this.data.type == SVGMapShape.TYPE_LINE)) {
delete data['sysmap_shapeid'];
this.data.sysmap_shapeid = getUniqueId();
}
for (key in data) {
this.data[key] = data[key];
}
['x', 'y', 'width', 'height'].forEach(function(name) {
this[name] = parseInt(this[name], 10);
}, this.data);
dimensions = this.getDimensions();
this.domNode
.css({
width: dimensions.width + 'px',
height: dimensions.height + 'px'
});
this.makeDraggable(true);
this.makeResizable(this.data.type != SVGMapShape.TYPE_LINE);
this.align(false);
this.trigger('afterMove', this);
for (key in data) {
this.data[key] = data[key];
}
if (invalidate) {
sysmap.expandMacros(this);
}
else {
sysmap.updateImage();
}
},
/**
* Return label based on map constructor configuration.
*
* @param {boolean} return label with expanded macros.
*
* @returns {string}
*/
getLabel: function (expand) {
var label = this.data.text;
if (typeof(expand) === 'undefined') {
expand = true;
}
if (expand && typeof(this.expanded) === 'string' && this.sysmap.data.expand_macros === '1') {
label = this.expanded;
}
return label;
},
/**
* Gets shape dimensions.
*/
getDimensions: function () {
var dimensions = {
x: parseInt(this.data.x, 10),
y: parseInt(this.data.y, 10),
width: parseInt(this.data.width, 10),
height: parseInt(this.data.height, 10)
};
if (this instanceof Shape && this.data.type == SVGMapShape.TYPE_LINE) {
var width = parseInt(this.sysmap.data.width),
height = parseInt(this.sysmap.data.height),
x = Math.min(Math.max(0, Math.min(dimensions.x, dimensions.width)), width),
y = Math.min(Math.max(0, Math.min(dimensions.y, dimensions.height)), height),
dx = Math.max(dimensions.x, dimensions.width) - x,
dy = Math.max(dimensions.y, dimensions.height) - y;
dimensions = {
x: x,
y: y,
width: Math.min(Math.max(0, dx), width - x),
height: Math.min(Math.max(0, dy), height - y)
};
}
return dimensions;
},
updateHandles: function() {
if (typeof this.handles === 'undefined') {
this.handles = [
$('<div>', {'class': 'ui-resize-dot cursor-move'}),
$('<div>', {'class': 'ui-resize-dot cursor-move'})
];
this.domNode.parent().append(this.handles);
for (var i = 0; i < 2; i++) {
this.handles[i].data('id', i);
this.handles[i].draggable({
containment: 'parent',
drag: $.proxy(function(event, data) {
var dimensions;
if (data.helper.data('id') === 0) {
this.data.x = parseInt(data.position.left, 10) + 4;
this.data.y = parseInt(data.position.top, 10) + 4;
}
else {
this.data.width = parseInt(data.position.left, 10) + 4;
this.data.height = parseInt(data.position.top, 10) + 4;
}
dimensions = this.getDimensions();
this.domNode.css({
top: dimensions.y + 'px',
left: dimensions.x + 'px',
width: dimensions.width + 'px',
height: dimensions.height + 'px'
});
this.trigger('afterMove', this);
}, this)
});
}
}
this.handles[0].css({
left: (this.data.x - 3) + 'px',
top: (this.data.y - 3) + 'px'
});
this.handles[1].css({
left: (this.data.width - 3) + 'px',
top: (this.data.height - 3) + 'px'
});
},
/**
* Allow dragging of shape.
*/
makeDraggable: function(enable) {
var node = this.domNode;
if (enable) {
if (this instanceof Shape && this.data.type == SVGMapShape.TYPE_LINE) {
this.updateHandles();
}
else {
if (typeof this.handles !== 'undefined') {
this.handles.forEach(function (handle) {
handle.remove();
});
delete this.handles;
}
}
if (!node.hasClass('ui-draggable')) {
node.draggable({
containment: 'parent',
helper: $.proxy(function() {
return this.sysmap.dragGroupPlaceholder();
}, this),
start: $.proxy(function() {
this.domNode
.addClass('cursor-dragging')
.removeClass('cursor-pointer');
this.sysmap.dragGroupInit(this);
}, this),
drag: $.proxy(function(event, data) {
this.sysmap.dragGroupDrag(data, this);
}, this),
stop: $.proxy(function() {
this.domNode
.addClass('cursor-pointer')
.removeClass('cursor-dragging');
this.sysmap.dragGroupStop(this);
}, this)
});
}
}
else {
if (typeof this.handles !== 'undefined') {
this.handles.forEach(function (handle) {
handle.remove();
});
delete this.handles;
}
if (node.hasClass('ui-draggable')) {
node.draggable("destroy");
}
}
},
/**
* Allow resizing of shape.
*/
makeResizable: function(enable) {
var node = this.domNode,
enabled = node.hasClass('ui-resizable');
if (enable === enabled) {
return;
}
if (enable) {
var handles = {};
$.each(['n', 'e', 's', 'w', 'ne', 'se', 'sw', 'nw'], function(index, key) {
var handle = $('<div>').addClass('ui-resizable-handle').addClass('ui-resizable-' + key);
if ($.inArray(key, ['n', 'e', 's', 'w']) >= 0) {
handle
.append($('<div>', {'class': 'ui-resize-dot'}))
.append($('<div>', {'class': 'ui-resizable-border-' + key}));
}
node.append(handle);
handles[key] = handle;
});
node.addClass('ui-inner-handles');
node.resizable({
handles: handles,
autoHide: true,
stop: $.proxy(function(event, data) {
this.updatePosition({
x: parseInt(data.position.left, 10),
y: parseInt(data.position.top, 10)
});
}, this)
});
}
else {
node.removeClass('ui-inner-handles');
node.resizable("destroy");
}
},
/**
* Toggle shape selection.
*
* @param {bool} state
*/
toggleSelect: function(state) {
state = state || !this.selected;
this.selected = state;
if (this.selected) {
this.domNode.addClass('map-element-selected');
}
else {
this.domNode.removeClass('map-element-selected');
}
return this.selected;
},
/**
* Align shape to map or map grid.
*
* @param {bool} doAutoAlign if we should align element to grid
*/
align: function(doAutoAlign) {
var dims = {
height: this.domNode.height(),
width: this.domNode.width()
},
dimensions = this.getDimensions(),
x = dimensions.x,
y = dimensions.y,
shiftX = Math.round(dims.width / 2),
shiftY = Math.round(dims.height / 2),
newX = x,
newY = y,
newWidth = dims.width,
newHeight = dims.height,
gridSize = parseInt(this.sysmap.data.grid_size, 10);
// Lines should not be aligned
if (this instanceof Shape && this.data.type == SVGMapShape.TYPE_LINE) {
this.domNode.css({
top: dimensions.y + 'px',
left: dimensions.x + 'px',
width: dimensions.width + 'px',
height: dimensions.height + 'px'
});
return;
}
// if 'fit to map' area coords are 0 always
if (this.data.elementsubtype === '1' && this.data.areatype === '0'
&& this.data.elementtype === '3') {
newX = 0;
newY = 0;
}
// if autoalign is off
else if (doAutoAlign === false
|| (typeof doAutoAlign === 'undefined' && this.sysmap.data.grid_align == '0')) {
if ((x + dims.width) > this.sysmap.data.width) {
newX = this.sysmap.data.width - dims.width;
}
if ((y + dims.height) > this.sysmap.data.height) {
newY = this.sysmap.data.height - dims.height;
}
if (newX < 0) {
newX = 0;
newWidth = this.sysmap.data.width;
}
if (newY < 0) {
newY = 0;
newHeight = this.sysmap.data.height;
}
}
else {
newX = x + shiftX;
newY = y + shiftY;
newX = Math.floor(newX / gridSize) * gridSize;
newY = Math.floor(newY / gridSize) * gridSize;
newX += Math.round(gridSize / 2) - shiftX;
newY += Math.round(gridSize / 2) - shiftY;
while ((newX + dims.width) > this.sysmap.data.width) {
newX -= gridSize;
}
while ((newY + dims.height) > this.sysmap.data.height) {
newY -= gridSize;
}
while (newX < 0) {
newX += gridSize;
}
while (newY < 0) {
newY += gridSize;
}
}
this.data.y = newY;
this.data.x = newX;
if (this instanceof Shape || this.data.elementsubtype === '1') {
this.data.width = newWidth;
this.data.height = newHeight;
}
this.domNode.css({
top: this.data.y + 'px',
left: this.data.x + 'px',
width: newWidth,
height: newHeight
});
},
/**
* Updates element position.
*
* @param {object} coords
*/
updatePosition: function(coords, invalidate) {
if (this instanceof Shape && this.data.type == SVGMapShape.TYPE_LINE) {
var dx = coords.x - Math.min(parseInt(this.data.x, 10), parseInt(this.data.width, 10)),
dy = coords.y - Math.min(parseInt(this.data.y, 10), parseInt(this.data.height, 10));
this.data.x = parseInt(this.data.x, 10) + dx;
this.data.y = parseInt(this.data.y, 10) + dy;
this.data.width = parseInt(this.data.width, 10) + dx;
this.data.height = parseInt(this.data.height, 10) + dy;
this.updateHandles();
}
else {
this.data.x = coords.x;
this.data.y = coords.y;
}
if (invalidate !== false) {
this.align();
this.trigger('afterMove', this);
}
else {
var dimensions = this.getDimensions();
this.domNode.css({
top: dimensions.y + 'px',
left: dimensions.x + 'px'
});
}
},
/**
* Removes Shape object, delete all reference to it.
*/
remove: function() {
this.makeDraggable(false);
this.domNode.remove();
delete this.sysmap.data.shapes[this.id];
delete this.sysmap.shapes[this.id];
if (typeof this.sysmap.selection.shapes[this.id] !== 'undefined') {
this.sysmap.selection.count.shapes--;
}
delete this.sysmap.selection.shapes[this.id];
},
/**
* Gets Shape data.
*
* @returns {Object}
*/
getData: function() {
return this.data;
}
};
Observer.makeObserver(Shape.prototype);
/**
* @class Creates a new Selement.
*
* @property {object} sysmap reference to Map object
* @property {object} data selement db values
* @property {bool} selected if element is now selected by user
* @property {string} id elementid
* @property {object} domNode reference to related DOM element
*
* @param {object} sysmap reference to Map object
* @param {object} selementData element db values
*/
function Selement(sysmap, selementData) {
this.sysmap = sysmap;
this.selected = false;
if (!selementData) {
selementData = {
selementid: getUniqueId(),
elementtype: '4', // image
elements: {},
iconid_off: this.sysmap.defaultIconId, // first imageid
label: t('S_NEW_ELEMENT'),
label_location: -1, // set default map label location
x: 0,
y: 0,
urls: {},
elementName: this.sysmap.defaultIconName, // first image name
use_iconmap: '1',
evaltype: 0, // TAG_EVAL_TYPE_AND_OR
tags: [],
inherited_label: null
};
}
else {
if ($.isArray(selementData.urls)) {
selementData.urls = {};
}
}
this.data = selementData;
this.updateLabel();
this.id = this.data.selementid;
this.expanded = this.data.expanded;
delete this.data.expanded;
// assign by reference
this.sysmap.data.selements[this.id] = this.data;
// create dom
this.domNode = $('<div>', {style: 'position: absolute; z-index: 100'})
.appendTo(this.sysmap.container)
.addClass('cursor-pointer sysmap_element')
.attr('data-id', this.id)
.attr('data-type', 'selements');
this.makeDraggable(true);
this.makeResizable(this.data.elementtype == 3 && this.data.elementsubtype == 1 && this.data.areatype == 1);
this.updateIcon();
this.domNode.css({
top: this.data.y + 'px',
left: this.data.x + 'px'
});
}
Selement.TYPE_HOST = 0; // SYSMAP_ELEMENT_TYPE_HOST
Selement.TYPE_MAP = 1; // SYSMAP_ELEMENT_TYPE_MAP
Selement.TYPE_TRIGGER = 2; // SYSMAP_ELEMENT_TYPE_TRIGGER
Selement.TYPE_HOST_GROUP = 3; // SYSMAP_ELEMENT_TYPE_HOST_GROUP
Selement.TYPE_IMAGE = 4; // SYSMAP_ELEMENT_TYPE_IMAGE
Selement.prototype = {
/**
* Returns element data.
*/
getData: Shape.prototype.getData,
/**
* Allows dragging of element
*/
makeDraggable: Shape.prototype.makeDraggable,
/**
* Allows resizing of element
*/
makeResizable: Shape.prototype.makeResizable,
/**
* Update label data inherited from map configuration.
*/
updateLabel: function () {
if (this.sysmap.data.label_format != 0) {
switch (parseInt(this.data.elementtype, 10)) {
case Selement.TYPE_HOST_GROUP:
this.data.label_type = this.sysmap.data.label_type_hostgroup;
if (this.data.label_type == CMap.LABEL_TYPE_CUSTOM) {
this.data.inherited_label = this.sysmap.data.label_string_hostgroup;
}
break;
case Selement.TYPE_HOST:
this.data.label_type = this.sysmap.data.label_type_host;
if (this.data.label_type == CMap.LABEL_TYPE_CUSTOM) {
this.data.inherited_label = this.sysmap.data.label_string_host;
}
break;
case Selement.TYPE_TRIGGER:
this.data.label_type = this.sysmap.data.label_type_trigger;
if (this.data.label_type == CMap.LABEL_TYPE_CUSTOM) {
this.data.inherited_label = this.sysmap.data.label_string_trigger;
}
break;
case Selement.TYPE_MAP:
this.data.label_type = this.sysmap.data.label_type_map;
if (this.data.label_type == CMap.LABEL_TYPE_CUSTOM) {
this.data.inherited_label = this.sysmap.data.label_string_map;
}
break;
case Selement.TYPE_IMAGE:
this.data.label_type = this.sysmap.data.label_type_image;
if (this.data.label_type == CMap.LABEL_TYPE_CUSTOM) {
this.data.inherited_label = this.sysmap.data.label_string_image;
}
break;
}
}
else {
this.data.label_type = this.sysmap.data.label_type;
this.data.inherited_label = null;
}
if (this.data.label_type == CMap.LABEL_TYPE_LABEL) {
this.data.inherited_label = this.data.label;
}
else if (this.data.label_type == CMap.LABEL_TYPE_NAME) {
if (this.data.elementtype != Selement.TYPE_IMAGE) {
this.data.inherited_label = this.data.elements[0].elementName;
}
else {
this.data.inherited_label = t('S_IMAGE');
}
}
if (this.data.label_type != CMap.LABEL_TYPE_CUSTOM && this.data.label_type != CMap.LABEL_TYPE_LABEL
&& this.data.label_type != CMap.LABEL_TYPE_IP) {
this.data.expanded = null;
}
else if (this.data.label_type == CMap.LABEL_TYPE_IP && this.data.elementtype == Selement.TYPE_HOST) {
this.data.inherited_label = '{HOST.IP}';
}
},
/**
* Return label based on map constructor configuration.
*
* @param {boolean} return label with expanded macros.
*
* @returns {string} or null
*/
getLabel: function (expand) {
var label = this.data.label;
if (typeof(expand) === 'undefined') {
expand = true;
}
if (this.data.label_type != CMap.LABEL_TYPE_NOTHING && this.data.label_type != CMap.LABEL_TYPE_STATUS) {
if (expand && typeof(this.expanded) === 'string' && (this.sysmap.data.expand_macros === '1'
|| (this.data.label_type == CMap.LABEL_TYPE_IP
&& this.data.elementtype == Selement.TYPE_HOST))) {
label = this.expanded;
}
else if (typeof this.data.inherited_label === 'string') {
label = this.data.inherited_label;
}
}
else {
label = null;
}
return label;
},
/**
* Updates element fields.
*
* @param {object} data
* @param {bool} unsetUndefined If true, all fields that are not in data parameter will be removed
* from element.
*/
update: function(data, unsetUndefined) {
var fieldName,
dataFelds = ['elementtype', 'elements', 'iconid_off', 'iconid_on', 'iconid_maintenance',
'iconid_disabled', 'label', 'label_location', 'x', 'y', 'elementsubtype', 'areatype', 'width',
'height', 'viewtype', 'urls', 'elementName', 'use_iconmap', 'evaltype', 'tags'
],
fieldsUnsettable = ['iconid_off', 'iconid_on', 'iconid_maintenance', 'iconid_disabled'],
i,
ln,
invalidate = ((typeof(data.label) !== 'undefined' && this.data.label !== data.label)
|| (typeof(data.elementtype) !== 'undefined' && this.data.elementtype !== data.elementtype)
|| (typeof(data.elements) !== 'undefined'
&& Object.keys(this.data.elements).length !== Object.keys(data.elements).length));
unsetUndefined = unsetUndefined || false;
if (!invalidate && typeof(data.elements) !== 'undefined') {
var k,
id,
key,
kln,
keys,
ids = Object.keys(this.data.elements);
for (i = 0, ln = ids.length; i < ln && !invalidate; i++) {
id = ids[i];
keys = Object.keys(this.data.elements[id]);
for (k = 0, kln = keys.length; k < kln; k++) {
key = keys[k];
if (this.data.elements[id][key] !== data.elements[id][key]) {
invalidate = true;
break;
}
}
}
}
// update elements fields, if not massupdate, remove fields that are not in new values
for (i = 0, ln = dataFelds.length; i < ln; i++) {
fieldName = dataFelds[i];
if (typeof data[fieldName] !== 'undefined') {
this.data[fieldName] = data[fieldName];
}
else if (unsetUndefined && (fieldsUnsettable.indexOf(fieldName) === -1)) {
delete this.data[fieldName];
}
}
// if elementsubtype is not set, it should be 0
if (unsetUndefined && typeof this.data.elementsubtype === 'undefined') {
this.data.elementsubtype = '0';
}
if (unsetUndefined && typeof this.data.use_iconmap === 'undefined') {
this.data.use_iconmap = '0';
}
this.makeResizable(
this.data.elementtype == 3 && this.data.elementsubtype == 1 && this.data.areatype == 1
);
if (this.data.elementtype === '2') {
// For element type trigger not exist single element name.
delete this.data['elementName'];
}
else if (this.data.elementtype === '4') {
// If element is image, unset advanced icons.
this.data.iconid_on = '0';
this.data.iconid_maintenance = '0';
this.data.iconid_disabled = '0';
// If image element, set elementName to image name.
for (i in this.sysmap.iconList) {
if (this.sysmap.iconList[i].imageid === this.data.iconid_off) {
this.data.elementName = this.sysmap.iconList[i].name;
}
}
}
else {
this.data.elementName = this.data.elements[0].elementName;
}
this.updateLabel();
this.updateIcon();
this.align(false);
this.trigger('afterMove', this);
if (invalidate) {
this.sysmap.expandMacros(this);
}
},
/**
* Updates element position.
*
* @param {object} coords
*/
updatePosition: Shape.prototype.updatePosition,
/**
* Remove element.
*/
remove: function() {
this.domNode.remove();
delete this.sysmap.data.selements[this.id];
delete this.sysmap.selements[this.id];
if (typeof this.sysmap.selection.selements[this.id] !== 'undefined') {
this.sysmap.selection.count.selements--;
}
delete this.sysmap.selection.selements[this.id];
},
/**
* Toggle element selection.
*
* @param {bool} state
*/
toggleSelect: Shape.prototype.toggleSelect,
/**
* Align element to map or map grid.
*
* @param {bool} doAutoAlign if we should align element to grid
*/
align: Shape.prototype.align,
/**
* Get element dimensions.
*/
getDimensions: Shape.prototype.getDimensions,
/**
* Updates element icon and height/width in case element is area type.
*/
updateIcon: function() {
var oldIconClass = this.domNode.get(0).className.match(/sysmap_iconid_\d+/);
if (oldIconClass !== null) {
this.domNode.removeClass(oldIconClass[0]);
}
if ((this.data.use_iconmap === '1' && this.sysmap.data.iconmapid !== '0')
&& (this.data.elementtype === '0'
|| (this.data.elementtype === '3' && this.data.elementsubtype === '1'))) {
this.domNode.addClass('sysmap_iconid_' + this.sysmap.defaultAutoIconId);
}
else {
this.domNode.addClass('sysmap_iconid_' + this.data.iconid_off);
}
if (this.data.elementtype === '3' && this.data.elementsubtype === '1') {
if (this.data.areatype === '1') {
this.domNode
.css({
width: this.data.width + 'px',
height: this.data.height + 'px'
})
.addClass('map-element-area-bg');
}
else {
this.domNode
.css({
width: this.sysmap.data.width + 'px',
height: this.sysmap.data.height + 'px'
})
.addClass('map-element-area-bg');
}
}
else {
this.domNode
.css({
width: '',
height: ''
})
.removeClass('map-element-area-bg');
}
},
getName: function () {
var name;
if (typeof this.data.elementName === 'undefined') {
name = this.data.elements[0].elementName;
if (Object.keys(this.data.elements).length > 1) {
name += '...';
}
}
else {
name = this.data.elementName;
}
return name;
}
};
Observer.makeObserver(Selement.prototype);
/**
* Form for elements.
*
* @param {object} formContainer jQuery object
* @param {object} sysmap
*/
function SelementForm(formContainer, sysmap) {
var formTplData = {
sysmapid: sysmap.sysmapid
},
tpl = new Template($('#mapElementFormTpl').html()),
i,
icon,
formActions = [
{
action: 'show',
value: '#subtypeRow, #hostGroupSelectRow',
cond: [{
elementType: '3'
}]
},
{
action: 'show',
value: '#hostSelectRow',
cond: [{
elementType: '0'
}]
},
{
action: 'show',
value: '#triggerSelectRow, #triggerListRow',
cond: [{
elementType: '2'
}]
},
{
action: 'show',
value: '#mapSelectRow',
cond: [{
elementType: '1'
}]
},
{
action: 'show',
value: '#areaTypeRow, #areaPlacingRow',
cond: [{
elementType: '3',
subtypeHostGroupElements: 'checked'
}]
},
{
action: 'show',
value: '#areaSizeRow',
cond: [{
elementType: '3',
subtypeHostGroupElements: 'checked',
areaTypeCustom: 'checked'
}]
},
{
action: 'hide',
value: '#iconProblemRow, #iconMainetnanceRow, #iconDisabledRow',
cond: [{
elementType: '4'
}]
},
{
action: 'disable',
value: '#iconid_off, #iconid_on, #iconid_maintenance, #iconid_disabled',
cond: [
{
use_iconmap: 'checked',
elementType: '0'
},
{
use_iconmap: 'checked',
elementType: '3',
subtypeHostGroupElements: 'checked'
}
]
},
{
action: 'show',
value: '#useIconMapRow',
cond: [
{
elementType: '0'
},
{
elementType: '3',
subtypeHostGroupElements: 'checked'
}
]
},
{
action: 'show',
value: '#tags-select-row',
cond: [
{
elementType: '0'
},
{
elementType: '3'
}
]
}
];
this.active = false;
this.sysmap = sysmap;
this.formContainer = formContainer;
// create form
this.domNode = $(tpl.evaluate(formTplData)).appendTo(formContainer);
// populate icons selects
const select_icon_off = document.getElementById('iconid_off');
const select_icon_on = document.getElementById('iconid_on');
const select_icon_maintenance = document.getElementById('iconid_maintenance');
const select_icon_disabled = document.getElementById('iconid_disabled');
select_icon_on.addOption({label: t('S_DEFAULT'), value: '0', class_name: ZBX_STYLE_DEFAULT_OPTION});
select_icon_maintenance.addOption({label: t('S_DEFAULT'), value: '0', class_name: ZBX_STYLE_DEFAULT_OPTION});
select_icon_disabled.addOption({label: t('S_DEFAULT'), value: '0', class_name: ZBX_STYLE_DEFAULT_OPTION});
for (i in this.sysmap.iconList) {
icon = this.sysmap.iconList[i];
select_icon_off.addOption({label: icon.name, value: icon.imageid});
select_icon_on.addOption({label: icon.name, value: icon.imageid});
select_icon_maintenance.addOption({label: icon.name, value: icon.imageid});
select_icon_disabled.addOption({label: icon.name, value: icon.imageid});
}
// hosts
$('#elementNameHost').multiSelectHelper({
id: 'elementNameHost',
object_name: 'hosts',
name: 'elementValue',
selectedLimit: 1,
popup: {
parameters: {
srctbl: 'hosts',
srcfld1: 'hostid',
dstfrm: 'selementForm',
dstfld1: 'elementNameHost'
}
}
});
// map
$('#elementNameMap').multiSelectHelper({
id: 'elementNameMap',
object_name: 'sysmaps',
name: 'elementValue',
selectedLimit: 1,
popup: {
parameters: {
srctbl: 'sysmaps',
srcfld1: 'sysmapid',
dstfrm: 'selementForm',
dstfld1: 'elementNameMap'
}
}
});
// triggers
$('#elementNameTriggers').multiSelectHelper({
id: 'elementNameTriggers',
object_name: 'triggers',
name: 'elementValue',
objectOptions: {
real_hosts: true
},
popup: {
parameters: {
srctbl: 'triggers',
srcfld1: 'triggerid',
dstfrm: 'selementForm',
dstfld1: 'elementNameTriggers',
with_triggers: '1',
real_hosts: '1',
multiselect: '1'
}
}
});
// host group
$('#elementNameHostGroup').multiSelectHelper({
id: 'elementNameHostGroup',
object_name: 'hostGroup',
name: 'elementValue',
selectedLimit: 1,
popup: {
parameters: {
srctbl: 'host_groups',
srcfld1: 'groupid',
dstfrm: 'selementForm',
dstfld1: 'elementNameHostGroup'
}
}
});
this.actionProcessor = new ActionProcessor(formActions);
this.actionProcessor.process();
}
SelementForm.prototype = {
/**
* Shows element form.
*/
show: function() {
this.formContainer.draggable('option', 'handle', '#formDragHandler');
this.formContainer.show();
this.domNode.show();
// Element must first be visible so that outerWidth() and outerHeight() are correct.
this.formContainer.positionOverlayDialogue();
this.active = true;
},
/**
* Hides element form.
*/
hide: function() {
this.domNode.toggle(false);
this.active = false;
},
/**
* Adds element urls to form.
*
* @param {object} urls
*/
addUrls: function(urls) {
var tpl = new Template($('#selementFormUrls').html()),
i,
url;
if (typeof urls === 'undefined' || $.isEmptyObject(urls)) {
urls = {empty: {}};
}
for (i in urls) {
url = urls[i];
// generate unique urlid
url.selementurlid = $('#urlContainer tbody tr[id^=urlrow]').length;
while ($('#urlrow_' + url.selementurlid).length) {
url.selementurlid++;
}
$(tpl.evaluate(url)).appendTo('#urlContainer tbody');
}
},
/**
* Append form tag field options.
*
* @param {array} tags
*/
addTags: function(tags) {
var tpl = new Template($('#tag-row-tmpl').html()),
$add_btn_row = $('#selement-tags .element-table-add').closest('tr'),
counter = $('#selement-tags').data('dynamicRows').counter;
for (const i in tags) {
var tag = jQuery.extend({tag: '', operator: 0, value: '', rowNum: ++counter}, tags[i]),
row = $(tpl.evaluate(tag));
row.insertBefore($add_btn_row);
row
.find('[name="tags[' + tag.rowNum + '][tag]"]')
.val(tag.tag);
row
.find('[name="tags[' + tag.rowNum + '][operator]"]')
.val(tag.operator);
row
.find('[name="tags[' + tag.rowNum + '][value]"]')
.val(tag.value);
new CTagFilterItem(row[0]);
}
$('#selement-tags').data('dynamicRows').counter = counter;
},
/**
* Set evaltype field value.
*
* @param {integer} value
*/
setEvaltype: function(value) {
$('#operator [name="evaltype"][value='+value+']').prop('checked', true);
},
/**
* Add triggers to the list.
*/
addTriggers: function(triggers) {
var tpl = new Template($('#selementFormTriggers').html()),
selected_triggers = $('#elementNameTriggers').multiSelect('getData'),
triggerids = [],
triggers_to_insert = [];
if (typeof triggers === 'undefined' || $.isEmptyObject(triggers)) {
triggers = [];
}
triggers = triggers.concat(selected_triggers);
if (triggers) {
triggers.forEach(function(trigger) {
if ($('input[name^="element_id[' + trigger.id + ']"]').length == 0) {
triggerids.push(trigger.id);
triggers_to_insert[trigger.id] = {
id: trigger.id,
name: typeof trigger.prefix == 'undefined'
? trigger.name
: trigger.prefix + trigger.name
};
}
});
if (triggerids.length != 0) {
// get priority
var ajaxUrl = new Curl('jsrpc.php');
ajaxUrl.setArgument('type', 11);
$.ajax({
url: ajaxUrl.getUrl(),
type: 'post',
dataType: 'html',
data: {
method: 'trigger.get',
triggerids: triggerids
},
success: function(data) {
data = JSON.parse(data);
triggers.forEach(function(sorted_trigger) {
data.result.forEach(function(trigger) {
if (sorted_trigger.id == trigger.triggerid) {
if ($('input[name^="element_id[' + trigger.triggerid + ']"]').length == 0) {
trigger.name = triggers_to_insert[trigger.triggerid].name;
$(tpl.evaluate(trigger)).appendTo('#triggerContainer tbody');
return false;
}
}
});
});
SelementForm.prototype.recalculateSortOrder();
SelementForm.prototype.initSortable();
}
});
}
$('#elementNameTriggers').multiSelect('clean');
}
},
/**
* Set form controls with element fields values.
*
* @param {object} selement
*/
setValues: function(selement) {
for (var elementName in selement) {
$('[name=' + elementName + ']', this.domNode).val([selement[elementName]]);
}
// set default icon state
if (empty(selement.iconid_on)) {
$('[name=iconid_on]', this.domNode).val(0);
}
if (empty(selement.iconid_disabled)) {
$('[name=iconid_disabled]', this.domNode).val(0);
}
if (empty(selement.iconid_maintenance)) {
$('[name=iconid_maintenance]', this.domNode).val(0);
}
// clear urls
$('#urlContainer tbody tr').remove();
this.addUrls(selement.urls);
// Set tag properties.
var tags = selement.tags;
if (!tags || Object.getOwnPropertyNames(tags).length == 0) {
tags = {0: {}};
}
this.cleanTagsField();
this.addTags(tags);
this.setEvaltype(selement.evaltype);
// Iconmap.
if (this.sysmap.data.iconmapid === '0') {
$('#use_iconmap').prop({
checked: false,
disabled: true
});
}
this.actionProcessor.process();
switch (selement.elementtype) {
// host
case '0':
$('#elementNameHost').multiSelect('addData', [{
'id': selement.elements[0].hostid,
'name': selement.elements[0].elementName
}]);
break;
// map
case '1':
$('#elementNameMap').multiSelect('addData', [{
'id': selement.elements[0].sysmapid,
'name': selement.elements[0].elementName
}]);
break;
// trigger
case '2':
var triggers = [];
for (i in selement.elements) {
triggers[i] = {'id': selement.elements[i].triggerid, 'name': selement.elements[i].elementName};
}
this.addTriggers(triggers);
break;
// host group
case '3':
$('#elementNameHostGroup').multiSelect('addData', [{
'id': selement.elements[0].groupid,
'name': selement.elements[0].elementName
}]);
break;
}
},
/**
* Remove tag filter rows from DOM.
*/
cleanTagsField: function() {
$('#selement-tags .form_row').remove();
},
/**
* Gets form values for element fields.
*
* @returns {Object|Boolean}
*/
getValues: function() {
var values = $(':input', '#selementForm')
.not(this.actionProcessor.hidden)
.not('[name^="tags"]')
.serializeArray(),
data = {
urls: {}
},
i,
urlPattern = /^url_(\d+)_(name|url)$/,
url,
urlNames = {},
elementsData = {};
for (i = 0; i < values.length; i++) {
url = urlPattern.exec(values[i].name);
if (url !== null) {
if (typeof data.urls[url[1]] === 'undefined') {
data.urls[url[1]] = {};
}
data.urls[url[1]][url[2]] = values[i].value.toString();
}
else {
data[values[i].name] = values[i].value.toString();
}
}
if (data.elementtype == '0' || data.elementtype == '3') {
data.tags = {};
$('input, z-select', '#selementForm').filter(function() {
return this.name.match(/tags\[\d+\]\[tag\]/);
}).each(function() {
if (this.value !== '') {
var nr = parseInt(this.name.match(/^tags\[(\d+)\]\[tag\]$/)[1]);
data.tags[Object.getOwnPropertyNames(data.tags).length] = {
tag: this.value,
operator: $('[name="tags['+nr+'][operator]"]').val(),
value: $('[name="tags['+nr+'][value]"]').val()
};
}
});
}
data.elements = {};
// set element id and name
switch (data.elementtype) {
// host
case '0':
elementsData = $('#elementNameHost').multiSelect('getData');
if (elementsData.length != 0) {
data.elements[0] = {
hostid: elementsData[0].id,
elementName: elementsData[0].name
};
}
break;
// map
case '1':
elementsData = $('#elementNameMap').multiSelect('getData');
if (elementsData.length != 0) {
data.elements[0] = {
sysmapid: elementsData[0].id,
elementName: elementsData[0].name
};
}
break;
// triggers
case '2':
i = 0;
$('input[name^="element_id"]').each(function() {
data.elements[i] = {
triggerid: $(this).val(),
elementName: $('input[name^="element_name[' + $(this).val() + ']"]').val(),
priority: $('input[name^="element_priority[' + $(this).val() + ']"]').val()
};
i++;
});
break;
// host group
case '3':
elementsData = $('#elementNameHostGroup').multiSelect('getData');
if (elementsData.length != 0) {
data.elements[0] = {
groupid: elementsData[0].id,
elementName: elementsData[0].name
};
}
break;
}
// validate urls
for (i in data.urls) {
if (data.urls[i].name === '' && data.urls[i].url === '') {
delete data.urls[i];
continue;
}
if (data.urls[i].name === '' || data.urls[i].url === '') {
alert(t('S_INCORRECT_ELEMENT_MAP_LINK'));
return false;
}
if (typeof urlNames[data.urls[i].name] !== 'undefined') {
alert(t('S_EACH_URL_SHOULD_HAVE_UNIQUE') + " '" + data.urls[i].name + "'.");
return false;
}
urlNames[data.urls[i].name] = 1;
}
// validate element id
if ($.isEmptyObject(data.elements) && data.elementtype !== '4') {
switch (data.elementtype) {
case '0': alert('Host is not selected.');
return false;
case '1': alert('Map is not selected.');
return false;
case '2': alert('Trigger is not selected.');
return false;
case '3': alert('Host group is not selected.');
return false;
}
}
return data;
},
/**
* Drag and drop trigger sorting.
*/
initSortable: function() {
var triggerContainer = $('#triggerContainer');
triggerContainer.sortable({
disabled: (triggerContainer.find('tr.sortable').length < 2),
items: 'tbody tr.sortable',
axis: 'y',
containment: 'parent',
cursor: 'grabbing',
handle: 'div.drag-icon',
tolerance: 'pointer',
opacity: 0.6,
update: this.recalculateSortOrder,
start: function(e, ui) {
$(ui.placeholder).height($(ui.helper).height());
}
});
},
/**
* Sorting triggers by severity.
*/
recalculateSortOrder: function() {
if ($('input[name^="element_id"]').length != 0) {
var triggers = [],
priority;
$('input[name^="element_id"]').each(function() {
priority = $('input[name^="element_priority[' + $(this).val() + ']"]').val()
if (!triggers[priority]) {
triggers[priority] = {
'priority': priority,
'html': $('#triggerrow_' + $(this).val())[0].outerHTML
}
}
else {
triggers[priority].html += $('#triggerrow_' + $(this).val())[0].outerHTML;
}
});
triggers.sort(function (a, b) {
return b.priority - a.priority;
});
$('#triggerContainer tbody').html('');
triggers.forEach(function(trigger) {
$('#triggerContainer tbody').append(trigger.html);
});
}
}
};
/**
* Elements mass update form.
*
* @param {object} formContainer jQuery object
* @param {object} sysmap
*/
function MassForm(formContainer, sysmap) {
var i,
icon,
formActions = [
{
action: 'enable',
value: '#massLabel',
cond: [{
chkboxLabel: 'checked'
}]
},
{
action: 'enable',
value: '#massLabelLocation',
cond: [{
chkboxLabelLocation: 'checked'
}]
},
{
action: 'enable',
value: '#massUseIconmap',
cond: [{
chkboxMassUseIconmap: 'checked'
}]
},
{
action: 'enable',
value: '#massIconidOff',
cond: [{
chkboxMassIconidOff: 'checked'
}]
},
{
action: 'enable',
value: '#massIconidOn',
cond: [{
chkboxMassIconidOn: 'checked'
}]
},
{
action: 'enable',
value: '#massIconidMaintenance',
cond: [{
chkboxMassIconidMaintenance: 'checked'
}]
},
{
action: 'enable',
value: '#massIconidDisabled',
cond: [{
chkboxMassIconidDisabled: 'checked'
}]
}
];
this.sysmap = sysmap;
this.formContainer = formContainer;
// create form
var tpl = new Template($('#mapMassFormTpl').html());
this.domNode = $(tpl.evaluate()).appendTo(formContainer);
// populate icons selects
const select_icon_off = document.getElementById('massIconidOff');
const select_icon_on = document.getElementById('massIconidOn');
const select_icon_maintenance = document.getElementById('massIconidMaintenance');
const select_icon_disabled = document.getElementById('massIconidDisabled');
select_icon_on.addOption({label: t('S_DEFAULT'), value: '0', class_name: ZBX_STYLE_DEFAULT_OPTION});
select_icon_maintenance.addOption({label: t('S_DEFAULT'), value: '0', class_name: ZBX_STYLE_DEFAULT_OPTION});
select_icon_disabled.addOption({label: t('S_DEFAULT'), value: '0', class_name: ZBX_STYLE_DEFAULT_OPTION});
for (i in this.sysmap.iconList) {
icon = this.sysmap.iconList[i];
select_icon_off.addOption({label: icon.name, value: icon.imageid});
select_icon_on.addOption({label: icon.name, value: icon.imageid});
select_icon_maintenance.addOption({label: icon.name, value: icon.imageid});
select_icon_disabled.addOption({label: icon.name, value: icon.imageid});
}
document.getElementById('massLabelLocation').selectedIndex = 0;
select_icon_off.selectedIndex = 0
select_icon_on.selectedIndex = 0
select_icon_maintenance.selectedIndex = 0
select_icon_disabled.selectedIndex = 0
this.actionProcessor = new ActionProcessor(formActions);
this.actionProcessor.process();
}
MassForm.prototype = {
/**
* Show mass update form.
*/
show: function() {
this.formContainer.draggable('option', 'handle', '#massDragHandler');
this.formContainer.show();
this.domNode.show();
// Element must first be visible so that outerWidth() and outerHeight() are correct.
this.formContainer.positionOverlayDialogue();
this.updateList();
},
/**
* Hide mass update form.
*/
hide: function() {
this.domNode.toggle(false);
$(':checkbox', this.domNode).prop('checked', false);
$('z-select', this.domNode).each(function() {
this.selectedIndex = 0;
});
$('textarea', this.domNode).val('');
this.actionProcessor.process();
},
/**
* Get values from mass update form that should be updated in all selected elements.
*
* @return array
*/
getValues: function() {
var values = $('#massForm').serializeArray(),
data = {},
i,
ln;
for (i = 0, ln = values.length; i < ln; i++) {
// special case for use iconmap checkbox, because unchecked checkbox is not submitted with form
if (values[i].name === 'chkbox_use_iconmap') {
data['use_iconmap'] = '0';
}
if (values[i].name.match(/^chkbox_/) !== null) {
continue;
}
data[values[i].name] = values[i].value.toString();
}
return data;
},
/**
* Updates list of selected elements in mass update form.
*/
updateList: function() {
var tpl = new Template($('#mapMassFormListRow').html()),
id,
list = [],
element,
elementTypeText,
i,
ln;
$('#massList tbody').empty();
for (id in this.sysmap.selection.selements) {
element = this.sysmap.selements[id];
switch (element.data.elementtype) {
case '0': elementTypeText = t('S_HOST'); break;
case '1': elementTypeText = t('S_MAP'); break;
case '2': elementTypeText = t('S_TRIGGER'); break;
case '3': elementTypeText = t('S_HOST_GROUP'); break;
case '4': elementTypeText = t('S_IMAGE'); break;
}
list.push({
elementType: elementTypeText,
elementName: element.getName().replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/\"/g,'&quot;').replace(/\'/g,'&apos;')
});
}
// sort by element type and then by element name
list.sort(function(a, b) {
var elementTypeA = a.elementType.toLowerCase(),
elementTypeB = b.elementType.toLowerCase(),
elementNameA,
elementNameB;
if (elementTypeA < elementTypeB) {
return -1;
}
if (elementTypeA > elementTypeB) {
return 1;
}
elementNameA = a.elementName.toLowerCase();
elementNameB = b.elementName.toLowerCase();
if (elementNameA < elementNameB) {
return -1;
}
if (elementNameA > elementNameB) {
return 1;
}
return 0;
});
for (i = 0, ln = list.length; i < ln; i++) {
$(tpl.evaluate(list[i])).appendTo('#massList tbody');
}
}
};
/**
* Form for shape editing.
*
* @param {object} formContainer jQuery object
* @param {object} sysmap
*/
function ShapeForm(formContainer, sysmap) {
this.sysmap = sysmap;
this.formContainer = formContainer;
this.triggerids = {};
this.domNode = $(new Template($('#mapShapeFormTpl').html()).evaluate()).appendTo(formContainer);
this.domNode.find('.color-picker input').colorpicker();
}
ShapeForm.prototype = {
/**
* Show form.
*/
show: function() {
this.formContainer.draggable('option', 'handle', '#shapeDragHandler');
this.formContainer.show();
this.domNode.show();
// Element must first be visible so that outerWidth() and outerHeight() are correct.
this.formContainer.positionOverlayDialogue();
this.active = true;
},
/**
* Hides element form.
*/
hide: function() {
this.domNode.toggle(false);
this.active = false;
},
/**
* Set form controls with shape fields values.
*
* @param {object} shape
*/
setValues: function(shape) {
for (var field in shape) {
$('[name=' + field + ']', this.domNode).val([shape[field]]);
}
$('.color-picker input', this.domNode).change();
$('#border_type').change();
$('#last_shape_type').val(shape.type);
$('input[type=radio][name=type]:checked').change();
},
/**
* Get values from shape update form that should be updated
*
* @return array
*/
getValues: function() {
var values = $('#shapeForm').serializeArray(),
data = {},
i,
ln,
min_size,
width = parseInt(this.sysmap.data.width),
height = parseInt(this.sysmap.data.height);
for (i = 0, ln = values.length; i < ln; i++) {
data[values[i].name] = values[i].value.toString();
}
data.x = parseInt(data.x, 10);
data.y = parseInt(data.y, 10);
data.width = parseInt(data.width, 10);
data.height = parseInt(data.height, 10);
data.x = isNaN(data.x) || (data.x < 0) ? 0 : data.x;
data.y = isNaN(data.y) || (data.y < 0) ? 0 : data.y;
min_size = (data.type != SVGMapShape.TYPE_LINE) ? 1 : 0;
data.width = isNaN(data.width) || (data.width < min_size) ? min_size : data.width;
data.height = isNaN(data.height) || (data.height < min_size) ? min_size : data.height;
data.x = (data.x >= width) ? width : data.x;
data.y = (data.y >= height) ? height : data.y;
data.width = (data.width >= width) ? width : data.width;
data.height = (data.height >= height) ? height : data.height;
return data;
}
};
/**
* Form for shape editing.
*
* @param {object} formContainer jQuery object
* @param {object} sysmap
*/
function MassShapeForm(formContainer, sysmap) {
var formActions = [];
var mapping = {
chkboxType: '[name="mass_type"]',
chkboxText: '#mass_text',
chkboxFont: '#mass_font',
chkboxFontSize: '#mass_font_size',
chkboxFontColor: '#mass_font_color',
chkboxTextHalign: '#mass_text_halign',
chkboxTextValign: '#mass_text_valign',
chkboxBackground: '#mass_background_color',
chkboxBorderType: '#mass_border_type',
chkboxBorderWidth: '#mass_border_width',
chkboxBorderColor: '#mass_border_color'
};
Object.keys(mapping).forEach(function (key) {
var condition = {};
condition[key] = 'checked';
formActions.push({
action: 'enable',
value: mapping[key],
cond: [condition]
});
});
this.sysmap = sysmap;
this.formContainer = formContainer;
this.triggerids = {};
this.domNode = $(new Template($('#mapMassShapeFormTpl').html()).evaluate()).appendTo(formContainer);
this.domNode.find('.color-picker input').colorpicker();
this.actionProcessor = new ActionProcessor(formActions);
this.actionProcessor.process();
}
MassShapeForm.prototype = {
/**
* Show form.
*/
show: function(figures) {
var value = figures ? 0 : 2;
$('.shape_figure_row', this.domNode).toggle(figures);
$('.switchable-content', this.domNode).each(function (i, element) {
element.textContent = element.hasAttribute('data-value-' + value) ?
element.getAttribute('data-value-' + value) :
element.getAttribute('data-value');
});
this.formContainer.draggable('option', 'handle', '#massShapeDragHandler');
this.formContainer.show();
this.domNode.show();
// Element must first be visible so that outerWidth() and outerHeight() are correct.
this.formContainer.positionOverlayDialogue();
this.active = true;
},
/**
* Hides element form.
*/
hide: function() {
this.domNode.toggle(false);
this.active = false;
$(':checkbox', this.domNode).prop('checked', false).prop("disabled", false);
$('textarea, input[type=text]', this.domNode).val('');
$('.color-picker input', this.domNode).change();
this.actionProcessor.process();
},
/**
* Get values from mass update form that should be updated in all selected shapes.
*
* @return array
*/
getValues: function() {
var values = $('#massShapeForm').serializeArray(),
data = {},
i,
ln;
for (i = 0, ln = values.length; i < ln; i++) {
if (values[i].name.match(/^mass_/) !== null) {
data[values[i].name.substring("mass_".length)] = values[i].value.toString();
}
}
return data;
}
};
/**
* Form for editin links.
*
* @param {object} formContainer jQuesry object
* @param {object} sysmap
*/
function LinkForm(formContainer, sysmap) {
this.sysmap = sysmap;
this.formContainer = formContainer;
this.triggerids = {};
this.domNode = $(new Template($('#linkFormTpl').html()).evaluate()).appendTo(formContainer);
this.domNode.find('.color-picker input').colorpicker();
}
LinkForm.prototype = {
/**
* Show form.
*/
show: function() {
this.domNode.show();
$('.element-edit-control').prop('disabled', true);
},
/**
* Hide form.
*/
hide: function() {
$('#linkForm').hide();
$('.element-edit-control').prop('disabled', false);
},
/**
* Get form values for link fields.
*/
getValues: function() {
var values = $('#linkForm').serializeArray(),
data = {
linktriggers: {}
},
i,
ln,
linkTriggerPattern = /^linktrigger_(\w+)_(triggerid|linktriggerid|drawtype|color|desc_exp)$/,
colorPattern = /^[0-9a-f]{6}$/i,
linkTrigger;
for (i = 0, ln = values.length; i < ln; i++) {
linkTrigger = linkTriggerPattern.exec(values[i].name);
if (linkTrigger !== null) {
if (linkTrigger[2] == 'color' && !values[i].value.toString().match(colorPattern)) {
throw sprintf(t('S_COLOR_IS_NOT_CORRECT'), values[i].value);
}
if (typeof data.linktriggers[linkTrigger[1]] === 'undefined') {
data.linktriggers[linkTrigger[1]] = {};
}
data.linktriggers[linkTrigger[1]][linkTrigger[2]] = values[i].value.toString();
}
else {
if (values[i].name == 'color' && !values[i].value.toString().match(colorPattern)) {
throw sprintf(t('S_COLOR_IS_NOT_CORRECT'), values[i].value);
}
data[values[i].name] = values[i].value.toString();
}
}
return data;
},
/**
* Update form controls with values from link.
*
* @param {object} link
*/
setValues: function(link) {
var selement1,
tmp,
selementid,
selement,
elementName,
optgroups = {},
optgroupType,
optgroupLabel,
optgroup,
i,
ln;
/*
* If only one element is selected, make sure that element1 is equal to the selected element and
* element2 - to the connected.
*/
if (this.sysmap.selection.count.selements === 1 && this.sysmap.selection.count.shapes === 0) {
// get currently selected element
for (selementid in this.sysmap.selection.selements) {
selement1 = this.sysmap.selements[selementid];
}
if (selement1.id !== link.selementid1) {
tmp = link.selementid1;
link.selementid1 = selement1.id;
link.selementid2 = tmp;
}
}
// populate list of elements to connect with
const connect_to_select = document.createElement('z-select');
connect_to_select._button.id = 'label-selementid2';
connect_to_select.id = 'selementid2';
connect_to_select.name = 'selementid2';
// sort by type
for (selementid in this.sysmap.selements) {
selement = this.sysmap.selements[selementid];
if (selement.id == link.selementid1) {
continue;
}
if (optgroups[selement.data.elementtype] === void(0)) {
optgroups[selement.data.elementtype] = [];
}
optgroups[selement.data.elementtype].push(selement);
}
for (optgroupType in optgroups) {
switch (optgroupType) {
case '0':
optgroupLabel = t('S_HOST');
break;
case '1':
optgroupLabel = t('S_MAP');
break;
case '2':
optgroupLabel = t('S_TRIGGER');
break;
case '3':
optgroupLabel = t('S_HOST_GROUP');
break;
case '4':
optgroupLabel = t('S_IMAGE');
break;
}
optgroup = {label: optgroupLabel, options: []};
for (i = 0, ln = optgroups[optgroupType].length; i < ln; i++) {
optgroup.options.push({
value: optgroups[optgroupType][i].id,
label: optgroups[optgroupType][i].getName()
});
}
connect_to_select.addOptionGroup(optgroup);
}
$('#selementid2').replaceWith(connect_to_select);
// set values for form elements
for (elementName in link) {
$('[name=' + elementName + ']', this.domNode).val(link[elementName]);
}
// clear triggers
this.triggerids = {};
$('#linkTriggerscontainer tbody tr').remove();
this.addLinkTriggers(link.linktriggers);
},
/**
* Add link triggers to link form.
*
* @param {object} triggers
*/
addLinkTriggers: function(triggers) {
var tpl = new Template($('#linkTriggerRow').html()),
linkTrigger,
table = $('#linkTriggerscontainer tbody');
for (linkTrigger in triggers) {
this.triggerids[triggers[linkTrigger].triggerid] = linkTrigger;
$(tpl.evaluate(triggers[linkTrigger])).appendTo(table);
$('#linktrigger_' + triggers[linkTrigger].linktriggerid + '_drawtype')
.val(triggers[linkTrigger].drawtype);
}
table.find('.color-picker input').colorpicker();
$('.color-picker input', this.domNode).change();
},
/**
* Add new triggers which were selected in popup to trigger list.
*
* @param {object} triggers
*/
addNewTriggers: function(triggers) {
var tpl = new Template($('#linkTriggerRow').html()),
linkTrigger = {
color: 'DD0000'
},
linktriggerid,
i,
ln,
table = $('#linkTriggerscontainer tbody');
for (i = 0, ln = triggers.length; i < ln; i++) {
if (typeof this.triggerids[triggers[i].triggerid] !== 'undefined') {
continue;
}
linktriggerid = getUniqueId();
// store linktriggerid to generate every time unique one
this.sysmap.allLinkTriggerIds[linktriggerid] = true;
// store triggerid to forbid selecting same trigger twice
this.triggerids[triggers[i].triggerid] = linktriggerid;
linkTrigger.linktriggerid = linktriggerid;
linkTrigger.desc_exp = triggers[i].description;
linkTrigger.triggerid = triggers[i].triggerid;
$(tpl.evaluate(linkTrigger)).appendTo(table);
}
table.find('.color-picker input').colorpicker();
$('.color-picker input', this.domNode).change();
},
/**
* Updates links list for element.
*
* @param {string} selementIds
*/
updateList: function(selementIds) {
var links = this.sysmap.getLinksBySelementIds(selementIds),
linkTable,
rowTpl,
list,
i, j,
selement,
tmp,
ln,
link,
linktriggers,
fromElementName,
toElementName;
$('.element-links').hide();
$('.element-links tbody').empty();
if (links.length) {
$('#mapLinksContainer').show();
if (objectSize(selementIds) > 1) {
rowTpl = '#massElementLinkTableRowTpl';
linkTable = $('#mass-element-links');
}
else {
rowTpl = '#elementLinkTableRowTpl';
linkTable = $('#element-links');
}
rowTpl = new Template($(rowTpl).html());
list = [];
for (i = 0, ln = links.length; i < ln; i++) {
link = this.sysmap.links[links[i]].data;
/*
* If one element selected and it's not link.selementid1, we need to swap link.selementid1
* and link.selementid2 in order that sorting works correctly.
*/
if (objectSize(selementIds) == 1 && !selementIds[link.selementid1]) {
// Get currently selected element.
for (var selementId in this.sysmap.selection.selements) {
selement = this.sysmap.selements[selementId];
}
if (selement.id !== link.selementid1) {
tmp = link.selementid1;
link.selementid1 = selement.id;
link.selementid2 = tmp;
}
}
linktriggers = [];
for (var linktrigger in link.linktriggers) {
linktriggers.push(link.linktriggers[linktrigger].desc_exp);
}
fromElementName = this.sysmap.selements[link.selementid1].getName();
toElementName = this.sysmap.selements[link.selementid2].getName();
list.push({
fromElementName: fromElementName,
toElementName: toElementName,
linkid: link.linkid,
linktriggers: linktriggers
});
}
// Sort by "from" element and then by "to" element.
list.sort(function(a, b) {
var fromElementA = a.fromElementName.toLowerCase(),
fromElementB = b.fromElementName.toLowerCase(),
toElementA = a.toElementName.toLowerCase(),
toElementB = b.toElementName.toLowerCase(),
linkIdA = a.linkid,
linkIdB = b.linkid;
if (fromElementA < fromElementB) {
return -1;
}
else if (fromElementA > fromElementB) {
return 1;
}
if (toElementA < toElementB) {
return -1;
}
else if (toElementA > toElementB) {
return 1;
}
if (linkIdA < linkIdB) {
return -1;
}
else if (linkIdA > linkIdB) {
return 1;
}
return 0;
});
for (i = 0, ln = list.length; i < ln; i++) {
var row = $(rowTpl.evaluate(list[i])),
row_urls = $('.element-urls', row);
for (j = 0; j < list[i].linktriggers.length; j++) {
if (j != 0) {
row_urls.append($('<br>'));
}
row_urls.append($('<span>').text(list[i].linktriggers[j]));
}
row.appendTo(linkTable.find('tbody'));
}
linkTable.closest('.element-links').show();
}
else {
$('#mapLinksContainer').hide();
}
}
};
var sysmap = new CMap(containerId, mapData);
Shape.prototype.bind('afterMove', function(event, element) {
if (sysmap.selection.count.shapes === 1 && sysmap.selection.count.selements === 0
&& sysmap.selection.shapes[element.id] !== void(0)) {
$('#shapeX').val(element.data.x);
$('#shapeY').val(element.data.y);
if (typeof element.data.width !== 'undefined') {
$('#shapeForm input[name=width]').val(element.data.width);
}
if (typeof element.data.height !== 'undefined') {
$('#shapeForm input[name=height]').val(element.data.height);
}
}
sysmap.updateImage();
});
Selement.prototype.bind('afterMove', function(event, element) {
if (sysmap.selection.count.selements === 1 && sysmap.selection.count.shapes === 0
&& sysmap.selection.selements[element.id] !== void(0)) {
$('#x').val(element.data.x);
$('#y').val(element.data.y);
if (typeof element.data.width !== 'undefined') {
$('#areaSizeWidth').val(element.data.width);
}
if (typeof element.data.height !== 'undefined') {
$('#areaSizeHeight').val(element.data.height);
}
}
if (sysmap.buffered_expand === false) {
sysmap.updateImage();
}
});
return sysmap;
}
return {
object: null,
run: function(containerId, mapData) {
if (this.object !== null) {
throw new Error('Map has already been run.');
}
this.object = createMap(containerId, mapData);
}
};
}(jQuery));
jQuery(function ($) {
/*
* Reposition the overlay dialogue window. The previous position is remembered using offset(). Each time overlay
* dialogue is opened, it could have different content (shape form, element form etc) and different size, so the
* new top and left position must be calculated. If the overlay dialogue is opened for the first time, position is
* set depending on map size and canvas top position. This makes map more visible at first. In case popup window is
* dragged outside visible view port or window is resized, popup will again be repositioned so it doesn't go outside
* the viewport. In case the popup is too large, position it with a small margin depending on whether is too long
* or too wide.
*/
$.fn.positionOverlayDialogue = function () {
var $map = $('#map-area'),
map_offset = $map.offset(),
map_margin = 10,
$dialogue = $(this),
$dialogue_host = $dialogue.offsetParent(),
dialogue_host_offset = $dialogue_host.offset(),
// Usable area relative to host.
dialogue_host_x_min = $dialogue_host.scrollLeft(),
dialogue_host_x_max = Math.min($dialogue_host[0].scrollWidth,
$(window).width() + $(window).scrollLeft() - dialogue_host_offset.left + $dialogue_host.scrollLeft()
) - 1,
dialogue_host_y_min = $dialogue_host.scrollTop(),
dialogue_host_y_max = Math.min($dialogue_host[0].scrollHeight,
$(window).height() + $(window).scrollTop() - dialogue_host_offset.top + $dialogue_host.scrollTop()
) - 1,
// Coordinates of map's top right corner relative to dialogue host.
pos_x = map_offset.left + $map[0].scrollWidth - dialogue_host_offset.left + $dialogue_host.scrollLeft(),
pos_y = map_offset.top - map_margin - dialogue_host_offset.top + $dialogue_host.scrollTop();
return this.css({
left: Math.max(dialogue_host_x_min, Math.min(dialogue_host_x_max - $dialogue.outerWidth(), pos_x)),
top: Math.max(dialogue_host_y_min, Math.min(dialogue_host_y_max - $dialogue.outerHeight(), pos_y))
});
};
});