123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- /**
- * (c) 2010-2019 Torstein Honsi
- *
- * License: www.highcharts.com/license
- */
- /**
- * Configuration hash for the data label and tooltip formatters.
- *
- * @interface Highcharts.PointLabelObject
- *//**
- * The point's current color.
- * @name Highcharts.PointLabelObject#color
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- *//**
- * The point's current color index, used in styled mode instead of `color`. The
- * color index is inserted in class names used for styling.
- * @name Highcharts.PointLabelObject#colorIndex
- * @type {number}
- *//**
- * The name of the related point.
- * @name Highcharts.PointLabelObject#key
- * @type {number|string}
- *//**
- * The percentage for related points in a stacked series or pies.
- * @name Highcharts.PointLabelObject#percentage
- * @type {number}
- *//**
- * The related point.
- * @name Highcharts.PointLabelObject#point
- * @type {Highcharts.Point}
- *//**
- * The related series.
- * @name Highcharts.PointLabelObject#series
- * @type {Highcharts.Series}
- *//**
- * The total of values in either a stack for stacked series, or a pie in a pie
- * series.
- * @name Highcharts.PointLabelObject#total
- * @type {number}
- *//**
- * For categorized axes this property holds the category name for the point. For
- * other axes it holds the X value.
- * @name Highcharts.PointLabelObject#x
- * @type {number|string}
- *//**
- * The y value of the point.
- * @name Highcharts.PointLabelObject#y
- * @type {number|undefined}
- */
- 'use strict';
- import Highcharts from './Globals.js';
- import './Utilities.js';
- var Point,
- H = Highcharts,
- extend = H.extend,
- erase = H.erase,
- fireEvent = H.fireEvent,
- format = H.format,
- isArray = H.isArray,
- isNumber = H.isNumber,
- pick = H.pick,
- uniqueKey = H.uniqueKey,
- defined = H.defined,
- removeEvent = H.removeEvent;
- /**
- * The Point object. The point objects are generated from the `series.data`
- * configuration objects or raw numbers. They can be accessed from the
- * `Series.points` array. Other ways to instantiate points are through {@link
- * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
- *
- * @class
- * @name Highcharts.Point
- */
- Highcharts.Point = Point = function () {};
- Highcharts.Point.prototype = {
- /**
- * Initialize the point. Called internally based on the `series.data`
- * option.
- *
- * @function Highcharts.Point#init
- *
- * @param {Highcharts.Series} series
- * The series object containing this point.
- *
- * @param {number|object|Array<number|string>|null} options
- * The data in either number, array or object format.
- *
- * @param {number} [x]
- * Optionally, the X value of the point.
- *
- * @return {Highcharts.Point}
- * The Point instance.
- *
- * @fires Highcharts.Point#event:afterInit
- */
- init: function (series, options, x) {
- var point = this,
- colors,
- optionsChart = series.chart.options.chart,
- colorCount = optionsChart.colorCount,
- styledMode = series.chart.styledMode,
- colorIndex;
- /**
- * The series object associated with the point.
- *
- * @name Highcharts.Point#series
- * @type {Highcharts.Series}
- */
- point.series = series;
- /**
- * The point's current color.
- *
- * @name Highcharts.Point#color
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- */
- if (!styledMode) {
- point.color = series.color; // #3445
- }
- point.applyOptions(options, x);
- // Add a unique ID to the point if none is assigned
- point.id = defined(point.id) ? point.id : uniqueKey();
- if (series.options.colorByPoint) {
- if (!styledMode) {
- colors = series.options.colors || series.chart.options.colors;
- point.color = point.color || colors[series.colorCounter];
- colorCount = colors.length;
- }
- colorIndex = series.colorCounter;
- series.colorCounter++;
- // loop back to zero
- if (series.colorCounter === colorCount) {
- series.colorCounter = 0;
- }
- } else {
- colorIndex = series.colorIndex;
- }
- /**
- * The point's current color index, used in styled mode instead of
- * `color`. The color index is inserted in class names used for styling.
- *
- * @name Highcharts.Point#colorIndex
- * @type {number}
- */
- point.colorIndex = pick(point.colorIndex, colorIndex);
- series.chart.pointCount++;
- fireEvent(point, 'afterInit');
- return point;
- },
- /**
- * Apply the options containing the x and y data and possible some extra
- * properties. Called on point init or from point.update.
- *
- * @private
- * @function Highcharts.Point#applyOptions
- *
- * @param {number|object|Array<number|string>|null} options
- * The point options as defined in series.data.
- *
- * @param {number} [x]
- * Optionally, the x value.
- *
- * @return {Highcharts.Point}
- * The Point instance.
- */
- applyOptions: function (options, x) {
- var point = this,
- series = point.series,
- pointValKey = series.options.pointValKey || series.pointValKey;
- options = Point.prototype.optionsToObject.call(this, options);
- // copy options directly to point
- extend(point, options);
- /**
- * The point's options as applied in the initial configuration, or
- * extended through `Point.update`.
- * @name Highcharts.Point#options
- * @type {object}
- */
- point.options = point.options ?
- extend(point.options, options) :
- options;
- // Since options are copied into the Point instance, some accidental
- // options must be shielded (#5681)
- if (options.group) {
- delete point.group;
- }
- if (options.dataLabels) {
- delete point.dataLabels;
- }
- /**
- * The y value of the point.
- * @name Highcharts.Point#y
- * @type {number|undefined}
- */
- // For higher dimension series types. For instance, for ranges, point.y
- // is mapped to point.low.
- if (pointValKey) {
- point.y = point[pointValKey];
- }
- point.isNull = pick(
- point.isValid && !point.isValid(),
- point.x === null || !isNumber(point.y, true)
- ); // #3571, check for NaN
- // The point is initially selected by options (#5777)
- if (point.selected) {
- point.state = 'select';
- }
- /**
- * The x value of the point.
- * @name Highcharts.Point#x
- * @type {number}
- */
- // If no x is set by now, get auto incremented value. All points must
- // have an x value, however the y value can be null to create a gap in
- // the series
- if (
- 'name' in point &&
- x === undefined &&
- series.xAxis &&
- series.xAxis.hasNames
- ) {
- point.x = series.xAxis.nameToX(point);
- }
- if (point.x === undefined && series) {
- if (x === undefined) {
- point.x = series.autoIncrement(point);
- } else {
- point.x = x;
- }
- }
- return point;
- },
- /**
- * Set a value in an object, on the property defined by key. The key
- * supports nested properties using dot notation. The function modifies the
- * input object and does not make a copy.
- *
- * @function Highcharts.Point#setNestedProperty
- *
- * @param {object} object
- * The object to set the value on.
- *
- * @param {*} value
- * The value to set.
- *
- * @param {string} key
- * Key to the property to set.
- *
- * @return {object}
- * The modified object.
- */
- setNestedProperty: function (object, value, key) {
- var nestedKeys = key.split('.');
- nestedKeys.reduce(function (result, key, i, arr) {
- var isLastKey = arr.length - 1 === i;
- result[key] = (
- isLastKey ?
- value :
- (H.isObject(result[key], true) ? result[key] : {})
- );
- return result[key];
- }, object);
- return object;
- },
- /**
- * Transform number or array configs into objects. Used internally to unify
- * the different configuration formats for points. For example, a simple
- * number `10` in a line series will be transformed to `{ y: 10 }`, and an
- * array config like `[1, 10]` in a scatter series will be transformed to
- * `{ x: 1, y: 10 }`.
- *
- * @function Highcharts.Point#optionsToObject
- *
- * @param {number|object|Array<number|string>|null} options
- * The input option.
- *
- * @return {object}
- * Transformed options.
- */
- optionsToObject: function (options) {
- var ret = {},
- series = this.series,
- keys = series.options.keys,
- pointArrayMap = keys || series.pointArrayMap || ['y'],
- valueCount = pointArrayMap.length,
- firstItemType,
- i = 0,
- j = 0;
- if (isNumber(options) || options === null) {
- ret[pointArrayMap[0]] = options;
- } else if (isArray(options)) {
- // with leading x value
- if (!keys && options.length > valueCount) {
- firstItemType = typeof options[0];
- if (firstItemType === 'string') {
- ret.name = options[0];
- } else if (firstItemType === 'number') {
- ret.x = options[0];
- }
- i++;
- }
- while (j < valueCount) {
- // Skip undefined positions for keys
- if (!keys || options[i] !== undefined) {
- if (pointArrayMap[j].indexOf('.') > 0) {
- // Handle nested keys, e.g. ['color.pattern.image']
- // Avoid function call unless necessary.
- H.Point.prototype.setNestedProperty(
- ret, options[i], pointArrayMap[j]
- );
- } else {
- ret[pointArrayMap[j]] = options[i];
- }
- }
- i++;
- j++;
- }
- } else if (typeof options === 'object') {
- ret = options;
- // This is the fastest way to detect if there are individual point
- // dataLabels that need to be considered in drawDataLabels. These
- // can only occur in object configs.
- if (options.dataLabels) {
- series._hasPointLabels = true;
- }
- // Same approach as above for markers
- if (options.marker) {
- series._hasPointMarkers = true;
- }
- }
- return ret;
- },
- /**
- * Get the CSS class names for individual points. Used internally where the
- * returned value is set on every point.
- *
- * @function Highcharts.Point#getClassName
- *
- * @return {string}
- * The class names.
- */
- getClassName: function () {
- return 'highcharts-point' +
- (this.selected ? ' highcharts-point-select' : '') +
- (this.negative ? ' highcharts-negative' : '') +
- (this.isNull ? ' highcharts-null-point' : '') +
- (this.colorIndex !== undefined ? ' highcharts-color-' +
- this.colorIndex : '') +
- (this.options.className ? ' ' + this.options.className : '') +
- (this.zone && this.zone.className ? ' ' +
- this.zone.className.replace('highcharts-negative', '') : '');
- },
- /**
- * In a series with `zones`, return the zone that the point belongs to.
- *
- * @function Highcharts.Point#getZone
- *
- * @return {Highcharts.PlotSeriesZonesOptions}
- * The zone item.
- */
- getZone: function () {
- var series = this.series,
- zones = series.zones,
- zoneAxis = series.zoneAxis || 'y',
- i = 0,
- zone;
- zone = zones[i];
- while (this[zoneAxis] >= zone.value) {
- zone = zones[++i];
- }
- // For resetting or reusing the point (#8100)
- if (!this.nonZonedColor) {
- this.nonZonedColor = this.color;
- }
- if (zone && zone.color && !this.options.color) {
- this.color = zone.color;
- } else {
- this.color = this.nonZonedColor;
- }
- return zone;
- },
- /**
- * Destroy a point to clear memory. Its reference still stays in
- * `series.data`.
- *
- * @private
- * @function Highcharts.Point#destroy
- */
- destroy: function () {
- var point = this,
- series = point.series,
- chart = series.chart,
- hoverPoints = chart.hoverPoints,
- prop;
- chart.pointCount--;
- if (hoverPoints) {
- point.setState();
- erase(hoverPoints, point);
- if (!hoverPoints.length) {
- chart.hoverPoints = null;
- }
- }
- if (point === chart.hoverPoint) {
- point.onMouseOut();
- }
- // Remove all events and elements
- if (point.graphic || point.dataLabel || point.dataLabels) {
- removeEvent(point);
- point.destroyElements();
- }
- if (point.legendItem) { // pies have legend items
- chart.legend.destroyItem(point);
- }
- for (prop in point) {
- point[prop] = null;
- }
- },
- /**
- * Destroy SVG elements associated with the point.
- *
- * @private
- * @function Highcharts.Point#destroyElements
- */
- destroyElements: function () {
- var point = this,
- props = [
- 'graphic',
- 'dataLabel',
- 'dataLabelUpper',
- 'connector',
- 'shadowGroup'
- ],
- prop,
- i = 6;
- while (i--) {
- prop = props[i];
- if (point[prop]) {
- point[prop] = point[prop].destroy();
- }
- }
- // Handle point.dataLabels and point.connectors
- if (point.dataLabels) {
- point.dataLabels.forEach(function (label) {
- if (label.element) {
- label.destroy();
- }
- });
- delete point.dataLabels;
- }
- if (point.connectors) {
- point.connectors.forEach(function (connector) {
- if (connector.element) {
- connector.destroy();
- }
- });
- delete point.connectors;
- }
- },
- /**
- * Return the configuration hash needed for the data label and tooltip
- * formatters.
- *
- * @function Highcharts.Point#getLabelConfig
- *
- * @return {Highcharts.PointLabelObject}
- * Abstract object used in formatters and formats.
- */
- getLabelConfig: function () {
- return {
- x: this.category,
- y: this.y,
- color: this.color,
- colorIndex: this.colorIndex,
- key: this.name || this.category,
- series: this.series,
- point: this,
- percentage: this.percentage,
- total: this.total || this.stackTotal
- };
- },
- /**
- * Extendable method for formatting each point's tooltip line.
- *
- * @function Highcharts.Point#tooltipFormatter
- *
- * @param {string} pointFormat
- * The point format.
- *
- * @return {string}
- * A string to be concatenated in to the common tooltip text.
- */
- tooltipFormatter: function (pointFormat) {
- // Insert options for valueDecimals, valuePrefix, and valueSuffix
- var series = this.series,
- seriesTooltipOptions = series.tooltipOptions,
- valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''),
- valuePrefix = seriesTooltipOptions.valuePrefix || '',
- valueSuffix = seriesTooltipOptions.valueSuffix || '';
- // Replace default point style with class name
- if (series.chart.styledMode) {
- pointFormat = series.chart.tooltip.styledModeFormat(pointFormat);
- }
- // Loop over the point array map and replace unformatted values with
- // sprintf formatting markup
- (series.pointArrayMap || ['y']).forEach(function (key) {
- key = '{point.' + key; // without the closing bracket
- if (valuePrefix || valueSuffix) {
- pointFormat = pointFormat.replace(
- RegExp(key + '}', 'g'),
- valuePrefix + key + '}' + valueSuffix
- );
- }
- pointFormat = pointFormat.replace(
- RegExp(key + '}', 'g'),
- key + ':,.' + valueDecimals + 'f}'
- );
- });
- return format(pointFormat, {
- point: this,
- series: this.series
- }, series.chart.time);
- },
- /**
- * Fire an event on the Point object.
- *
- * @private
- * @function Highcharts.Point#firePointEvent
- *
- * @param {string} eventType
- * Type of the event.
- *
- * @param {object} eventArgs
- * Additional event arguments.
- *
- * @param {Function} defaultFunction
- * Default event handler.
- *
- * @fires Highcharts.Point#event:*
- */
- firePointEvent: function (eventType, eventArgs, defaultFunction) {
- var point = this,
- series = this.series,
- seriesOptions = series.options;
- // load event handlers on demand to save time on mouseover/out
- if (
- seriesOptions.point.events[eventType] ||
- (
- point.options &&
- point.options.events &&
- point.options.events[eventType]
- )
- ) {
- this.importEvents();
- }
- // add default handler if in selection mode
- if (eventType === 'click' && seriesOptions.allowPointSelect) {
- defaultFunction = function (event) {
- // Control key is for Windows, meta (= Cmd key) for Mac, Shift
- // for Opera.
- if (point.select) { // #2911
- point.select(
- null,
- event.ctrlKey || event.metaKey || event.shiftKey
- );
- }
- };
- }
- fireEvent(this, eventType, eventArgs, defaultFunction);
- },
- /**
- * For categorized axes this property holds the category name for the
- * point. For other axes it holds the X value.
- *
- * @name Highcharts.Point#category
- * @type {number|string}
- */
- /**
- * The name of the point. The name can be given as the first position of the
- * point configuration array, or as a `name` property in the configuration:
- *
- * @example
- * // Array config
- * data: [
- * ['John', 1],
- * ['Jane', 2]
- * ]
- *
- * // Object config
- * data: [{
- * name: 'John',
- * y: 1
- * }, {
- * name: 'Jane',
- * y: 2
- * }]
- *
- * @name Highcharts.Point#name
- * @type {string}
- */
- /**
- * The percentage for points in a stacked series or pies.
- *
- * @name Highcharts.Point#percentage
- * @type {number}
- */
- /**
- * The total of values in either a stack for stacked series, or a pie in a
- * pie series.
- *
- * @name Highcharts.Point#total
- * @type {number}
- */
- /**
- * For certain series types, like pie charts, where individual points can
- * be shown or hidden.
- *
- * @name Highcharts.Point#visible
- * @type {boolean}
- */
- visible: true
- };
|