| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092 |
- /* *
- *
- * 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 '../../Core/Globals.js';
- import NodesMixin from '../../Mixins/Nodes.js';
- import Point from '../../Core/Series/Point.js';
- import U from '../../Core/Utilities.js';
- var addEvent = U.addEvent, css = U.css, defined = U.defined, pick = U.pick, seriesType = U.seriesType;
- /**
- * 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 '../../Core/Options.js';
- import './Layouts.js';
- import './DraggableNodes.js';
- import '../../Core/Series/Series.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, 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
- */
- {
- 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|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 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|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.
- *
- * @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 +
- '<br>' +
- 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: NodesMixin.createNode,
- destroy: function () {
- if (this.layout) {
- this.layout.removeElementFromCollection(this, this.layout.series);
- }
- 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;
- 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: 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<number>}
- * 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<Highcharts.SVGPathArray>}
- * 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|Partial<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, 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,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<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, 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<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
- *
- * @apioption series.networkgraph.nodes.dataLabels
- */
- ''; // adds doclets above to transpiled file
|