broken-axis.src.js 28 KB

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