broken-axis.src.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /**
  2. * @license Highcharts JS v9.1.1 (2021-06-04)
  3. *
  4. * (c) 2009-2021 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. (function (factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. factory['default'] = factory;
  12. module.exports = factory;
  13. } else if (typeof define === 'function' && define.amd) {
  14. define('highcharts/modules/broken-axis', ['highcharts'], function (Highcharts) {
  15. factory(Highcharts);
  16. factory.Highcharts = Highcharts;
  17. return factory;
  18. });
  19. } else {
  20. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  21. }
  22. }(function (Highcharts) {
  23. var _modules = Highcharts ? Highcharts._modules : {};
  24. function _registerModule(obj, path, args, fn) {
  25. if (!obj.hasOwnProperty(path)) {
  26. obj[path] = fn.apply(null, args);
  27. }
  28. }
  29. _registerModule(_modules, 'Core/Axis/BrokenAxis.js', [_modules['Core/Axis/Axis.js'], _modules['Core/Series/Series.js'], _modules['Extensions/Stacking.js'], _modules['Core/Utilities.js']], function (Axis, Series, StackItem, U) {
  30. /* *
  31. *
  32. * (c) 2009-2021 Torstein Honsi
  33. *
  34. * License: www.highcharts.com/license
  35. *
  36. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37. *
  38. * */
  39. var addEvent = U.addEvent,
  40. find = U.find,
  41. fireEvent = U.fireEvent,
  42. isArray = U.isArray,
  43. isNumber = U.isNumber,
  44. pick = U.pick;
  45. /**
  46. * Axis with support of broken data rows.
  47. * @private
  48. * @class
  49. */
  50. var BrokenAxis;
  51. (function (BrokenAxis) {
  52. /* *
  53. *
  54. * Functions
  55. *
  56. * */
  57. /* eslint-disable valid-jsdoc */
  58. /**
  59. * Adds support for broken axes.
  60. * @private
  61. */
  62. function compose(AxisClass, SeriesClass) {
  63. if (AxisClass.keepProps.indexOf('brokenAxis') === -1) {
  64. AxisClass.keepProps.push('brokenAxis');
  65. var seriesProto = Series.prototype;
  66. seriesProto.drawBreaks = seriesDrawBreaks;
  67. seriesProto.gappedPath = seriesGappedPath;
  68. addEvent(AxisClass, 'init', onInit);
  69. addEvent(AxisClass, 'afterInit', onAfterInit);
  70. addEvent(AxisClass, 'afterSetTickPositions', onAfterSetTickPositions);
  71. addEvent(AxisClass, 'afterSetOptions', onAfterSetOptions);
  72. addEvent(SeriesClass, 'afterGeneratePoints', onSeriesAfterGeneratePoints);
  73. addEvent(SeriesClass, 'afterRender', onSeriesAfterRender);
  74. }
  75. return AxisClass;
  76. }
  77. BrokenAxis.compose = compose;
  78. /**
  79. * @private
  80. */
  81. function onAfterInit() {
  82. if (typeof this.brokenAxis !== 'undefined') {
  83. this.brokenAxis.setBreaks(this.options.breaks, false);
  84. }
  85. }
  86. /**
  87. * Force Axis to be not-ordinal when breaks are defined.
  88. * @private
  89. */
  90. function onAfterSetOptions() {
  91. var axis = this;
  92. if (axis.brokenAxis && axis.brokenAxis.hasBreaks) {
  93. axis.options.ordinal = false;
  94. }
  95. }
  96. /**
  97. * @private
  98. */
  99. function onAfterSetTickPositions() {
  100. var axis = this,
  101. brokenAxis = axis.brokenAxis;
  102. if (brokenAxis &&
  103. brokenAxis.hasBreaks) {
  104. var tickPositions = axis.tickPositions,
  105. info = axis.tickPositions.info,
  106. newPositions = [];
  107. for (var i = 0; i < tickPositions.length; i++) {
  108. if (!brokenAxis.isInAnyBreak(tickPositions[i])) {
  109. newPositions.push(tickPositions[i]);
  110. }
  111. }
  112. axis.tickPositions = newPositions;
  113. axis.tickPositions.info = info;
  114. }
  115. }
  116. /**
  117. * @private
  118. */
  119. function onInit() {
  120. var axis = this;
  121. if (!axis.brokenAxis) {
  122. axis.brokenAxis = new Additions(axis);
  123. }
  124. }
  125. /**
  126. * @private
  127. */
  128. function onSeriesAfterGeneratePoints() {
  129. var _a = this,
  130. isDirty = _a.isDirty,
  131. connectNulls = _a.options.connectNulls,
  132. points = _a.points,
  133. xAxis = _a.xAxis,
  134. yAxis = _a.yAxis;
  135. // Set, or reset visibility of the points. Axis.setBreaks marks
  136. // the series as isDirty
  137. if (isDirty) {
  138. var i = points.length;
  139. while (i--) {
  140. var point = points[i];
  141. // Respect nulls inside the break (#4275)
  142. var nullGap = point.y === null && connectNulls === false;
  143. var isPointInBreak = (!nullGap && ((xAxis &&
  144. xAxis.brokenAxis &&
  145. xAxis.brokenAxis.isInAnyBreak(point.x,
  146. true)) || (yAxis &&
  147. yAxis.brokenAxis &&
  148. yAxis.brokenAxis.isInAnyBreak(point.y,
  149. true))));
  150. // Set point.visible if in any break.
  151. // If not in break, reset visible to original value.
  152. point.visible = isPointInBreak ?
  153. false :
  154. point.options.visible !== false;
  155. }
  156. }
  157. }
  158. /**
  159. * @private
  160. */
  161. function onSeriesAfterRender() {
  162. this.drawBreaks(this.xAxis, ['x']);
  163. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  164. }
  165. /**
  166. * @private
  167. */
  168. function seriesDrawBreaks(axis, keys) {
  169. var series = this,
  170. points = series.points;
  171. var breaks,
  172. threshold,
  173. eventName,
  174. y;
  175. if (axis && // #5950
  176. axis.brokenAxis &&
  177. axis.brokenAxis.hasBreaks) {
  178. var brokenAxis_1 = axis.brokenAxis;
  179. keys.forEach(function (key) {
  180. breaks = brokenAxis_1 && brokenAxis_1.breakArray || [];
  181. threshold = axis.isXAxis ?
  182. axis.min :
  183. pick(series.options.threshold, axis.min);
  184. points.forEach(function (point) {
  185. y = pick(point['stack' + key.toUpperCase()], point[key]);
  186. breaks.forEach(function (brk) {
  187. if (isNumber(threshold) && isNumber(y)) {
  188. eventName = false;
  189. if ((threshold < brk.from && y > brk.to) ||
  190. (threshold > brk.from && y < brk.from)) {
  191. eventName = 'pointBreak';
  192. }
  193. else if ((threshold < brk.from && y > brk.from && y < brk.to) ||
  194. (threshold > brk.from && y > brk.to && y < brk.from)) {
  195. eventName = 'pointInBreak';
  196. }
  197. if (eventName) {
  198. fireEvent(axis, eventName, { point: point, brk: brk });
  199. }
  200. }
  201. });
  202. });
  203. });
  204. }
  205. }
  206. /**
  207. * Extend getGraphPath by identifying gaps in the data so that we
  208. * can draw a gap in the line or area. This was moved from ordinal
  209. * axis module to broken axis module as of #5045.
  210. *
  211. * @private
  212. * @function Highcharts.Series#gappedPath
  213. *
  214. * @return {Highcharts.SVGPathArray}
  215. * Gapped path
  216. */
  217. function seriesGappedPath() {
  218. var currentDataGrouping = this.currentDataGrouping,
  219. groupingSize = currentDataGrouping && currentDataGrouping.gapSize,
  220. points = this.points.slice(),
  221. yAxis = this.yAxis;
  222. var gapSize = this.options.gapSize,
  223. i = points.length - 1,
  224. stack;
  225. /**
  226. * Defines when to display a gap in the graph, together with the
  227. * [gapUnit](plotOptions.series.gapUnit) option.
  228. *
  229. * In case when `dataGrouping` is enabled, points can be grouped
  230. * into a larger time span. This can make the grouped points to
  231. * have a greater distance than the absolute value of `gapSize`
  232. * property, which will result in disappearing graph completely.
  233. * To prevent this situation the mentioned distance between
  234. * grouped points is used instead of previously defined
  235. * `gapSize`.
  236. *
  237. * In practice, this option is most often used to visualize gaps
  238. * in time series. In a stock chart, intraday data is available
  239. * for daytime hours, while gaps will appear in nights and
  240. * weekends.
  241. *
  242. * @see [gapUnit](plotOptions.series.gapUnit)
  243. * @see [xAxis.breaks](#xAxis.breaks)
  244. *
  245. * @sample {highstock} stock/plotoptions/series-gapsize/
  246. * Setting the gap size to 2 introduces gaps for weekends in
  247. * daily datasets.
  248. *
  249. * @type {number}
  250. * @default 0
  251. * @product highstock
  252. * @requires modules/broken-axis
  253. * @apioption plotOptions.series.gapSize
  254. */
  255. /**
  256. * Together with [gapSize](plotOptions.series.gapSize), this
  257. * option defines where to draw gaps in the graph.
  258. *
  259. * When the `gapUnit` is `"relative"` (default), a gap size of 5
  260. * means that if the distance between two points is greater than
  261. * 5 times that of the two closest points, the graph will be
  262. * broken.
  263. *
  264. * When the `gapUnit` is `"value"`, the gap is based on absolute
  265. * axis values, which on a datetime axis is milliseconds. This
  266. * also applies to the navigator series that inherits gap
  267. * options from the base series.
  268. *
  269. * @see [gapSize](plotOptions.series.gapSize)
  270. *
  271. * @type {string}
  272. * @default relative
  273. * @since 5.0.13
  274. * @product highstock
  275. * @validvalue ["relative", "value"]
  276. * @requires modules/broken-axis
  277. * @apioption plotOptions.series.gapUnit
  278. */
  279. if (gapSize && i > 0) { // #5008
  280. // Gap unit is relative
  281. if (this.options.gapUnit !== 'value') {
  282. gapSize *= this.basePointRange;
  283. }
  284. // Setting a new gapSize in case dataGrouping is enabled
  285. // (#7686)
  286. if (groupingSize &&
  287. groupingSize > gapSize &&
  288. // Except when DG is forced (e.g. from other series)
  289. // and has lower granularity than actual points (#11351)
  290. groupingSize >= this.basePointRange) {
  291. gapSize = groupingSize;
  292. }
  293. // extension for ordinal breaks
  294. var current = void 0,
  295. next = void 0;
  296. while (i--) {
  297. // Reassign next if it is not visible
  298. if (!(next && next.visible !== false)) {
  299. next = points[i + 1];
  300. }
  301. current = points[i];
  302. // Skip iteration if one of the points is not visible
  303. if (next.visible === false || current.visible === false) {
  304. continue;
  305. }
  306. if (next.x - current.x > gapSize) {
  307. var xRange = (current.x + next.x) / 2;
  308. points.splice(// insert after this one
  309. i + 1, 0, {
  310. isNull: true,
  311. x: xRange
  312. });
  313. // For stacked chart generate empty stack items,
  314. // #6546
  315. if (yAxis.stacking && this.options.stacking) {
  316. stack = yAxis.stacking.stacks[this.stackKey][xRange] =
  317. new StackItem(yAxis, yAxis.options
  318. .stackLabels, false, xRange, this.stack);
  319. stack.total = 0;
  320. }
  321. }
  322. // Assign current to next for the upcoming iteration
  323. next = current;
  324. }
  325. }
  326. // Call base method
  327. return this.getGraphPath(points);
  328. }
  329. /* *
  330. *
  331. * Class
  332. *
  333. * */
  334. /**
  335. * Provides support for broken axes.
  336. * @private
  337. * @class
  338. */
  339. var Additions = /** @class */ (function () {
  340. /* *
  341. *
  342. * Constructors
  343. *
  344. * */
  345. function Additions(axis) {
  346. this.hasBreaks = false;
  347. this.axis = axis;
  348. }
  349. /* *
  350. *
  351. * Static Functions
  352. *
  353. * */
  354. /**
  355. * @private
  356. */
  357. Additions.isInBreak = function (brk, val) {
  358. var repeat = brk.repeat || Infinity,
  359. from = brk.from,
  360. length = brk.to - brk.from,
  361. test = (val >= from ?
  362. (val - from) % repeat :
  363. repeat - ((from - val) % repeat));
  364. var ret;
  365. if (!brk.inclusive) {
  366. ret = test < length && test !== 0;
  367. }
  368. else {
  369. ret = test <= length;
  370. }
  371. return ret;
  372. };
  373. /**
  374. * @private
  375. */
  376. Additions.lin2Val = function (val) {
  377. var axis = this;
  378. var brokenAxis = axis.brokenAxis;
  379. var breakArray = brokenAxis && brokenAxis.breakArray;
  380. if (!breakArray || !isNumber(val)) {
  381. return val;
  382. }
  383. var nval = val,
  384. brk,
  385. i;
  386. for (i = 0; i < breakArray.length; i++) {
  387. brk = breakArray[i];
  388. if (brk.from >= nval) {
  389. break;
  390. }
  391. else if (brk.to < nval) {
  392. nval += brk.len;
  393. }
  394. else if (Additions.isInBreak(brk, nval)) {
  395. nval += brk.len;
  396. }
  397. }
  398. return nval;
  399. };
  400. /**
  401. * @private
  402. */
  403. Additions.val2Lin = function (val) {
  404. var axis = this;
  405. var brokenAxis = axis.brokenAxis;
  406. var breakArray = brokenAxis && brokenAxis.breakArray;
  407. if (!breakArray || !isNumber(val)) {
  408. return val;
  409. }
  410. var nval = val,
  411. brk,
  412. i;
  413. for (i = 0; i < breakArray.length; i++) {
  414. brk = breakArray[i];
  415. if (brk.to <= val) {
  416. nval -= brk.len;
  417. }
  418. else if (brk.from >= val) {
  419. break;
  420. }
  421. else if (Additions.isInBreak(brk, val)) {
  422. nval -= (val - brk.from);
  423. break;
  424. }
  425. }
  426. return nval;
  427. };
  428. /* *
  429. *
  430. * Functions
  431. *
  432. * */
  433. /**
  434. * Returns the first break found where the x is larger then break.from
  435. * and smaller then break.to.
  436. *
  437. * @param {number} x
  438. * The number which should be within a break.
  439. *
  440. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  441. * The array of breaks to search within.
  442. *
  443. * @return {Highcharts.XAxisBreaksOptions|undefined}
  444. * Returns the first break found that matches, returns false if no break
  445. * is found.
  446. */
  447. Additions.prototype.findBreakAt = function (x, breaks) {
  448. return find(breaks, function (b) {
  449. return b.from < x && x < b.to;
  450. });
  451. };
  452. /**
  453. * @private
  454. */
  455. Additions.prototype.isInAnyBreak = function (val, testKeep) {
  456. var brokenAxis = this,
  457. axis = brokenAxis.axis,
  458. breaks = axis.options.breaks || [];
  459. var i = breaks.length,
  460. inbrk,
  461. keep,
  462. ret;
  463. if (i && isNumber(val)) {
  464. while (i--) {
  465. if (Additions.isInBreak(breaks[i], val)) {
  466. inbrk = true;
  467. if (!keep) {
  468. keep = pick(breaks[i].showPoints, !axis.isXAxis);
  469. }
  470. }
  471. }
  472. if (inbrk && testKeep) {
  473. ret = inbrk && !keep;
  474. }
  475. else {
  476. ret = inbrk;
  477. }
  478. }
  479. return ret;
  480. };
  481. /**
  482. * Dynamically set or unset breaks in an axis. This function in lighter
  483. * than usin Axis.update, and it also preserves animation.
  484. *
  485. * @private
  486. * @function Highcharts.Axis#setBreaks
  487. *
  488. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  489. * The breaks to add. When `undefined` it removes existing breaks.
  490. *
  491. * @param {boolean} [redraw=true]
  492. * Whether to redraw the chart immediately.
  493. */
  494. Additions.prototype.setBreaks = function (breaks, redraw) {
  495. var brokenAxis = this;
  496. var axis = brokenAxis.axis;
  497. var hasBreaks = (isArray(breaks) && !!breaks.length);
  498. axis.isDirty = brokenAxis.hasBreaks !== hasBreaks;
  499. brokenAxis.hasBreaks = hasBreaks;
  500. axis.options.breaks = axis.userOptions.breaks = breaks;
  501. axis.forceRedraw = true; // Force recalculation in setScale
  502. // Recalculate series related to the axis.
  503. axis.series.forEach(function (series) {
  504. series.isDirty = true;
  505. });
  506. if (!hasBreaks && axis.val2lin === Additions.val2Lin) {
  507. // Revert to prototype functions
  508. delete axis.val2lin;
  509. delete axis.lin2val;
  510. }
  511. if (hasBreaks) {
  512. axis.userOptions.ordinal = false;
  513. axis.lin2val = Additions.lin2Val;
  514. axis.val2lin = Additions.val2Lin;
  515. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  516. // If trying to set extremes inside a break, extend min to
  517. // after, and max to before the break ( #3857 )
  518. if (brokenAxis.hasBreaks) {
  519. var breaks_1 = (this.options.breaks || []);
  520. var axisBreak = void 0;
  521. while ((axisBreak = brokenAxis.findBreakAt(newMin, breaks_1))) {
  522. newMin = axisBreak.to;
  523. }
  524. while ((axisBreak = brokenAxis.findBreakAt(newMax, breaks_1))) {
  525. newMax = axisBreak.from;
  526. }
  527. // If both min and max is within the same break.
  528. if (newMax < newMin) {
  529. newMax = newMin;
  530. }
  531. }
  532. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  533. };
  534. axis.setAxisTranslation = function () {
  535. Axis.prototype.setAxisTranslation.call(this);
  536. brokenAxis.unitLength = void 0;
  537. if (brokenAxis.hasBreaks) {
  538. var breaks_2 = axis.options.breaks || [],
  539. // Temporary one:
  540. breakArrayT_1 = [],
  541. breakArray_1 = [],
  542. pointRangePadding = pick(axis.pointRangePadding, 0);
  543. var length_1 = 0,
  544. inBrk_1,
  545. repeat_1,
  546. min_1 = axis.userMin || axis.min,
  547. max_1 = axis.userMax || axis.max,
  548. start_1,
  549. i_1;
  550. // Min & max check (#4247)
  551. breaks_2.forEach(function (brk) {
  552. repeat_1 = brk.repeat || Infinity;
  553. if (isNumber(min_1) && isNumber(max_1)) {
  554. if (Additions.isInBreak(brk, min_1)) {
  555. min_1 += (brk.to % repeat_1) - (min_1 % repeat_1);
  556. }
  557. if (Additions.isInBreak(brk, max_1)) {
  558. max_1 -= (max_1 % repeat_1) - (brk.from % repeat_1);
  559. }
  560. }
  561. });
  562. // Construct an array holding all breaks in the axis
  563. breaks_2.forEach(function (brk) {
  564. start_1 = brk.from;
  565. repeat_1 = brk.repeat || Infinity;
  566. if (isNumber(min_1) && isNumber(max_1)) {
  567. while (start_1 - repeat_1 > min_1) {
  568. start_1 -= repeat_1;
  569. }
  570. while (start_1 < min_1) {
  571. start_1 += repeat_1;
  572. }
  573. for (i_1 = start_1; i_1 < max_1; i_1 += repeat_1) {
  574. breakArrayT_1.push({
  575. value: i_1,
  576. move: 'in'
  577. });
  578. breakArrayT_1.push({
  579. value: i_1 + brk.to - brk.from,
  580. move: 'out',
  581. size: brk.breakSize
  582. });
  583. }
  584. }
  585. });
  586. breakArrayT_1.sort(function (a, b) {
  587. return ((a.value === b.value) ?
  588. ((a.move === 'in' ? 0 : 1) -
  589. (b.move === 'in' ? 0 : 1)) :
  590. a.value - b.value);
  591. });
  592. // Simplify the breaks
  593. inBrk_1 = 0;
  594. start_1 = min_1;
  595. breakArrayT_1.forEach(function (brk) {
  596. inBrk_1 += (brk.move === 'in' ? 1 : -1);
  597. if (inBrk_1 === 1 && brk.move === 'in') {
  598. start_1 = brk.value;
  599. }
  600. if (inBrk_1 === 0 && isNumber(start_1)) {
  601. breakArray_1.push({
  602. from: start_1,
  603. to: brk.value,
  604. len: brk.value - start_1 - (brk.size || 0)
  605. });
  606. length_1 += brk.value - start_1 - (brk.size || 0);
  607. }
  608. });
  609. brokenAxis.breakArray = breakArray_1;
  610. // Used with staticScale, and below the actual axis
  611. // length, when breaks are substracted.
  612. if (isNumber(min_1) && isNumber(max_1) && isNumber(axis.min)) {
  613. brokenAxis.unitLength = max_1 - min_1 - length_1 +
  614. pointRangePadding;
  615. fireEvent(axis, 'afterBreaks');
  616. if (axis.staticScale) {
  617. axis.transA = axis.staticScale;
  618. }
  619. else if (brokenAxis.unitLength) {
  620. axis.transA *=
  621. (max_1 - axis.min + pointRangePadding) /
  622. brokenAxis.unitLength;
  623. }
  624. if (pointRangePadding) {
  625. axis.minPixelPadding =
  626. axis.transA * (axis.minPointOffset || 0);
  627. }
  628. axis.min = min_1;
  629. axis.max = max_1;
  630. }
  631. }
  632. };
  633. }
  634. if (pick(redraw, true)) {
  635. axis.chart.redraw();
  636. }
  637. };
  638. return Additions;
  639. }());
  640. BrokenAxis.Additions = Additions;
  641. })(BrokenAxis || (BrokenAxis = {}));
  642. /* *
  643. *
  644. * Default Export
  645. *
  646. * */
  647. return BrokenAxis;
  648. });
  649. _registerModule(_modules, 'masters/modules/broken-axis.src.js', [_modules['Core/Globals.js'], _modules['Core/Axis/BrokenAxis.js']], function (Highcharts, BrokenAxis) {
  650. var G = Highcharts;
  651. // Compositions
  652. BrokenAxis.compose(G.Axis, G.Series);
  653. });
  654. }));