diff --git a/dist/html-static/css/resizeable.css b/dist/html-static/css/resizeable.css
new file mode 100644
index 00000000..d96ca42d
--- /dev/null
+++ b/dist/html-static/css/resizeable.css
@@ -0,0 +1,69 @@
+
+#panel-vpn-service {
+ top: 85px;
+ right: 40px;
+
+ /* height: 400px; */
+ width: 340px;
+
+ max-height: 85vh; /* 80% of the viewport height */
+ max-width: 80vw; /* 80% of the viewport width */
+
+ /* min-height: 400px; */
+ min-width: 340px;
+}
+
+.resizable {
+ /* box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.4); */
+ /* background-color: rgba(140, 140, 140, 1); */
+ position: absolute;
+}
+
+.resizable .resizers{
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+}
+
+.resizable .resizers .resizer{
+ width: 10px;
+ height: 10px;
+ border-radius: 50%; /*magic to turn square into circle*/
+ position: absolute;
+}
+
+.resizable .resizers .resizer.top-left {
+ left: -5px;
+ top: -5px;
+ cursor: nwse-resize; /*resizer cursor*/
+ background-color: rgba(140, 140, 140, 0.5);
+}
+.resizable .resizers .resizer.top-right {
+ right: -5px;
+ top: -5px;
+ cursor: nesw-resize;
+ background-color: rgba(140, 140, 140, 0.5);
+}
+.resizable .resizers .resizer.bottom-left {
+ left: -5px;
+ bottom: 5px;
+ cursor: nesw-resize;
+ background-color: rgba(140, 140, 140, 0.5);
+}
+.resizable .resizers .resizer.bottom-right {
+ right: -5px;
+ bottom: -5px;
+ cursor: nwse-resize;
+ background-color: rgba(140, 140, 140, 0.5);
+}
+
+.resizable .resizers .resizer.mid-left {
+
+ /* left: -5px;
+ bottom: 5px; */
+ top: 40px;
+ cursor: ew-resize;
+
+ background-color: rgba(140, 140, 140, 0.5);
+
+}
\ No newline at end of file
diff --git a/dist/html-static/js/dev.js b/dist/html-static/js/dev.js
index f55ca22d..e142e6d6 100644
--- a/dist/html-static/js/dev.js
+++ b/dist/html-static/js/dev.js
@@ -660,6 +660,10 @@ document.addEventListener("DOMContentLoaded", async function() {
topoViewerNode = cy.filter('node[name = "topoviewer"]');
topoViewerNode.remove();
+ // remove node TopoViewerParentNode
+ topoViewerParentNode = cy.filter('node[name = "TopoViewer:1"]');
+ topoViewerParentNode.remove();
+
var cyExpandCollapse = cy.expandCollapse({
layoutBy: null, // null means use existing layout
undoable: false,
diff --git a/dist/html-static/js/library/bulma-resizeable-panel.min.js b/dist/html-static/js/library/bulma-resizeable-panel.min.js
new file mode 100644
index 00000000..c4615686
--- /dev/null
+++ b/dist/html-static/js/library/bulma-resizeable-panel.min.js
@@ -0,0 +1,170 @@
+ /* Make resizable div by Hung Nguyen */
+ function makeResizableDiv(div) {
+ const element = document.querySelector(div);
+ const resizers = document.querySelectorAll(div + ' .resizer');
+ // const minimum_size = 20;
+
+ var panelNode = document.getElementById("panel-vpn-service");
+
+ console.log("panelNode: ", panelNode);
+
+ const rect = panelNode.getBoundingClientRect();
+ const minimum_size_width = rect.width;
+ const minimum_size_height = rect.height;
+
+ console.log("Rendered Width: ", minimum_size_width);
+ console.log("Rendered Height: ", minimum_size_height);
+
+ const minimum_size = 20
+
+ let original_width = 0;
+ let original_height = 0;
+ let original_x = 0;
+ let original_y = 0;
+ let original_mouse_x = 0;
+ let original_mouse_y = 0;
+
+ for (let i = 0; i < resizers.length; i++) {
+ const currentResizer = resizers[i];
+ currentResizer.addEventListener('mousedown', function(e) {
+ e.preventDefault();
+ original_width = parseFloat(
+ getComputedStyle(element, null).getPropertyValue('width').replace('px', '')
+ );
+ original_height = parseFloat(
+ getComputedStyle(element, null).getPropertyValue('height').replace('px', '')
+ );
+ original_x = element.getBoundingClientRect().left;
+ original_y = element.getBoundingClientRect().top;
+ original_mouse_x = e.pageX;
+ original_mouse_y = e.pageY;
+ window.addEventListener('mousemove', resize);
+ window.addEventListener('mouseup', stopResize);
+ });
+
+ function resize(e) {
+ if (currentResizer.classList.contains('bottom-right')) {
+ const width = original_width + (e.pageX - original_mouse_x);
+ const height = original_height + (e.pageY - original_mouse_y);
+ if (width > minimum_size) {
+ element.style.width = width + 'px';
+ }
+ if (height > minimum_size) {
+ element.style.height = height + 'px';
+ }
+
+ } else if (currentResizer.classList.contains('mid-left')) {
+ const height = original_height + (e.pageY - original_mouse_y);
+ const width = original_width - (e.pageX - original_mouse_x);
+ if (width > minimum_size_width) {
+ element.style.width = width + 'px';
+ // element.style.left = original_x + (e.pageX - original_mouse_x) + 'px';
+ console.log("left: ", element.style.left);
+ }
+
+
+ } else if (currentResizer.classList.contains('bottom-left')) {
+ const height = original_height + (e.pageY - original_mouse_y);
+ const width = original_width - (e.pageX - original_mouse_x);
+ if (height > minimum_size_height) {
+ element.style.height = height + 'px';
+ console.log("height: ", element.style.height);
+ }
+ if (width > minimum_size_width) {
+ element.style.width = width + 'px';
+ element.style.left = original_x + (e.pageX - original_mouse_x) + 'px';
+ console.log("left: ", element.style.left);
+ }
+
+
+
+ } else if (currentResizer.classList.contains('top-right')) {
+ const width = original_width + (e.pageX - original_mouse_x);
+ const height = original_height - (e.pageY - original_mouse_y);
+ if (width > minimum_size) {
+ element.style.width = width + 'px';
+ }
+ if (height > minimum_size) {
+ element.style.height = height + 'px';
+ element.style.top = original_y + (e.pageY - original_mouse_y) + 'px';
+ }
+ } else {
+ const width = original_width - (e.pageX - original_mouse_x);
+ const height = original_height - (e.pageY - original_mouse_y);
+ if (width > minimum_size) {
+ element.style.width = width + 'px';
+ element.style.left = original_x + (e.pageX - original_mouse_x) + 'px';
+ }
+ if (height > minimum_size) {
+ element.style.height = height + 'px';
+ element.style.top = original_y + (e.pageY - original_mouse_y) + 'px';
+ }
+ }
+ }
+
+ function stopResize() {
+ window.removeEventListener('mousemove', resize);
+ }
+ }
+ }
+
+ makeResizableDiv('.resizable');
+
+
+ // tabulator
+ // Sample data
+ const activeSymptoms = Array.from({
+ length: 120
+ }, (_, i) => ({
+ rootCause: `eBGP Session to neighbor 30.1.1.${i + 2} is not up for Device: Node-${i + 5}, Vrf: vpn-${i + 100}`,
+ subservice: "subservice.ebgp.nbr.health system",
+ priority: Math.floor(Math.random() * 256),
+ lastUpdated: `19-Jan-2023 ${("0" + (i % 12 + 1)).slice(-2)}:${("0" + (i % 60)).slice(-2)}:34 AM`,
+ }));
+
+ // Tabulator Column Definitions
+ const columns = [{
+ title: "Root Cause",
+ field: "rootCause",
+ widthGrow: 3,
+ headerFilter: "input"
+ },
+ {
+ title: "Subservice",
+ field: "subservice",
+ widthGrow: 2,
+ headerFilter: "input"
+ },
+ {
+ title: "Priority",
+ field: "priority",
+ widthGrow: 1,
+ hozAlign: "right",
+ sorter: "number",
+ headerFilter: "number"
+ },
+ {
+ title: "Last Updated",
+ field: "lastUpdated",
+ widthGrow: 1,
+ sorter: "datetime",
+ headerFilter: "input"
+ },
+ ];
+
+ // Initialize Tabulator
+ new Tabulator("#tabulator-grid", {
+ data: activeSymptoms,
+ layout: "fitData",
+ // responsiveLayout: true,
+ tooltips: true,
+ pagination: "local",
+ paginationSize: 5,
+ movableColumns: true,
+ resizableRows: true,
+ initialSort: [{
+ column: "priority",
+ dir: "desc"
+ }],
+ columns,
+ });
\ No newline at end of file
diff --git a/dist/topoviewer b/dist/topoviewer
index e9b6fd2c..14a4173a 100755
Binary files a/dist/topoviewer and b/dist/topoviewer differ
diff --git a/go_cloudshellwrapper/constants.go b/go_cloudshellwrapper/constants.go
index 8c277aac..61a55e2a 100755
--- a/go_cloudshellwrapper/constants.go
+++ b/go_cloudshellwrapper/constants.go
@@ -1,6 +1,6 @@
package cloudshellwrapper
-var VersionInfo string = "nightly-25.01.01"
+var VersionInfo string = "nightly-25.01.02"
// create html-public files
var HtmlPublicPrefixPath string = "./html-public/"
diff --git a/go_cloudshellwrapper/constants.go.bak b/go_cloudshellwrapper/constants.go.bak
index cd6ec464..8c277aac 100755
--- a/go_cloudshellwrapper/constants.go.bak
+++ b/go_cloudshellwrapper/constants.go.bak
@@ -1,6 +1,6 @@
package cloudshellwrapper
-var VersionInfo string = "nightly-24.12.31"
+var VersionInfo string = "nightly-25.01.01"
// create html-public files
var HtmlPublicPrefixPath string = "./html-public/"
diff --git a/go_topoengine/adaptorClab.go b/go_topoengine/adaptorClab.go
index e5c2cf30..5d3f899e 100755
--- a/go_topoengine/adaptorClab.go
+++ b/go_topoengine/adaptorClab.go
@@ -221,6 +221,12 @@ func (cyTopo *CytoTopology) UnmarshalContainerLabTopoV2(topoFile []byte, clabHos
cytoJson.Data.Weight = "30"
cytoJson.Data.Name = node.ID
cytoJson.Data.TopoViewerRole = node.Labels.TopoViewerRole
+ if len(node.Labels.TopoViewerRole) != 0 {
+ cytoJson.Data.TopoViewerRole = node.Labels.TopoViewerRole
+ } else {
+ // defaulting to "PE"
+ cytoJson.Data.TopoViewerRole = "pe"
+ }
// if node.ID == "topoviewer" {
// cytoJson.Data.Lat = "51.45664108633426"
// cytoJson.Data.Lng = "7.00441511803141"
@@ -239,6 +245,9 @@ func (cyTopo *CytoTopology) UnmarshalContainerLabTopoV2(topoFile []byte, clabHos
if len(node.Labels.TopoViewerGroup) != 0 && len(node.Labels.TopoViewerGroupLevel) != 0 {
// combine to be "Data Center Spine:1"
cytoJson.Data.Parent = node.Labels.TopoViewerGroup + ":" + node.Labels.TopoViewerGroupLevel
+ } else {
+ // defaulting to "topoviewer:1"
+ cytoJson.Data.Parent = "topoviewer:1"
}
// create list of parent nodes
@@ -346,6 +355,7 @@ func (cyTopo *CytoTopology) UnmarshalContainerLabTopoV2(topoFile []byte, clabHos
cytoJson.Data.ID = n
cytoJson.Data.Name = strings.Split(n, ":")[0]
cytoJson.Data.TopoViewerRole = "group"
+
cytoJson.Data.Weight = "1000"
// defaulting to the same lat and lng as the last-node
// cytoJson.Data.Lat = "51.45664108633426"
diff --git a/html-static/css/resizeable.css b/html-static/css/resizeable.css
new file mode 100644
index 00000000..d96ca42d
--- /dev/null
+++ b/html-static/css/resizeable.css
@@ -0,0 +1,69 @@
+
+#panel-vpn-service {
+ top: 85px;
+ right: 40px;
+
+ /* height: 400px; */
+ width: 340px;
+
+ max-height: 85vh; /* 80% of the viewport height */
+ max-width: 80vw; /* 80% of the viewport width */
+
+ /* min-height: 400px; */
+ min-width: 340px;
+}
+
+.resizable {
+ /* box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.4); */
+ /* background-color: rgba(140, 140, 140, 1); */
+ position: absolute;
+}
+
+.resizable .resizers{
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+}
+
+.resizable .resizers .resizer{
+ width: 10px;
+ height: 10px;
+ border-radius: 50%; /*magic to turn square into circle*/
+ position: absolute;
+}
+
+.resizable .resizers .resizer.top-left {
+ left: -5px;
+ top: -5px;
+ cursor: nwse-resize; /*resizer cursor*/
+ background-color: rgba(140, 140, 140, 0.5);
+}
+.resizable .resizers .resizer.top-right {
+ right: -5px;
+ top: -5px;
+ cursor: nesw-resize;
+ background-color: rgba(140, 140, 140, 0.5);
+}
+.resizable .resizers .resizer.bottom-left {
+ left: -5px;
+ bottom: 5px;
+ cursor: nesw-resize;
+ background-color: rgba(140, 140, 140, 0.5);
+}
+.resizable .resizers .resizer.bottom-right {
+ right: -5px;
+ bottom: -5px;
+ cursor: nwse-resize;
+ background-color: rgba(140, 140, 140, 0.5);
+}
+
+.resizable .resizers .resizer.mid-left {
+
+ /* left: -5px;
+ bottom: 5px; */
+ top: 40px;
+ cursor: ew-resize;
+
+ background-color: rgba(140, 140, 140, 0.5);
+
+}
\ No newline at end of file
diff --git a/html-static/js/dev.js b/html-static/js/dev.js
index f55ca22d..e142e6d6 100644
--- a/html-static/js/dev.js
+++ b/html-static/js/dev.js
@@ -660,6 +660,10 @@ document.addEventListener("DOMContentLoaded", async function() {
topoViewerNode = cy.filter('node[name = "topoviewer"]');
topoViewerNode.remove();
+ // remove node TopoViewerParentNode
+ topoViewerParentNode = cy.filter('node[name = "TopoViewer:1"]');
+ topoViewerParentNode.remove();
+
var cyExpandCollapse = cy.expandCollapse({
layoutBy: null, // null means use existing layout
undoable: false,
diff --git a/html-static/js/library/bulma-resizeable-panel.min.js b/html-static/js/library/bulma-resizeable-panel.min.js
new file mode 100644
index 00000000..c4615686
--- /dev/null
+++ b/html-static/js/library/bulma-resizeable-panel.min.js
@@ -0,0 +1,170 @@
+ /* Make resizable div by Hung Nguyen */
+ function makeResizableDiv(div) {
+ const element = document.querySelector(div);
+ const resizers = document.querySelectorAll(div + ' .resizer');
+ // const minimum_size = 20;
+
+ var panelNode = document.getElementById("panel-vpn-service");
+
+ console.log("panelNode: ", panelNode);
+
+ const rect = panelNode.getBoundingClientRect();
+ const minimum_size_width = rect.width;
+ const minimum_size_height = rect.height;
+
+ console.log("Rendered Width: ", minimum_size_width);
+ console.log("Rendered Height: ", minimum_size_height);
+
+ const minimum_size = 20
+
+ let original_width = 0;
+ let original_height = 0;
+ let original_x = 0;
+ let original_y = 0;
+ let original_mouse_x = 0;
+ let original_mouse_y = 0;
+
+ for (let i = 0; i < resizers.length; i++) {
+ const currentResizer = resizers[i];
+ currentResizer.addEventListener('mousedown', function(e) {
+ e.preventDefault();
+ original_width = parseFloat(
+ getComputedStyle(element, null).getPropertyValue('width').replace('px', '')
+ );
+ original_height = parseFloat(
+ getComputedStyle(element, null).getPropertyValue('height').replace('px', '')
+ );
+ original_x = element.getBoundingClientRect().left;
+ original_y = element.getBoundingClientRect().top;
+ original_mouse_x = e.pageX;
+ original_mouse_y = e.pageY;
+ window.addEventListener('mousemove', resize);
+ window.addEventListener('mouseup', stopResize);
+ });
+
+ function resize(e) {
+ if (currentResizer.classList.contains('bottom-right')) {
+ const width = original_width + (e.pageX - original_mouse_x);
+ const height = original_height + (e.pageY - original_mouse_y);
+ if (width > minimum_size) {
+ element.style.width = width + 'px';
+ }
+ if (height > minimum_size) {
+ element.style.height = height + 'px';
+ }
+
+ } else if (currentResizer.classList.contains('mid-left')) {
+ const height = original_height + (e.pageY - original_mouse_y);
+ const width = original_width - (e.pageX - original_mouse_x);
+ if (width > minimum_size_width) {
+ element.style.width = width + 'px';
+ // element.style.left = original_x + (e.pageX - original_mouse_x) + 'px';
+ console.log("left: ", element.style.left);
+ }
+
+
+ } else if (currentResizer.classList.contains('bottom-left')) {
+ const height = original_height + (e.pageY - original_mouse_y);
+ const width = original_width - (e.pageX - original_mouse_x);
+ if (height > minimum_size_height) {
+ element.style.height = height + 'px';
+ console.log("height: ", element.style.height);
+ }
+ if (width > minimum_size_width) {
+ element.style.width = width + 'px';
+ element.style.left = original_x + (e.pageX - original_mouse_x) + 'px';
+ console.log("left: ", element.style.left);
+ }
+
+
+
+ } else if (currentResizer.classList.contains('top-right')) {
+ const width = original_width + (e.pageX - original_mouse_x);
+ const height = original_height - (e.pageY - original_mouse_y);
+ if (width > minimum_size) {
+ element.style.width = width + 'px';
+ }
+ if (height > minimum_size) {
+ element.style.height = height + 'px';
+ element.style.top = original_y + (e.pageY - original_mouse_y) + 'px';
+ }
+ } else {
+ const width = original_width - (e.pageX - original_mouse_x);
+ const height = original_height - (e.pageY - original_mouse_y);
+ if (width > minimum_size) {
+ element.style.width = width + 'px';
+ element.style.left = original_x + (e.pageX - original_mouse_x) + 'px';
+ }
+ if (height > minimum_size) {
+ element.style.height = height + 'px';
+ element.style.top = original_y + (e.pageY - original_mouse_y) + 'px';
+ }
+ }
+ }
+
+ function stopResize() {
+ window.removeEventListener('mousemove', resize);
+ }
+ }
+ }
+
+ makeResizableDiv('.resizable');
+
+
+ // tabulator
+ // Sample data
+ const activeSymptoms = Array.from({
+ length: 120
+ }, (_, i) => ({
+ rootCause: `eBGP Session to neighbor 30.1.1.${i + 2} is not up for Device: Node-${i + 5}, Vrf: vpn-${i + 100}`,
+ subservice: "subservice.ebgp.nbr.health system",
+ priority: Math.floor(Math.random() * 256),
+ lastUpdated: `19-Jan-2023 ${("0" + (i % 12 + 1)).slice(-2)}:${("0" + (i % 60)).slice(-2)}:34 AM`,
+ }));
+
+ // Tabulator Column Definitions
+ const columns = [{
+ title: "Root Cause",
+ field: "rootCause",
+ widthGrow: 3,
+ headerFilter: "input"
+ },
+ {
+ title: "Subservice",
+ field: "subservice",
+ widthGrow: 2,
+ headerFilter: "input"
+ },
+ {
+ title: "Priority",
+ field: "priority",
+ widthGrow: 1,
+ hozAlign: "right",
+ sorter: "number",
+ headerFilter: "number"
+ },
+ {
+ title: "Last Updated",
+ field: "lastUpdated",
+ widthGrow: 1,
+ sorter: "datetime",
+ headerFilter: "input"
+ },
+ ];
+
+ // Initialize Tabulator
+ new Tabulator("#tabulator-grid", {
+ data: activeSymptoms,
+ layout: "fitData",
+ // responsiveLayout: true,
+ tooltips: true,
+ pagination: "local",
+ paginationSize: 5,
+ movableColumns: true,
+ resizableRows: true,
+ initialSort: [{
+ column: "priority",
+ dir: "desc"
+ }],
+ columns,
+ });
\ No newline at end of file