/* ** 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. **/ class CWidgetNavTree extends CWidget { static EVENT_MARK = 'widget-navtree.mark'; static EVENT_SELECT = 'widget-navtree.select'; static WIDGET_MAP_EVENT_SUBMAP_SELECT = 'widget-map.submap-select'; static hasReferenceField() { return true; } onInitialize() { this._severity_levels = null; this._navtree = []; this._maps = []; this._navtree_items_opened = []; this._navtree_item_selected = null; this._maps_accessible = null; this._show_unavailable = false; this._problems = null; this._max_depth = null; this._last_id = null; this._has_contents = false; this._registerContentsEvents(); } onActivate() { if (this._has_contents) { if (this._target.querySelector('.root') === null) { this._makeTree(); this._activateTree(); } this._activateContentsEvents(); } } onDeactivate() { this._deactivateContentsEvents(); } announceWidgets(widgets) { super.announceWidgets(widgets); this._deactivateContentsEvents(); this._maps = []; for (const widget of widgets) { if (this._fields.reference === widget._fields.filter_widget_reference) { this._maps.push(widget); } } if (this._has_contents) { this._activateContentsEvents(); } } getDataCopy({is_single_copy}) { this._deactivateContentsEvents(); this._setTreeHandlers(); this._updateWidgetFields(); this._activateContentsEvents(); return super.getDataCopy({is_single_copy}); } onEdit() { if (this._has_contents) { this._deactivateContentsEvents(); this._removeTree(); } if (this._has_contents && this._state === WIDGET_STATE_ACTIVE) { this._makeTree(); this._activateTree(); this._activateContentsEvents(); } } processUpdateResponse(response) { if (this._has_contents) { this._deactivateContentsEvents(); this._removeTree(); this._has_contents = false; } super.processUpdateResponse(response); if (response.navtree_data !== undefined) { this._has_contents = true; this._severity_levels = response.navtree_data.severity_levels; this._navtree = response.navtree_data.navtree; this._navtree_items_opened = response.navtree_data.navtree_items_opened; this._navtree_item_selected = response.navtree_data.navtree_item_selected; this._maps_accessible = response.navtree_data.maps_accessible; this._show_unavailable = response.navtree_data.show_unavailable; this._problems = response.navtree_data.problems; this._max_depth = response.navtree_data.max_depth; this._makeTree(); this._activateTree(); this._activateContentsEvents(); } } _registerContentsEvents() { this._events = { ...this._events, addChild: (e) => { const button = e.target; const depth = parseInt(button.closest('.tree-list').getAttribute('data-depth')); const parent = button.getAttribute('data-id'); this._itemEditDialog(0, parent, depth + 1, button); }, addMaps: (e) => { const button = e.target; const depth = parseInt(button.closest('.tree-list').getAttribute('data-depth')); const id = button.getAttribute('data-id'); if (typeof window.addPopupValues === 'function') { window.old_addPopupValues = window.addPopupValues; } window.addPopupValues = (data) => { this._deactivateContentsEvents(); const root = this._target.querySelector(`.tree-item[data-id="${id}"] > ul.tree-list`); if (root !== null) { for (const item of data.values) { root.appendChild(this._makeTreeItem({ id: this._getNextId(), name: item.name, sysmapid: item.id, parent: id }, depth + 1)); } const tree_item = root.closest('.tree-item'); tree_item.classList.remove('closed'); tree_item.classList.add('opened'); } this._setTreeHandlers(); this._updateWidgetFields(); if (typeof old_addPopupValues === 'function') { window.addPopupValues = old_addPopupValues; delete window.old_addPopupValues; } this._activateContentsEvents(); }; return PopUp('popup.generic', { srctbl: 'sysmaps', srcfld1: 'sysmapid', srcfld2: 'name', multiselect: '1' }, {dialogue_class: 'modal-popup-generic', trigger_element: e.target}); }, editItem: (e) => { const button = e.target; const id = button.getAttribute('data-id'); const parent = this._target.querySelector(`input[name="navtree.parent.${id}"]`).value; const depth = parseInt(button.closest('[data-depth]').getAttribute('data-depth')); this._itemEditDialog(id, parent, depth, button); }, removeItem: (e) => { const button = e.target; this._deactivateContentsEvents(); this._target.querySelector(`[data-id="${button.getAttribute('data-id')}"]`).remove(); this._setTreeHandlers(); this._updateWidgetFields(); this._activateContentsEvents(); }, select: (e) => { const link = e.target; const itemid = link.closest('.tree-item').getAttribute('data-id'); if (this._markTreeItemSelected(itemid)) { this._openBranch(this._navtree_item_selected); updateUserProfile('web.dashboard.widget.navtree.item.selected', this._navtree_item_selected, [this._widgetid] ); this.fire(CWidgetNavTree.EVENT_SELECT, { sysmapid: this._navtree[this._navtree_item_selected].sysmapid, itemid: this._navtree_item_selected }); } e.preventDefault(); }, selectSubmap: (e) => { if (e.detail.back) { if (e.detail.parent_itemid !== null) { this._markTreeItemSelected(e.detail.parent_itemid); } } else { for (let [itemid, item] of Object.entries(this._navtree)) { if (item.sysmapid != e.detail.sysmapid || item.parent != e.detail.parent_itemid) { continue; } if (this._markTreeItemSelected(itemid)) { this._openBranch(this._navtree_item_selected); } } } } } } _activateTree() { if (this._is_edit_mode) { this._makeSortable(); } else { this._parseProblems(); if (this._navtree_item_selected === null || !jQuery(`.tree-item[data-id=${this._navtree_item_selected}]`).is(':visible')) { this._navtree_item_selected = jQuery('.tree-item:visible', jQuery(this._target)) .not('[data-sysmapid="0"]') .first() .data('id'); } let sysmapid = 0; if (this._markTreeItemSelected(this._navtree_item_selected)) { sysmapid = this._navtree[this._navtree_item_selected].sysmapid; } this.fire(CWidgetNavTree.EVENT_SELECT, {sysmapid, itemid: this._navtree_item_selected}); } } _activateContentsEvents() { if (this._state === WIDGET_STATE_ACTIVE && this._has_contents) { if (this._is_edit_mode) { for (const button of this._target.querySelectorAll('.js-add-child')) { button.addEventListener('click', this._events.addChild); } for (const button of this._target.querySelectorAll('.js-add-maps')) { button.addEventListener('click', this._events.addMaps); } for (const button of this._target.querySelectorAll('.js-edit')) { button.addEventListener('click', this._events.editItem); } for (const button of this._target.querySelectorAll('.js-remove')) { button.addEventListener('click', this._events.removeItem); } } else { for (const link of this._target.querySelectorAll('a[data-sysmapid]')) { link.addEventListener('click', this._events.select); } } } if (!this._is_edit_mode) { for (const widget of this._maps) { widget.on(CWidgetNavTree.WIDGET_MAP_EVENT_SUBMAP_SELECT, this._events.selectSubmap); } } } _deactivateContentsEvents() { if (this._has_contents) { if (this._is_edit_mode) { for (const button of this._target.querySelectorAll('.js-add-child')) { button.removeEventListener('click', this._events.addChild); } for (const button of this._target.querySelectorAll('.js-add-maps')) { button.removeEventListener('click', this._events.addMaps); } for (const button of this._target.querySelectorAll('.js-edit')) { button.removeEventListener('click', this._events.editItem); } for (const button of this._target.querySelectorAll('.js-remove')) { button.removeEventListener('click', this._events.removeItem); } } else { for (const link of this._target.querySelectorAll('a[data-sysmapid]')) { link.removeEventListener('click', this._events.select); } } } if (!this._is_edit_mode) { for (const widget of this._maps) { widget.off(CWidgetNavTree.WIDGET_MAP_EVENT_SUBMAP_SELECT, this._events.selectSubmap); } } } _buildTree(parent_id = 0) { const tree = []; for (const i in this._navtree) { const tree_item = { id: i, name: this._navtree[i].name, order: this._navtree[i].order, parent: this._navtree[i].parent, sysmapid: this._navtree[i].sysmapid }; if (tree_item.id > this._last_id) { this._last_id = tree_item.id; } if (tree_item.parent == parent_id) { const children = this._buildTree(tree_item.id); if (children.length > 0) { tree_item.children = children; } tree_item.item_active = (tree_item.sysmapid == 0 || this._maps_accessible.includes(tree_item.sysmapid)); tree_item.item_visible = (this._show_unavailable || tree_item.item_active); tree.push(tree_item); } } tree.sort((a, b) => { return a.order - b.order; }); return tree; } _makeTree() { const tree = this._buildTree(); let root = this._makeTreeBranch(); root.classList.add('root'); this._target.querySelector('.tree').appendChild(root); if (this._is_edit_mode) { const root_item = this._makeTreeItem({name: t('root'), id: 0}, 0, false); root.appendChild(root_item); if (tree.length > 0) { if (root_item.classList.contains('closed')) { root_item.classList.add('opened'); root_item.classList.remove('closed'); } } root = document.getElementById(`${this._unique_id}_children-of-0`); } for (const item of tree) { root.appendChild(this._makeTreeItem(item)); } this._setTreeHandlers(); } _makeTreeBranch(parent_id = null) { const ul = document.createElement('ul'); if (parent_id !== null) { ul.id = `${this._unique_id}_children-of-${parent_id}`; } ul.classList.add('tree-list'); return ul; } _makeTreeItem(item, depth = 1, editable = true) { const li_item = document.createElement('li'); li_item.classList.add('tree-item'); if (!editable || this._navtree_items_opened.includes(item.id)) { li_item.classList.add('opened'); } else { li_item.classList.add('closed'); } if (!editable) { li_item.classList.add('root-item'); } if (this._is_edit_mode && item.sysmapid == 0) { li_item.classList.add('no-map'); } const ul = this._makeTreeBranch(item.id); if (item.children !== undefined && this._max_depth > depth) { let child_items_visible = 0; for (const child of item.children) { if (typeof child !== 'object') { continue; } ul.appendChild(this._makeTreeItem(child, depth + 1)); if (child.id > this._last_id) { this._last_id = child.id; } if (child.item_visible === true) { child_items_visible++; } } if (item.children.length > 0 && child_items_visible > 0) { li_item.classList.add('is-parent'); } } if (!this._is_edit_mode && item.sysmapid != 0 && !item.item_active) { li_item.classList.add('inaccessible'); } let link; if (!this._is_edit_mode && item.sysmapid != 0 && item.item_active) { link = document.createElement('a'); link.href = '#'; link.setAttribute('data-sysmapid', item.sysmapid); } else { link = document.createElement('span'); } link.title = item.name; link.innerText = item.name; link.classList.add('item-name'); li_item.id = `${this._unique_id}_tree-item-${item.id}`; li_item.setAttribute('data-id', item.id); if (item.sysmapid != 0) { li_item.setAttribute('data-sysmapid', item.sysmapid); } if (item.item_visible === false) { li_item.style.display = 'none'; } const tree_row = document.createElement('div'); tree_row.classList.add('tree-row'); li_item.appendChild(tree_row); const content = document.createElement('div'); content.classList.add('content'); tree_row.appendChild(content); const margin_lvl = document.createElement('div'); margin_lvl.classList.add('margin-lvl'); content.appendChild(margin_lvl); if (this._is_edit_mode) { const tools = document.createElement('div'); tools.classList.add('tools'); tree_row.appendChild(tools); const button_add_child = document.createElement('button'); button_add_child.type = 'button'; button_add_child.title = t('Add child element'); button_add_child.classList.add(ZBX_STYLE_BTN_ICON, ZBX_ICON_PLUS, 'js-add-child'); button_add_child.setAttribute('data-id', item.id); tools.appendChild(button_add_child); const button_add_maps = document.createElement('button'); button_add_maps.type = 'button'; button_add_maps.title = t('Add multiple maps'); button_add_maps.classList.add(ZBX_STYLE_BTN_ICON, ZBX_ICON_COPY, 'js-add-maps'); button_add_maps.setAttribute('data-id', item.id); tools.appendChild(button_add_maps); if (editable) { const button_edit = document.createElement('button'); button_edit.type = 'button'; button_edit.title = t('Edit'); button_edit.classList.add(ZBX_STYLE_BTN_ICON, ZBX_ICON_PENCIL, 'js-edit'); button_edit.setAttribute('data-id', item.id); tools.appendChild(button_edit); const button_remove = document.createElement('button'); button_remove.type = 'button'; button_remove.title = t('Remove'); button_remove.classList.add(ZBX_STYLE_BTN_ICON, ZBX_ICON_REMOVE_SMALL, 'js-remove'); button_remove.setAttribute('data-id', item.id); tools.appendChild(button_remove); const drag = document.createElement('div'); drag.classList.add('drag-icon'); content.appendChild(drag); } } else { const problems = document.createElement('div'); problems.classList.add('problem-icon-list'); tree_row.appendChild(problems); } const arrow = document.createElement('div'); arrow.classList.add('arrow'); content.appendChild(arrow); if (editable) { const arrow_btn = document.createElement('button'); const arrow_span = document.createElement('span'); arrow_btn.type = 'button'; arrow_btn.classList.add('treeview'); arrow_span.classList.add(li_item.classList.contains('opened') ? 'arrow-down' : 'arrow-right'); arrow_btn.appendChild(arrow_span); arrow.appendChild(arrow_btn); arrow_btn.addEventListener('click', () => { const branch = arrow_btn.closest('[data-id]'); let closed_state = '1'; if (branch.classList.contains('opened')) { arrow_btn.querySelector('span').classList.add('arrow-right'); arrow_btn.querySelector('span').classList.remove('arrow-down'); branch.classList.add('closed'); branch.classList.remove('opened'); } else { arrow_btn.querySelector('span').classList.add('arrow-down'); arrow_btn.querySelector('span').classList.remove('arrow-right'); branch.classList.add('opened'); branch.classList.remove('closed'); closed_state = '0'; } if (this._widgetid !== null) { updateUserProfile(`web.dashboard.widget.navtree.item-${branch.getAttribute('data-id')}.toggle`, closed_state, [this._widgetid] ); const index = this._navtree_items_opened.indexOf(branch.getAttribute('data-id')); if (index > -1) { if (closed_state === '1') { this._navtree_items_opened.splice(index, 1); } else { this._navtree_items_opened.push(branch.getAttribute('data-id')); } } else if (index === -1 && closed_state === '0') { this._navtree_items_opened.push(branch.getAttribute('data-id')); } } }); } content.appendChild(link); li_item.appendChild(ul); if (this._is_edit_mode && editable) { const name_fld = document.createElement('input'); name_fld.id = `${this._unique_id}_navtree.name.${item.id}`; name_fld.type = 'hidden'; name_fld.name = `navtree.name.${item.id}`; name_fld.value = item.name; li_item.appendChild(name_fld); const parent_fld = document.createElement('input'); parent_fld.id = `${this._unique_id}_navtree.parent.${item.id}`; parent_fld.type = 'hidden'; parent_fld.name = `navtree.parent.${item.id}`; parent_fld.value = item.parent || 0; li_item.appendChild(parent_fld); const mapid_fld = document.createElement('input'); mapid_fld.id = `${this._unique_id}_navtree.sysmapid.${item.id}`; mapid_fld.type = 'hidden'; mapid_fld.name = `navtree.sysmapid.${item.id}`; mapid_fld.value = item.sysmapid; li_item.appendChild(mapid_fld); } return li_item; } _removeTree() { const root = this._target.querySelector('.root'); if (root !== null) { root.remove(); } } _setTreeHandlers() { // Add .is-parent class for branches with sub-items. jQuery('.tree-list', jQuery(this._target)).not('.ui-sortable, .root').each(function() { if (jQuery('>li', jQuery(this)).not('.inaccessible').length) { jQuery(this).closest('.tree-item').addClass('is-parent'); } else { jQuery(this).closest('.tree-item').removeClass('is-parent'); } }); // Set [data-depth] for list and each sublist. jQuery('.tree-list', jQuery(this._target)).each(function() { jQuery(this).attr('data-depth', jQuery(this).parents('.tree-list').length); }); // Change arrow style. jQuery('.is-parent', jQuery(this._target)).each(function() { const $arrow = jQuery('> .tree-row > .content > .arrow > .treeview > span', jQuery(this)); if (jQuery(this).hasClass('opened')) { $arrow.removeClass('arrow-right').addClass('arrow-down'); } else { $arrow.removeClass('arrow-down a1').addClass('arrow-right'); } }); for (const tree_element of document.querySelectorAll('.tree-list')) { for (const button of tree_element.querySelectorAll('.js-add-child, .js-add-maps')) { button.disabled = tree_element.dataset.depth >= this._max_depth; } } } _markTreeItemSelected(itemid) { const selected_item = document.getElementById(`${this._unique_id}_tree-item-${itemid}`); const item = this._navtree[itemid]; if (item === undefined || selected_item === null || item === this._navtree_item_selected) { return false; } this._navtree_item_selected = itemid; let step_in_path = selected_item.closest('.tree-item'); this._target.querySelectorAll('.selected').forEach((selected) => { selected.classList.remove('selected'); }); while (step_in_path !== null) { step_in_path.classList.add('selected'); step_in_path = step_in_path.parentNode.closest('.tree-item'); } updateUserProfile('web.dashboard.widget.navtree.item.selected', this._navtree_item_selected, [this._widgetid]); this.fire(CWidgetNavTree.EVENT_MARK, {itemid: this._navtree_item_selected}); return true; } _openBranch(itemid) { if (!jQuery(`.tree-item[data-id=${itemid}]`).is(':visible')) { const selector = '> .tree-row > .content > .arrow > .treeview > span'; let branch_to_open = jQuery(`.tree-item[data-id=${itemid}]`).closest('.tree-list').not('.root'); while (branch_to_open.length) { branch_to_open.closest('.tree-item.is-parent') .removeClass('closed') .addClass('opened'); jQuery(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'); } } } _getNextId() { this._last_id++; while (jQuery(`[name="navtree.name.${this._last_id}"]`).length) { this._last_id++; } return this._last_id; } _makeSortable() { jQuery('.root-item > .tree-list', jQuery(this._target)) .sortable_tree({ max_depth: this._max_depth, stop: () => { this._setTreeHandlers(); this._updateWidgetFields(); } }) .disableSelection(); } _parseProblems() { if (this._severity_levels === null) { return false; } const empty_template = {}; for (const [severity, _] in Object.entries(this._severity_levels)) { empty_template[severity] = 0; } for (const [itemid, problems] of Object.entries(this._problems)) { for (const [severity, value] of Object.entries(problems || empty_template)) { if (value) { this._target.querySelectorAll(`.tree-item[data-id="${itemid}"]`).forEach((item) => { item.setAttribute(`data-problems${severity}`, value); }) } } } for (const [severity, value] of Object.entries(this._severity_levels)) { for (const problem of this._target.querySelectorAll(`[data-problems${severity}]`)) { const indicator = document.createElement('span'); indicator.title = value.name; indicator.classList.add('problem-icon-list-item', value.style_class); indicator.innerText = problem.getAttribute(`data-problems${severity}`); problem.querySelector('.tree-row > .problem-icon-list') .appendChild(indicator) } } } _itemEditDialog(id, parent, depth, trigger_elmnt) { const url = new Curl('zabbix.php'); const item_edit = id != 0; url.setArgument('action', 'widget.navtree.item.edit'); jQuery.ajax({ url: url.getUrl(), method: 'POST', data: { name: item_edit ? this._target.querySelector(`[name="navtree.name.${id}"]`).value : '', sysmapid: item_edit ? this._target.querySelector(`[name="navtree.sysmapid.${id}"]`).value : 0, depth: depth }, dataType: 'json', success: (resp) => { if (resp.debug !== undefined) { resp.body += resp.debug; } overlayDialogue({ 'title': t('Edit tree element'), 'class': 'modal-popup', 'content': resp.body, 'buttons': [ { 'title': item_edit ? t('Apply') : t('Add'), 'class': 'dialogue-widget-save', 'action': (overlay) => { const form = document.getElementById('widget-dialogue-form'); const form_inputs = form.elements; const url = new Curl('zabbix.php'); url.setArgument('action', 'widget.navtree.item.update'); overlay.setLoading(); overlay.xhr = $.ajax({ url: url.getUrl(), method: 'POST', data: { name: form_inputs.name.value.trim(), sysmapid: form_inputs.sysmapid !== undefined ? form_inputs.sysmapid.value : '0', add_submaps: form_inputs.add_submaps !== undefined && form_inputs.add_submaps.checked ? 1 : 0, depth: depth }, dataType: 'json', complete: () => { overlay.unsetLoading(); }, success: (resp) => { form.querySelectorAll('.msg-bad').forEach((msg) => { msg.remove(); }) if ('error' in resp) { const message_box = makeMessageBox('bad', resp.error.messages, resp.error.title )[0]; form.insertAdjacentElement('afterbegin', message_box); return false; } else { this._deactivateContentsEvents(); if (item_edit) { const $row = jQuery(`[data-id="${id}"]`, jQuery(this._target)); jQuery(`[name="navtree.name.${id}"]`, $row).val(resp.name); jQuery(`[name="navtree.sysmapid.${id}"]`, $row) .val(resp['sysmapid']); jQuery('> .tree-row > .content > .item-name', $row) .empty() .attr('title', resp['name']) .append(jQuery('').text(resp.name)); $row.toggleClass('no-map', resp.sysmapid == 0); } else { const root = this._target .querySelector(`.tree-item[data-id="${parent}"]>ul.tree-list`); id = this._getNextId(); if (root !== null) { root.append(this._makeTreeItem({ id: id, name: resp['name'], sysmapid: resp['sysmapid'], parent: parent }, depth + 1)); root.closest('.tree-item').classList.remove('closed'); root.closest('.tree-item').classList.add('opened', 'is-parent'); } } const add_child_level = (sysmapid, itemid, depth) => { if (resp.hierarchy[sysmapid] !== undefined && depth <= this._max_depth) { const root = this._target .querySelector(`.tree-item[data-id="${itemid}"]>ul.tree-list`); $.each(resp.hierarchy[sysmapid], (i, submapid) => { if (resp.submaps[submapid] === undefined) { return; } const submap_item = resp.submaps[submapid]; const submap_itemid = this._getNextId(); root.append(this._makeTreeItem({ id: submap_itemid, name: submap_item['name'], sysmapid: submap_item['sysmapid'], parent: itemid })); add_child_level(submapid, submap_itemid, depth + 1); }); root.closest('.tree-item').classList.remove('closed'); root.closest('.tree-item').classList.add('opened', 'is-parent'); } }; add_child_level(resp['sysmapid'], id, depth + 1); overlayDialogueDestroy(overlay.dialogueid); this._updateWidgetFields(); this._setTreeHandlers(); this._activateContentsEvents(); } } }); return false; }, 'isSubmit': true }, { 'title': t('Cancel'), 'class': 'btn-alt', 'action': () => {} } ], 'dialogueid': 'navtreeitem', 'script_inline': resp.script_inline }, trigger_elmnt); } }); } _updateWidgetFields() { const prefix = `${this.getUniqueId()}_`; if (!this._is_edit_mode) { return false; } for (const name in this._fields) { if (/^navtree\.(name|order|parent|sysmapid)\.\d+$/.test(name)) { delete this._fields[name] } } jQuery('input[name^="navtree.name."]', jQuery(this._body)).each((index, field) => { const id = field.getAttribute('name').substr(13); if (id) { const parent = document.getElementById(`${prefix}navtree.parent.${id}`).value; const sysmapid = document.getElementById(`${prefix}navtree.sysmapid.${id}`).value; const element = document.getElementById(`${prefix}children-of-${parent}`); let order = 0; if (element !== null) { const sibling = element.childNodes; while (sibling[order] !== undefined && sibling[order].getAttribute('data-id') != id) { order++; } } this._fields[`navtree.name.${id}`] = field.value; if (parent != 0) { this._fields[`navtree.parent.${id}`] = parent; } if (order != 0) { this._fields[`navtree.order.${id}`] = order + 1; } if (sysmapid != 0) { this._fields[`navtree.sysmapid.${id}`] = sysmapid; } } }); } }