/* * * * Networkgraph series * * (c) 2010-2020 Paweł Fus * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import H from '../../parts/Globals.js'; /** * Formatter callback function. * * @callback Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction * * @param {Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject|Highcharts.PointLabelObject} this * Data label context to format * * @return {string} * Formatted data label text */ /** * Context for the formatter function. * * @interface Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject * @extends Highcharts.PointLabelObject * @since 7.0.0 */ /** * The color of the node. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#color * @type {Highcharts.ColorString} * @since 7.0.0 */ /** * The point (node) object. The node name, if defined, is available through * `this.point.name`. Arrays: `this.point.linksFrom` and `this.point.linksTo` * contains all nodes connected to this point. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#point * @type {Highcharts.Point} * @since 7.0.0 */ /** * The ID of the node. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#key * @type {string} * @since 7.0.0 */ ''; // detach doclets above import Point from '../../parts/Point.js'; import U from '../../parts/Utilities.js'; var addEvent = U.addEvent, css = U.css, defined = U.defined, pick = U.pick, seriesType = U.seriesType; import '../../parts/Options.js'; import '../../mixins/nodes.js'; import './layouts.js'; import './draggable-nodes.js'; var seriesTypes = H.seriesTypes, Series = H.Series, dragNodesMixin = H.dragNodesMixin; /** * @private * @class * @name Highcharts.seriesTypes.networkgraph * * @extends Highcharts.Series */ seriesType('networkgraph', 'line', /** * 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, dragDrop, * getExtremesFromAll, label, linecap, negativeColor, * pointInterval, pointIntervalUnit, pointPlacement, * pointStart, softThreshold, stack, stacking, step, * threshold, xAxis, yAxis, zoneAxis, dataSorting * @requires modules/networkgraph * @optionparent plotOptions.networkgraph */ { stickyTracking: false, /** * @ignore-option * @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|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 comming from the hovered node. * * @declare Highcharts.SeriesStatesInactiveOptionsObject */ inactive: { /** * Opacity of inactive links. */ linkOpacity: 0.3, /** * Animation when not hovering over the node. * * @type {boolean|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. * * @type {Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction} * @since 7.0.0 */ formatter: function () { return 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`. * * @type {Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction} * @since 7.1.0 */ linkFormatter: function () { return (this.point.fromNode.name + '
' + this.point.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' } }, /** * Link style options * @private */ link: { /** * A name for the dash style to use for links. * * @type {string} * @apioption plotOptions.networkgraph.link.dashStyle */ /** * 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 caculate 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 calculateing 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 * posittion 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 }, { /** * Array of internal forces. Each force should be later defined in * integrations.js. * @private */ forces: ['barycenter', 'repulsive', 'attractive'], hasDraggableNodes: true, drawGraph: null, isCartesian: false, requireSorting: false, directTouch: true, noSharedTooltip: true, pointArrayMap: ['from', 'to'], trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'], drawTracker: H.TrackerMixin.drawTrackerPoint, // Animation is run in `series.simulation`. animate: null, buildKDTree: H.noop, /** * Create a single node that holds information on incoming and outgoing * links. * @private */ createNode: H.NodesMixin.createNode, destroy: function () { this.layout.removeElementFromCollection(this, this.layout.series); H.NodesMixin.destroy.call(this); }, /* eslint-disable no-invalid-this, valid-jsdoc */ /** * Extend init with base event, which should stop simulation during * update. After data is updated, `chart.render` resumes the simulation. * @private */ init: function () { Series.prototype.init.apply(this, arguments); addEvent(this, 'updatedData', function () { if (this.layout) { this.layout.stop(); } }); return this; }, /** * Extend generatePoints by adding the nodes, which are Point objects * but pushed to the this.nodes array. * @private */ generatePoints: function () { var node, i; H.NodesMixin.generatePoints.apply(this, arguments); // In networkgraph, it's fine to define stanalone nodes, create // them: if (this.options.nodes) { this.options.nodes.forEach(function (nodeOptions) { if (!this.nodeLookup[nodeOptions.id]) { this.nodeLookup[nodeOptions.id] = this.createNode(nodeOptions.id); } }, this); } for (i = this.nodes.length - 1; i >= 0; i--) { node = this.nodes[i]; node.degree = node.getDegree(); node.radius = pick(node.marker && node.marker.radius, this.options.marker && this.options.marker.radius, 0); // If node exists, but it's not available in nodeLookup, // then it's leftover from previous runs (e.g. setData) if (!this.nodeLookup[node.id]) { node.remove(); } } this.data.forEach(function (link) { link.formatPrefix = 'link'; }); this.indexateNodes(); }, /** * In networkgraph, series.points refers to links, * but series.nodes refers to actual points. * @private */ getPointsCollection: function () { return this.nodes || []; }, /** * Set index for each node. Required for proper `node.update()`. * Note that links are indexated out of the box in `generatePoints()`. * * @private */ indexateNodes: function () { this.nodes.forEach(function (node, index) { node.index = index; }); }, /** * Extend the default marker attribs by using a non-rounded X position, * otherwise the nodes will jump from pixel to pixel which looks a bit * jaggy when approaching equilibrium. * @private */ markerAttribs: function (point, state) { var attribs = Series.prototype.markerAttribs.call(this, point, state); // series.render() is called before initial positions are set: if (!defined(point.plotY)) { attribs.y = 0; } attribs.x = (point.plotX || 0) - (attribs.width / 2 || 0); return attribs; }, /** * Run pre-translation and register nodes&links to the deffered layout. * @private */ 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; }); }); }, /** * Defer the layout. * Each series first registers all nodes and links, then layout * calculates all nodes positions and calls `series.render()` in every * simulation step. * * Note: * Animation is done through `requestAnimationFrame` directly, without * `Highcharts.animate()` use. * @private */ deferLayout: function () { var layoutOptions = this.options.layoutAlgorithm, graphLayoutsStorage = this.chart.graphLayoutsStorage, graphLayoutsLookup = this.chart.graphLayoutsLookup, chartOptions = this.chart.options.chart, layout; if (!this.visible) { return; } if (!graphLayoutsStorage) { this.chart.graphLayoutsStorage = graphLayoutsStorage = {}; this.chart.graphLayoutsLookup = graphLayoutsLookup = []; } layout = graphLayoutsStorage[layoutOptions.type]; if (!layout) { layoutOptions.enableSimulation = !defined(chartOptions.forExport) ? layoutOptions.enableSimulation : !chartOptions.forExport; graphLayoutsStorage[layoutOptions.type] = layout = new H.layouts[layoutOptions.type](); layout.init(layoutOptions); graphLayoutsLookup.splice(layout.index, 0, layout); } this.layout = layout; layout.setArea(0, 0, this.chart.plotWidth, this.chart.plotHeight); layout.addElementsToCollection([this], layout.series); layout.addElementsToCollection(this.nodes, layout.nodes); layout.addElementsToCollection(this.points, layout.links); }, /** * Extend the render function to also render this.nodes together with * the points. * @private */ render: function () { var series = this, points = series.points, hoverPoint = series.chart.hoverPoint, dataLabels = []; // Render markers: series.points = series.nodes; seriesTypes.line.prototype.render.call(this); series.points = points; points.forEach(function (point) { if (point.fromNode && point.toNode) { point.renderLink(); point.redrawLink(); } }); if (hoverPoint && hoverPoint.series === series) { series.redrawHalo(hoverPoint); } if (series.chart.hasRendered && !series.options.dataLabels.allowOverlap) { series.nodes.concat(series.points).forEach(function (node) { if (node.dataLabel) { dataLabels.push(node.dataLabel); } }); series.chart.hideOverlappingLabels(dataLabels); } }, // Networkgraph has two separate collecions of nodes and lines, render // dataLabels for both sets: drawDataLabels: function () { var textPath = this.options.dataLabels.textPath; // Render node labels: Series.prototype.drawDataLabels.apply(this, arguments); // Render link labels: this.points = this.data; this.options.dataLabels.textPath = this.options.dataLabels.linkTextPath; Series.prototype.drawDataLabels.apply(this, arguments); // Restore nodes this.points = this.nodes; this.options.dataLabels.textPath = textPath; }, // Return the presentational attributes. pointAttribs: function (point, state) { // By default, only `selected` state is passed on var pointState = state || point && point.state || 'normal', attribs = Series.prototype.pointAttribs.call(this, point, pointState), stateOptions = this.options.states[pointState]; if (point && !point.isNode) { attribs = point.getLinkAttributes(); // For link, get prefixed names: if (stateOptions) { attribs = { // TO DO: API? stroke: stateOptions.linkColor || attribs.stroke, dashstyle: (stateOptions.linkDashStyle || attribs.dashstyle), opacity: pick(stateOptions.linkOpacity, attribs.opacity), 'stroke-width': stateOptions.linkColor || attribs['stroke-width'] }; } } return attribs; }, // Draggable mode: /** * Redraw halo on mousemove during the drag&drop action. * @private * @param {Highcharts.Point} point The point that should show halo. */ redrawHalo: dragNodesMixin.redrawHalo, /** * Mouse down action, initializing drag&drop mode. * @private * @param {global.Event} event Browser event, before normalization. * @param {Highcharts.Point} point The point that event occured. */ onMouseDown: dragNodesMixin.onMouseDown, /** * Mouse move action during drag&drop. * @private * @param {global.Event} event Browser event, before normalization. * @param {Highcharts.Point} point The point that event occured. */ onMouseMove: dragNodesMixin.onMouseMove, /** * Mouse up action, finalizing drag&drop. * @private * @param {Highcharts.Point} point The point that event occured. */ onMouseUp: dragNodesMixin.onMouseUp, /** * When state should be passed down to all points, concat nodes and * links and apply this state to all of them. * @private */ setState: function (state, inherit) { if (inherit) { this.points = this.nodes.concat(this.data); Series.prototype.setState.apply(this, arguments); this.points = this.data; } else { Series.prototype.setState.apply(this, arguments); } // If simulation is done, re-render points with new states: if (!this.layout.simulation && !state) { this.render(); } } }, { setState: H.NodesMixin.setNodeState, /** * Basic `point.init()` and additional styles applied when * `series.draggable` is enabled. * @private */ init: function () { Point.prototype.init.apply(this, arguments); if (this.series.options.draggable && !this.series.chart.styledMode) { addEvent(this, 'mouseOver', function () { css(this.series.chart.container, { cursor: 'move' }); }); addEvent(this, 'mouseOut', function () { css(this.series.chart.container, { cursor: 'default' }); }); } return this; }, /** * Return degree of a node. If node has no connections, it still has * deg=1. * @private * @return {number} */ getDegree: function () { var deg = this.isNode ? this.linksFrom.length + this.linksTo.length : 0; return deg === 0 ? 1 : deg; }, // Links: /** * Get presentational attributes of link connecting two nodes. * @private * @return {Highcharts.SVGAttributes} */ getLinkAttributes: function () { var linkOptions = this.series.options.link, pointOptions = this.options; return { 'stroke-width': pick(pointOptions.width, linkOptions.width), stroke: (pointOptions.color || linkOptions.color), dashstyle: (pointOptions.dashStyle || linkOptions.dashStyle), opacity: pick(pointOptions.opacity, linkOptions.opacity, 1) }; }, /** * Render link and add it to the DOM. * @private */ renderLink: function () { var attribs; if (!this.graphic) { this.graphic = this.series.chart.renderer .path(this.getLinkPath()) .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 }); } }); } } }, /** * Redraw link's path. * @private */ redrawLink: function () { var path = this.getLinkPath(), 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 var start = path[0]; var end = path[1]; if (start[0] === 'M' && end[0] === 'L') { this.plotX = (start[1] + end[1]) / 2; this.plotY = (start[2] + end[2]) / 2; } } }, /** * 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} * For example `{ fromNode: 0.5, toNode: 0.5 }` */ getMass: function () { var m1 = this.fromNode.mass, m2 = this.toNode.mass, sum = m1 + m2; return { fromNode: 1 - m1 / sum, toNode: 1 - m2 / sum }; }, /** * Get link path connecting two nodes. * @private * @return {Array} * Path: `['M', x, y, 'L', x, y]` */ getLinkPath: function () { var 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 ];*/ }, isValid: function () { return !this.isNode || defined(this.id); }, /** * 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|Highcharts.AnimationOptionsObject} [animation=false] * Whether to apply animation, and optionally animation * configuration. * @return {void} */ remove: function (redraw, animation) { var point = this, series = point.series, nodesOptions = series.options.nodes || [], 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 Series.prototype.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); } }, /** * Destroy point. If it's a node, remove all links coming out of this * node. Then remove point from the layout. * @private * @return {void} */ destroy: function () { 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); } }); /** * 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, dragDrop, getExtremesFromAll, label, linecap, * negativeColor, pointInterval, pointIntervalUnit, * pointPlacement, pointStart, softThreshold, stack, stacking, * step, threshold, xAxis, yAxis, zoneAxis, dataSorting * @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} * @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 */ /** * @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array} * @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, refering 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 */ /** * 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} * * @apioption series.networkgraph.nodes.dataLabels */ ''; // adds doclets above to transpiled file