Files
T-DAS/Epost.TestToolsWeb/Content/code/modules/networkgraph.src.js
2023-01-13 15:30:20 +08:00

1282 lines
40 KiB
JavaScript

/**
* @license Highcharts JS v7.0.0 (2018-12-11)
* Force directed graph module
*
* (c) 2010-2018 Torstein Honsi
*
* License: www.highcharts.com/license
*/
'use strict';
(function (factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory;
} else if (typeof define === 'function' && define.amd) {
define(function () {
return factory;
});
} else {
factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
}
}(function (Highcharts) {
(function (H) {
H.NodesMixin = {
// Create a single node that holds information on incoming and outgoing
// links.
createNode: function (id) {
function findById(nodes, id) {
return H.find(nodes, function (node) {
return node.id === id;
});
}
var node = findById(this.nodes, id),
PointClass = this.pointClass,
options;
if (!node) {
options = this.options.nodes && findById(this.options.nodes, id);
node = (new PointClass()).init(
this,
H.extend({
className: 'highcharts-node',
isNode: true,
id: id,
y: 1 // Pass isNull test
}, options)
);
node.linksTo = [];
node.linksFrom = [];
node.formatPrefix = 'node';
node.name = node.name || node.options.id; // for use in formats
// Return the largest sum of either the incoming or outgoing links.
node.getSum = function () {
var sumTo = 0,
sumFrom = 0;
node.linksTo.forEach(function (link) {
sumTo += link.weight;
});
node.linksFrom.forEach(function (link) {
sumFrom += link.weight;
});
return Math.max(sumTo, sumFrom);
};
// Get the offset in weight values of a point/link.
node.offset = function (point, coll) {
var offset = 0;
for (var i = 0; i < node[coll].length; i++) {
if (node[coll][i] === point) {
return offset;
}
offset += node[coll][i].weight;
}
};
// Return true if the node has a shape, otherwise all links are
// outgoing.
node.hasShape = function () {
var outgoing = 0;
node.linksTo.forEach(function (link) {
if (link.outgoing) {
outgoing++;
}
});
return !node.linksTo.length || outgoing !== node.linksTo.length;
};
this.nodes.push(node);
}
return node;
}
};
}(Highcharts));
(function (H) {
/**
* Networkgraph series
*
* (c) 2010-2018 Paweł Fus
*
* License: www.highcharts.com/license
*/
var pick = H.pick;
H.layouts = {
'reingold-fruchterman': function (options) {
this.options = options;
this.nodes = [];
this.links = [];
this.series = [];
this.box = {
x: 0,
y: 0,
width: 0,
height: 0
};
this.setInitialRendering(true);
}
};
H.extend(
/**
* Reingold-Fruchterman algorithm from
* "Graph Drawing by Force-directed Placement" paper.
*/
H.layouts['reingold-fruchterman'].prototype,
{
run: function () {
var layout = this,
series = this.series,
options = this.options;
if (layout.initialRendering) {
layout.initPositions();
// Render elements in initial positions:
series.forEach(function (s) {
s.render();
});
}
// Algorithm:
function localLayout() {
// Barycenter forces:
layout.applyBarycenterForces();
// Repulsive forces:
layout.applyRepulsiveForces();
// Attractive forces:
layout.applyAttractiveForces();
// Limit to the plotting area and cool down:
layout.applyLimits(layout.temperature);
// Cool down:
layout.temperature -= layout.diffTemperature;
layout.prevSystemTemperature = layout.systemTemperature;
layout.systemTemperature = layout.getSystemTemperature();
if (options.enableSimulation) {
series.forEach(function (s) {
s.render();
});
if (
layout.maxIterations-- &&
!layout.isStable()
) {
layout.simulation = H.win.requestAnimationFrame(
localLayout
);
} else {
layout.simulation = false;
}
}
}
layout.setK();
layout.resetSimulation(options);
if (options.enableSimulation) {
// Animate it:
layout.simulation = H.win.requestAnimationFrame(localLayout);
} else {
// Synchronous rendering:
while (
layout.maxIterations-- &&
!layout.isStable()
) {
localLayout();
}
series.forEach(function (s) {
s.render();
});
}
},
stop: function () {
if (this.simulation) {
H.win.cancelAnimationFrame(this.simulation);
}
},
setArea: function (x, y, w, h) {
this.box = {
left: x,
top: y,
width: w,
height: h
};
},
setK: function () {
// Optimal distance between nodes,
// available space around the node:
this.k = this.options.linkLength ||
Math.pow(
this.box.width * this.box.height / this.nodes.length,
0.4
);
},
addNodes: function (nodes) {
nodes.forEach(function (node) {
if (this.nodes.indexOf(node) === -1) {
this.nodes.push(node);
}
}, this);
},
removeNode: function (node) {
var index = this.nodes.indexOf(node);
if (index !== -1) {
this.nodes.splice(index, 1);
}
},
removeLink: function (link) {
var index = this.links.indexOf(link);
if (index !== -1) {
this.links.splice(index, 1);
}
},
addLinks: function (links) {
links.forEach(function (link) {
if (this.links.indexOf(link) === -1) {
this.links.push(link);
}
}, this);
},
addSeries: function (series) {
if (this.series.indexOf(series) === -1) {
this.series.push(series);
}
},
clear: function () {
this.nodes.length = 0;
this.links.length = 0;
this.series.length = 0;
this.resetSimulation();
},
resetSimulation: function () {
this.forcedStop = false;
this.systemTemperature = 0;
this.setMaxIterations();
this.setTemperature();
this.setDiffTemperature();
},
setMaxIterations: function (maxIterations) {
this.maxIterations = pick(
maxIterations,
this.options.maxIterations
);
},
setTemperature: function () {
this.temperature = Math.sqrt(this.nodes.length);
},
setDiffTemperature: function () {
this.diffTemperature = this.temperature /
(this.options.maxIterations + 1);
},
setInitialRendering: function (enable) {
this.initialRendering = enable;
},
initPositions: function () {
var initialPositions = this.options.initialPositions;
if (H.isFunction(initialPositions)) {
initialPositions.call(this);
} else if (initialPositions === 'circle') {
this.setCircularPositions();
} else {
this.setRandomPositions();
}
},
setCircularPositions: function () {
var box = this.box,
nodes = this.nodes,
nodesLength = nodes.length + 1,
angle = 2 * Math.PI / nodesLength,
rootNodes = nodes.filter(function (node) {
return node.linksTo.length === 0;
}),
sortedNodes = [];
function addToNodes(node) {
node.linksFrom.forEach(function (link) {
sortedNodes.push(link.toNode);
addToNodes(link.toNode);
});
}
// Start with identified root nodes an sort the nodes by their
// hierarchy. In trees, this ensures that branches don't cross
// eachother.
rootNodes.forEach(function (rootNode) {
sortedNodes.push(rootNode);
addToNodes(rootNode);
});
// Cyclic tree, no root node found
if (!sortedNodes.length) {
sortedNodes = nodes;
// Dangling, cyclic trees
} else {
nodes.forEach(function (node) {
if (sortedNodes.indexOf(node) === -1) {
sortedNodes.push(node);
}
});
}
// Initial positions are laid out along a small circle, appearing
// as a cluster in the middle
sortedNodes.forEach(function (node, index) {
node.plotX = pick(
node.plotX,
box.width / 2 + Math.cos(index * angle)
);
node.plotY = pick(
node.plotY,
box.height / 2 + Math.sin(index * angle)
);
node.dispX = 0;
node.dispY = 0;
});
},
setRandomPositions: function () {
var box = this.box,
nodes = this.nodes,
nodesLength = nodes.length + 1;
// Return a repeatable, quasi-random number based on an integer
// input. For the initial positions
function unrandom(n) {
var rand = n * n / Math.PI;
rand = rand - Math.floor(rand);
return rand;
}
// Initial positions:
nodes.forEach(
function (node, index) {
node.plotX = pick(
node.plotX,
box.width * unrandom(index)
);
node.plotY = pick(
node.plotY,
box.height * unrandom(nodesLength + index)
);
node.dispX = 0;
node.dispY = 0;
}
);
},
applyBarycenterForces: function () {
var nodesLength = this.nodes.length,
gravitationalConstant = this.options.gravitationalConstant,
cx = 0,
cy = 0;
// Calculate center:
this.nodes.forEach(function (node) {
cx += node.plotX;
cy += node.plotY;
});
this.barycenter = {
x: cx,
y: cy
};
// Apply forces:
this.nodes.forEach(function (node) {
var degree = node.getDegree(),
phi = degree * (1 + degree / 2);
node.dispX = (cx / nodesLength - node.plotX) *
gravitationalConstant * phi;
node.dispY = (cy / nodesLength - node.plotY) *
gravitationalConstant * phi;
});
},
applyRepulsiveForces: function () {
var layout = this,
nodes = layout.nodes,
options = layout.options,
k = this.k;
nodes.forEach(function (node) {
nodes.forEach(function (repNode) {
var force,
distanceR,
distanceXY;
if (
// Node can not repulse itself:
node !== repNode &&
// Only close nodes affect each other:
/* layout.getDistR(node, repNode) < 2 * k && */
// Not dragged:
!node.fixedPosition
) {
distanceXY = layout.getDistXY(node, repNode);
distanceR = layout.vectorLength(distanceXY);
if (distanceR !== 0) {
force = options.repulsiveForce.call(
layout, distanceR, k
);
node.dispX += (distanceXY.x / distanceR) * force;
node.dispY += (distanceXY.y / distanceR) * force;
}
}
});
});
},
applyAttractiveForces: function () {
var layout = this,
links = layout.links,
options = this.options,
k = this.k;
links.forEach(function (link) {
var distanceXY = layout.getDistXY(
link.fromNode,
link.toNode
),
distanceR = layout.vectorLength(distanceXY),
force = options.attractiveForce.call(
layout, distanceR, k
);
if (distanceR !== 0) {
if (!link.fromNode.fixedPosition) {
link.fromNode.dispX -= (distanceXY.x / distanceR) *
force;
link.fromNode.dispY -= (distanceXY.y / distanceR) *
force;
}
if (!link.toNode.fixedPosition) {
link.toNode.dispX += (distanceXY.x / distanceR) *
force;
link.toNode.dispY += (distanceXY.y / distanceR) *
force;
}
}
});
},
applyLimits: function (temperature) {
var layout = this,
options = layout.options,
nodes = layout.nodes,
box = layout.box,
distanceR;
nodes.forEach(function (node) {
if (node.fixedPosition) {
return;
}
// Friction:
node.dispX += options.friction * node.dispX;
node.dispY += options.friction * node.dispY;
distanceR = node.temperature = layout.vectorLength({
x: node.dispX,
y: node.dispY
});
// Place nodes:
if (distanceR !== 0) {
node.plotX += node.dispX / distanceR *
Math.min(Math.abs(node.dispX), temperature);
node.plotY += node.dispY / distanceR *
Math.min(Math.abs(node.dispY), temperature);
}
/*
TO DO: Consider elastic collision instead of stopping.
o' means end position when hitting plotting area edge:
- "inealstic":
o
\
______
| o'
| \
| \
- "elastic"/"bounced":
o
\
______
| ^
| / \
|o' \
*/
// Limit X-coordinates:
node.plotX = Math.round(
Math.max(
Math.min(
node.plotX,
box.width
),
box.left
)
);
// Limit Y-coordinates:
node.plotY = Math.round(
Math.max(
Math.min(
node.plotY,
box.height
),
box.top
)
);
// Reset displacement:
node.dispX = 0;
node.dispY = 0;
});
},
isStable: function () {
return Math.abs(
this.systemTemperature -
this.prevSystemTemperature
) === 0;
},
getSystemTemperature: function () {
return this.nodes.reduce(function (value, node) {
return value + node.temperature;
}, 0);
},
vectorLength: function (vector) {
return Math.sqrt(vector.x * vector.x + vector.y * vector.y);
},
getDistR: function (nodeA, nodeB) {
var distance = this.getDistXY(nodeA, nodeB);
return Math.sqrt(
distance.x * distance.x +
distance.y * distance.y
);
},
getDistXY: function (nodeA, nodeB) {
var xDist = nodeA.plotX - nodeB.plotX,
yDist = nodeA.plotY - nodeB.plotY;
return {
x: xDist,
y: yDist,
absX: Math.abs(xDist),
absY: Math.abs(yDist)
};
}
}
);
}(Highcharts));
(function (H) {
/**
* Networkgraph series
*
* (c) 2010-2018 Paweł Fus
*
* License: www.highcharts.com/license
*/
var addEvent = H.addEvent,
defined = H.defined,
seriesType = H.seriesType,
seriesTypes = H.seriesTypes,
pick = H.pick,
Chart = H.Chart,
Point = H.Point,
Series = H.Series;
/**
* A networkgraph is a type of relationship chart, where connnections
* (links) attracts nodes (points) and other nodes repulse each other.
*
* @extends plotOptions.line
* @product highcharts
* @sample highcharts/demo/network-graph/
* Networkgraph
* @since 7.0.0
* @excluding boostThreshold, animation, animationLimit, connectEnds,
* connectNulls, dragDrop, getExtremesFromAll, label, linecap,
* negativeColor, pointInterval, pointIntervalUnit,
* pointPlacement, pointStart, softThreshold, stack, stacking,
* step, threshold, xAxis, yAxis, zoneAxis
* @optionparent plotOptions.networkgraph
*/
seriesType('networkgraph', 'line', {
marker: {
enabled: true
},
dataLabels: {
format: '{key}'
},
/**
* Link style options
*/
link: {
/**
* A name for the dash style to use for links.
*
* @type {String}
* @apioption plotOptions.networkgraph.link.dashStyle
* @defaults undefined
*/
/**
* Color of the link between two nodes.
*/
color: 'rgba(100, 100, 100, 0.5)',
/**
* Width (px) of the link between two nodes.
*/
width: 1
},
/**
* Flag to determine if nodes are draggable or not.
*/
draggable: true,
layoutAlgorithm: {
/**
* Ideal length (px) of the link between two nodes. When not defined,
* length is calculated as:
* `Math.pow(availableWidth * availableHeight / nodesLength, 0.4);`
*
* Note: Because of the algorithm specification, length of each link
* might be not exactly as specified.
*
* @type {number}
* @apioption series.networkgraph.layoutAlgorithm.linkLength
* @sample highcharts/series-networkgraph/styled-links/
* Numerical values
* @defaults undefined
*/
/**
* Initial layout algorithm for positioning nodes. Can be one of
* built-in options ("circle", "random") or a function where positions
* should be set on each node (`this.nodes`) as `node.plotX` and
* `node.plotY`
*
* @sample highcharts/series-networkgraph/initial-positions/
* Initial positions with callback
* @type {String|Function}
* @validvalue ["circle", "random"]
*/
initialPositions: 'circle',
/**
* Experimental. Enables live simulation of the algorithm
* implementation. All nodes are animated as the forces applies on
* them.
*
* @sample highcharts/demo/network-graph/
* Live simulation enabled
*/
enableSimulation: false,
/**
* Type of the algorithm used when positioning nodes.
*
* @validvalue ["reingold-fruchterman"]
*/
type: 'reingold-fruchterman',
/**
* Max number of iterations before algorithm will stop. In general,
* algorithm should find positions sooner, but when rendering huge
* number of nodes, it is recommended to increase this value as
* finding perfect graph positions can require more time.
*/
maxIterations: 1000,
/**
* Gravitational const used in the barycenter force of the algorithm.
*
* @sample highcharts/series-networkgraph/forces/
* Custom forces
*/
gravitationalConstant: 0.0625,
/**
* Friction applied on forces to prevent nodes rushing to fast to the
* desired positions.
*/
friction: -0.981,
/**
* Repulsive force applied on a node. Passed are two arguments:
* - `d` - which is current distance between two nodes
* - `k` - which is desired distance between two nodes
*
* @sample highcharts/series-networkgraph/forces/
* Custom forces
* @type {Function}
* @default function (d, k) { return k * k / d; }
*/
repulsiveForce: function (d, k) {
/*
basic, not recommended:
return k / d;
*/
/*
standard:
return k * k / d;
*/
/*
grid-variant:
return k * k / d * (2 * k - d > 0 ? 1 : 0);
*/
return k * k / d;
},
/**
* Attraction force applied on a node which is conected to another node
* by a link. Passed are two arguments:
* - `d` - which is current distance between two nodes
* - `k` - which is desired distance between two nodes
*
* @sample highcharts/series-networkgraph/forces/
* Custom forces
* @type {Function}
* @default function (d, k) { return k * k / d; }
*/
attractiveForce: function (d, k) {
/*
basic, not recommended:
return d / k;
*/
return d * d / k;
}
},
showInLegend: false
}, {
isNetworkgraph: true,
drawGraph: null,
isCartesian: false,
requireSorting: false,
directTouch: true,
noSharedTooltip: true,
trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
drawTracker: H.TrackerMixin.drawTrackerPoint,
// Animation is run in `series.simulation`.
animate: null,
/**
* Create a single node that holds information on incoming and outgoing
* links.
*/
createNode: H.NodesMixin.createNode,
/**
* Extend generatePoints by adding the nodes, which are Point objects
* but pushed to the this.nodes array.
*/
generatePoints: function () {
var nodeLookup = {},
chart = this.chart;
H.Series.prototype.generatePoints.call(this);
if (!this.nodes) {
this.nodes = []; // List of Point-like node items
}
this.colorCounter = 0;
// Reset links from previous run
this.nodes.forEach(function (node) {
node.linksFrom.length = 0;
node.linksTo.length = 0;
});
// Create the node list and set up links
this.points.forEach(function (point) {
if (defined(point.from)) {
if (!nodeLookup[point.from]) {
nodeLookup[point.from] = this.createNode(point.from);
}
nodeLookup[point.from].linksFrom.push(point);
point.fromNode = nodeLookup[point.from];
// Point color defaults to the fromNode's color
if (chart.styledMode) {
point.colorIndex = pick(
point.options.colorIndex,
nodeLookup[point.from].colorIndex
);
} else {
point.color =
point.options.color || nodeLookup[point.from].color;
}
}
if (defined(point.to)) {
if (!nodeLookup[point.to]) {
nodeLookup[point.to] = this.createNode(point.to);
}
nodeLookup[point.to].linksTo.push(point);
point.toNode = nodeLookup[point.to];
}
point.name = point.name || point.id; // for use in formats
}, this);
if (this.options.nodes) {
this.options.nodes.forEach(
function (nodeOptions) {
if (!nodeLookup[nodeOptions.id]) {
nodeLookup[nodeOptions.id] = this
.createNode(nodeOptions.id);
}
},
this
);
}
},
/**
* Run pre-translation by generating the nodeColumns.
*/
translate: function () {
if (!this.processedXData) {
this.processData();
}
this.generatePoints();
this.deferLayout();
this.nodes.forEach(function (node) {
// Draw the links from this node
node.isInside = true;
node.linksFrom.forEach(function (point) {
point.shapeType = 'path';
// Pass test in drawPoints
point.y = 1;
});
});
},
deferLayout: function () {
var layoutOptions = this.options.layoutAlgorithm,
graphLayoutsStorage = this.chart.graphLayoutsStorage,
chartOptions = this.chart.options.chart,
layout;
if (!this.visible) {
return;
}
if (!graphLayoutsStorage) {
this.chart.graphLayoutsStorage = graphLayoutsStorage = {};
}
layout = graphLayoutsStorage[layoutOptions.type];
if (!layout) {
layoutOptions.enableSimulation = !defined(chartOptions.forExport) ?
layoutOptions.enableSimulation :
!chartOptions.forExport;
graphLayoutsStorage[layoutOptions.type] = layout =
new H.layouts[layoutOptions.type](layoutOptions);
}
this.layout = layout;
layout.setArea(0, 0, this.chart.plotWidth, this.chart.plotHeight);
layout.addSeries(this);
layout.addNodes(this.nodes);
layout.addLinks(this.points);
},
/**
* Extend the render function to also render this.nodes together with
* the points.
*/
render: function () {
var points = this.points,
hoverPoint = this.chart.hoverPoint,
dataLabels = [];
// Render markers:
this.points = this.nodes;
seriesTypes.line.prototype.render.call(this);
this.points = points;
points.forEach(function (point) {
point.renderLink();
point.redrawLink();
});
if (hoverPoint && hoverPoint.series === this) {
this.redrawHalo(hoverPoint);
}
this.nodes.forEach(function (node) {
if (node.dataLabel) {
dataLabels.push(node.dataLabel);
}
});
H.Chart.prototype.hideOverlappingLabels(dataLabels);
},
/*
* Draggable mode:
*/
redrawHalo: function (point) {
if (point && this.halo) {
this.halo.attr({
d: point.haloPath(
this.options.states.hover.halo.size
)
});
}
},
onMouseDown: function (point, event) {
var normalizedEvent = this.chart.pointer.normalize(event);
point.fixedPosition = {
chartX: normalizedEvent.chartX,
chartY: normalizedEvent.chartY,
plotX: point.plotX,
plotY: point.plotY
};
},
onMouseMove: function (point, event) {
if (point.fixedPosition) {
var series = this,
chart = series.chart,
normalizedEvent = chart.pointer.normalize(event),
diffX = point.fixedPosition.chartX - normalizedEvent.chartX,
diffY = point.fixedPosition.chartY - normalizedEvent.chartY,
newPlotX,
newPlotY;
// At least 5px to apply change (avoids simple click):
if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
newPlotX = point.fixedPosition.plotX - diffX;
newPlotY = point.fixedPosition.plotY - diffY;
if (chart.isInsidePlot(newPlotX, newPlotY)) {
point.plotX = newPlotX;
point.plotY = newPlotY;
series.redrawHalo();
if (!series.layout.simulation) {
// Start new simulation:
if (!series.layout.enableSimulation) {
// Run only one iteration to speed things up:
series.layout.setMaxIterations(1);
}
// When dragging nodes, we don't need to calculate
// initial positions and rendering nodes:
series.layout.setInitialRendering(false);
series.layout.run();
// Restore defaults:
series.layout.setInitialRendering(true);
} else {
// Extend current simulation:
series.layout.resetSimulation();
}
}
}
}
},
onMouseUp: function (point) {
if (point.fixedPosition) {
this.layout.run();
delete point.fixedPosition;
}
},
destroy: function () {
this.nodes.forEach(function (node) {
node.destroy();
});
return Series.prototype.destroy.apply(this, arguments);
}
}, {
getDegree: function () {
var deg = this.isNode ? this.linksFrom.length + this.linksTo.length : 0;
return deg === 0 ? 1 : deg;
},
// Links:
getLinkAttribues: function () {
var linkOptions = this.series.options.link;
return {
'stroke-width': linkOptions.width,
stroke: linkOptions.color,
dashstyle: linkOptions.dashStyle
};
},
renderLink: function () {
if (!this.graphic) {
this.graphic = this.series.chart.renderer
.path(
this.getLinkPath(this.fromNode, this.toNode)
)
.attr(this.getLinkAttribues())
.add(this.series.group);
}
},
redrawLink: function () {
if (this.graphic) {
this.graphic.animate({
d: this.getLinkPath(this.fromNode, this.toNode)
});
}
},
getLinkPath: function (from, to) {
return [
'M',
from.plotX,
from.plotY,
'L',
to.plotX,
to.plotY
];
/*
IDEA: different link shapes?
return [
'M',
from.plotX,
from.plotY,
'Q',
(to.plotX + from.plotX) / 2,
(to.plotY + from.plotY) / 2 + 15,
to.plotX,
to.plotY
];*/
},
// Default utils:
destroy: function () {
if (this.isNode) {
this.linksFrom.forEach(
function (linkFrom) {
if (linkFrom.graphic) {
linkFrom.graphic = linkFrom.graphic.destroy();
}
}
);
}
return Point.prototype.destroy.apply(this, arguments);
}
});
addEvent(seriesTypes.networkgraph, 'updatedData', function () {
if (this.layout) {
this.layout.stop();
}
});
addEvent(seriesTypes.networkgraph.prototype.pointClass, 'remove', function () {
if (this.isNode && this.series.layout) {
this.series.layout.removeNode(this);
} else {
this.series.layout.removeLink(this);
}
});
/*
* Multiple series support:
*/
// Clear previous layouts
addEvent(Chart, 'predraw', function () {
if (this.graphLayoutsStorage) {
H.objectEach(
this.graphLayoutsStorage,
function (layout) {
layout.stop();
}
);
}
});
addEvent(Chart, 'render', function () {
if (this.graphLayoutsStorage) {
H.setAnimation(false, this);
H.objectEach(
this.graphLayoutsStorage,
function (layout) {
layout.run();
}
);
}
});
/*
* Draggable mode:
*/
addEvent(
seriesTypes.networkgraph.prototype.pointClass,
'mouseOver',
function () {
H.css(this.series.chart.container, { cursor: 'move' });
}
);
addEvent(
seriesTypes.networkgraph.prototype.pointClass,
'mouseOut',
function () {
H.css(this.series.chart.container, { cursor: 'default' });
}
);
addEvent(
Chart,
'load',
function () {
var chart = this,
unbinders = [];
unbinders.push(
addEvent(
chart.container,
'mousedown',
function (event) {
var point = chart.hoverPoint;
if (
point &&
point.series &&
point.series.isNetworkgraph &&
point.series.options.draggable
) {
point.series.onMouseDown(point, event);
unbinders.push(
addEvent(
chart.container,
'mousemove',
function (e) {
return point.series.onMouseMove(point, e);
}
)
);
unbinders.push(
addEvent(
chart.container.ownerDocument,
'mouseup',
function (e) {
return point.series.onMouseUp(point, e);
}
)
);
}
}
)
);
addEvent(chart, 'destroy', function () {
unbinders.forEach(function (unbind) {
unbind();
});
});
}
);
/**
* A `networkgraph` series. If the [type](#series.networkgraph.type) option is
* not specified, it is inherited from [chart.type](#chart.type).
*
* @type {Object}
* @extends series,plotOptions.networkgraph
* @excluding boostThreshold, animation, animationLimit, connectEnds,
* connectNulls, dragDrop, getExtremesFromAll, label, linecap,
* negativeColor, pointInterval, pointIntervalUnit,
* pointPlacement, pointStart, softThreshold, stack, stacking,
* step, threshold, xAxis, yAxis, zoneAxis
* @product highcharts
* @apioption series.networkgraph
*/
/**
* An array of data points for the series. For the `networkgraph` series type,
* points can be given in the following way:
*
* An array of objects with named values. The following snippet shows only a
* few settings, see the complete options set below. If the total number of
* data points exceeds the series'
* [turboThreshold](#series.area.turboThreshold), this option is not available.
*
* ```js
* data: [{
* from: 'Category1',
* to: 'Category2'
* }, {
* from: 'Category1',
* to: 'Category3'
* }]
* ```
*
* @type {Array<Object|Array|Number>}
* @extends series.line.data
* @excluding drilldown,marker,x,y,draDrop
* @sample {highcharts} highcharts/chart/reflow-true/
* Numerical values
* @sample {highcharts} highcharts/series/data-array-of-arrays/
* Arrays of numeric x and y
* @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
* Arrays of datetime x and y
* @sample {highcharts} highcharts/series/data-array-of-name-value/
* Arrays of point.name and y
* @sample {highcharts} highcharts/series/data-array-of-objects/
* Config objects
* @product highcharts
* @apioption series.networkgraph.data
*/
/**
* The node that the link runs from.
*
* @type {String}
* @product highcharts
* @apioption series.networkgraph.data.from
*/
/**
* The node that the link runs to.
*
* @type {String}
* @product highcharts
* @apioption series.networkgraph.data.to
*/
/**
* The weight of the link.
*
* @type {Number}
* @product highcharts
* @apioption series.networkgraph.data.weight
*/
}(Highcharts));
return (function () {
}());
}));