Point.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. /* *
  2. *
  3. * (c) 2010-2020 Torstein Honsi
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8. *
  9. * */
  10. 'use strict';
  11. import H from '../Globals.js';
  12. import U from '../Utilities.js';
  13. var animObject = U.animObject, defined = U.defined, erase = U.erase, extend = U.extend, fireEvent = U.fireEvent, format = U.format, getNestedProperty = U.getNestedProperty, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, syncTimeout = U.syncTimeout, pick = U.pick, removeEvent = U.removeEvent, uniqueKey = U.uniqueKey;
  14. /**
  15. * Function callback when a series point is clicked. Return false to cancel the
  16. * action.
  17. *
  18. * @callback Highcharts.PointClickCallbackFunction
  19. *
  20. * @param {Highcharts.Point} this
  21. * The point where the event occured.
  22. *
  23. * @param {Highcharts.PointClickEventObject} event
  24. * Event arguments.
  25. */
  26. /**
  27. * Common information for a click event on a series point.
  28. *
  29. * @interface Highcharts.PointClickEventObject
  30. * @extends Highcharts.PointerEventObject
  31. */ /**
  32. * Clicked point.
  33. * @name Highcharts.PointClickEventObject#point
  34. * @type {Highcharts.Point}
  35. */
  36. /**
  37. * Configuration hash for the data label and tooltip formatters.
  38. *
  39. * @interface Highcharts.PointLabelObject
  40. */ /**
  41. * The point's current color.
  42. * @name Highcharts.PointLabelObject#color
  43. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  44. */ /**
  45. * The point's current color index, used in styled mode instead of `color`. The
  46. * color index is inserted in class names used for styling.
  47. * @name Highcharts.PointLabelObject#colorIndex
  48. * @type {number}
  49. */ /**
  50. * The name of the related point.
  51. * @name Highcharts.PointLabelObject#key
  52. * @type {string|undefined}
  53. */ /**
  54. * The percentage for related points in a stacked series or pies.
  55. * @name Highcharts.PointLabelObject#percentage
  56. * @type {number}
  57. */ /**
  58. * The related point. The point name, if defined, is available through
  59. * `this.point.name`.
  60. * @name Highcharts.PointLabelObject#point
  61. * @type {Highcharts.Point}
  62. */ /**
  63. * The related series. The series name is available through `this.series.name`.
  64. * @name Highcharts.PointLabelObject#series
  65. * @type {Highcharts.Series}
  66. */ /**
  67. * The total of values in either a stack for stacked series, or a pie in a pie
  68. * series.
  69. * @name Highcharts.PointLabelObject#total
  70. * @type {number|undefined}
  71. */ /**
  72. * For categorized axes this property holds the category name for the point. For
  73. * other axes it holds the X value.
  74. * @name Highcharts.PointLabelObject#x
  75. * @type {number|string|undefined}
  76. */ /**
  77. * The y value of the point.
  78. * @name Highcharts.PointLabelObject#y
  79. * @type {number|undefined}
  80. */
  81. /**
  82. * Gets fired when the mouse leaves the area close to the point.
  83. *
  84. * @callback Highcharts.PointMouseOutCallbackFunction
  85. *
  86. * @param {Highcharts.Point} this
  87. * Point where the event occured.
  88. *
  89. * @param {global.PointerEvent} event
  90. * Event that occured.
  91. */
  92. /**
  93. * Gets fired when the mouse enters the area close to the point.
  94. *
  95. * @callback Highcharts.PointMouseOverCallbackFunction
  96. *
  97. * @param {Highcharts.Point} this
  98. * Point where the event occured.
  99. *
  100. * @param {global.Event} event
  101. * Event that occured.
  102. */
  103. /**
  104. * The generic point options for all series.
  105. *
  106. * In TypeScript you have to extend `PointOptionsObject` with an additional
  107. * declaration to allow custom data options:
  108. *
  109. * ```
  110. * declare interface PointOptionsObject {
  111. * customProperty: string;
  112. * }
  113. * ```
  114. *
  115. * @interface Highcharts.PointOptionsObject
  116. */
  117. /**
  118. * Possible option types for a data point.
  119. *
  120. * @typedef {number|string|Array<(number|string)>|Highcharts.PointOptionsObject|null} Highcharts.PointOptionsType
  121. */
  122. /**
  123. * Gets fired when the point is removed using the `.remove()` method.
  124. *
  125. * @callback Highcharts.PointRemoveCallbackFunction
  126. *
  127. * @param {Highcharts.Point} this
  128. * Point where the event occured.
  129. *
  130. * @param {global.Event} event
  131. * Event that occured.
  132. */
  133. /**
  134. * Possible key values for the point state options.
  135. *
  136. * @typedef {"hover"|"inactive"|"normal"|"select"} Highcharts.PointStateValue
  137. */
  138. /**
  139. * Gets fired when the point is updated programmatically through the `.update()`
  140. * method.
  141. *
  142. * @callback Highcharts.PointUpdateCallbackFunction
  143. *
  144. * @param {Highcharts.Point} this
  145. * Point where the event occured.
  146. *
  147. * @param {Highcharts.PointUpdateEventObject} event
  148. * Event that occured.
  149. */
  150. /**
  151. * Information about the update event.
  152. *
  153. * @interface Highcharts.PointUpdateEventObject
  154. * @extends global.Event
  155. */ /**
  156. * Options data of the update event.
  157. * @name Highcharts.PointUpdateEventObject#options
  158. * @type {Highcharts.PointOptionsType}
  159. */
  160. ''; // detach doclet above
  161. /* eslint-disable no-invalid-this, valid-jsdoc */
  162. /**
  163. * The Point object. The point objects are generated from the `series.data`
  164. * configuration objects or raw numbers. They can be accessed from the
  165. * `Series.points` array. Other ways to instantiate points are through {@link
  166. * Highcharts.Series#addPoint} or {@link Highcharts.Series#setData}.
  167. *
  168. * @class
  169. * @name Highcharts.Point
  170. */
  171. var Point = /** @class */ (function () {
  172. function Point() {
  173. /* *
  174. *
  175. * Properties
  176. *
  177. * */
  178. /**
  179. * For categorized axes this property holds the category name for the
  180. * point. For other axes it holds the X value.
  181. *
  182. * @name Highcharts.Point#category
  183. * @type {string}
  184. */
  185. this.category = void 0;
  186. /**
  187. * The point's current color index, used in styled mode instead of
  188. * `color`. The color index is inserted in class names used for styling.
  189. *
  190. * @name Highcharts.Point#colorIndex
  191. * @type {number}
  192. */
  193. this.colorIndex = void 0;
  194. this.formatPrefix = 'point';
  195. this.id = void 0;
  196. this.isNull = false;
  197. /**
  198. * The name of the point. The name can be given as the first position of the
  199. * point configuration array, or as a `name` property in the configuration:
  200. *
  201. * @example
  202. * // Array config
  203. * data: [
  204. * ['John', 1],
  205. * ['Jane', 2]
  206. * ]
  207. *
  208. * // Object config
  209. * data: [{
  210. * name: 'John',
  211. * y: 1
  212. * }, {
  213. * name: 'Jane',
  214. * y: 2
  215. * }]
  216. *
  217. * @name Highcharts.Point#name
  218. * @type {string}
  219. */
  220. this.name = void 0;
  221. /**
  222. * The point's options as applied in the initial configuration, or
  223. * extended through `Point.update`.
  224. *
  225. * In TypeScript you have to extend `PointOptionsObject` via an
  226. * additional interface to allow custom data options:
  227. *
  228. * ```
  229. * declare interface PointOptionsObject {
  230. * customProperty: string;
  231. * }
  232. * ```
  233. *
  234. * @name Highcharts.Point#options
  235. * @type {Highcharts.PointOptionsObject}
  236. */
  237. this.options = void 0;
  238. /**
  239. * The percentage for points in a stacked series or pies.
  240. *
  241. * @name Highcharts.Point#percentage
  242. * @type {number|undefined}
  243. */
  244. this.percentage = void 0;
  245. this.selected = false;
  246. /**
  247. * The series object associated with the point.
  248. *
  249. * @name Highcharts.Point#series
  250. * @type {Highcharts.Series}
  251. */
  252. this.series = void 0;
  253. /**
  254. * The total of values in either a stack for stacked series, or a pie in a
  255. * pie series.
  256. *
  257. * @name Highcharts.Point#total
  258. * @type {number|undefined}
  259. */
  260. this.total = void 0;
  261. /**
  262. * For certain series types, like pie charts, where individual points can
  263. * be shown or hidden.
  264. *
  265. * @name Highcharts.Point#visible
  266. * @type {boolean}
  267. * @default true
  268. */
  269. this.visible = true;
  270. this.x = void 0;
  271. }
  272. /* *
  273. *
  274. * Functions
  275. *
  276. * */
  277. /**
  278. * Animate SVG elements associated with the point.
  279. *
  280. * @private
  281. * @function Highcharts.Point#animateBeforeDestroy
  282. */
  283. Point.prototype.animateBeforeDestroy = function () {
  284. var point = this, animateParams = { x: point.startXPos, opacity: 0 }, isDataLabel, graphicalProps = point.getGraphicalProps();
  285. graphicalProps.singular.forEach(function (prop) {
  286. isDataLabel = prop === 'dataLabel';
  287. point[prop] = point[prop].animate(isDataLabel ? {
  288. x: point[prop].startXPos,
  289. y: point[prop].startYPos,
  290. opacity: 0
  291. } : animateParams);
  292. });
  293. graphicalProps.plural.forEach(function (plural) {
  294. point[plural].forEach(function (item) {
  295. if (item.element) {
  296. item.animate(extend({ x: point.startXPos }, (item.startYPos ? {
  297. x: item.startXPos,
  298. y: item.startYPos
  299. } : {})));
  300. }
  301. });
  302. });
  303. };
  304. /**
  305. * Apply the options containing the x and y data and possible some extra
  306. * properties. Called on point init or from point.update.
  307. *
  308. * @private
  309. * @function Highcharts.Point#applyOptions
  310. *
  311. * @param {Highcharts.PointOptionsType} options
  312. * The point options as defined in series.data.
  313. *
  314. * @param {number} [x]
  315. * Optionally, the x value.
  316. *
  317. * @return {Highcharts.Point}
  318. * The Point instance.
  319. */
  320. Point.prototype.applyOptions = function (options, x) {
  321. var point = this, series = point.series, pointValKey = series.options.pointValKey || series.pointValKey;
  322. options = Point.prototype.optionsToObject.call(this, options);
  323. // copy options directly to point
  324. extend(point, options);
  325. point.options = point.options ? extend(point.options, options) : options;
  326. // Since options are copied into the Point instance, some accidental
  327. // options must be shielded (#5681)
  328. if (options.group) {
  329. delete point.group;
  330. }
  331. if (options.dataLabels) {
  332. delete point.dataLabels;
  333. }
  334. /**
  335. * The y value of the point.
  336. * @name Highcharts.Point#y
  337. * @type {number|undefined}
  338. */
  339. // For higher dimension series types. For instance, for ranges, point.y
  340. // is mapped to point.low.
  341. if (pointValKey) {
  342. point.y = Point.prototype.getNestedProperty.call(point, pointValKey);
  343. }
  344. point.isNull = pick(point.isValid && !point.isValid(), point.x === null || !isNumber(point.y)); // #3571, check for NaN
  345. point.formatPrefix = point.isNull ? 'null' : 'point'; // #9233, #10874
  346. // The point is initially selected by options (#5777)
  347. if (point.selected) {
  348. point.state = 'select';
  349. }
  350. /**
  351. * The x value of the point.
  352. * @name Highcharts.Point#x
  353. * @type {number}
  354. */
  355. // If no x is set by now, get auto incremented value. All points must
  356. // have an x value, however the y value can be null to create a gap in
  357. // the series
  358. if ('name' in point &&
  359. typeof x === 'undefined' &&
  360. series.xAxis &&
  361. series.xAxis.hasNames) {
  362. point.x = series.xAxis.nameToX(point);
  363. }
  364. if (typeof point.x === 'undefined' && series) {
  365. if (typeof x === 'undefined') {
  366. point.x = series.autoIncrement(point);
  367. }
  368. else {
  369. point.x = x;
  370. }
  371. }
  372. return point;
  373. };
  374. /**
  375. * Destroy a point to clear memory. Its reference still stays in
  376. * `series.data`.
  377. *
  378. * @private
  379. * @function Highcharts.Point#destroy
  380. */
  381. Point.prototype.destroy = function () {
  382. var point = this, series = point.series, chart = series.chart, dataSorting = series.options.dataSorting, hoverPoints = chart.hoverPoints, globalAnimation = point.series.chart.renderer.globalAnimation, animation = animObject(globalAnimation), prop;
  383. /**
  384. * Allow to call after animation.
  385. * @private
  386. */
  387. function destroyPoint() {
  388. // Remove all events and elements
  389. if (point.graphic || point.dataLabel || point.dataLabels) {
  390. removeEvent(point);
  391. point.destroyElements();
  392. }
  393. for (prop in point) { // eslint-disable-line guard-for-in
  394. point[prop] = null;
  395. }
  396. }
  397. if (point.legendItem) { // pies have legend items
  398. chart.legend.destroyItem(point);
  399. }
  400. if (hoverPoints) {
  401. point.setState();
  402. erase(hoverPoints, point);
  403. if (!hoverPoints.length) {
  404. chart.hoverPoints = null;
  405. }
  406. }
  407. if (point === chart.hoverPoint) {
  408. point.onMouseOut();
  409. }
  410. // Remove properties after animation
  411. if (!dataSorting || !dataSorting.enabled) {
  412. destroyPoint();
  413. }
  414. else {
  415. this.animateBeforeDestroy();
  416. syncTimeout(destroyPoint, animation.duration);
  417. }
  418. chart.pointCount--;
  419. };
  420. /**
  421. * Destroy SVG elements associated with the point.
  422. *
  423. * @private
  424. * @function Highcharts.Point#destroyElements
  425. * @param {Highcharts.Dictionary<number>} [kinds]
  426. */
  427. Point.prototype.destroyElements = function (kinds) {
  428. var point = this, props = point.getGraphicalProps(kinds);
  429. props.singular.forEach(function (prop) {
  430. point[prop] = point[prop].destroy();
  431. });
  432. props.plural.forEach(function (plural) {
  433. point[plural].forEach(function (item) {
  434. if (item.element) {
  435. item.destroy();
  436. }
  437. });
  438. delete point[plural];
  439. });
  440. };
  441. /**
  442. * Fire an event on the Point object.
  443. *
  444. * @private
  445. * @function Highcharts.Point#firePointEvent
  446. *
  447. * @param {string} eventType
  448. * Type of the event.
  449. *
  450. * @param {Highcharts.Dictionary<any>|Event} [eventArgs]
  451. * Additional event arguments.
  452. *
  453. * @param {Highcharts.EventCallbackFunction<Highcharts.Point>|Function} [defaultFunction]
  454. * Default event handler.
  455. *
  456. * @fires Highcharts.Point#event:*
  457. */
  458. Point.prototype.firePointEvent = function (eventType, eventArgs, defaultFunction) {
  459. var point = this, series = this.series, seriesOptions = series.options;
  460. // load event handlers on demand to save time on mouseover/out
  461. if (seriesOptions.point.events[eventType] ||
  462. (point.options &&
  463. point.options.events &&
  464. point.options.events[eventType])) {
  465. point.importEvents();
  466. }
  467. // add default handler if in selection mode
  468. if (eventType === 'click' && seriesOptions.allowPointSelect) {
  469. defaultFunction = function (event) {
  470. // Control key is for Windows, meta (= Cmd key) for Mac, Shift
  471. // for Opera.
  472. if (point.select) { // #2911
  473. point.select(null, event.ctrlKey || event.metaKey || event.shiftKey);
  474. }
  475. };
  476. }
  477. fireEvent(point, eventType, eventArgs, defaultFunction);
  478. };
  479. /**
  480. * Get the CSS class names for individual points. Used internally where the
  481. * returned value is set on every point.
  482. *
  483. * @function Highcharts.Point#getClassName
  484. *
  485. * @return {string}
  486. * The class names.
  487. */
  488. Point.prototype.getClassName = function () {
  489. var point = this;
  490. return 'highcharts-point' +
  491. (point.selected ? ' highcharts-point-select' : '') +
  492. (point.negative ? ' highcharts-negative' : '') +
  493. (point.isNull ? ' highcharts-null-point' : '') +
  494. (typeof point.colorIndex !== 'undefined' ?
  495. ' highcharts-color-' + point.colorIndex : '') +
  496. (point.options.className ? ' ' + point.options.className : '') +
  497. (point.zone && point.zone.className ? ' ' +
  498. point.zone.className.replace('highcharts-negative', '') : '');
  499. };
  500. /**
  501. * Get props of all existing graphical point elements.
  502. *
  503. * @private
  504. * @function Highcharts.Point#getGraphicalProps
  505. * @param {Highcharts.Dictionary<number>} [kinds]
  506. * @return {Highcharts.PointGraphicalProps}
  507. */
  508. Point.prototype.getGraphicalProps = function (kinds) {
  509. var point = this, props = [], prop, i, graphicalProps = { singular: [], plural: [] };
  510. kinds = kinds || { graphic: 1, dataLabel: 1 };
  511. if (kinds.graphic) {
  512. props.push('graphic', 'shadowGroup');
  513. }
  514. if (kinds.dataLabel) {
  515. props.push('dataLabel', 'dataLabelUpper', 'connector');
  516. }
  517. i = props.length;
  518. while (i--) {
  519. prop = props[i];
  520. if (point[prop]) {
  521. graphicalProps.singular.push(prop);
  522. }
  523. }
  524. ['dataLabel', 'connector'].forEach(function (prop) {
  525. var plural = prop + 's';
  526. if (kinds[prop] && point[plural]) {
  527. graphicalProps.plural.push(plural);
  528. }
  529. });
  530. return graphicalProps;
  531. };
  532. /**
  533. * Return the configuration hash needed for the data label and tooltip
  534. * formatters.
  535. *
  536. * @function Highcharts.Point#getLabelConfig
  537. *
  538. * @return {Highcharts.PointLabelObject}
  539. * Abstract object used in formatters and formats.
  540. */
  541. Point.prototype.getLabelConfig = function () {
  542. return {
  543. x: this.category,
  544. y: this.y,
  545. color: this.color,
  546. colorIndex: this.colorIndex,
  547. key: this.name || this.category,
  548. series: this.series,
  549. point: this,
  550. percentage: this.percentage,
  551. total: this.total || this.stackTotal
  552. };
  553. };
  554. /**
  555. * Returns the value of the point property for a given value.
  556. * @private
  557. */
  558. Point.prototype.getNestedProperty = function (key) {
  559. if (!key) {
  560. return;
  561. }
  562. if (key.indexOf('custom.') === 0) {
  563. return getNestedProperty(key, this.options);
  564. }
  565. return this[key];
  566. };
  567. /**
  568. * In a series with `zones`, return the zone that the point belongs to.
  569. *
  570. * @function Highcharts.Point#getZone
  571. *
  572. * @return {Highcharts.SeriesZonesOptionsObject}
  573. * The zone item.
  574. */
  575. Point.prototype.getZone = function () {
  576. var series = this.series, zones = series.zones, zoneAxis = series.zoneAxis || 'y', i = 0, zone;
  577. zone = zones[i];
  578. while (this[zoneAxis] >= zone.value) {
  579. zone = zones[++i];
  580. }
  581. // For resetting or reusing the point (#8100)
  582. if (!this.nonZonedColor) {
  583. this.nonZonedColor = this.color;
  584. }
  585. if (zone && zone.color && !this.options.color) {
  586. this.color = zone.color;
  587. }
  588. else {
  589. this.color = this.nonZonedColor;
  590. }
  591. return zone;
  592. };
  593. /**
  594. * Utility to check if point has new shape type. Used in column series and
  595. * all others that are based on column series.
  596. *
  597. * @return boolean|undefined
  598. */
  599. Point.prototype.hasNewShapeType = function () {
  600. var point = this;
  601. var oldShapeType = point.graphic &&
  602. (point.graphic.symbolName || point.graphic.element.nodeName);
  603. return oldShapeType !== this.shapeType;
  604. };
  605. /**
  606. * Initialize the point. Called internally based on the `series.data`
  607. * option.
  608. *
  609. * @function Highcharts.Point#init
  610. *
  611. * @param {Highcharts.Series} series
  612. * The series object containing this point.
  613. *
  614. * @param {Highcharts.PointOptionsType} options
  615. * The data in either number, array or object format.
  616. *
  617. * @param {number} [x]
  618. * Optionally, the X value of the point.
  619. *
  620. * @return {Highcharts.Point}
  621. * The Point instance.
  622. *
  623. * @fires Highcharts.Point#event:afterInit
  624. */
  625. Point.prototype.init = function (series, options, x) {
  626. this.series = series;
  627. this.applyOptions(options, x);
  628. // Add a unique ID to the point if none is assigned
  629. this.id = defined(this.id) ? this.id : uniqueKey();
  630. this.resolveColor();
  631. series.chart.pointCount++;
  632. fireEvent(this, 'afterInit');
  633. return this;
  634. };
  635. /**
  636. * Transform number or array configs into objects. Also called for object
  637. * configs. Used internally to unify the different configuration formats for
  638. * points. For example, a simple number `10` in a line series will be
  639. * transformed to `{ y: 10 }`, and an array config like `[1, 10]` in a
  640. * scatter series will be transformed to `{ x: 1, y: 10 }`.
  641. *
  642. * @function Highcharts.Point#optionsToObject
  643. *
  644. * @param {Highcharts.PointOptionsType} options
  645. * The input option.
  646. *
  647. * @return {Highcharts.Dictionary<*>}
  648. * Transformed options.
  649. */
  650. Point.prototype.optionsToObject = function (options) {
  651. var ret = {}, series = this.series, keys = series.options.keys, pointArrayMap = keys || series.pointArrayMap || ['y'], valueCount = pointArrayMap.length, firstItemType, i = 0, j = 0;
  652. if (isNumber(options) || options === null) {
  653. ret[pointArrayMap[0]] = options;
  654. }
  655. else if (isArray(options)) {
  656. // with leading x value
  657. if (!keys && options.length > valueCount) {
  658. firstItemType = typeof options[0];
  659. if (firstItemType === 'string') {
  660. ret.name = options[0];
  661. }
  662. else if (firstItemType === 'number') {
  663. ret.x = options[0];
  664. }
  665. i++;
  666. }
  667. while (j < valueCount) {
  668. // Skip undefined positions for keys
  669. if (!keys || typeof options[i] !== 'undefined') {
  670. if (pointArrayMap[j].indexOf('.') > 0) {
  671. // Handle nested keys, e.g. ['color.pattern.image']
  672. // Avoid function call unless necessary.
  673. Point.prototype.setNestedProperty(ret, options[i], pointArrayMap[j]);
  674. }
  675. else {
  676. ret[pointArrayMap[j]] = options[i];
  677. }
  678. }
  679. i++;
  680. j++;
  681. }
  682. }
  683. else if (typeof options === 'object') {
  684. ret = options;
  685. // This is the fastest way to detect if there are individual point
  686. // dataLabels that need to be considered in drawDataLabels. These
  687. // can only occur in object configs.
  688. if (options.dataLabels) {
  689. series._hasPointLabels = true;
  690. }
  691. // Same approach as above for markers
  692. if (options.marker) {
  693. series._hasPointMarkers = true;
  694. }
  695. }
  696. return ret;
  697. };
  698. /**
  699. * @private
  700. * @function Highcharts.Point#resolveColor
  701. * @return {void}
  702. */
  703. Point.prototype.resolveColor = function () {
  704. var series = this.series, colors, optionsChart = series.chart.options.chart, colorCount = optionsChart.colorCount, styledMode = series.chart.styledMode, colorIndex;
  705. // remove points nonZonedColor for later recalculation
  706. delete this.nonZonedColor;
  707. /**
  708. * The point's current color.
  709. *
  710. * @name Highcharts.Point#color
  711. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
  712. */
  713. if (!styledMode && !this.options.color) {
  714. this.color = series.color; // #3445
  715. }
  716. if (series.options.colorByPoint) {
  717. if (!styledMode) {
  718. colors = series.options.colors || series.chart.options.colors;
  719. this.color = this.color || colors[series.colorCounter];
  720. colorCount = colors.length;
  721. }
  722. colorIndex = series.colorCounter;
  723. series.colorCounter++;
  724. // loop back to zero
  725. if (series.colorCounter === colorCount) {
  726. series.colorCounter = 0;
  727. }
  728. }
  729. else {
  730. colorIndex = series.colorIndex;
  731. }
  732. this.colorIndex = pick(this.colorIndex, colorIndex);
  733. };
  734. /**
  735. * Set a value in an object, on the property defined by key. The key
  736. * supports nested properties using dot notation. The function modifies the
  737. * input object and does not make a copy.
  738. *
  739. * @function Highcharts.Point#setNestedProperty<T>
  740. *
  741. * @param {T} object
  742. * The object to set the value on.
  743. *
  744. * @param {*} value
  745. * The value to set.
  746. *
  747. * @param {string} key
  748. * Key to the property to set.
  749. *
  750. * @return {T}
  751. * The modified object.
  752. */
  753. Point.prototype.setNestedProperty = function (object, value, key) {
  754. var nestedKeys = key.split('.');
  755. nestedKeys.reduce(function (result, key, i, arr) {
  756. var isLastKey = arr.length - 1 === i;
  757. result[key] = (isLastKey ?
  758. value :
  759. isObject(result[key], true) ?
  760. result[key] :
  761. {});
  762. return result[key];
  763. }, object);
  764. return object;
  765. };
  766. /**
  767. * Extendable method for formatting each point's tooltip line.
  768. *
  769. * @function Highcharts.Point#tooltipFormatter
  770. *
  771. * @param {string} pointFormat
  772. * The point format.
  773. *
  774. * @return {string}
  775. * A string to be concatenated in to the common tooltip text.
  776. */
  777. Point.prototype.tooltipFormatter = function (pointFormat) {
  778. // Insert options for valueDecimals, valuePrefix, and valueSuffix
  779. var series = this.series, seriesTooltipOptions = series.tooltipOptions, valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), valuePrefix = seriesTooltipOptions.valuePrefix || '', valueSuffix = seriesTooltipOptions.valueSuffix || '';
  780. // Replace default point style with class name
  781. if (series.chart.styledMode) {
  782. pointFormat =
  783. series.chart.tooltip.styledModeFormat(pointFormat);
  784. }
  785. // Loop over the point array map and replace unformatted values with
  786. // sprintf formatting markup
  787. (series.pointArrayMap || ['y']).forEach(function (key) {
  788. key = '{point.' + key; // without the closing bracket
  789. if (valuePrefix || valueSuffix) {
  790. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), valuePrefix + key + '}' + valueSuffix);
  791. }
  792. pointFormat = pointFormat.replace(RegExp(key + '}', 'g'), key + ':,.' + valueDecimals + 'f}');
  793. });
  794. return format(pointFormat, {
  795. point: this,
  796. series: this.series
  797. }, series.chart);
  798. };
  799. return Point;
  800. }());
  801. H.Point = Point;
  802. export default Point;