broken-axis.src.js 26 KB

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