%PDF- %PDF-
| Direktori : /proc/self/root/backups/router/usr/local/opnsense/www/js/ |
| Current File : //proc/self/root/backups/router/usr/local/opnsense/www/js/opnsense-treeview.js |
/**
* Copyright (C) 2023 Deciso B.V.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* jqtree expects a list + dict type structure, transform key value store into expected output
* https://mbraak.github.io/jqTree/#general
*
* @param {*} node source data
* @param {*} path reference
* @returns jqTree output
*/
function dict_to_tree(node, path) {
// some entries are lists, try use a name for the nodes in that case
let node_name_keys = ['name', 'interface-name'];
let result = [];
path = path === undefined ? "" : path + ".";
for (key in node) {
if (typeof node[key] === "function") {
continue;
}
let item_path = path + key;
if (node[key] instanceof Object) {
let node_name = key;
for (idx=0; idx < node_name_keys.length; ++idx) {
if (/^(0|[1-9]\d*)$/.test(node_name) && node[key][node_name_keys[idx]] !== undefined) {
node_name = node[key][node_name_keys[idx]];
break;
}
}
result.push({
name: node_name,
id: item_path,
children: dict_to_tree(node[key], item_path)
});
} else {
result.push({
name: key,
value: node[key],
id: item_path
});
}
}
return result;
}
/**
* return child data for provided node
* @param {*} raw_data all source data
* @param {*} node jqTree node to traverse
* @returns
*/
function tree_node_data(raw_data, node)
{
let path = [];
while (node.id !== undefined) {
path.push(node.name);
node = node.parent;
}
let tmp = raw_data;
for (let i=path.length -1 ; i >=0 ; i--) {
tmp = tmp[path[i]];
}
return tmp;
}
/**
* create or update tree
* @param {*} src_data source data
* @param {*} target object reference
* @returns tree
*/
function update_tree(src_data, target)
{
let $tree = $(target);
let tree_data = dict_to_tree(src_data);
if ($(target + ' > ul').length == 0) {
$tree.tree({
data: tree_data,
autoOpen: false,
dragAndDrop: false,
selectable: false,
closedIcon: $('<i class="fa fa-plus-square-o"></i>'),
openedIcon: $('<i class="fa fa-minus-square-o"></i>'),
onCreateLi: function(node, $li) {
let n_title = $li.find('.jqtree-title');
n_title.text(n_title.text().replaceAll('>','\>').replaceAll('<','\<'));
if (node.value !== undefined) {
$li.find('.jqtree-element').append(
' <strong>:</strong> ' + node.value
);
}
if (node.selected) {
$li.addClass("node-selected");
} else {
$li.removeClass("node-selected");
}
}
});
// initial view, collapse first level if there's only one node
if (Object.keys(src_data).length == 1) {
for (key in src_data) {
$tree.tree('openNode', $tree.tree('getNodeById', key));
}
}
// open node on label click
$tree.bind('tree.click', function(e) {
$tree.tree('toggle', e.node);
});
// open table on label dblclick
$tree.bind('tree.dblclick',function(event) {
let table = treeview_node_to_table(event.node);
if (table) {
BootstrapDialog.show({
title: event.node.id,
message: table,
type: BootstrapDialog.TYPE_INFO,
draggable: true,
buttons: [{
label: '<i class="fa fa-fw fa-close"></i>',
action: function(dialogItself){
dialogItself.close();
}
}]
});
}
});
} else {
let curent_state = $tree.tree('getState');
$tree.tree('loadData', tree_data);
$tree.tree('setState', curent_state);
}
return $tree;
}
let apply_tree_search_timer = null;
function tree_delayed_live_search()
{
let sender = $(this);
clearTimeout(apply_tree_search_timer);
apply_tree_search_timer = setTimeout(function(){
let searchTerm = sender.val().toLowerCase();
let target = $("#"+sender.attr('for'));
let tree = target.tree("getTree");
let selected = [];
if (tree !== null) {
tree.iterate((node) => {
let matched = false;
if (searchTerm !== "") {
matched = node.name.toLowerCase().includes(searchTerm);
if (!matched && typeof node.value === 'string') {
matched = node.value.toLowerCase().includes(searchTerm);
}
}
node["selected"] = matched;
if (matched) {
selected.push(node);
if (node.isFolder()) {
node.is_open = true;
}
let parent = node.parent;
while (parent) {
parent.is_open = true;
parent = parent.parent;
}
} else if (node.isFolder()) {
node.is_open = false;
}
return true;
});
target.tree("refresh");
if (selected.length > 0) {
target.tree('scrollToNode', selected[0]);
}
}
}, 500);
}
/**
* convert treeview node to table, when there are children
* @param {*} node
* @returns table|null
*/
function treeview_node_to_table(node)
{
let data = node.getData();
if (data === undefined || data.length == 0) {
return;
}
let table = $("<table class='table table-bordered table-striped table-condensed table-hover'/>");
if (data[0].children) {
/* recordset */
let fieldnames = {}
let dataset = [];
for (i=0 ; i < data.length ; ++i) {
let record = {'__id__': data[i].id};
for (j=0; j < data[i].children.length; j++) {
if (data[i].children[j].children) {
continue
} else if (fieldnames[data[i].children[j].name] === undefined) {
fieldnames[data[i].children[j].name] = j;
}
record[data[i].children[j].name] = data[i].children[j].value;
}
dataset.push(record);
}
if (Object.keys(fieldnames).length) {
fieldnames = Object.entries(fieldnames).sort(([,a],[,b]) => a-b);
let tr = $("<tr/>");
tr.append($("<th/>")); /* id field */
for (i = 0; i < fieldnames.length; ++i) {
tr.append($("<th/>").html(fieldnames[i][0]));
}
table.append($("<thead/>").append(tr));
let tbody = $("<tbody/>");
for (i = 0; i < dataset.length ; ++i) {
tr = $("<tr/>");
tr.append($("<td/>").html(dataset[i]['__id__']));
for (j = 0; j < fieldnames.length; ++j) {
tr.append($("<td/>").html(dataset[i][fieldnames[j][0]] ?? ''));
}
tbody.append(tr);
}
table.append(tbody);
}
}
if (table.children().length === 0) {
/* single record */
let tbody = $("<tbody/>");
for (i=0 ; i < data.length ; ++i) {
let tr = $("<tr/>");
tr.append($("<td/>").html(data[i].name));
tr.append($("<td/>").html(data[i].value));
tbody.append(tr);
}
table.append(tbody);
}
return table;
}