Created
August 24, 2019 18:42
-
-
Save AlphaNerd/86d0d3a1e3fa80c8df4b3d3e6b5470ab to your computer and use it in GitHub Desktop.
A gist for sharing sample Javascript code to prospective employers/clients
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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