Skip to content

Instantly share code, notes, and snippets.

@AlphaNerd
Created August 24, 2019 18:42
Show Gist options
  • Save AlphaNerd/86d0d3a1e3fa80c8df4b3d3e6b5470ab to your computer and use it in GitHub Desktop.
Save AlphaNerd/86d0d3a1e3fa80c8df4b3d3e6b5470ab to your computer and use it in GitHub Desktop.
A gist for sharing sample Javascript code to prospective employers/clients
/**
* GoJS Library
* @file GoJSDiagram.js
* @author Mariner Partners Inc
* @see https://gojs.net/
*/
import appEvents from 'app/core/app_events';
import angular from '../angularjs/index';
/**
* GoJS Diagram Constructor
* @external go
* @alias $
*/
import go from '../gojs/go';
import Helpers from './Helpers';
import DataProcessor from './DataProcessor';
import SVGProcessor from './SVGProcessor';
// import Geometry from './Geometry';
// import ForceDirectedLayout from './Layouts/ForceDirectedLayout';
import DragZoomingTool from './DragZoomingTool';
import LinkLabelOnPathDraggingTool from './LinkLabelOnPathDraggingTool';
import SpacingCommandHandler from './SpacingCommandHandler';
import {
ContinuousForceDirectedLayout,
DemoForceDirectedLayout,
ParallelRouteLink,
} from './diagram/layouts';
import './rollup-filter';
/**
* Configuration object for the app
* @module Config
*/
import config from '../config';
function emit(name, data) {
appEvents.emit(`${cnofig.namespace}-${name}`, data);
}
/*
GoJS init
*/
/**
* The GoJS object wrapper {@link external:go}
* @const
*/
const $ = go.GraphObject.make;
const SVG_FOLDER = '/public/plugins/geographic-view-panel/assets/svg';
const CONTEXT_MENU = [
makeButton("Zoom to node", (e, obj) => {
let node = obj.part.adornedPart;
this.centerOnNode(node.key);
// this.myDiagram.scale < 2 ?
this.myDiagram.commandHandler.increaseZoom(3)
// this.myDiagram.commandHandler.decreaseZoom(3);
}),
makeButton("List View", (e, obj) => {
let node = obj.part.adornedPart;
appEvents.emit('show-modal', {
src: 'public/plugins/logic-view-panel/partials/list-view-modal.html',
modalClass: 'confirm-modal',
model: {
panel: this.panel,
ctrl: this,
node,
data: node.data
},
});
})
];
function buildContextMenu(menus) {
if (!menus) menus = CONTEXT_MENU;
let ctx = $("ContextMenu")
menus.forEach(menu => ctx.add(menu));
return ctx;
}
// highlights the group containing this object, specific method for links
// returns the containing group of x
function containing(x, i) {
var container = x.containingGroup;
if (container !== null) container.highlight = i;
return container;
}
// container is the group that contains this node and
// will be the parameter x for the next call of this function.
// Calling containing(x,i) highlights each group the appropriate color
function containingAll(x, i) {
containing(x, i);
var container = x.containingGroup;
if (container !== null) containingAll(container, i + 1);
}
// perform the actual highlighting
function highlight(shp, obj2, hl) {
var color;
var width = 3;
if (hl === 0) {
color = "black";
width = 1;
} else if (hl === 1) {
color = "blue";
} else if (hl === 2) {
color = "green";
} else if (hl === 3) {
color = "orange";
} else if (hl === 4) {
color = "red";
} else {
color = "purple";
}
// shp.stroke = color;
// shp.strokeWidth = width;
if (obj2 !== null) {
obj2.stroke = color;
obj2.fill = color;
}
}
// return the selected radio button in "highlight"
function getRadioButton() {
var radio = document.getElementsByName("highlight");
for (var i = 0; i < radio.length; i++)
if (radio[i].checked) return radio[i];
}
// To simplify this code we define a function for creating a context menu button:
// To simplify this code we define a function for creating a context menu button:
function makeButton(text, action, visiblePredicate) {
return $("ContextMenuButton",
$(go.TextBlock, text), {
click: action
},
// don't bother with binding GraphObject.visible if there's no predicate
visiblePredicate ? new go.Binding("visible", "", function (o, e) {
return o.diagram ? visiblePredicate(o, e) : false;
}).ofObject() : {});
}
/* Debugging Snippet */
// mariner.logicview.diagram.model.nodeDataArray.filter(node=>
// (node.name && node.name.search("null") >= 0) ||
// (node.key && node.key.search("null") >= 0) ||
// (node.key && node.key === null) ||
// (node.name && node.name == null)
// )
export class GoJSDiagram {
/**
* @type {Array}
* @description contains the link_data when user chooses to toggle show/hide link data
* @param {Array} value an array representing the link state of the GoJS diagram's model
* @returns {Array} returns the link state array that was copied from the GoJS diagram's model
*/
get storedLinkData() {
return this._storedLinkData;
}
set storedLinkData(value) {
this._storedLinkData = Helpers.Serialize(value);
}
/**
* @type {Boolean}
* @description gets/sets the state of the panel property `nodeByRegion` {@link panelDefaults}
* @param {Boolean} value a state to set the Grafana panel property `groupByRegion` {@link panelDefaults}
* @returns {Boolean} a Grafana panel property
*/
get groupByRegion() {
return this.panel.groupByRegion;
}
set groupByRegion(value) {
this.panel.groupByRegion = Helpers.Serialize(value);
}
/**
* @class GoJSDiagram
* @classdesc The GoJS diagram manager that is to be overlayed over
* the LeafletMap
* @param {Object} $injector the AngularJS $injector
* @param {Object} panel the Grafana panel
*/
constructor({
$window,
$injector,
$timeout,
$scope
}, {
panel
}) {
// Some Debugging stuff
$window.mariner = $window.mariner || {};
$window.mariner.gojs = $window.mariner.gojs || this;
$window.mariner.gojs.DataProcessor = DataProcessor;
$window.mariner.ctx_menu = CONTEXT_MENU;
$window.mariner.$ = $;
this.$window = $window;
this.$timeout = $timeout;
this.$injector = $injector;
this.$scope = $scope;
this.panel = panel;
this.reloading = false;
this.timeSrv = $injector.get('timeSrv');
this.dataprocessor = new DataProcessor;
this.helper = new Helpers(this.panel.devmode);
this.initializedOnce = false;
this.myUpdatingGoJS = false;
this.myDiagram = null;
this.groupToolTipTemplate = null;
this.groupTemplate = null;
this.nodeToolTipTemplate = null;
this.nodeTemplate = null;
this.linkToolTipTemplate = null;
this.linkTemplate = null;
this.infoBoxH = null;
this.lastStroked = null;
this.scaleLevel = 1;
this.factor = 1.25;
// Set some defaults
this.map_data = [];
this.link_data = [];
// Settings
this._storedLinkData = [];
this.tooltipHTMLData = null;
this._layoutClasses = {
CircularLayout: new go.CircularLayout,
ForceDirectedLayout: new DemoForceDirectedLayout,
LayeredDigraphLayout: new go.LayeredDigraphLayout,
ContinuousForceDirectedLayout: new ContinuousForceDirectedLayout,
TreeLayout: new go.TreeLayout,
};
this.diagramLayouts = {
CircularLayout: $(go.CircularLayout, {
spacing: 500
}),
ForceDirectedLayout: $(DemoForceDirectedLayout, {
arrangementSpacing: new go.Size(10000, 10000)
}),
LayeredDigraphLayout: $(go.LayeredDigraphLayout, {
layerSpacing: 25
}),
ContinuousForceDirectedLayout: $(ContinuousForceDirectedLayout, {
defaultElectricalCharge: 20000,
defaultGravitationalMass: 5000,
arrangementSpacing: new go.Size(1000, 1000),
infinityDistance: 1000,
}),
TreeLayout: $(go.TreeLayout, {}),
};
this.groupLayouts = {
CircularLayout: $(go.CircularLayout, {
spacing: 500
}),
ForceDirectedLayout: $(DemoForceDirectedLayout, {
arrangementSpacing: new go.Size(1000, 1000)
}),
LayeredDigraphLayout: $(go.LayeredDigraphLayout, {
layerSpacing: 25,
linkSpacing: 2000
}),
ContinuousForceDirectedLayout: $(ContinuousForceDirectedLayout, {
defaultElectricalCharge: 20000,
defaultGravitationalMass: 5000,
arrangementSpacing: new go.Size(1000, 1000),
infinityDistance: 1000,
}),
TreeLayout: $(go.TreeLayout, {}),
};
this.SVG_Shapes = [];
// Gather all SVG Shapes
this.getAllSVGShapes();
}
dismiss() {
appEvents.emit('hide-modal');
}
getAllSVGShapes() {
this.panel.mappedMetrics.nodes.icon.types.forEach(icon => {
try {
this.$window.fetch(`${SVG_FOLDER}/${icon.filename}`)
.then(r => r.text())
.then(svg_string => {
// Process the SVG string
let svg = new SVGProcessor(svg_string);
this.SVG_Shapes[icon.value] = svg;
return svg;
})
.catch(err => this.helper.console.warn("Error getting SVG", err))
} catch (err) {
// Strange glitch where catch from fetch promise does not get caught
this.helper.console.warn("Error getting SVG", err)
}
})
}
centerOnNode(node_name) {
let node = this.myDiagram.findPartForKey(node_name);
let shifted_bounds = node.actualBounds;
// shifted_bounds.x += 100;
this.myDiagram.centerRect(shifted_bounds);
this.myDiagram.select(node);
}
changeLayout(part_to_change) {
this.myDiagram.model.startTransaction("Change Layout")
let layout;
console.log(`Switching ${part_to_change}'s layout`)
switch (part_to_change.toLowerCase())
// switch ('group')
{
case 'group':
layout = this.groupLayouts[this.panel.groupLayout];
this.myDiagram.groupTemplate.layout = layout;
break;
case 'diagram':
layout = this.diagramLayouts[this.panel.diagramLayout];
this.myDiagram.layout = this._layoutClasses[this.panel.diagramLayout];
break;
}
this.myDiagram.layout.invalidateLayout();
this.myDiagram.model.commitTransaction("Change Layout")
console.log("Committed Transaction")
this.reloadDiagram();
}
getLayouts() {
let layouts;
try {
layouts = Object.keys(this._layoutClasses);
return layouts
} catch (e) {
this.helper.console.warn("No layouts could be assertained", e)
}
return layouts;
}
listAllDimensions() {
let dimensions;
if (this.map_data.length <= 0) return [];
try {
dimensions = Object.keys(this.map_data[0])
} catch (e) {
this.helper.console.warn("No dimensions could be assertained", e)
}
dimensions = dimensions.sort((a, b) => a > b ?
1 :
a < b ?
-1 :
0)
return dimensions;
}
/**
* Handles clearing the options page's side panel metric data
*/
clearAllAvailableMetrics() {
this.panel.sidePanelMetrics.map(metric => {
metric.weight = this.panel.sidePanelMetrics.length - 1;
metric.enabled = false;
return metric;
});
}
/**
* Handles discovering new metrics and purging old ones
* each time new data is presented
*/
findAllAvailableMetrics() {
// let keys = Object.keys(this.map_data[0]) || [];
let keys = this.listAllDimensions();
let panel_keys = this.panel.sidePanelMetrics.map(metric => metric.key)
let new_keys = keys.filter(new_key => panel_keys.indexOf(new_key) < 0);
let removal_keys = panel_keys.filter(removal_key => keys.indexOf(removal_key) < 0);
// add unique keys
// loop over unique and add them to the panelDefaults
new_keys.forEach((new_key, weight) => {
let metric = {
key: new_key,
enabled: false,
weight: new_keys.length - 1
}
this.panel.sidePanelMetrics.push(metric)
})
// remove missing keys
// loop over unique and remove them to the panelDefaults
removal_keys.forEach(removal_key => {
let index = this.panel.sidePanelMetrics.findIndex(metric => metric.key === removal_key);
this.panel.sidePanelMetrics.splice(index, 1);
})
return this.panel.sidePanelMetrics
.sort((a, b) => a.key > b.key ?
1 :
a.key < b.key ?
-1 :
0)
}
plus(scaleLevelChange) {
try {
if (!scaleLevelChange) scaleLevelChange = 1;
if (this.myDiagram.commandHandler.canIncreaseZoom()) {
// this.zoomInButtonClickCount = ++this.zoomInButtonClickCount >= 1 ? ((this.zoomInButtonClickCount - 1) * 2) : this.zoomInButtonClickCount;
this.scaleLevel = scaleLevelChange * this.myDiagram.scale * this.factor;
// this.myDiagram.commandHandler.zoomFactor = this.scaleLevel;
// Set scale level to number of times button clicked
this.myDiagram.commandHandler.increaseZoom(this.scaleLevel);
// Scale the diagram
this.myDiagram.scale = this.scaleLevel;
}
} catch (e) {
this.helper.console.warn("Something went wrong while zooming", {
e,
scaleLevelChange,
scaleLevel: this.scaleLevel,
factor: this.factor
})
}
}
minus(scaleLevelChange) {
try {
if (!scaleLevelChange) scaleLevelChange = 1;
if (this.myDiagram.commandHandler.canDecreaseZoom()) {
this.scaleLevel = scaleLevelChange * this.myDiagram.scale / this.factor;
// this.myDiagram.commandHandler.zoomFactor = this.scaleLevel;
// Set scale level to number of times button clicked
this.myDiagram.commandHandler.decreaseZoom(this.scaleLevel);
// Scale the diagram
this.myDiagram.scale = this.scaleLevel;
}
} catch (e) {
this.helper.console.warn("Something went wrong while unzooming", {
e,
scaleLevelChange,
scaleLevel: this.scaleLevel,
factor: this.factor
})
}
}
infiniteScroll() {
this.myDiagram.scrollMode = this.panel.infiniteScroll ? go.Diagram.InfiniteScroll : go.Diagram.DocumentScroll;
}
/**
* Handle when the Grafana panel is destroyed
*/
onPanelTeardown() {
// this.$timeout.cancel(this.nextTickPromise);
this.onPanelClear();
this.helper.console.log("Teardown")
}
/**
* Handle when user hovers over grafana panel
* @param {Event} evt an event that triggers the panel hover
*/
onPanelHover(evt) {}
/**
* Handle when Grafana panel gets cleared
* @param {Event} evt an event that trigers a panel clear
*/
onPanelClear(evt) {
if (this.myDiagram)
this.myDiagram.div = null;
}
/**
* Handle when Grafana's panel changes size
*/
onPanelSizeChanged() {
this.helper.console.warn("Requesting Update")
if (this.myDiagram)
this.myDiagram.requestUpdate();
}
/**
* @param {Array} data received from a Grafana Datasource {@link stubs/sample-received-data.js}
*/
onDataReceived(data) {
this.map_data = data.nodes;
this.link_data = this.storedLinkData = data.links;
// this.helper.console.log("Received Data: Geo", {data})
let layout = this.myDiagram ?
this._layoutClasses[this.panel.diagramLayout] :
this.getDefaultLayout();
if (!this.myDiagram) {
this.myDiagram = layout;
// Link eh diagram's context menu
this.myDiagram.contextMenu =
$("ContextMenu",
// makeButton(`${this.panel.groupLocked ? 'Unlock' : 'Lock'} group layer`.toString(), (e, obj) => {
// console.log(`${this.panel.groupLocked ? 'Unlock' : 'Lock'}ing layer`);
// this.panel.groupLocked = !this.panel.groupLocked;
// this.myDiagram.layout.invalidateLayout();
// }),
makeButton("Zoom to Fit", (e, obj) => {
this.myDiagram.zoomToFit()
}),
makeButton("Collapse All", (e, obj) => {
this.myDiagram.nodes.each((node) => node.data.isGroup ? node.collapseSubGraph() : null)
}),
makeButton("Expand All", (e, obj) => {
this.myDiagram.nodes.each((node) => node.data.isGroup ? node.expandSubGraph() : null);
})
);
// manually reset scale to auto generated
this.scaleLevel = this.myDiagram.scale;
// Add an instance of the custom tool defined in DragZoomingTool.js.
// This needs to be inserted before the standard DragSelectingTool,
// which is normally the third Tool in the ToolManager.mouseMoveTools list.
this.myDiagram.toolManager.mouseMoveTools.insertAt(2, new DragZoomingTool());
// install the LinkLabelDraggingTool as a "mouse move" tool
this.myDiagram.toolManager.mouseMoveTools.insertAt(0, new LinkLabelOnPathDraggingTool());
// when the document is modified, add a "*" to the title and enable the "Save" button
// this.myDiagram.addDiagramListener("Modified", (event) => {
// this.helper.console.log("Model has been updated", {event})
// });
}
// else
// this.myDiagram.startTransaction((model) => {
// this.myDiagram.layout = layout;
// }, 'updating-layout')
this.reloadDiagram();
}
/**
* Destroy the gojs diagram by setting it to null
*/
destroyGraph() {
if (this.myDiagram) this.myDiagram.div = null;
}
/**
* Reload the GoJS Diagram
*/
reloadDiagram() {
if (this.reloading)
return this.helper.console.warn("Cannot Reload! Reload already in progress");
try {
// if (this.myDiagram) return this.helper.console.log("Cannot Reload! No active diagram started")
this.reloading = true;
if (!this.initializedOnce) {
// Build the model
this.model = {
linkFromPortIdProperty: "from-port",
linkToPortIdProperty: "to-port",
linkFromKeyProperty: "from",
linkToKeyProperty: "to",
nodeDataArray: this.map_data,
linkDataArray: this.link_data,
};
this.getAllSVGShapes();
this.assignAllTemplatesToDiagram();
this.myDiagram.model = $(go.GraphLinksModel, this.model);
} else {
let to_be_removed;
try {
this.myDiagram.model.commit((model) => {
this.helper.console.warn("Starting Transaction", {
count: this.map_data.length,
map_data: this.map_data
})
/*
* Add/Remove Nodes
*/
this.map_data.forEach(node => {
if (!node.key || (!node.group && !node.isGroup)) return this.helper.console.warn(`Could not process node`, node);
let index = this.myDiagram.model.nodeDataArray
.findIndex(_node => _node.key === node.key);
let found = index >= 0;
if (found) {
this.helper.console.warn("Updating node", {
node,
found,
index
})
// udpate the node data
if (!node.isGroup) model.setDataProperty(node);
} else {
// add the node data
this.helper.console.warn("Node to be added", {
node,
found,
index
});
if (node.key) model.addNodeData(node);
}
})
// Remove old refs
to_be_removed = this.myDiagram.model.nodeDataArray
.filter(node => !this.map_data.some(_node => _node.key === node.key))
// this.helper.console.log("Nodes to be removed", {to_be_removed, data: this.map_data});
model.removeNodeDataCollection(to_be_removed);
this.helper.console.log("End of Transaction", {
count: this.map_data.length,
map_data: this.map_data
})
/*
* -- EXPERIMENTAL --
* Add/Remove Links
*/
this.link_data.forEach(link => {
// if (!node.key) console.warn(`Could not process '${JSON.stringify(node)}'`)
let index = this.myDiagram.model.linkDataArray
.findIndex(_link => _link.from === link.from && _link.to === link.to);
let found = index >= 0;
// console.warn("Updating link", {link, found, index})
if (found) {
// udpate the link data
model.setDataProperty(link);
} else {
// add the link data
// console.warn("Node to be added", {link, found, index});
model.addLinkData(link);
}
})
// Remove old refs
to_be_removed = this.myDiagram.model.linkDataArray
.filter(link => !this.link_data.some(_link => _link.from === link.from && _link.to === link.to))
model.removeLinkDataCollection(to_be_removed);
// console.warn("Links to be removed", {to_be_removed, data: this.link_data});
}, 'data-received')
} catch (e) {
console.warn("Something went wrong", e)
}
this.assignAllTemplatesToDiagram();
this.getAllSVGShapes();
// this.myDiagram.model = $(go.GraphLinksModel, this.model);
}
if (this.map_data.length > 0) {
this.findAllAvailableMetrics()
// this.$scope.$apply()
}
/* Fix for blank view */
// appEvents.emit('panel-resize')
this.helper.console.warn("Reloading Logic View Diagram")
} catch (e) {
this.helper.console.warn(e);
}
this.reloading = false;
// this.onPanelSizeChanged()
this.initializedOnce = true;
}
/**
* Reset all of the diagram templates
*/
assignAllTemplatesToDiagram() {
this.myDiagram.groupTemplate = this.getGroupTemplate();
this.myDiagram.nodeTemplate = this.getNodeTemplate();
this.myDiagram.linkTemplate = this.getLinkTemplate();
}
/**
* Create a GoJS group tooltip template for the diagram and set
* the instance variable to store it
* @param {Object} template a GoJS group tooltip template
* @param {Object} opts currently unused parameter
* @returns {Object} the GoJS group tooltip
*/
getGroupToolTipTemplate(template, opts) {
if (!template) template =
$(go.Adornment, "Auto",
$(go.Shape, {
fill: "#FFF"
}),
$(go.TextBlock, {
margin: 4
},
new go.Binding("text", "", (d) => {
return d.key;
}))
);
// Set the template
this.groupToolTipTemplate = template;
return this.groupToolTipTemplate;
}
/**
* Create a GoJS group template for the diagram and set
* the instance variable to store it
* @param {Object} template a GoJS group template
* @param {Object} opts currently unused parameter
* @returns {Object} the GoJS group template
*/
getGroupTemplate(template, opts) {
let layout = this.groupLayouts[this.panel.groupLayout];
// define the group template
try {
if (!template) template =
$(go.Group, "Auto", { // define the group's internal layout
layerName: "Background",
toolTip: this.getGroupToolTipTemplate(),
layout: layout, //this._layoutClasses[this.panel.defaultLayout],
isSubGraphExpanded: true,
zOrder: NaN,
selectable: !this.panel.groupLocked,
contextMenu: $("ContextMenu",
makeButton("Zoom to group", (e, obj) => {
let node = obj.part.adornedPart;
this.centerOnNode(node.key);
// this.myDiagram.scale < 2 ?
this.myDiagram.commandHandler.increaseZoom(3)
// this.myDiagram.commandHandler.decreaseZoom(3);
}),
),
},
$(go.Shape, "RoundedRectangle", {
fill: "rgba(0, 100, 166, 0.15)",
stroke: "#0064a6",
strokeWidth: 2,
}),
$(go.Panel, "Vertical", {
defaultAlignment: go.Spot.TopLeft,
margin: 10
},
$(go.Panel, "Horizontal", {
defaultAlignment: go.Spot.TopLeft,
margin: 20
},
// the SubGraphExpanderButton is a panel that functions as a button to expand or collapse the subGraph
$("SubGraphExpanderButton", {
desiredSize: new go.Size(50, 50),
// _subGraphExpandedFigure: "TriangleUp",
// _subGraphCollapsedFigure: "TriangleDown",
// _buttonFillNormal: "white",
// _buttonStrokeNormal: "black",
// _buttonFillOver: "lightgreen",
// _buttonStrokeOver: "green",
}),
// $(go.TextBlock, {
// font: "bold 14px Sans-Serif",
// margin: 4,
// wrap: go.TextBlock.WrapFit,
// textAlign: "center"
// },
// new go.Binding("text", "", (d) => {
// return `${d.key}`;
// })
// )
$(
go.Panel, "Vertical", {},
$(go.TextBlock, {
font: "bold 23pt Sans-Serif",
margin: 4,
wrap: go.TextBlock.WrapFit,
textAlign: "center"
},
new go.Binding("text", "", (d) => {
return `${d.key}`;
})
)
),
),
// create a placeholder to represent the area where the contents of the group are
$(go.Placeholder, {
padding: new go.Margin(0, 10)
})
) // end Vertical Panel
);
} catch (e) {
this.helper.console.warn("Something went wrong while getting group template", {
e,
template
})
}
// Set the template
this.groupTemplate = template;
return this.groupTemplate;
}
/**
* Create a GoJS node tooltip template for the diagram and set
* the instance variable to store it
* @param {Object} template a GoJS node tooltip template
* @param {Object} opts currently unused parameter
* @returns {Object} the GoJS node tooltip
*/
getNodeToolTipTemplate(template, opts) {
if (!template) template =
$(go.HTMLInfo, {
// These methods should be wrapped in try/catch blocks in the future
show: (obj, diagram, tooltip) => {
let element = this.$window.document.getElementsByClassName('tooltip-template')[0];
let pt = diagram.lastInput.viewPoint;
element.style.left = (pt.x + 10) + "px";
element.style.top = (pt.y + 10) + "px";
element.style.display = "block";
this.tooltipHTMLData = obj.data;
this.$scope.$apply();
},
hide: (diagram, tooltip) => {
let element = this.$window.document.getElementsByClassName('tooltip-template')[0];
element.style.display = "none";
this.tooltipHTMLData = null;
},
}, )
// else
this.nodeToolTipTemplate = template;
return this.nodeToolTipTemplate;
}
/**
* Create a GoJS node template for the diagram and set
* the instance variable to store it
* @param {Object} template a GoJS node template
* @param {Object} opts currently unused parameter
* @returns {Object} the GoJS node template
*/
getNodeTemplate(template, opts) {
let get_color = (_value) => {
let color;
try {
let value = Helpers.RollUp(_value)
let levels = this.panel.mappedMetrics.nodes.thresholds
.map((threshold, index) => {
let ret = {
passed: threshold.min <= value && threshold.max > value,
index
}
return ret;
});
// this.helper.console.warn("Levels", levels);
if (levels.some(trial => trial.passed)) {
let index = levels.find(trial => trial.passed).index;
if (!index) color = this.panel.defaultColor;
color = this.panel.mappedMetrics.nodes.thresholds[index].color;
// this.helper.console.warn("Finding index ... hehe", {levels, level: levels[index], color, index})
} else
color = this.panel.defaultColor;
} catch (e) {
// this.helper.console.warn("Could not determine link color", e)
}
// this.helper.console.log("Color is: ", {color,value, level_one, level_two, level_three})
return color;
}
function spacedLocationParse(str) {
var cmd = myDiagram.commandHandler;
if (!(cmd instanceof SpacingCommandHandler)) throw new Error("not using SpacingCommandHandler");
var pt = go.Point.parse(str);
pt.x = (pt.x - cmd.spaceCenter.x) * cmd.space + cmd.spaceCenter.x;
if (cmd.isYSpaced) {
pt.y = (pt.y - cmd.spaceCenter.y) * cmd.space + cmd.spaceCenter.y;
}
return pt;
}
function spacedLocationStringify(pt, data) {
var cmd = myDiagram.commandHandler;
if (!cmd._isUpdating) {
pt = pt.copy();
pt.x = (pt.x - cmd.spaceCenter.x) / cmd.space + cmd.spaceCenter.x;
if (cmd.isYSpaced) {
pt.y = (pt.y - cmd.spaceCenter.y) / cmd.space + cmd.spaceCenter.y;
}
return go.Point.stringify(pt);
} else {
return data.loc;
}
}
let self = this;
if (!template) template =
$(
go.Node, "Table", {
layerName: "Foreground",
locationSpot: go.Spot.Center,
locationObjectName: "theNode",
locationSpot: go.Spot.Center,
selectionObjectName: "theNode",
toolTip: this.getNodeToolTipTemplate(), // define a tooltip for each node that displays its information
contextMenu: $("ContextMenu"),
click: (e, _node) => {
let node = this.myDiagram.model.findNodeDataForKey(_node.data.key)
// Send the node's data over to the side panel
this.$scope.$emit('toggle-filter-panel', true);
this.$scope.$emit('side-panel-rollover', node)
},
contextClick: (evt, obj) => {
let panel = obj.part;
let node = obj.part.data;
let cm = obj.contextMenu;
console.warn("Context Menu",{evt, obj, panel, node, cm} )
// first clear out any existing ContextMenuButtons
while (cm.elements.count > 0) cm.removeAt(0);
// then add those menu items that you want
cm.add(
makeButton("Zoom to node", (e, obj) => {
let node = obj.part.adornedPart;
this.centerOnNode(node.key);
// this.myDiagram.scale < 2 ?
this.myDiagram.commandHandler.increaseZoom(3)
// this.myDiagram.commandHandler.decreaseZoom(3);
}),
)
cm.add(
makeButton("List View", (e, obj) => {
let node = obj.part.adornedPart;
appEvents.emit('show-modal', {
src: 'public/plugins/logic-view-panel/partials/list-view-modal.html',
modalClass: 'confirm-modal',
model: {
panel: this.panel,
ctrl: this,
node,
data: node.data
},
});
})
)
this.panel.mappedMetrics.nodes.action_menu.forEach(_menu => {
let menu = makeButton(_menu.label, (e, obj) => {
let node = obj.part.adornedPart;
let data = node.data;
// Get the params and values
let params = _menu.params.map(param => ({
[param]: data[param]
}))
.reduce((p, c, i, a) => {
let key = Object.keys(c)[0];
p["var-" + key] = c[key];
return p;
}, {});
// Add the current window of time to the action menu's params
params.from = self.timeSrv.timeRange().from.toDate().getTime();
params.to = self.timeSrv.timeRange().to.toDate().getTime();
let query_string = encodeQueryData(params);
let window;
window = this.panel.openLinksInNewWindow
? self.$window.open(`${_menu.url}?${query_string}`)
: self.$window.location.href = `${_menu.url}?${query_string}`.toString()
self.helper.console.warn(`Adding Context Menu ${_menu.label}`, {
params,
node,
data,
query_string
})
})
cm.add(menu)
})
function encodeQueryData(data) {
const ret = [];
for (let d in data)
ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
return ret.join('&');
}
},
},
$(
go.Panel, "Auto", {
row: 1,
column: 1,
name: "theNode",
// stretch: go.GraphObject.Fill,
desiredSize: new go.Size(150, 150),
defaultAlignment: go.Spot.Center,
},
$(
go.Shape, "Rectangle", {
fill: "rgba(255, 255, 255, 0)",
stroke: null,
strokeWidth: 0,
minSize: new go.Size(56, 56)
},
),
$(
go.Panel, "Auto", {
alignment: go.Spot.Center,
stretch: go.GraphObject.Fill,
},
$(
go.Shape, "RoundedRectangle", {
minSize: new go.Size(120, 120),
fill: "white",
stroke: "#0064a6",
strokeWidth: 2,
},
),
$(
go.TextBlock, {
_purpose: "Node Top Text Label",
alignment: go.Spot.Top,
margin: new go.Margin(5, 0)
},
new go.Binding('stroke', '', (data) => {
let color = this.panel.mappedMetrics.nodes.topTextColor || "black";
return color;
}),
new go.Binding("text", "", (data) => {
let text = this.panel.mappedMetrics.nodes.topTextMetric ?
data[this.panel.mappedMetrics.nodes.topTextMetric] :
data.key;
text = (text.constructor === Array) ?
text[text.length - 1].toString() :
text;
return text;
}),
),
(!this.panel.showIcons ?
$(go.Shape, "Ellipse", {
name: "OBJSHAPE",
// width: 10,
// height: 10,
desiredSize: new go.Size(60, 60),
alignment: go.Spot.Center
},
new go.Binding("fill", "", (node) => {
let node_value, valid_color, color;
// this.helper.console.log("Find Node", {node})
if (!node) return this.panel.defaultColor;
try {
if (this.panel.mappedMetrics.nodes.color) {
node_value = node[this.panel.mappedMetrics.nodes.color];
valid_color = typeof node_value !== 'undefined' || node_value !== null;
}
let the_color = get_color(node_value);
color = valid_color ? get_color(node_value) : this.panel.defaultColor;
} catch (e) {
this.helper.console.warn("Something went wrong getting color", e)
}
return color;
}),
new go.Binding("text", "key")
)
: // Ternary Operator REQUIRED
$(go.Shape, {
name: "OBJSHAPE",
// width: 18,
// height: 18,
desiredSize: new go.Size(60, 60),
stroke: "rgba(255,255,255,0)",
alignment: go.Spot.Center
},
new go.Binding("fill", "", (node) => {
let node_value, valid_color, color;
if (!node) return this.panel.defaultColor;
try {
if (this.panel.mappedMetrics.nodes.color) {
node_value = node[this.panel.mappedMetrics.nodes.color];
valid_color = typeof node_value !== 'undefined' || node_value !== null;
}
let the_color = get_color(node_value);
color = valid_color ? get_color(node_value) : this.panel.defaultColor;
} catch (e) {
this.helper.console.warn("Something went wrong getting color", e)
}
return color;
}),
new go.Binding("geometry", "", (data) => {
let default_shape = go.Geometry.parse(`M55.5,0C24.85,0,0,24.85,0,55.5C0,86.15,24.85,111,55.5,111c30.65,0,55.5-24.85,55.5-55.5
C111,24.85,86.15,0,55.5,0z`.toString(), true);
let icon_data, svg;
try {
icon_data = this.panel.mappedMetrics.nodes.icon.types
.find(type => type.value.toLowerCase() == data.type.toLowerCase());
// Parse the SVG data into a go Geometry
svg = go.Geometry.parse(this.SVG_Shapes[data.type].data, true);
// this.helper.console.warn("Getting SVG", {svg, data, shapes: this.SVG_Shapes, shape: this.SVG_Shapes[data.type].data})
} catch (e) {
this.helper.console.warn("Error figuring out geometry", {
data,
e
})
}
return this.SVG_Shapes[data.type].data !== "" ? svg : default_shape;
}),
// new go.Binding("text", "key")
)
), // End of ternary body
$(
go.TextBlock, {
_purpose: "Node Bottom Text Label",
alignment: go.Spot.Bottom,
margin: new go.Margin(5, 0)
},
new go.Binding('stroke', '', (data) => {
let color = this.panel.mappedMetrics.nodes.bottomTextColor || "black";
return color;
}),
new go.Binding('text', '', data => {
this.helper.console.warn("Choosing the last IP that matches the known `datapoints['ip']` metric")
let text = this.panel.mappedMetrics.nodes.bottomTextMetric ?
data[this.panel.mappedMetrics.nodes.bottomTextMetric] :
(data['ip'].constructor === Array) ?
data['ip'][data['ip'].length - 1] :
data['ip'];
text = (text.constructor === Array) ?
text[text.length - 1].toString() :
text;
return text;
}),
),
), // End of Auto Panel
),
// the Panel holding the left port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.leftArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "logical-interface", (data) => {
let index = 0;
// return an array of logical-interface names
let filtered = Array.isArray(data) ?
data
.map(_interface => ({
portId: _interface
}))
.filter((_interface, idx) => {
let res = idx % 4 === index ? _interface : null;
// idx % data.length-1 === index ? console.warn(`Interface: ${_interface} - IDX: ${idx}`, {res, data}) : null;
return res;
}) :
[{
portId: data
}];
// console.log(`filtering 'left' side interface index: ${index}`, {filtered, data: data.map(_interface => ({portId: _interface})), dataLength: data.length, filterLength: filtered.length})
return filtered;
}), {
_nodeSide: "left",
// alignment: go.Spot.Top,
row: 1,
column: 0,
itemTemplate: $(go.Panel, {
// margin: new go.Margin(0, 0),
fromSpot: go.Spot.Left,
toSpot: go.Spot.Left,
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
toolTip: $(go.Adornment, "Auto",
$(go.Shape, {
fill: "#FFF"
}),
$(go.TextBlock, {
margin: 4
},
new go.Binding("text", "", (d) => {
return d.portId;
}))
),
contextMenu: $("ContextMenu",
makeButton("TBA",
function (e, obj) {
let part = obj.part.adornedObject;
console.log(
`Clicked port: ${part.portId}`, {
e,
obj,
part,
data: part.data
}
);
}),
)
},
new go.Binding("portId", "portId", (data) => {
// console.warn("Port ID suppose to be here", data)
return data;
}),
$(go.Shape, "Rectangle", {
stroke: null,
strokeWidth: 0,
desiredSize: new go.Size(12, 12),
margin: new go.Margin(2, 0)
}, )
) // end itemTemplate
}
), // end Vertical Panel
$(go.Panel, "Horizontal",
new go.Binding("itemArray", "logical-interface", (data) => {
let index = 1;
// return an array of logical-interface names
let filtered = Array.isArray(data) ?
data
.map(_interface => ({
portId: _interface
}))
.filter((_interface, idx) => {
let res = idx % 4 === index ? _interface : null;
// idx % data.length-1 === index ? console.warn(`Interface: ${_interface} - IDX: ${idx}`, {res, data}) : null;
return res;
}) :
[{
portId: data
}];
// console.log(`filtering 'right' side interface index: ${index}`, {filtered, data: data.map(_interface => ({portId: _interface})), dataLength: data.length, filterLength: filtered.length})
return filtered;
}), {
_nodeSide: "top",
// alignment: go.Spot.Top,
row: 0,
column: 1,
itemTemplate: $(go.Panel, {
// margin: new go.Margin(0, 0),
fromSpot: go.Spot.Top,
toSpot: go.Spot.Top,
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
toolTip: $(go.Adornment, "Auto",
$(go.Shape, {
fill: "#FFF"
}),
$(go.TextBlock, {
margin: 4
},
new go.Binding("text", "", (d) => {
return d.portId;
}))
),
contextMenu: $("ContextMenu",
makeButton("TBA",
function (e, obj) {
let part = obj.part.adornedObject;
console.log(
`Clicked port: ${part.portId}`, {
e,
obj,
part,
data: part.data
}
);
}),
)
},
new go.Binding("portId", "portId", (data) => {
// console.warn("Port ID suppose to be here", data)
return data;
}),
$(go.Shape, "Rectangle", {
stroke: null,
strokeWidth: 0,
desiredSize: new go.Size(12, 12),
margin: new go.Margin(0, 2)
}, )
) // end itemTemplate
}
), // end Horizontal Panel
$(go.Panel, "Vertical",
new go.Binding("itemArray", "logical-interface", (data) => {
let index = 2;
// return an array of logical-interface names
let filtered = Array.isArray(data) ?
data
.map(_interface => ({
portId: _interface
}))
.filter((_interface, idx) => {
let res = idx % 4 === index ? _interface : null;
// idx % data.length-1 === index ? console.warn(`Interface: ${_interface} - IDX: ${idx}`, {res, data}) : null;
return res;
}) :
[{
portId: data
}];
// console.log(`filtering 'right' side interface index: ${index}`, {filtered, data: data.map(_interface => ({portId: _interface})), dataLength: data.length, filterLength: filtered.length})
return filtered;
}), {
_nodeSide: "right",
// alignment: go.Spot.Right,
row: 1,
column: 2,
itemTemplate: $(go.Panel, {
// margin: new go.Margin(0, 0),
fromSpot: go.Spot.Right,
toSpot: go.Spot.Right,
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
toolTip: $(go.Adornment, "Auto",
$(go.Shape, {
fill: "#FFF"
}),
$(go.TextBlock, {
margin: 4
},
new go.Binding("text", "", (d) => {
return d.portId;
}))
),
contextMenu: $("ContextMenu",
makeButton("TBA",
function (e, obj) {
let part = obj.part.adornedObject;
console.log(
`Clicked port: ${part.portId}`, {
e,
obj,
part,
data: part.data
}
);
}),
)
},
new go.Binding("portId", "portId", (data) => {
// console.warn("Port ID suppose to be here", data)
return data;
}),
$(go.Shape, "Rectangle", {
stroke: null,
strokeWidth: 0,
desiredSize: new go.Size(12, 12),
margin: new go.Margin(2, 0)
}, )
) // end itemTemplate
}
), // end Vertical Panel
$(go.Panel, "Horizontal",
new go.Binding("itemArray", "logical-interface", (data) => {
let index = 3;
// return an array of logical-interface names
let filtered = Array.isArray(data) ?
data
.map(_interface => ({
portId: _interface
}))
.filter((_interface, idx) => {
let res = idx % 4 === index ? _interface : null;
// idx % data.length-1 === index ? console.warn(`Interface: ${_interface} - IDX: ${idx}`, {res, data}) : null;
return res;
}) :
[{
portId: data
}];
// console.log(`filtering 'bottom' side interface index: ${index}`, {filtered, data: data.map(_interface => ({portId: _interface})), dataLength: data.length, filterLength: filtered.length})
return filtered;
}), {
_nodeSide: "bottom",
// alignment: go.Spot.Bottom,
row: 2,
column: 1,
itemTemplate: $(go.Panel, {
// margin: new go.Margin(0, 0),
fromSpot: go.Spot.Bottom,
toSpot: go.Spot.Bottom,
fromLinkable: true,
toLinkable: true,
cursor: "pointer",
toolTip: $(go.Adornment, "Auto",
$(go.Shape, {
fill: "#FFF"
}),
$(go.TextBlock, {
margin: 4
},
new go.Binding("text", "", (d) => {
return d.portId;
}))
),
contextMenu: $("ContextMenu",
makeButton("TBA",
function (e, obj) {
let part = obj.part.adornedObject;
console.log(
`Clicked port: ${part.portId}`, {
e,
obj,
part,
data: part.data
}
);
}),
)
},
new go.Binding("portId", "portId", (data) => {
// console.warn("Port ID suppose to be here", data)
return data;
}),
$(go.Shape, "Rectangle", {
stroke: null,
strokeWidth: 0,
desiredSize: new go.Size(12, 12),
margin: new go.Margin(0, 2)
}, )
) // end itemTemplate
}
), // end Horizontal Panel
);
this.nodeTemplate = template;
return this.nodeTemplate;
}
/**
* Create a GoJS link tooltip template for the diagram and set
* the instance variable to store it
* @param {Object} template a GoJS link tooltip template
* @param {Object} opts currently unused parameter
* @returns {Object} the GoJS link tooltip
*/
getLinkToolTipTemplate(template, opts) {
if (!template) template =
$(go.Adornment, "Auto",
$(go.Shape, {
fill: "#FFF"
}),
$(go.TextBlock, {
margin: 4
},
new go.Binding("text", "", (d) => {
let from = this.map_data.find(n => d.from === n.name)
let to = this.map_data.find(n => d.to === n.name)
let text = "";
// this.helper.console.log("Stuff", {
// from,
// to,
// text,
// d,
// map: this.map_data
// })
/*
From
Location: Some Name
Device: Some Name
Port:
To
Location: Some Name
Device: Some Name
Port:
*/
text += `From\n`
text += `Location: ${from.city} ${from.province}, ${from.country}\n`
text += `Device Type: ${from.type}\n`
text += `Name: ${from.name}\n`
text += `Port: ${d['from-port']}\n\n`
text += `To\n`
text += `Location: ${to.city} ${to.province}, ${to.country}\n`
text += `Device Type: ${to.type}\n`
text += `Name: ${to.name}\n`
text += `Port: ${d['to-port']}`
return text
}))
);
this.linkToolTipTemplate = template;
return this.linkToolTipTemplate;
}
/**
* Create a GoJS link template for the diagram and sets
* the instance variable to store it
* @param {Object} template a GoJS link template
* @param {Object} opts currently unused parameter
* @returns {Object} the GoJS link template
*/
getLinkTemplate(template, opts) {
let get_color = (_value) => {
let color;
try {
let value = Helpers.RollUp(_value)
let levels = this.panel.mappedMetrics.links.thresholds
.map((threshold, index) => ({
passed: threshold.min <= value && threshold.max > value,
index
}));
// this.helper.console.warn("Levels", levels);
if (levels.some(trial => trial.passed)) {
let index = levels.find(trial => trial.passed).index;
if (!index) color = this.panel.defaultColor;
color = this.panel.mappedMetrics.links.thresholds[index].color;
// this.helper.console.warn("Finding index ... hehe", {levels, level: levels[index], color, index})
} else
color = this.panel.defaultColor;
} catch (e) {
// this.helper.console.warn("Could not determine link color", e)
}
// this.helper.console.log("Color is: ", {color,value, level_one, level_two, level_three})
return color;
}
if (!template) template =
$(go.Link, {
layerName: "Background",
fromLinkableDuplicates: true,
fromEndSegmentLength: 200,
toEndSegmentLength: 200,
// fromShortLength: 10,
// toShortLength: 10,
toLinkableDuplicates: true,
toolTip: this.panel.showToolTips ? this.getLinkToolTipTemplate() : null,
// curve: go.Link.Bezier,
// curviness: 15,
// relinkableFrom: true,
// relinkableTo: true,
reshapable: true,
// resegmentable: true,
routing: this.panel.lineType ? go.Link.AvoidsNodes : 'none',
corner: 2,
contextMenu: $("ContextMenu",
makeButton("Zoom to node", (e, obj) => {
let node = obj.part.adornedPart;
this.centerOnNode(node.key);
// this.myDiagram.scale < 2 ?
this.myDiagram.commandHandler.increaseZoom(3)
// this.myDiagram.commandHandler.decreaseZoom(3);
}),
),
click: (evt, link) => {
let data = link.part.data;
let fromNode = this.myDiagram.model.findNodeDataForKey(data.from)
let toNode = this.myDiagram.model.findNodeDataForKey(data.to)
console.log("Side Panel Rollover", {link, from: fromNode, to: toNode, data})
// Send the node's data over to the side panel
this.$scope.$emit('toggle-filter-panel', true);
this.$scope.$emit('side-panel-rollover', {link: data, from: fromNode, to: toNode})
},
},
$(go.Shape, {
opacity: this.panel.showLinks ? 1 : 0,
},
new go.Binding("strokeWidth", "", (data) => {
if (!data) return this.helper.console.log("There wasn't any stroke width data", data);
let link_value, valid_width, width;
let node = this.map_data.find(node => node.name === data.from);
// Just return 1 if the link metric is disabled
if (!this.panel.showLinkWidthMetric || !node) return 1;
try {
if (this.panel.mappedMetrics.links) {
link_value = node[this.panel.mappedMetrics.links.width];
valid_width = typeof link_value !== null || typeof link_value !== 'undefined';
}
if (link_value === null || link_value === undefined)
return;
let sizes = this.panel.threshold.limits.links.width.size;
let widths = this.panel.threshold.limits.links.width.threshold;
let the_width = link_value.Scale(widths.min, widths.max, sizes.min, sizes.max);
width = valid_width ? the_width : 1;
} catch (e) {
this.helper.console.warn("Something went wrong getting color", e)
}
return width;
}),
new go.Binding("stroke", "", (data) => {
let link_value, valid_color, color;
let node = this.map_data.find(node => node.name === data.from);
if (!this.panel.showLinkColorMetric || !node) return this.panel.defaultColor;
try {
if (this.panel.mappedMetrics.links.color) {
link_value = node[this.panel.mappedMetrics.links.color];
valid_color = typeof link_value !== 'undefined' || link_value !== null;
}
let the_color = get_color(link_value);
color = valid_color ? get_color(link_value) : this.panel.defaultColor;
} catch (e) {
this.helper.console.warn("Something went wrong getting color", e)
}
return color;
})
),
$(go.Shape, // the arrowhead
{
toArrow: "Standard",
opacity: this.panel.showLinks ? 1 : 0,
// fill: "rgba(255,255,255,0)"
}),
// $(go.Shape, "Circle", {
// fill: "white",
// stroke: "black",
// desiredSize: new go.Size(20, 20),
// }),
// $("TreeExpanderButton",
// { alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Top },
// { visible: true }
// ),
// $("Button",
// { margin: 2},
// $(go.TextBlock, "Click me!", {
// font: "bold 14px Sans-Serif",
// margin: new go.Margin(10)
// })
// ),
// Good to go
$(
go.Panel,
'Auto', {
_purpose: 'to metric top label',
_isLinkLabel: true,
segmentOrientation: go.Link.OrientUpright,
segmentIndex: -1,
segmentFraction: 0.5,
segmentOffset: new go.Point(
this.panel.mappedMetrics.links.toTopXOffsetMetric || NaN,
this.panel.mappedMetrics.links.toTopYOffsetMetric || NaN
),
// padding: new go.Margin(0, 40),
segmentOrientation: go.Link.OrientUpright,
}, // marks this Panel as being a draggable label
$(go.Shape, 'Rectangle', {
stroke: "white",
strokeWidth: 2,
opacity: this.panel.showLinks && this.panel.show.toLabelSettings && this.panel.mappedMetrics.links.toTopMetric !== "" ? 1 : 0,
},
new go.Binding("fill", "", (data) => {
let color = this.panel.mappedMetrics.links.toTopBackgroundColor || "orange";
return color;
}),
),
$(go.TextBlock, {
margin: new go.Margin(2, 5),
_purpose: 'upper right label over the link line',
_isLinkLabel: true,
},
new go.Binding('stroke', '', (data) => {
let color = this.panel.mappedMetrics.links.toTopTextColor || "black";
return color;
}),
new go.Binding('text', '', (data) => {
if (!this.panel.show.toLabelSettings) return;
// get reference to the to node
let to = this.myDiagram.findNodeForKey(data['to']).data;
// Dynamic binding of text
let value = Helpers.RollUp(to[this.panel.mappedMetrics.links.toTopMetric]);
// this.helper.console.warn("Value of mapped 'toTopLabel' label: " + text);
return value.toString();
}),
)
),
$(
go.Panel,
'Auto', {
_purpose: 'to metric bottom label',
_isLinkLabel: true,
segmentOrientation: go.Link.OrientUpright,
segmentIndex: -1,
segmentFraction: 0.5,
segmentOffset: new go.Point(
this.panel.mappedMetrics.links.toBottomXOffsetMetric || NaN,
this.panel.mappedMetrics.links.toBottomYOffsetMetric || NaN
),
// padding: new go.Margin(0, 15),
// segmentOrientation: go.Link.OrientUpright,
}, // marks this Panel as being a draggable label
$(go.Shape, 'Rectangle', {
stroke: "white",
strokeWidth: 2,
opacity: this.panel.showLinks && this.panel.show.toLabelSettings && this.panel.mappedMetrics.links.toBottomMetric !== "" ? 1 : 0,
},
new go.Binding("fill", "", (data) => {
let color = this.panel.mappedMetrics.links.toBottomBackgroundColor || "orange";
return color;
}),
),
$(go.TextBlock, {
margin: new go.Margin(2, 5),
_purpose: 'upper right label over the link line',
_isLinkLabel: true,
},
new go.Binding('stroke', '', (data) => {
let color = this.panel.mappedMetrics.links.toBottomTextColor || "black";
return color;
}),
new go.Binding('text', '', (data) => {
if (!this.panel.show.toLabelSettings) return;
// get reference to the to node
let to = this.myDiagram.findNodeForKey(data['to']).data;
// Dynamic binding of text
let value = Helpers.RollUp(to[this.panel.mappedMetrics.links.toBottomMetric]);
// this.helper.console.warn("Value of mapped 'toBottomLabel' label: " + text);
return value.toString();
}),
)
),
$(
go.Panel,
'Auto', {
_purpose: 'to_port end',
_isLinkLabel: true,
segmentOrientation: go.Link.OrientUpright,
segmentIndex: -1,
segmentFraction: 0.5,
segmentOffset: new go.Point(0, 0),
}, // marks this Panel as being a draggable label
$(go.Shape, {
fill: "#0064a6",
opacity: this.panel.showLinks && this.panel.show.toLabelSettings ? 1 : 0,
stroke: "white",
strokeWidth: 2,
}),
$(
go.TextBlock, {
margin: new go.Margin(2, 5),
stroke: "white",
margin: new go.Margin(5),
},
new go.Binding('text', 'to-port', data => {
if (!this.panel.show.toLabelSettings) return;
// this.helper.console.log("Got data", data['to-port'])
return data;
})
)
),
// Good to go
$(
go.Panel,
'Auto', {
_purpose: 'from metric top label',
_isLinkLabel: true,
segmentOrientation: go.Link.OrientUpright,
segmentIndex: 0,
segmentFraction: 0.5,
segmentOffset: new go.Point(
this.panel.mappedMetrics.links.fromTopXOffsetMetric || NaN,
this.panel.mappedMetrics.links.fromTopYOffsetMetric || NaN
),
}, // marks this Panel as being a draggable label
$(go.Shape, 'Rectangle', {
stroke: "white",
strokeWidth: 2,
opacity: this.panel.showLinks && this.panel.show.fromLabelSettings && this.panel.mappedMetrics.links.fromTopMetric !== "" ? 1 : 0,
},
new go.Binding("fill", "", (data) => {
let color = this.panel.mappedMetrics.links.fromTopBackgroundColor || "orange";
return color;
}),
),
$(
go.TextBlock, {
margin: new go.Margin(2, 5),
_purpose: 'upper left label over the link line',
_isLinkLabel: true,
},
new go.Binding('stroke', '', (data) => {
let color = this.panel.mappedMetrics.links.fromTopTextColor || "black";
return color;
}),
new go.Binding('text', '', (data) => {
if (!this.panel.show.fromLabelSettings) return;
// get reference to the from node
let from = this.myDiagram.findNodeForKey(data['from']).data
// Dynamic binding of text
let value = Helpers.RollUp(from[this.panel.mappedMetrics.links.fromTopMetric]);
// this.helper.console.warn("Value of mapped 'fromTopLabel' label: " + text);
return value.toString();
})
)
),
$(
go.Panel,
'Auto', {
_purpose: 'from metric bottom label',
_isLinkLabel: true,
segmentOrientation: go.Link.OrientUpright,
segmentIndex: 0,
segmentFraction: 0.5,
segmentOffset: new go.Point(
this.panel.mappedMetrics.links.fromBottomXOffsetMetric || NaN,
this.panel.mappedMetrics.links.fromBottomYOffsetMetric || NaN
),
}, // marks this Panel as being a draggable label
$(go.Shape, 'Rectangle', {
stroke: "white",
strokeWidth: 2,
opacity: this.panel.showLinks && this.panel.show.fromLabelSettings && this.panel.mappedMetrics.links.fromBottomMetric !== "" ? 1 : 0,
},
new go.Binding("fill", "", (data) => {
let color = this.panel.mappedMetrics.links.fromBottomBackgroundColor || "orange";
return color;
}),
),
$(go.TextBlock, {
margin: new go.Margin(2, 5),
_purpose: 'upper left label below the link line',
_isLinkLabel: true,
},
new go.Binding('stroke', '', (data) => {
let color = this.panel.mappedMetrics.links.fromBottomTextColor || "black";
return color;
}),
new go.Binding('text', '', (data) => {
if (!this.panel.show.fromLabelSettings) return;
// get reference to the from node
let from = this.myDiagram.findNodeForKey(data['from']).data;
// Dynamic binding of text
let value = Helpers.RollUp(from[this.panel.mappedMetrics.links.fromBottomMetric]);
// this.helper.console.warn("Value of mapped 'fromBottomLabel' label: " + text);
return value.toString();
})
)
),
$(
go.Panel,
'Auto', {
_purpose: 'from_port end',
_isLinkLabel: true,
// defaultAlignment: go.Spot.Center,
segmentOrientation: go.Link.OrientUpright,
segmentIndex: 0,
segmentFraction:0.5,
segmentOffset: new go.Point(0, 0),
}, // marks this Panel as being a draggable label
$(go.Shape, {
fill: "#0064a6",
opacity: this.panel.showLinks && this.panel.show.fromLabelSettings ? 1 : 0,
stroke: "white",
strokeWidth: 2,
}),
$(
go.TextBlock, {
margin: new go.Margin(2, 5),
stroke: "white",
margin: new go.Margin(5),
},
new go.Binding('text', 'from-port', data => {
if (!this.panel.show.fromLabelSettings) return;
// this.helper.console.log("Got data", data['to-port'])
return data;
}),
)
),
);
this.linkTemplate = template;
return this.linkTemplate;
}
getDefaultLayout() {
let self = this;
let layout = this.diagramLayouts[this.panel.diagramLayout];
let diagram = $(go.Diagram, "myLogicView", {
"ModelChanged": function (e) {
// console.warn("Model Changed", e)
},
layout: layout,
padding: new go.Margin(5, 5, 25, 5), // to see the names of shapes on the bottom row
initialAutoScale: go.Diagram.Uniform, // zoom to make everything fit in
// initialContentAlignment: go.Spot.Center,
// // allowMove: false,
// allowZoom: true,
// "dragSelectingTool.isEnabled": false,
// // "animationManager.isEnabled": false,
// maxSelectionCount: 1, // no more than 1 element can be selected at a time
// initialDocumentSpot: go.Spot.Center,
// // initialScaleLevel: 0.1,
// minScale: 0.01,
// maxScale: 10,
// "LinkReshaped": function (e) {
// e.subject.routing = go.Link.Orthogonal;
// },
// initialAutoScale: go.Diagram.Uniform, // zoom to make everything fit in the viewport
// layout: layout,
// padding: new go.Margin(5, 5, 25, 5),
// other properties are set by the layout function, defined below
// "contextMenuTool.showContextMenu": function (cm, obj) {
// if (!self.panel.showActionMenu) return self.helper.console.warn("Context Menu's Disabled");
// // let data = obj.part.data;
// // first clear out any existing ContextMenuButtons
// while (cm.elements.count > 0) cm.removeAt(0);
// self.panel.mappedMetrics.nodes.action_menu.forEach(_menu => {
// let menu = makeButton(_menu.label, (e, obj) => {
// let node = obj.part.adornedPart;
// let data = node.data;
// // Get the params and values
// let params = _menu.params.map(param => ({
// [param]: data[param]
// }))
// .reduce((p, c, i, a) => {
// let key = Object.keys(c)[0];
// p["var-" + key] = c[key];
// return p;
// }, {});
// // Add the current window of time to the action menu's params
// params.from = self.timeSrv.timeRange().from.toDate().getTime();
// params.to = self.timeSrv.timeRange().to.toDate().getTime();
// let query_string = encodeQueryData(params);
// let window = self.$window.open(`${_menu.url}?${query_string}`)
// self.helper.console.warn(`Adding Context Menu ${_menu.label}`, {
// params,
// node,
// data,
// query_string
// })
// })
// cm.add(menu)
// })
// go.ContextMenuTool.prototype.showContextMenu.call(this, cm, obj);
// function encodeQueryData(data) {
// const ret = [];
// for (let d in data)
// ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
// return ret.join('&');
// }
// },
});
return diagram;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment