highcharts
Version:
JavaScript charting framework
1,473 lines (1,451 loc) • 115 kB
JavaScript
/**
* @license Highcharts JS v12.2.0 (2025-04-07)
* @module highcharts/modules/networkgraph
* @requires highcharts
*
* Force directed graph module
*
* (c) 2010-2025 Torstein Honsi
*
* License: www.highcharts.com/license
*/
import * as __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__ from "../highcharts.src.js";
/******/ // The require scope
/******/ var __webpack_require__ = {};
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/************************************************************************/
;// external ["../highcharts.src.js","default"]
const external_highcharts_src_js_default_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"];
var external_highcharts_src_js_default_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_namespaceObject);
;// external ["../highcharts.src.js","default","SVGElement"]
const external_highcharts_src_js_default_SVGElement_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"].SVGElement;
var external_highcharts_src_js_default_SVGElement_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_SVGElement_namespaceObject);
;// ./code/es-modules/Series/DragNodesComposition.js
/* *
*
* Networkgraph series
*
* (c) 2010-2025 Paweł Fus
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { composed } = (external_highcharts_src_js_default_default());
const { addEvent, pushUnique } = (external_highcharts_src_js_default_default());
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(ChartClass) {
if (pushUnique(composed, 'DragNodes')) {
addEvent(ChartClass, 'load', onChartLoad);
}
}
/**
* Draggable mode:
* @private
*/
function onChartLoad() {
const chart = this;
let mousedownUnbinder, mousemoveUnbinder, mouseupUnbinder, point;
if (chart.container) {
mousedownUnbinder = addEvent(chart.container, 'mousedown', (event) => {
if (mousemoveUnbinder) {
mousemoveUnbinder();
}
if (mouseupUnbinder) {
mouseupUnbinder();
}
point = chart.hoverPoint;
if (point &&
point.series &&
point.series.hasDraggableNodes &&
point.series.options.draggable) {
point.series.onMouseDown(point, event);
mousemoveUnbinder = addEvent(chart.container, 'mousemove', (e) => (point &&
point.series &&
point.series.onMouseMove(point, e)));
mouseupUnbinder = addEvent(chart.container.ownerDocument, 'mouseup', (e) => {
mousemoveUnbinder();
mouseupUnbinder();
return point &&
point.series &&
point.series.onMouseUp(point, e);
});
}
});
}
addEvent(chart, 'destroy', function () {
mousedownUnbinder();
});
}
/**
* Mouse down action, initializing drag&drop mode.
*
* @private
* @param {Highcharts.Point} point
* The point that event occurred.
* @param {Highcharts.PointerEventObject} event
* Browser event, before normalization.
*/
function onMouseDown(point, event) {
const normalizedEvent = this.chart.pointer?.normalize(event) || event;
point.fixedPosition = {
chartX: normalizedEvent.chartX,
chartY: normalizedEvent.chartY,
plotX: point.plotX,
plotY: point.plotY
};
point.inDragMode = true;
}
/**
* Mouse move action during drag&drop.
*
* @private
*
* @param {Highcharts.Point} point
* The point that event occurred.
* @param {global.Event} event
* Browser event, before normalization.
*/
function onMouseMove(point, event) {
if (point.fixedPosition && point.inDragMode) {
const series = this, chart = series.chart, normalizedEvent = chart.pointer?.normalize(event) || event, diffX = point.fixedPosition.chartX - normalizedEvent.chartX, diffY = point.fixedPosition.chartY - normalizedEvent.chartY, graphLayoutsLookup = chart.graphLayoutsLookup;
let 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;
point.hasDragged = true;
this.redrawHalo(point);
graphLayoutsLookup.forEach((layout) => {
layout.restartSimulation();
});
}
}
}
}
/**
* Mouse up action, finalizing drag&drop.
*
* @private
* @param {Highcharts.Point} point
* The point that event occurred.
*/
function onMouseUp(point) {
if (point.fixedPosition) {
if (point.hasDragged) {
if (this.layout.enableSimulation) {
this.layout.start();
}
else {
this.chart.redraw();
}
}
point.inDragMode = point.hasDragged = false;
if (!this.options.fixedDraggable) {
delete point.fixedPosition;
}
}
}
/**
* Redraw halo on mousemove during the drag&drop action.
*
* @private
* @param {Highcharts.Point} point
* The point that should show halo.
*/
function redrawHalo(point) {
if (point && this.halo) {
this.halo.attr({
d: point.haloPath(this.options.states.hover.halo.size)
});
}
}
/* *
*
* Default Export
*
* */
const DragNodesComposition = {
compose,
onMouseDown,
onMouseMove,
onMouseUp,
redrawHalo
};
/* harmony default export */ const Series_DragNodesComposition = (DragNodesComposition);
;// ./code/es-modules/Series/GraphLayoutComposition.js
/* *
*
* Networkgraph series
*
* (c) 2010-2025 Paweł Fus
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { setAnimation } = (external_highcharts_src_js_default_default());
const { composed: GraphLayoutComposition_composed } = (external_highcharts_src_js_default_default());
const { addEvent: GraphLayoutComposition_addEvent, pushUnique: GraphLayoutComposition_pushUnique } = (external_highcharts_src_js_default_default());
/* *
*
* Constants
*
* */
const integrations = {};
const layouts = {};
/* *
*
* Functions
*
* */
/**
* @private
*/
function GraphLayoutComposition_compose(ChartClass) {
if (GraphLayoutComposition_pushUnique(GraphLayoutComposition_composed, 'GraphLayout')) {
GraphLayoutComposition_addEvent(ChartClass, 'afterPrint', onChartAfterPrint);
GraphLayoutComposition_addEvent(ChartClass, 'beforePrint', onChartBeforePrint);
GraphLayoutComposition_addEvent(ChartClass, 'predraw', onChartPredraw);
GraphLayoutComposition_addEvent(ChartClass, 'render', onChartRender);
}
}
/**
* Re-enable simulation after print.
* @private
*/
function onChartAfterPrint() {
if (this.graphLayoutsLookup) {
this.graphLayoutsLookup.forEach((layout) => {
// Return to default simulation
layout.updateSimulation();
});
this.redraw();
}
}
/**
* Disable simulation before print if enabled.
* @private
*/
function onChartBeforePrint() {
if (this.graphLayoutsLookup) {
this.graphLayoutsLookup.forEach((layout) => {
layout.updateSimulation(false);
});
this.redraw();
}
}
/**
* Clear previous layouts.
* @private
*/
function onChartPredraw() {
if (this.graphLayoutsLookup) {
this.graphLayoutsLookup.forEach((layout) => {
layout.stop();
});
}
}
/**
* @private
*/
function onChartRender() {
let systemsStable, afterRender = false;
const layoutStep = (layout) => {
if (layout.maxIterations-- &&
isFinite(layout.temperature) &&
!layout.isStable() &&
!layout.enableSimulation) {
// Hook similar to build-in addEvent, but instead of
// creating whole events logic, use just a function.
// It's faster which is important for rAF code.
// Used e.g. in packed-bubble series for bubble radius
// calculations
if (layout.beforeStep) {
layout.beforeStep();
}
layout.step();
systemsStable = false;
afterRender = true;
}
};
if (this.graphLayoutsLookup) {
setAnimation(false, this);
// Start simulation
this.graphLayoutsLookup.forEach((layout) => layout.start());
// Just one sync step, to run different layouts similar to
// async mode.
while (!systemsStable) {
systemsStable = true;
this.graphLayoutsLookup.forEach(layoutStep);
}
if (afterRender) {
this.series.forEach((series) => {
if (series && series.layout) {
series.render();
}
});
}
}
}
/* *
*
* Default Export
*
* */
const GraphLayoutComposition = {
compose: GraphLayoutComposition_compose,
integrations,
layouts
};
/* harmony default export */ const Series_GraphLayoutComposition = (GraphLayoutComposition);
;// external ["../highcharts.src.js","default","SeriesRegistry"]
const external_highcharts_src_js_default_SeriesRegistry_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"].SeriesRegistry;
var external_highcharts_src_js_default_SeriesRegistry_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_SeriesRegistry_namespaceObject);
;// ./code/es-modules/Series/NodesComposition.js
/* *
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { series: { prototype: seriesProto, prototype: { pointClass: { prototype: pointProto } } } } = (external_highcharts_src_js_default_SeriesRegistry_default());
const { defined, extend, find, merge, pick } = (external_highcharts_src_js_default_default());
/* *
*
* Composition
*
* */
var NodesComposition;
(function (NodesComposition) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* @private
*/
function compose(PointClass, SeriesClass) {
const pointProto = PointClass.prototype, seriesProto = SeriesClass.prototype;
pointProto.setNodeState = setNodeState;
pointProto.setState = setNodeState;
pointProto.update = updateNode;
seriesProto.destroy = destroy;
seriesProto.setData = setData;
return SeriesClass;
}
NodesComposition.compose = compose;
/**
* Create a single node that holds information on incoming and outgoing
* links.
* @private
*/
function createNode(id) {
const PointClass = this.pointClass, findById = (nodes, id) => find(nodes, (node) => node.id === id);
let node = findById(this.nodes, id), options;
if (!node) {
options = this.options.nodes && findById(this.options.nodes, id);
const newNode = new PointClass(this, extend({
className: 'highcharts-node',
isNode: true,
id: id,
y: 1 // Pass isNull test
}, options));
newNode.linksTo = [];
newNode.linksFrom = [];
/**
* Return the largest sum of either the incoming or outgoing links.
* @private
*/
newNode.getSum = function () {
let sumTo = 0, sumFrom = 0;
newNode.linksTo.forEach((link) => {
sumTo += link.weight || 0;
});
newNode.linksFrom.forEach((link) => {
sumFrom += link.weight || 0;
});
return Math.max(sumTo, sumFrom);
};
/**
* Get the offset in weight values of a point/link.
* @private
*/
newNode.offset = function (point, coll) {
let offset = 0;
for (let i = 0; i < newNode[coll].length; i++) {
if (newNode[coll][i] === point) {
return offset;
}
offset += newNode[coll][i].weight;
}
};
// Return true if the node has a shape, otherwise all links are
// outgoing.
newNode.hasShape = function () {
let outgoing = 0;
newNode.linksTo.forEach((link) => {
if (link.outgoing) {
outgoing++;
}
});
return (!newNode.linksTo.length ||
outgoing !== newNode.linksTo.length);
};
newNode.index = this.nodes.push(newNode) - 1;
node = newNode;
}
node.formatPrefix = 'node';
// For use in formats
node.name = node.name || node.options.id || '';
// Mass is used in networkgraph:
node.mass = pick(
// Node:
node.options.mass, node.options.marker && node.options.marker.radius,
// Series:
this.options.marker && this.options.marker.radius,
// Default:
4);
return node;
}
NodesComposition.createNode = createNode;
/**
* Destroy all nodes and links.
* @private
*/
function destroy() {
// Nodes must also be destroyed (#8682, #9300)
this.data = []
.concat(this.points || [], this.nodes);
return seriesProto.destroy.apply(this, arguments);
}
NodesComposition.destroy = destroy;
/**
* Extend generatePoints by adding the nodes, which are Point objects but
* pushed to the this.nodes array.
* @private
*/
function generatePoints() {
const chart = this.chart, nodeLookup = {};
seriesProto.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((node) => {
node.linksFrom.length = 0;
node.linksTo.length = 0;
node.level = node.options.level;
});
// Create the node list and set up links
this.points.forEach((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);
// Store lookup table for later use
this.nodeLookup = nodeLookup;
}
NodesComposition.generatePoints = generatePoints;
/**
* Destroy all nodes on setting new data
* @private
*/
function setData() {
if (this.nodes) {
this.nodes.forEach((node) => {
node.destroy();
});
this.nodes.length = 0;
}
seriesProto.setData.apply(this, arguments);
}
/**
* When hovering node, highlight all connected links. When hovering a link,
* highlight all connected nodes.
* @private
*/
function setNodeState(state) {
const args = arguments, others = this.isNode ? this.linksTo.concat(this.linksFrom) :
[this.fromNode, this.toNode];
if (state !== 'select') {
others.forEach((linkOrNode) => {
if (linkOrNode && linkOrNode.series) {
pointProto.setState.apply(linkOrNode, args);
if (!linkOrNode.isNode) {
if (linkOrNode.fromNode.graphic) {
pointProto.setState.apply(linkOrNode.fromNode, args);
}
if (linkOrNode.toNode && linkOrNode.toNode.graphic) {
pointProto.setState.apply(linkOrNode.toNode, args);
}
}
}
});
}
pointProto.setState.apply(this, args);
}
NodesComposition.setNodeState = setNodeState;
/**
* When updating a node, don't update `series.options.data`, but
* `series.options.nodes`
* @private
*/
function updateNode(options, redraw, animation, runEvent) {
const nodes = this.series.options.nodes, data = this.series.options.data, dataLength = data?.length || 0, linkConfig = data?.[this.index];
pointProto.update.call(this, options, this.isNode ? false : redraw, // Hold the redraw for nodes
animation, runEvent);
if (this.isNode) {
// `this.index` refers to `series.nodes`, not `options.nodes` array
const nodeIndex = (nodes || [])
.reduce(// Array.findIndex needs a polyfill
(prevIndex, n, index) => (this.id === n.id ? index : prevIndex), -1),
// Merge old config with new config. New config is stored in
// options.data, because of default logic in point.update()
nodeConfig = merge(nodes && nodes[nodeIndex] || {}, data?.[this.index] || {});
// Restore link config
if (data) {
if (linkConfig) {
data[this.index] = linkConfig;
}
else {
// Remove node from config if there's more nodes than links
data.length = dataLength;
}
}
// Set node config
if (nodes) {
if (nodeIndex >= 0) {
nodes[nodeIndex] = nodeConfig;
}
else {
nodes.push(nodeConfig);
}
}
else {
this.series.options.nodes = [nodeConfig];
}
if (pick(redraw, true)) {
this.series.chart.redraw(animation);
}
}
}
NodesComposition.updateNode = updateNode;
})(NodesComposition || (NodesComposition = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ const Series_NodesComposition = (NodesComposition);
;// ./code/es-modules/Series/Networkgraph/NetworkgraphPoint.js
/* *
*
* Networkgraph series
*
* (c) 2010-2025 Paweł Fus
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
const { series: { prototype: NetworkgraphPoint_seriesProto, prototype: { pointClass: Point } } } = (external_highcharts_src_js_default_SeriesRegistry_default());
const { addEvent: NetworkgraphPoint_addEvent, css, defined: NetworkgraphPoint_defined, extend: NetworkgraphPoint_extend, pick: NetworkgraphPoint_pick } = (external_highcharts_src_js_default_default());
/* *
*
* Class
*
* */
class NetworkgraphPoint extends Point {
/* *
*
* Functions
*
* */
/**
* Destroy point. If it's a node, remove all links coming out of this
* node. Then remove point from the layout.
* @private
*/
destroy() {
if (this.isNode) {
this.linksFrom.concat(this.linksTo).forEach(function (link) {
// Removing multiple nodes at the same time
// will try to remove link between nodes twice
if (link.destroyElements) {
link.destroyElements();
}
});
}
this.series.layout.removeElementFromCollection(this, this.series.layout[this.isNode ? 'nodes' : 'links']);
return Point.prototype.destroy.apply(this, arguments);
}
/**
* Return degree of a node. If node has no connections, it still has
* deg=1.
* @private
*/
getDegree() {
const deg = this.isNode ?
this.linksFrom.length + this.linksTo.length :
0;
return deg === 0 ? 1 : deg;
}
/**
* Get presentational attributes of link connecting two nodes.
* @private
*/
getLinkAttributes() {
const linkOptions = this.series.options.link, pointOptions = this.options;
return {
'stroke-width': NetworkgraphPoint_pick(pointOptions.width, linkOptions.width),
stroke: (pointOptions.color || linkOptions.color),
dashstyle: (pointOptions.dashStyle || linkOptions.dashStyle),
opacity: NetworkgraphPoint_pick(pointOptions.opacity, linkOptions.opacity, 1)
};
}
/**
* Get link path connecting two nodes.
* @private
* @return {Array<Highcharts.SVGPathArray>}
* Path: `['M', x, y, 'L', x, y]`
*/
getLinkPath() {
let left = this.fromNode, right = this.toNode;
// Start always from left to the right node, to prevent rendering
// labels upside down
if (left.plotX > right.plotX) {
left = this.toNode;
right = this.fromNode;
}
return [
['M', left.plotX || 0, left.plotY || 0],
['L', right.plotX || 0, right.plotY || 0]
];
/*
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
];*/
}
/**
* Get mass fraction applied on two nodes connected to each other. By
* default, when mass is equal to `1`, mass fraction for both nodes
* equal to 0.5.
* @private
* @return {Highcharts.Dictionary<number>}
* For example `{ fromNode: 0.5, toNode: 0.5 }`
*/
getMass() {
const m1 = this.fromNode.mass, m2 = this.toNode.mass, sum = m1 + m2;
return {
fromNode: 1 - m1 / sum,
toNode: 1 - m2 / sum
};
}
/**
* Basic `point.init()` and additional styles applied when
* `series.draggable` is enabled.
* @private
*/
constructor(series, options, x) {
super(series, options, x);
if (this.series.options.draggable &&
!this.series.chart.styledMode) {
NetworkgraphPoint_addEvent(this, 'mouseOver', function () {
css(this.series.chart.container, { cursor: 'move' });
});
NetworkgraphPoint_addEvent(this, 'mouseOut', function () {
css(this.series.chart.container, { cursor: 'default' });
});
}
}
/**
* @private
*/
isValid() {
return !this.isNode || NetworkgraphPoint_defined(this.id);
}
/**
* Redraw link's path.
* @private
*/
redrawLink() {
const path = this.getLinkPath();
let attribs;
if (this.graphic) {
this.shapeArgs = {
d: path
};
if (!this.series.chart.styledMode) {
attribs = this.series.pointAttribs(this);
this.graphic.attr(attribs);
(this.dataLabels || []).forEach(function (label) {
if (label) {
label.attr({
opacity: attribs.opacity
});
}
});
}
this.graphic.animate(this.shapeArgs);
// Required for dataLabels
const start = path[0];
const end = path[1];
if (start[0] === 'M' && end[0] === 'L') {
this.plotX = (start[1] + end[1]) / 2;
this.plotY = (start[2] + end[2]) / 2;
}
}
}
/**
* Common method for removing points and nodes in networkgraph. To
* remove `link`, use `series.data[index].remove()`. To remove `node`
* with all connections, use `series.nodes[index].remove()`.
* @private
* @param {boolean} [redraw=true]
* Whether to redraw the chart or wait for an explicit call. When
* doing more operations on the chart, for example running
* `point.remove()` in a loop, it is best practice to set
* `redraw` to false and call `chart.redraw()` after.
* @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=false]
* Whether to apply animation, and optionally animation
* configuration.
*/
remove(redraw, animation) {
const point = this, series = point.series, nodesOptions = series.options.nodes || [];
let index, i = nodesOptions.length;
// For nodes, remove all connected links:
if (point.isNode) {
// Temporary disable series.points array, because
// Series.removePoint() modifies it
series.points = [];
// Remove link from all nodes collections:
[]
.concat(point.linksFrom)
.concat(point.linksTo)
.forEach(function (linkFromTo) {
// Incoming links
index = linkFromTo.fromNode.linksFrom.indexOf(linkFromTo);
if (index > -1) {
linkFromTo.fromNode.linksFrom.splice(index, 1);
}
// Outcoming links
index = linkFromTo.toNode.linksTo.indexOf(linkFromTo);
if (index > -1) {
linkFromTo.toNode.linksTo.splice(index, 1);
}
// Remove link from data/points collections
NetworkgraphPoint_seriesProto.removePoint.call(series, series.data.indexOf(linkFromTo), false, false);
});
// Restore points array, after links are removed
series.points = series.data.slice();
// Proceed with removing node. It's similar to
// Series.removePoint() method, but doesn't modify other arrays
series.nodes.splice(series.nodes.indexOf(point), 1);
// Remove node options from config
while (i--) {
if (nodesOptions[i].id === point.options.id) {
series.options.nodes.splice(i, 1);
break;
}
}
if (point) {
point.destroy();
}
// Run redraw if requested
series.isDirty = true;
series.isDirtyData = true;
if (redraw) {
series.chart.redraw(redraw);
}
}
else {
series.removePoint(series.data.indexOf(point), redraw, animation);
}
}
/**
* Render link and add it to the DOM.
* @private
*/
renderLink() {
let attribs;
if (!this.graphic) {
this.graphic = this.series.chart.renderer
.path(this.getLinkPath())
.addClass(this.getClassName(), true)
.add(this.series.group);
if (!this.series.chart.styledMode) {
attribs = this.series.pointAttribs(this);
this.graphic.attr(attribs);
(this.dataLabels || []).forEach(function (label) {
if (label) {
label.attr({
opacity: attribs.opacity
});
}
});
}
}
}
}
NetworkgraphPoint_extend(NetworkgraphPoint.prototype, {
setState: Series_NodesComposition.setNodeState
});
/* *
*
* Default Export
*
* */
/* harmony default export */ const Networkgraph_NetworkgraphPoint = (NetworkgraphPoint);
;// ./code/es-modules/Series/Networkgraph/NetworkgraphSeriesDefaults.js
/* *
*
* Networkgraph series
*
* (c) 2010-2025 Paweł Fus
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
/* *
*
* Constants
*
* */
/**
* 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,
* colorAxis, colorKey, connectNulls, cropThreshold, dragDrop,
* getExtremesFromAll, label, linecap, negativeColor,
* pointInterval, pointIntervalUnit, pointPlacement,
* pointStart, softThreshold, stack, stacking, step,
* threshold, xAxis, yAxis, zoneAxis, dataSorting,
* boostBlending
* @requires modules/networkgraph
* @optionparent plotOptions.networkgraph
*
* @private
*/
const NetworkgraphSeriesDefaults = {
stickyTracking: false,
/**
* @default true
* @extends plotOptions.series.inactiveOtherPoints
* @private
*/
inactiveOtherPoints: true,
marker: {
enabled: true,
states: {
/**
* The opposite state of a hover for a single point node.
* Applied to all not connected nodes to the hovered one.
*
* @declare Highcharts.PointStatesInactiveOptionsObject
*/
inactive: {
/**
* Opacity of inactive markers.
*/
opacity: 0.3,
/**
* Animation when not hovering over the node.
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
*/
animation: {
/** @internal */
duration: 50
}
}
}
},
states: {
/**
* The opposite state of a hover for a single point link. Applied
* to all links that are not coming from the hovered node.
*
* @declare Highcharts.SeriesStatesInactiveOptionsObject
*/
inactive: {
/**
* Opacity of inactive links.
*/
linkOpacity: 0.3,
/**
* Animation when not hovering over the node.
*
* @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
*/
animation: {
/** @internal */
duration: 50
}
}
},
/**
* @sample highcharts/series-networkgraph/link-datalabels
* Networkgraph with labels on links
* @sample highcharts/series-networkgraph/textpath-datalabels
* Networkgraph with labels around nodes
* @sample highcharts/series-networkgraph/link-datalabels
* Data labels moved into the nodes
* @sample highcharts/series-networkgraph/link-datalabels
* Data labels moved under the links
*
* @declare Highcharts.SeriesNetworkgraphDataLabelsOptionsObject
*
* @private
*/
dataLabels: {
/**
* The
* [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
* specifying what to show for _node_ in the networkgraph. In v7.0
* defaults to `{key}`, since v7.1 defaults to `undefined` and
* `formatter` is used instead.
*
* @type {string}
* @since 7.0.0
* @apioption plotOptions.networkgraph.dataLabels.format
*/
// eslint-disable-next-line valid-jsdoc
/**
* Callback JavaScript function to format the data label for a node.
* Note that if a `format` is defined, the format takes precedence
* and the formatter is ignored.
*
* @since 7.0.0
*/
formatter: function () {
return String(this.key ?? '');
},
/**
* The
* [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
* specifying what to show for _links_ in the networkgraph.
* (Default: `undefined`)
*
* @type {string}
* @since 7.1.0
* @apioption plotOptions.networkgraph.dataLabels.linkFormat
*/
// eslint-disable-next-line valid-jsdoc
/**
* Callback to format data labels for _links_ in the sankey diagram.
* The `linkFormat` option takes precedence over the
* `linkFormatter`.
*
* @since 7.1.0
*/
linkFormatter: function () {
return (this.fromNode.name +
'<br>' +
this.toNode.name);
},
/**
* Options for a _link_ label text which should follow link
* connection. Border and background are disabled for a label that
* follows a path.
*
* **Note:** Only SVG-based renderer supports this option. Setting
* `useHTML` to true will disable this option.
*
* @extends plotOptions.networkgraph.dataLabels.textPath
* @since 7.1.0
*/
linkTextPath: {
enabled: true
},
textPath: {
enabled: false
},
style: {
transition: 'opacity 2000ms'
},
defer: true,
animation: {
defer: 1000
}
},
/**
* Link style options
* @private
*/
link: {
/**
* A name for the dash style to use for links.
*
* @type {string}
* @apioption plotOptions.networkgraph.link.dashStyle
*/
/**
* Opacity of the link between two nodes.
*
* @type {number}
* @default 1
* @apioption plotOptions.networkgraph.link.opacity
*/
/**
* 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.
* @private
*/
draggable: true,
layoutAlgorithm: {
/**
* 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
*
* In `verlet` integration, defaults to:
* `function (d, k) { return (k - d) / d * (k > d ? 1 : 0) }`
*
* @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
*
* @sample highcharts/series-networkgraph/forces/
* Custom forces with Euler integration
* @sample highcharts/series-networkgraph/cuboids/
* Custom forces with Verlet integration
*
* @type {Function}
* @default function (d, k) { return k * k / d; }
* @apioption plotOptions.networkgraph.layoutAlgorithm.repulsiveForce
*/
/**
* 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
*
* In `verlet` integration, defaults to:
* `function (d, k) { return (k - d) / d; }`
*
* @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
*
* @sample highcharts/series-networkgraph/forces/
* Custom forces with Euler integration
* @sample highcharts/series-networkgraph/cuboids/
* Custom forces with Verlet integration
*
* @type {Function}
* @default function (d, k) { return k * k / d; }
* @apioption plotOptions.networkgraph.layoutAlgorithm.attractiveForce
*/
/**
* 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.
*
* @sample highcharts/series-networkgraph/styled-links/
* Numerical values
*
* @type {number}
* @apioption plotOptions.networkgraph.layoutAlgorithm.linkLength
*/
/**
* 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 {"circle"|"random"|Function}
*/
initialPositions: 'circle',
/**
* When `initialPositions` are set to 'circle',
* `initialPositionRadius` is a distance from the center of circle,
* in which nodes are created.
*
* @type {number}
* @default 1
* @since 7.1.0
*/
initialPositionRadius: 1,
/**
* 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,
/**
* Barnes-Hut approximation only.
* Deteremines when distance between cell and node is small enough
* to calculate forces. Value of `theta` is compared directly with
* quotient `s / d`, where `s` is the size of the cell, and `d` is
* distance between center of cell's mass and currently compared
* node.
*
* @see [layoutAlgorithm.approximation](#series.networkgraph.layoutAlgorithm.approximation)
*
* @since 7.1.0
*/
theta: 0.5,
/**
* Verlet integration only.
* Max speed that node can get in one iteration. In terms of
* simulation, it's a maximum translation (in pixels) that node can
* move (in both, x and y, dimensions). While `friction` is applied
* on all nodes, max speed is applied only for nodes that move very
* fast, for example small or disconnected ones.
*
* @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
* @see [layoutAlgorithm.friction](#series.networkgraph.layoutAlgorithm.friction)
*
* @since 7.1.0
*/
maxSpeed: 10,
/**
* Approximation used to calculate repulsive forces affecting nodes.
* By default, when calculating net force, nodes are compared
* against each other, which gives O(N^2) complexity. Using
* Barnes-Hut approximation, we decrease this to O(N log N), but the
* resulting graph will have different layout. Barnes-Hut
* approximation divides space into rectangles via quad tree, where
* forces exerted on nodes are calculated directly for nearby cells,
* and for all others, cells are treated as a separate node with
* center of mass.
*
* @see [layoutAlgorithm.theta](#series.networkgraph.layoutAlgorithm.theta)
*
* @sample highcharts/series-networkgraph/barnes-hut-approximation/
* A graph with Barnes-Hut approximation
*
* @type {string}
* @validvalue ["barnes-hut", "none"]
* @since 7.1.0
*/
approximation: 'none',
/**
* Type of the algorithm used when positioning nodes.
*
* @type {string}
* @validvalue ["reingold-fruchterman"]
*/
type: 'reingold-fruchterman',
/**
* Integration type. Available options are `'euler'` and `'verlet'`.
* Integration determines how forces are applied on particles. In
* Euler integration, force is applied direct as
* `newPosition += velocity;`.
* In Verlet integration, new position is based on a previous
* position without velocity:
* `newPosition += previousPosition - newPosition`.
*
* Note that different integrations give different results as forces
* are different.
*
* In Highcharts v7.0.x only `'euler'` integration was supported.
*
* @sample highcharts/series-networkgraph/integration-comparison/
* Comparison of Verlet and Euler integrations
*
* @type {string}
* @validvalue ["euler", "verlet"]
* @since 7.1.0
*/
integration: 'euler',
/**
* 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 with Euler integration
*/
gravitationalConstant: 0.0625,
/**
* Friction applied on forces to prevent nodes rushing to fast to
* the desired positions.
*/
friction: -0.981
},
showInLegend: false
};
/* *
*
* Default Export
*
* */
/* harmony default export */ const Networkgraph_NetworkgraphSeriesDefaults = (NetworkgraphSeriesDefaults);
/* *
*
* API Options
*
* */
/**
* Fires after the simulation is ended and the layout is stable.
*
* @type {Highcharts.NetworkgraphAfterSimulationCallbackFunction}
* @product highcharts
* @apioption series.networkgraph.events.afterSimulation
*/
/**
* A `networkgraph` series. If the [type](#series.networkgraph.type) option is
* not specified, it is inherited from [chart.type](#chart.type).
*
* @extends series,plotOptions.networkgraph
* @excluding boostThreshold, animation, animationLimit, connectEnds,
* connectNulls, cropThreshold, dragDrop, getExtremesFromAll, label,
* linecap, negativeColor, pointInterval, pointIntervalUnit,
* pointPlacement, pointStart, softThreshold, stack, stacking,
* step, threshold, xAxis, yAxis, zoneAxis, dataSorting,
* boostBlending
* @product highcharts
* @requires modules/networkgraph
* @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,dragDrop
* @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
*/
/**
* @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
* @product highcharts
* @apioption series.networkgraph.data.dataLabels
*/
/**
* 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
*/
/**
* A collection of options for the individual nodes. The nodes in a
* networkgraph diagram are auto-generated instances of `Highcharts.Point`,
* but options can be applied here and linked by the `id`.
*
* @sample highcharts/series-networkgraph/data-options/
* Networkgraph diagram with node options
*
* @type {Array<*>}
* @product highcharts
* @apioption series.networkgraph.nodes
*/
/**
* The id of the auto-generated node, referring to the `from` or `to` setting of
* the link.
*
* @type {string}
* @product highcharts
* @apioption series.networkgraph.nodes.id
*/
/**
* The color of the auto generated node.
*
* @type {Highcharts.ColorString}
* @product highcharts
* @apioption series.networkgraph.nodes.color
*/
/**
* The color index of the auto generated node, especially for use in styled
* mode.
*
* @type {number}
* @product highcharts
* @apioption series.networkgraph.nodes.colorIndex
*/
/**
* The name to display for the node in data labels and tooltips. Use this when
* the name is different from the `id`. Where the id must be unique for each
* node, this is not necessary for the name.
*
* @sample highcharts/series-networkgraph/data-options/
* Networkgraph diagram with node options
*
* @type {string}
* @product highcharts
* @apioption series.networkgraph.nodes.name
*/
/**
* Mass of the node. By default, each node has mass equal to it's marker radius
* . Mass is used to determine how two connected nodes should affect
* each other:
*
* Attractive force is multiplied by the ratio of two connected
* nodes; if a big node has weights twice as the small one, then the small one
* will move towards the big one twice faster than the big one to the small one
* .
*
* @sample highcharts/series-networkgraph/ragdoll/
* Mass determined by marker.radius
*
* @type {number}
* @product highcharts
* @apioption series.networkgraph.nodes.mass
*/
/**
* Options for the node markers.
*
* @extends plotOptions.networkgraph.marker
* @apioption series.networkgraph.nodes.marker
*/
/**
* Individual data label for each node. The options are the same as
* the ones for [series.networkgraph.dataLabels](#series.networkgraph.dataLabels).
*
* @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
*
* @apioption series.networkgraph.nodes.dataLabels
*/
''; // Adds doclets above to transpiled file
;// ./code/es-modules/Series/Networkgraph/EulerIntegration.js
/* *
*
* Networkgraph series
*
* (c) 2010-2025 Paweł Fus
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */