treegrid.src.js 103 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259
  1. /**
  2. * @license Highcharts Gantt JS v8.0.0 (2019-12-10)
  3. *
  4. * Tree Grid
  5. *
  6. * (c) 2016-2019 Jon Arild Nygard
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/treegrid', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'parts-gantt/GridAxis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  32. /* *
  33. *
  34. * (c) 2016 Highsoft AS
  35. * Authors: Lars A. V. Cabrera
  36. *
  37. * License: www.highcharts.com/license
  38. *
  39. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  40. *
  41. * */
  42. var defined = U.defined, erase = U.erase, isArray = U.isArray, isNumber = U.isNumber, pick = U.pick, wrap = U.wrap;
  43. var addEvent = H.addEvent, argsToArray = function (args) {
  44. return Array.prototype.slice.call(args, 1);
  45. }, dateFormat = H.dateFormat, isObject = function (x) {
  46. // Always use strict mode
  47. return U.isObject(x, true);
  48. }, merge = H.merge, Chart = H.Chart, Axis = H.Axis, Tick = H.Tick;
  49. var applyGridOptions = function applyGridOptions(axis) {
  50. var options = axis.options;
  51. // Center-align by default
  52. if (!options.labels) {
  53. options.labels = {};
  54. }
  55. options.labels.align = pick(options.labels.align, 'center');
  56. // @todo: Check against tickLabelPlacement between/on etc
  57. /* Prevents adding the last tick label if the axis is not a category
  58. axis.
  59. Since numeric labels are normally placed at starts and ends of a
  60. range of value, and this module makes the label point at the value,
  61. an "extra" label would appear. */
  62. if (!axis.categories) {
  63. options.showLastLabel = false;
  64. }
  65. // Prevents rotation of labels when squished, as rotating them would not
  66. // help.
  67. axis.labelRotation = 0;
  68. options.labels.rotation = 0;
  69. };
  70. /**
  71. * Set grid options for the axis labels. Requires Highcharts Gantt.
  72. *
  73. * @since 6.2.0
  74. * @product gantt
  75. * @apioption xAxis.grid
  76. */
  77. /**
  78. * Enable grid on the axis labels. Defaults to true for Gantt charts.
  79. *
  80. * @type {boolean}
  81. * @default true
  82. * @since 6.2.0
  83. * @product gantt
  84. * @apioption xAxis.grid.enabled
  85. */
  86. /**
  87. * Set specific options for each column (or row for horizontal axes) in the
  88. * grid. Each extra column/row is its own axis, and the axis options can be set
  89. * here.
  90. *
  91. * @sample gantt/demo/left-axis-table
  92. * Left axis as a table
  93. *
  94. * @type {Array<Highcharts.XAxisOptions>}
  95. * @apioption xAxis.grid.columns
  96. */
  97. /**
  98. * Set border color for the label grid lines.
  99. *
  100. * @type {Highcharts.ColorString}
  101. * @apioption xAxis.grid.borderColor
  102. */
  103. /**
  104. * Set border width of the label grid lines.
  105. *
  106. * @type {number}
  107. * @default 1
  108. * @apioption xAxis.grid.borderWidth
  109. */
  110. /**
  111. * Set cell height for grid axis labels. By default this is calculated from font
  112. * size. This option only applies to horizontal axes.
  113. *
  114. * @sample gantt/grid-axis/cellheight
  115. * Gant chart with custom cell height
  116. * @type {number}
  117. * @apioption xAxis.grid.cellHeight
  118. */
  119. // Enum for which side the axis is on.
  120. // Maps to axis.side
  121. var axisSide = {
  122. top: 0,
  123. right: 1,
  124. bottom: 2,
  125. left: 3,
  126. 0: 'top',
  127. 1: 'right',
  128. 2: 'bottom',
  129. 3: 'left'
  130. };
  131. /**
  132. * Checks if an axis is the outer axis in its dimension. Since
  133. * axes are placed outwards in order, the axis with the highest
  134. * index is the outermost axis.
  135. *
  136. * Example: If there are multiple x-axes at the top of the chart,
  137. * this function returns true if the axis supplied is the last
  138. * of the x-axes.
  139. *
  140. * @private
  141. * @function Highcharts.Axis#isOuterAxis
  142. *
  143. * @return {boolean}
  144. * true if the axis is the outermost axis in its dimension; false if not
  145. */
  146. Axis.prototype.isOuterAxis = function () {
  147. var axis = this, chart = axis.chart, columnIndex = axis.columnIndex, columns = axis.linkedParent && axis.linkedParent.columns ||
  148. axis.columns, parentAxis = columnIndex ? axis.linkedParent : axis, thisIndex = -1, lastIndex = 0;
  149. chart[axis.coll].forEach(function (otherAxis, index) {
  150. if (otherAxis.side === axis.side && !otherAxis.options.isInternal) {
  151. lastIndex = index;
  152. if (otherAxis === parentAxis) {
  153. // Get the index of the axis in question
  154. thisIndex = index;
  155. }
  156. }
  157. });
  158. return (lastIndex === thisIndex &&
  159. (isNumber(columnIndex) ? columns.length === columnIndex : true));
  160. };
  161. /**
  162. * Get the largest label width and height.
  163. *
  164. * @private
  165. * @function Highcharts.Axis#getMaxLabelDimensions
  166. *
  167. * @param {Highcharts.Dictionary<Highcharts.Tick>} ticks
  168. * All the ticks on one axis.
  169. *
  170. * @param {Array<number|string>} tickPositions
  171. * All the tick positions on one axis.
  172. *
  173. * @return {Highcharts.SizeObject}
  174. * object containing the properties height and width.
  175. */
  176. Axis.prototype.getMaxLabelDimensions = function (ticks, tickPositions) {
  177. var dimensions = {
  178. width: 0,
  179. height: 0
  180. };
  181. tickPositions.forEach(function (pos) {
  182. var tick = ticks[pos], tickHeight = 0, tickWidth = 0, label;
  183. if (isObject(tick)) {
  184. label = isObject(tick.label) ? tick.label : {};
  185. // Find width and height of tick
  186. tickHeight = label.getBBox ? label.getBBox().height : 0;
  187. if (label.textStr && !isNumber(label.textPxLength)) {
  188. label.textPxLength = label.getBBox().width;
  189. }
  190. tickWidth = isNumber(label.textPxLength) ?
  191. // Math.round ensures crisp lines
  192. Math.round(label.textPxLength) :
  193. 0;
  194. // Update the result if width and/or height are larger
  195. dimensions.height = Math.max(tickHeight, dimensions.height);
  196. dimensions.width = Math.max(tickWidth, dimensions.width);
  197. }
  198. });
  199. return dimensions;
  200. };
  201. // Add custom date formats
  202. H.dateFormats.W = function (timestamp) {
  203. var d = new Date(timestamp), yearStart, weekNo;
  204. d.setHours(0, 0, 0, 0);
  205. d.setDate(d.getDate() - (d.getDay() || 7));
  206. yearStart = new Date(d.getFullYear(), 0, 1);
  207. weekNo =
  208. Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
  209. return weekNo;
  210. };
  211. // First letter of the day of the week, e.g. 'M' for 'Monday'.
  212. H.dateFormats.E = function (timestamp) {
  213. return dateFormat('%a', timestamp, true).charAt(0);
  214. };
  215. /* eslint-disable no-invalid-this, valid-jsdoc */
  216. addEvent(Tick, 'afterGetLabelPosition',
  217. /**
  218. * Center tick labels in cells.
  219. *
  220. * @private
  221. */
  222. function (e) {
  223. var tick = this, label = tick.label, axis = tick.axis, reversed = axis.reversed, chart = axis.chart, options = axis.options, gridOptions = ((options && isObject(options.grid)) ? options.grid : {}), labelOpts = axis.options.labels, align = labelOpts.align,
  224. // verticalAlign is currently not supported for axis.labels.
  225. verticalAlign = 'middle', // labelOpts.verticalAlign,
  226. side = axisSide[axis.side], tickmarkOffset = e.tickmarkOffset, tickPositions = axis.tickPositions, tickPos = tick.pos - tickmarkOffset, nextTickPos = (isNumber(tickPositions[e.index + 1]) ?
  227. tickPositions[e.index + 1] - tickmarkOffset :
  228. axis.max + tickmarkOffset), tickSize = axis.tickSize('tick', true), tickWidth = isArray(tickSize) ? tickSize[0] : 0, crispCorr = tickSize && tickSize[1] / 2, labelHeight, lblMetrics, lines, bottom, top, left, right;
  229. // Only center tick labels in grid axes
  230. if (gridOptions.enabled === true) {
  231. // Calculate top and bottom positions of the cell.
  232. if (side === 'top') {
  233. bottom = axis.top + axis.offset;
  234. top = bottom - tickWidth;
  235. }
  236. else if (side === 'bottom') {
  237. top = chart.chartHeight - axis.bottom + axis.offset;
  238. bottom = top + tickWidth;
  239. }
  240. else {
  241. bottom = axis.top + axis.len - axis.translate(reversed ? nextTickPos : tickPos);
  242. top = axis.top + axis.len - axis.translate(reversed ? tickPos : nextTickPos);
  243. }
  244. // Calculate left and right positions of the cell.
  245. if (side === 'right') {
  246. left = chart.chartWidth - axis.right + axis.offset;
  247. right = left + tickWidth;
  248. }
  249. else if (side === 'left') {
  250. right = axis.left + axis.offset;
  251. left = right - tickWidth;
  252. }
  253. else {
  254. left = Math.round(axis.left + axis.translate(reversed ? nextTickPos : tickPos)) - crispCorr;
  255. right = Math.round(axis.left + axis.translate(reversed ? tickPos : nextTickPos)) - crispCorr;
  256. }
  257. tick.slotWidth = right - left;
  258. // Calculate the positioning of the label based on alignment.
  259. e.pos.x = (align === 'left' ?
  260. left :
  261. align === 'right' ?
  262. right :
  263. left + ((right - left) / 2) // default to center
  264. );
  265. e.pos.y = (verticalAlign === 'top' ?
  266. top :
  267. verticalAlign === 'bottom' ?
  268. bottom :
  269. top + ((bottom - top) / 2) // default to middle
  270. );
  271. lblMetrics = chart.renderer.fontMetrics(labelOpts.style.fontSize, label.element);
  272. labelHeight = label.getBBox().height;
  273. // Adjustment to y position to align the label correctly.
  274. // Would be better to have a setter or similar for this.
  275. if (!labelOpts.useHTML) {
  276. lines = Math.round(labelHeight / lblMetrics.h);
  277. e.pos.y += (
  278. // Center the label
  279. // TODO: why does this actually center the label?
  280. ((lblMetrics.b - (lblMetrics.h - lblMetrics.f)) / 2) +
  281. // Adjust for height of additional lines.
  282. -(((lines - 1) * lblMetrics.h) / 2));
  283. }
  284. else {
  285. e.pos.y += (
  286. // Readjust yCorr in htmlUpdateTransform
  287. lblMetrics.b +
  288. // Adjust for height of html label
  289. -(labelHeight / 2));
  290. }
  291. e.pos.x += (axis.horiz && labelOpts.x || 0);
  292. }
  293. });
  294. // Draw vertical axis ticks extra long to create cell floors and roofs.
  295. // Overrides the tickLength for vertical axes.
  296. addEvent(Axis, 'afterTickSize', function (e) {
  297. var _a = this, defaultLeftAxisOptions = _a.defaultLeftAxisOptions, horiz = _a.horiz, _b = _a.options.grid, gridOptions = _b === void 0 ? {} : _b;
  298. var dimensions = this.maxLabelDimensions;
  299. if (gridOptions.enabled) {
  300. var labelPadding = (Math.abs(defaultLeftAxisOptions.labels.x) * 2);
  301. var distance = horiz ?
  302. gridOptions.cellHeight || labelPadding + dimensions.height :
  303. labelPadding + dimensions.width;
  304. if (isArray(e.tickSize)) {
  305. e.tickSize[0] = distance;
  306. }
  307. else {
  308. e.tickSize = [distance];
  309. }
  310. }
  311. });
  312. addEvent(Axis, 'afterGetTitlePosition', function (e) {
  313. var axis = this, options = axis.options, gridOptions = (options && isObject(options.grid)) ? options.grid : {};
  314. if (gridOptions.enabled === true) {
  315. // compute anchor points for each of the title align options
  316. var title = axis.axisTitle, titleWidth = title && title.getBBox().width, horiz = axis.horiz, axisLeft = axis.left, axisTop = axis.top, axisWidth = axis.width, axisHeight = axis.height, axisTitleOptions = options.title, opposite = axis.opposite, offset = axis.offset, tickSize = axis.tickSize() || [0], xOption = axisTitleOptions.x || 0, yOption = axisTitleOptions.y || 0, titleMargin = pick(axisTitleOptions.margin, horiz ? 5 : 10), titleFontSize = axis.chart.renderer.fontMetrics(axisTitleOptions.style &&
  317. axisTitleOptions.style.fontSize, title).f,
  318. // TODO account for alignment
  319. // the position in the perpendicular direction of the axis
  320. offAxis = (horiz ? axisTop + axisHeight : axisLeft) +
  321. (horiz ? 1 : -1) * // horizontal axis reverses the margin
  322. (opposite ? -1 : 1) * // so does opposite axes
  323. (tickSize[0] / 2) +
  324. (axis.side === axisSide.bottom ? titleFontSize : 0);
  325. e.titlePosition.x = horiz ?
  326. axisLeft - titleWidth / 2 - titleMargin + xOption :
  327. offAxis + (opposite ? axisWidth : 0) + offset + xOption;
  328. e.titlePosition.y = horiz ?
  329. (offAxis -
  330. (opposite ? axisHeight : 0) +
  331. (opposite ? titleFontSize : -titleFontSize) / 2 +
  332. offset +
  333. yOption) :
  334. axisTop - titleMargin + yOption;
  335. }
  336. });
  337. // Avoid altering tickInterval when reserving space.
  338. wrap(Axis.prototype, 'unsquish', function (proceed) {
  339. var axis = this, options = axis.options, gridOptions = (options && isObject(options.grid)) ? options.grid : {};
  340. if (gridOptions.enabled === true && this.categories) {
  341. return this.tickInterval;
  342. }
  343. return proceed.apply(this, argsToArray(arguments));
  344. });
  345. addEvent(Axis, 'afterSetOptions',
  346. /**
  347. * Creates a left and right wall on horizontal axes:
  348. *
  349. * - Places leftmost tick at the start of the axis, to create a left wall
  350. *
  351. * - Ensures that the rightmost tick is at the end of the axis, to create a
  352. * right wall.
  353. *
  354. * @private
  355. * @function
  356. */
  357. function (e) {
  358. var options = this.options, userOptions = e.userOptions, gridAxisOptions, gridOptions = ((options && isObject(options.grid)) ? options.grid : {});
  359. if (gridOptions.enabled === true) {
  360. // Merge the user options into default grid axis options so that
  361. // when a user option is set, it takes presedence.
  362. gridAxisOptions = merge(true, {
  363. className: ('highcharts-grid-axis ' + (userOptions.className || '')),
  364. dateTimeLabelFormats: {
  365. hour: {
  366. list: ['%H:%M', '%H']
  367. },
  368. day: {
  369. list: ['%A, %e. %B', '%a, %e. %b', '%E']
  370. },
  371. week: {
  372. list: ['Week %W', 'W%W']
  373. },
  374. month: {
  375. list: ['%B', '%b', '%o']
  376. }
  377. },
  378. grid: {
  379. borderWidth: 1
  380. },
  381. labels: {
  382. padding: 2,
  383. style: {
  384. fontSize: '13px'
  385. }
  386. },
  387. margin: 0,
  388. title: {
  389. text: null,
  390. reserveSpace: false,
  391. rotation: 0
  392. },
  393. // In a grid axis, only allow one unit of certain types, for
  394. // example we shouln't have one grid cell spanning two days.
  395. units: [[
  396. 'millisecond',
  397. [1, 10, 100]
  398. ], [
  399. 'second',
  400. [1, 10]
  401. ], [
  402. 'minute',
  403. [1, 5, 15]
  404. ], [
  405. 'hour',
  406. [1, 6]
  407. ], [
  408. 'day',
  409. [1]
  410. ], [
  411. 'week',
  412. [1]
  413. ], [
  414. 'month',
  415. [1]
  416. ], [
  417. 'year',
  418. null
  419. ]]
  420. }, userOptions);
  421. // X-axis specific options
  422. if (this.coll === 'xAxis') {
  423. // For linked axes, tickPixelInterval is used only if the
  424. // tickPositioner below doesn't run or returns undefined (like
  425. // multiple years)
  426. if (defined(userOptions.linkedTo) &&
  427. !defined(userOptions.tickPixelInterval)) {
  428. gridAxisOptions.tickPixelInterval = 350;
  429. }
  430. // For the secondary grid axis, use the primary axis' tick
  431. // intervals and return ticks one level higher.
  432. if (
  433. // Check for tick pixel interval in options
  434. !defined(userOptions.tickPixelInterval) &&
  435. // Only for linked axes
  436. defined(userOptions.linkedTo) &&
  437. !defined(userOptions.tickPositioner) &&
  438. !defined(userOptions.tickInterval)) {
  439. gridAxisOptions.tickPositioner = function (min, max) {
  440. var parentInfo = (this.linkedParent &&
  441. this.linkedParent.tickPositions &&
  442. this.linkedParent.tickPositions.info);
  443. if (parentInfo) {
  444. var unitIdx, count, unitName, i, units = gridAxisOptions.units, unitRange;
  445. for (i = 0; i < units.length; i++) {
  446. if (units[i][0] ===
  447. parentInfo.unitName) {
  448. unitIdx = i;
  449. break;
  450. }
  451. }
  452. // Get the first allowed count on the next unit.
  453. if (units[unitIdx + 1]) {
  454. unitName = units[unitIdx + 1][0];
  455. count =
  456. (units[unitIdx + 1][1] || [1])[0];
  457. // In case the base X axis shows years, make the
  458. // secondary axis show ten times the years (#11427)
  459. }
  460. else if (parentInfo.unitName === 'year') {
  461. unitName = 'year';
  462. count = parentInfo.count * 10;
  463. }
  464. unitRange = H.timeUnits[unitName];
  465. this.tickInterval = unitRange * count;
  466. return this.getTimeTicks({
  467. unitRange: unitRange,
  468. count: count,
  469. unitName: unitName
  470. }, min, max, this.options.startOfWeek);
  471. }
  472. };
  473. }
  474. }
  475. // Now merge the combined options into the axis options
  476. merge(true, this.options, gridAxisOptions);
  477. if (this.horiz) {
  478. /* _________________________
  479. Make this: ___|_____|_____|_____|__|
  480. ^ ^
  481. _________________________
  482. Into this: |_____|_____|_____|_____|
  483. ^ ^ */
  484. options.minPadding = pick(userOptions.minPadding, 0);
  485. options.maxPadding = pick(userOptions.maxPadding, 0);
  486. }
  487. // If borderWidth is set, then use its value for tick and line
  488. // width.
  489. if (isNumber(options.grid.borderWidth)) {
  490. options.tickWidth = options.lineWidth = gridOptions.borderWidth;
  491. }
  492. }
  493. });
  494. addEvent(Axis, 'afterSetAxisTranslation', function () {
  495. var axis = this, options = axis.options, gridOptions = ((options && isObject(options.grid)) ? options.grid : {}), tickInfo = this.tickPositions && this.tickPositions.info, userLabels = this.userOptions.labels || {};
  496. if (this.horiz) {
  497. if (gridOptions.enabled === true) {
  498. axis.series.forEach(function (series) {
  499. series.options.pointRange = 0;
  500. });
  501. }
  502. // Lower level time ticks, like hours or minutes, represent points
  503. // in time and not ranges. These should be aligned left in the grid
  504. // cell by default. The same applies to years of higher order.
  505. if (tickInfo &&
  506. (options.dateTimeLabelFormats[tickInfo.unitName]
  507. .range === false ||
  508. tickInfo.count > 1 // years
  509. ) &&
  510. !defined(userLabels.align)) {
  511. options.labels.align = 'left';
  512. if (!defined(userLabels.x)) {
  513. options.labels.x = 3;
  514. }
  515. }
  516. }
  517. });
  518. // @todo Does this function do what the drawing says? Seems to affect ticks and
  519. // not the labels directly?
  520. addEvent(Axis, 'trimTicks',
  521. /**
  522. * Makes tick labels which are usually ignored in a linked axis displayed if
  523. * they are within range of linkedParent.min.
  524. * ```
  525. * _____________________________
  526. * | | | | |
  527. * Make this: | | 2 | 3 | 4 |
  528. * |___|_______|_______|_______|
  529. * ^
  530. * _____________________________
  531. * | | | | |
  532. * Into this: | 1 | 2 | 3 | 4 |
  533. * |___|_______|_______|_______|
  534. * ^
  535. * ```
  536. *
  537. * @private
  538. */
  539. function () {
  540. var axis = this, options = axis.options, gridOptions = ((options && isObject(options.grid)) ? options.grid : {}), categoryAxis = axis.categories, tickPositions = axis.tickPositions, firstPos = tickPositions[0], lastPos = tickPositions[tickPositions.length - 1], linkedMin = axis.linkedParent && axis.linkedParent.min, linkedMax = axis.linkedParent && axis.linkedParent.max, min = linkedMin || axis.min, max = linkedMax || axis.max, tickInterval = axis.tickInterval, endMoreThanMin = (firstPos < min &&
  541. firstPos + tickInterval > min), startLessThanMax = (lastPos > max &&
  542. lastPos - tickInterval < max);
  543. if (gridOptions.enabled === true &&
  544. !categoryAxis &&
  545. (axis.horiz || axis.isLinked)) {
  546. if (endMoreThanMin && !options.startOnTick) {
  547. tickPositions[0] = min;
  548. }
  549. if (startLessThanMax && !options.endOnTick) {
  550. tickPositions[tickPositions.length - 1] = max;
  551. }
  552. }
  553. });
  554. addEvent(Axis, 'afterRender',
  555. /**
  556. * Draw an extra line on the far side of the outermost axis,
  557. * creating floor/roof/wall of a grid. And some padding.
  558. * ```
  559. * Make this:
  560. * (axis.min) __________________________ (axis.max)
  561. * | | | | |
  562. * Into this:
  563. * (axis.min) __________________________ (axis.max)
  564. * ___|____|____|____|____|__
  565. * ```
  566. *
  567. * @private
  568. * @function
  569. *
  570. * @param {Function} proceed
  571. * the original function
  572. */
  573. function () {
  574. var axis = this, options = axis.options, gridOptions = ((options && isObject(options.grid)) ? options.grid : {}), yStartIndex, yEndIndex, xStartIndex, xEndIndex, renderer = axis.chart.renderer;
  575. if (gridOptions.enabled === true) {
  576. // @todo acutual label padding (top, bottom, left, right)
  577. axis.maxLabelDimensions = axis.getMaxLabelDimensions(axis.ticks, axis.tickPositions);
  578. // Remove right wall before rendering if updating
  579. if (axis.rightWall) {
  580. axis.rightWall.destroy();
  581. }
  582. /*
  583. Draw an extra axis line on outer axes
  584. >
  585. Make this: |______|______|______|___
  586. > _________________________
  587. Into this: |______|______|______|__|
  588. */
  589. if (axis.isOuterAxis() && axis.axisLine) {
  590. var lineWidth = options.lineWidth;
  591. if (lineWidth) {
  592. var linePath = axis.getLinePath(lineWidth);
  593. xStartIndex = linePath.indexOf('M') + 1;
  594. xEndIndex = linePath.indexOf('L') + 1;
  595. yStartIndex = linePath.indexOf('M') + 2;
  596. yEndIndex = linePath.indexOf('L') + 2;
  597. // Negate distance if top or left axis
  598. // Subtract 1px to draw the line at the end of the tick
  599. var distance = (axis.tickSize('tick')[0] - 1) * ((axis.side === axisSide.top ||
  600. axis.side === axisSide.left) ? -1 : 1);
  601. // If axis is horizontal, reposition line path vertically
  602. if (axis.horiz) {
  603. linePath[yStartIndex] =
  604. linePath[yStartIndex] + distance;
  605. linePath[yEndIndex] =
  606. linePath[yEndIndex] + distance;
  607. }
  608. else {
  609. // If axis is vertical, reposition line path
  610. // horizontally
  611. linePath[xStartIndex] =
  612. linePath[xStartIndex] + distance;
  613. linePath[xEndIndex] =
  614. linePath[xEndIndex] + distance;
  615. }
  616. if (!axis.axisLineExtra) {
  617. axis.axisLineExtra = renderer
  618. .path(linePath)
  619. .attr({
  620. zIndex: 7
  621. })
  622. .addClass('highcharts-axis-line')
  623. .add(axis.axisGroup);
  624. if (!renderer.styledMode) {
  625. axis.axisLineExtra.attr({
  626. stroke: options.lineColor,
  627. 'stroke-width': lineWidth
  628. });
  629. }
  630. }
  631. else {
  632. axis.axisLineExtra.animate({
  633. d: linePath
  634. });
  635. }
  636. // show or hide the line depending on options.showEmpty
  637. axis.axisLine[axis.showAxis ? 'show' : 'hide'](true);
  638. }
  639. }
  640. (axis.columns || []).forEach(function (column) {
  641. column.render();
  642. });
  643. }
  644. });
  645. // Handle columns and getOffset
  646. var onGridAxisAfterGetOffset = function onGridAxisAfterGetOffset() {
  647. (this.columns || []).forEach(function (column) {
  648. column.getOffset();
  649. });
  650. };
  651. var onGridAxisAfterInit = function onGridAxisAfterInit() {
  652. var axis = this, chart = axis.chart, userOptions = axis.userOptions, options = axis.options, gridOptions = options && isObject(options.grid) ? options.grid : {};
  653. if (gridOptions.enabled) {
  654. applyGridOptions(axis);
  655. // TODO: wrap the axis instead
  656. wrap(axis, 'labelFormatter', function (proceed) {
  657. var axis = this.axis, tickPos = axis.tickPositions, value = this.value, series = (axis.isLinked ?
  658. axis.linkedParent :
  659. axis).series[0], isFirst = value === tickPos[0], isLast = value === tickPos[tickPos.length - 1], point = series && H.find(series.options.data, function (p) {
  660. return p[axis.isXAxis ? 'x' : 'y'] === value;
  661. });
  662. // Make additional properties available for the
  663. // formatter
  664. this.isFirst = isFirst;
  665. this.isLast = isLast;
  666. this.point = point;
  667. // Call original labelFormatter
  668. return proceed.call(this);
  669. });
  670. }
  671. if (gridOptions.columns) {
  672. var columns = axis.columns = [], columnIndex = axis.columnIndex = 0;
  673. // Handle columns, each column is a grid axis
  674. while (++columnIndex < gridOptions.columns.length) {
  675. var columnOptions = merge(userOptions, gridOptions.columns[gridOptions.columns.length - columnIndex - 1], {
  676. linkedTo: 0,
  677. // Force to behave like category axis
  678. type: 'category'
  679. });
  680. delete columnOptions.grid.columns; // Prevent recursion
  681. var column = new Axis(axis.chart, columnOptions, true);
  682. column.isColumn = true;
  683. column.columnIndex = columnIndex;
  684. // Remove column axis from chart axes array, and place it
  685. // in the columns array.
  686. erase(chart.axes, column);
  687. erase(chart[axis.coll], column);
  688. columns.push(column);
  689. }
  690. }
  691. };
  692. var onGridAxisAfterSetChartSize = function onGridAxisAfterSetChartSize() {
  693. this.axes.forEach(function (axis) {
  694. (axis.columns || []).forEach(function (column) {
  695. column.setAxisSize();
  696. column.setAxisTranslation();
  697. });
  698. });
  699. };
  700. // Handle columns and setScale
  701. var onGridAxisAfterSetScale = function onGridAxisAfterSetScale() {
  702. (this.columns || []).forEach(function (column) {
  703. column.setScale();
  704. });
  705. };
  706. var onGridAxisDestroy = function onGridAxisDestroy(e) {
  707. (this.columns || []).forEach(function (column) {
  708. column.destroy(e.keepEvents);
  709. });
  710. };
  711. // Wraps axis init to draw cell walls on vertical axes.
  712. var onGridAxisInit = function onGridAxisInit(e) {
  713. var userOptions = e.userOptions, gridOptions = ((userOptions && isObject(userOptions.grid)) ?
  714. userOptions.grid :
  715. {});
  716. if (gridOptions.enabled && defined(gridOptions.borderColor)) {
  717. userOptions.tickColor = userOptions.lineColor = gridOptions.borderColor;
  718. }
  719. };
  720. var onGridAxisAfterSetOptions = function onGridAxisAfterSetOptions(e) {
  721. var axis = this, userOptions = e.userOptions, gridOptions = ((userOptions && isObject(userOptions.grid)) ?
  722. userOptions.grid :
  723. {}), columns = gridOptions.columns;
  724. // Add column options to the parent axis.
  725. // Children has their column options set on init in onGridAxisAfterInit.
  726. if (gridOptions.enabled && columns) {
  727. merge(true, axis.options, columns[columns.length - 1]);
  728. }
  729. };
  730. var axisEvents = {
  731. afterGetOffset: onGridAxisAfterGetOffset,
  732. afterInit: onGridAxisAfterInit,
  733. afterSetOptions: onGridAxisAfterSetOptions,
  734. afterSetScale: onGridAxisAfterSetScale,
  735. destroy: onGridAxisDestroy,
  736. init: onGridAxisInit
  737. };
  738. // Add event handlers
  739. Object.keys(axisEvents).forEach(function (event) {
  740. addEvent(Axis, event, axisEvents[event]);
  741. });
  742. addEvent(Chart, 'afterSetChartSize', onGridAxisAfterSetChartSize);
  743. });
  744. _registerModule(_modules, 'parts-gantt/Tree.js', [_modules['parts/Utilities.js']], function (U) {
  745. /* *
  746. *
  747. * (c) 2016-2019 Highsoft AS
  748. *
  749. * Authors: Jon Arild Nygard
  750. *
  751. * License: www.highcharts.com/license
  752. *
  753. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  754. *
  755. * */
  756. /* eslint no-console: 0 */
  757. var extend = U.extend, isNumber = U.isNumber, pick = U.pick;
  758. var isFunction = function (x) {
  759. return typeof x === 'function';
  760. };
  761. /**
  762. * Creates an object map from parent id to childrens index.
  763. *
  764. * @private
  765. * @function Highcharts.Tree#getListOfParents
  766. *
  767. * @param {Array<*>} data
  768. * List of points set in options. `Array.parent` is parent id of point.
  769. *
  770. * @param {Array<string>} ids
  771. * List of all point ids.
  772. *
  773. * @return {Highcharts.Dictionary<Array<*>>}
  774. * Map from parent id to children index in data
  775. */
  776. var getListOfParents = function (data, ids) {
  777. var listOfParents = data.reduce(function (prev, curr) {
  778. var parent = pick(curr.parent, '');
  779. if (typeof prev[parent] === 'undefined') {
  780. prev[parent] = [];
  781. }
  782. prev[parent].push(curr);
  783. return prev;
  784. }, {}), parents = Object.keys(listOfParents);
  785. // If parent does not exist, hoist parent to root of tree.
  786. parents.forEach(function (parent, list) {
  787. var children = listOfParents[parent];
  788. if ((parent !== '') && (ids.indexOf(parent) === -1)) {
  789. children.forEach(function (child) {
  790. list[''].push(child);
  791. });
  792. delete list[parent];
  793. }
  794. });
  795. return listOfParents;
  796. };
  797. var getNode = function (id, parent, level, data, mapOfIdToChildren, options) {
  798. var descendants = 0, height = 0, after = options && options.after, before = options && options.before, node = {
  799. data: data,
  800. depth: level - 1,
  801. id: id,
  802. level: level,
  803. parent: parent
  804. }, start, end, children;
  805. // Allow custom logic before the children has been created.
  806. if (isFunction(before)) {
  807. before(node, options);
  808. }
  809. // Call getNode recursively on the children. Calulate the height of the
  810. // node, and the number of descendants.
  811. children = ((mapOfIdToChildren[id] || [])).map(function (child) {
  812. var node = getNode(child.id, id, (level + 1), child, mapOfIdToChildren, options), childStart = child.start, childEnd = (child.milestone === true ?
  813. childStart :
  814. child.end);
  815. // Start should be the lowest child.start.
  816. start = ((!isNumber(start) || childStart < start) ?
  817. childStart :
  818. start);
  819. // End should be the largest child.end.
  820. // If child is milestone, then use start as end.
  821. end = ((!isNumber(end) || childEnd > end) ?
  822. childEnd :
  823. end);
  824. descendants = descendants + 1 + node.descendants;
  825. height = Math.max(node.height + 1, height);
  826. return node;
  827. });
  828. // Calculate start and end for point if it is not already explicitly set.
  829. if (data) {
  830. data.start = pick(data.start, start);
  831. data.end = pick(data.end, end);
  832. }
  833. extend(node, {
  834. children: children,
  835. descendants: descendants,
  836. height: height
  837. });
  838. // Allow custom logic after the children has been created.
  839. if (isFunction(after)) {
  840. after(node, options);
  841. }
  842. return node;
  843. };
  844. var getTree = function (data, options) {
  845. var ids = data.map(function (d) {
  846. return d.id;
  847. }), mapOfIdToChildren = getListOfParents(data, ids);
  848. return getNode('', null, 1, null, mapOfIdToChildren, options);
  849. };
  850. var Tree = {
  851. getListOfParents: getListOfParents,
  852. getNode: getNode,
  853. getTree: getTree
  854. };
  855. return Tree;
  856. });
  857. _registerModule(_modules, 'mixins/tree-series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  858. /* *
  859. *
  860. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  861. *
  862. * */
  863. var extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, pick = U.pick;
  864. var isBoolean = function (x) {
  865. return typeof x === 'boolean';
  866. }, isFn = function (x) {
  867. return typeof x === 'function';
  868. }, merge = H.merge;
  869. /* eslint-disable valid-jsdoc */
  870. /**
  871. * @todo Combine buildTree and buildNode with setTreeValues
  872. * @todo Remove logic from Treemap and make it utilize this mixin.
  873. * @private
  874. */
  875. var setTreeValues = function setTreeValues(tree, options) {
  876. var before = options.before, idRoot = options.idRoot, mapIdToNode = options.mapIdToNode, nodeRoot = mapIdToNode[idRoot], levelIsConstant = (isBoolean(options.levelIsConstant) ?
  877. options.levelIsConstant :
  878. true), points = options.points, point = points[tree.i], optionsPoint = point && point.options || {}, childrenTotal = 0, children = [], value;
  879. extend(tree, {
  880. levelDynamic: tree.level - (levelIsConstant ? 0 : nodeRoot.level),
  881. name: pick(point && point.name, ''),
  882. visible: (idRoot === tree.id ||
  883. (isBoolean(options.visible) ? options.visible : false))
  884. });
  885. if (isFn(before)) {
  886. tree = before(tree, options);
  887. }
  888. // First give the children some values
  889. tree.children.forEach(function (child, i) {
  890. var newOptions = extend({}, options);
  891. extend(newOptions, {
  892. index: i,
  893. siblings: tree.children.length,
  894. visible: tree.visible
  895. });
  896. child = setTreeValues(child, newOptions);
  897. children.push(child);
  898. if (child.visible) {
  899. childrenTotal += child.val;
  900. }
  901. });
  902. tree.visible = childrenTotal > 0 || tree.visible;
  903. // Set the values
  904. value = pick(optionsPoint.value, childrenTotal);
  905. extend(tree, {
  906. children: children,
  907. childrenTotal: childrenTotal,
  908. isLeaf: tree.visible && !childrenTotal,
  909. val: value
  910. });
  911. return tree;
  912. };
  913. /**
  914. * @private
  915. */
  916. var getColor = function getColor(node, options) {
  917. var index = options.index, mapOptionsToLevel = options.mapOptionsToLevel, parentColor = options.parentColor, parentColorIndex = options.parentColorIndex, series = options.series, colors = options.colors, siblings = options.siblings, points = series.points, getColorByPoint, chartOptionsChart = series.chart.options.chart, point, level, colorByPoint, colorIndexByPoint, color, colorIndex;
  918. /**
  919. * @private
  920. */
  921. function variation(color) {
  922. var colorVariation = level && level.colorVariation;
  923. if (colorVariation) {
  924. if (colorVariation.key === 'brightness') {
  925. return H.color(color).brighten(colorVariation.to * (index / siblings)).get();
  926. }
  927. }
  928. return color;
  929. }
  930. if (node) {
  931. point = points[node.i];
  932. level = mapOptionsToLevel[node.level] || {};
  933. getColorByPoint = point && level.colorByPoint;
  934. if (getColorByPoint) {
  935. colorIndexByPoint = point.index % (colors ?
  936. colors.length :
  937. chartOptionsChart.colorCount);
  938. colorByPoint = colors && colors[colorIndexByPoint];
  939. }
  940. // Select either point color, level color or inherited color.
  941. if (!series.chart.styledMode) {
  942. color = pick(point && point.options.color, level && level.color, colorByPoint, parentColor && variation(parentColor), series.color);
  943. }
  944. colorIndex = pick(point && point.options.colorIndex, level && level.colorIndex, colorIndexByPoint, parentColorIndex, options.colorIndex);
  945. }
  946. return {
  947. color: color,
  948. colorIndex: colorIndex
  949. };
  950. };
  951. /**
  952. * Creates a map from level number to its given options.
  953. *
  954. * @private
  955. * @function getLevelOptions
  956. * @param {object} params
  957. * Object containing parameters.
  958. * - `defaults` Object containing default options. The default options
  959. * are merged with the userOptions to get the final options for a
  960. * specific level.
  961. * - `from` The lowest level number.
  962. * - `levels` User options from series.levels.
  963. * - `to` The highest level number.
  964. * @return {Highcharts.Dictionary<object>|null}
  965. * Returns a map from level number to its given options.
  966. */
  967. var getLevelOptions = function getLevelOptions(params) {
  968. var result = null, defaults, converted, i, from, to, levels;
  969. if (isObject(params)) {
  970. result = {};
  971. from = isNumber(params.from) ? params.from : 1;
  972. levels = params.levels;
  973. converted = {};
  974. defaults = isObject(params.defaults) ? params.defaults : {};
  975. if (isArray(levels)) {
  976. converted = levels.reduce(function (obj, item) {
  977. var level, levelIsConstant, options;
  978. if (isObject(item) && isNumber(item.level)) {
  979. options = merge({}, item);
  980. levelIsConstant = (isBoolean(options.levelIsConstant) ?
  981. options.levelIsConstant :
  982. defaults.levelIsConstant);
  983. // Delete redundant properties.
  984. delete options.levelIsConstant;
  985. delete options.level;
  986. // Calculate which level these options apply to.
  987. level = item.level + (levelIsConstant ? 0 : from - 1);
  988. if (isObject(obj[level])) {
  989. extend(obj[level], options);
  990. }
  991. else {
  992. obj[level] = options;
  993. }
  994. }
  995. return obj;
  996. }, {});
  997. }
  998. to = isNumber(params.to) ? params.to : 1;
  999. for (i = 0; i <= to; i++) {
  1000. result[i] = merge({}, defaults, isObject(converted[i]) ? converted[i] : {});
  1001. }
  1002. }
  1003. return result;
  1004. };
  1005. /**
  1006. * Update the rootId property on the series. Also makes sure that it is
  1007. * accessible to exporting.
  1008. *
  1009. * @private
  1010. * @function updateRootId
  1011. *
  1012. * @param {object} series
  1013. * The series to operate on.
  1014. *
  1015. * @return {string}
  1016. * Returns the resulting rootId after update.
  1017. */
  1018. var updateRootId = function (series) {
  1019. var rootId, options;
  1020. if (isObject(series)) {
  1021. // Get the series options.
  1022. options = isObject(series.options) ? series.options : {};
  1023. // Calculate the rootId.
  1024. rootId = pick(series.rootNode, options.rootId, '');
  1025. // Set rootId on series.userOptions to pick it up in exporting.
  1026. if (isObject(series.userOptions)) {
  1027. series.userOptions.rootId = rootId;
  1028. }
  1029. // Set rootId on series to pick it up on next update.
  1030. series.rootNode = rootId;
  1031. }
  1032. return rootId;
  1033. };
  1034. var result = {
  1035. getColor: getColor,
  1036. getLevelOptions: getLevelOptions,
  1037. setTreeValues: setTreeValues,
  1038. updateRootId: updateRootId
  1039. };
  1040. return result;
  1041. });
  1042. _registerModule(_modules, 'modules/broken-axis.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  1043. /* *
  1044. *
  1045. * (c) 2009-2019 Torstein Honsi
  1046. *
  1047. * License: www.highcharts.com/license
  1048. *
  1049. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1050. *
  1051. * */
  1052. var extend = U.extend, isArray = U.isArray, pick = U.pick;
  1053. var addEvent = H.addEvent, find = H.find, fireEvent = H.fireEvent, Axis = H.Axis, Series = H.Series;
  1054. /**
  1055. * Returns the first break found where the x is larger then break.from and
  1056. * smaller then break.to.
  1057. *
  1058. * @param {number} x
  1059. * The number which should be within a break.
  1060. * @param {Array<Highcharts.XAxisBreaksOptions>} breaks
  1061. * The array of breaks to search within.
  1062. * @return {Highcharts.XAxisBreaksOptions|undefined}
  1063. * Returns the first break found that matches, returns false if no break
  1064. * is found.
  1065. */
  1066. var findBreakAt = function (x, breaks) {
  1067. return find(breaks, function (b) {
  1068. return b.from < x && x < b.to;
  1069. });
  1070. };
  1071. extend(Axis.prototype, {
  1072. isInBreak: function (brk, val) {
  1073. var ret, repeat = brk.repeat || Infinity, from = brk.from, length = brk.to - brk.from, test = (val >= from ?
  1074. (val - from) % repeat :
  1075. repeat - ((from - val) % repeat));
  1076. if (!brk.inclusive) {
  1077. ret = test < length && test !== 0;
  1078. }
  1079. else {
  1080. ret = test <= length;
  1081. }
  1082. return ret;
  1083. },
  1084. isInAnyBreak: function (val, testKeep) {
  1085. var breaks = this.options.breaks, i = breaks && breaks.length, inbrk, keep, ret;
  1086. if (i) {
  1087. while (i--) {
  1088. if (this.isInBreak(breaks[i], val)) {
  1089. inbrk = true;
  1090. if (!keep) {
  1091. keep = pick(breaks[i].showPoints, !this.isXAxis);
  1092. }
  1093. }
  1094. }
  1095. if (inbrk && testKeep) {
  1096. ret = inbrk && !keep;
  1097. }
  1098. else {
  1099. ret = inbrk;
  1100. }
  1101. }
  1102. return ret;
  1103. }
  1104. });
  1105. /* eslint-disable no-invalid-this */
  1106. addEvent(Axis, 'afterInit', function () {
  1107. if (typeof this.setBreaks === 'function') {
  1108. this.setBreaks(this.options.breaks, false);
  1109. }
  1110. });
  1111. addEvent(Axis, 'afterSetTickPositions', function () {
  1112. if (this.isBroken) {
  1113. var axis = this, tickPositions = this.tickPositions, info = this.tickPositions.info, newPositions = [], i;
  1114. for (i = 0; i < tickPositions.length; i++) {
  1115. if (!axis.isInAnyBreak(tickPositions[i])) {
  1116. newPositions.push(tickPositions[i]);
  1117. }
  1118. }
  1119. this.tickPositions = newPositions;
  1120. this.tickPositions.info = info;
  1121. }
  1122. });
  1123. // Force Axis to be not-ordinal when breaks are defined
  1124. addEvent(Axis, 'afterSetOptions', function () {
  1125. if (this.isBroken) {
  1126. this.options.ordinal = false;
  1127. }
  1128. });
  1129. /**
  1130. * Dynamically set or unset breaks in an axis. This function in lighter than
  1131. * usin Axis.update, and it also preserves animation.
  1132. *
  1133. * @private
  1134. * @function Highcharts.Axis#setBreaks
  1135. *
  1136. * @param {Array<Highcharts.XAxisBreaksOptions>} [breaks]
  1137. * The breaks to add. When `undefined` it removes existing breaks.
  1138. *
  1139. * @param {boolean} [redraw=true]
  1140. * Whether to redraw the chart immediately.
  1141. *
  1142. * @return {void}
  1143. */
  1144. Axis.prototype.setBreaks = function (breaks, redraw) {
  1145. var axis = this, isBroken = (isArray(breaks) && !!breaks.length);
  1146. /* eslint-disable valid-jsdoc */
  1147. /**
  1148. * @private
  1149. */
  1150. function breakVal2Lin(val) {
  1151. var nval = val, brk, i;
  1152. for (i = 0; i < axis.breakArray.length; i++) {
  1153. brk = axis.breakArray[i];
  1154. if (brk.to <= val) {
  1155. nval -= brk.len;
  1156. }
  1157. else if (brk.from >= val) {
  1158. break;
  1159. }
  1160. else if (axis.isInBreak(brk, val)) {
  1161. nval -= (val - brk.from);
  1162. break;
  1163. }
  1164. }
  1165. return nval;
  1166. }
  1167. /**
  1168. * @private
  1169. */
  1170. function breakLin2Val(val) {
  1171. var nval = val, brk, i;
  1172. for (i = 0; i < axis.breakArray.length; i++) {
  1173. brk = axis.breakArray[i];
  1174. if (brk.from >= nval) {
  1175. break;
  1176. }
  1177. else if (brk.to < nval) {
  1178. nval += brk.len;
  1179. }
  1180. else if (axis.isInBreak(brk, nval)) {
  1181. nval += brk.len;
  1182. }
  1183. }
  1184. return nval;
  1185. }
  1186. /* eslint-enable valid-jsdoc */
  1187. axis.isDirty = axis.isBroken !== isBroken;
  1188. axis.isBroken = isBroken;
  1189. axis.options.breaks = axis.userOptions.breaks = breaks;
  1190. axis.forceRedraw = true; // Force recalculation in setScale
  1191. // Recalculate series related to the axis.
  1192. axis.series.forEach(function (series) {
  1193. series.isDirty = true;
  1194. });
  1195. if (!isBroken && axis.val2lin === breakVal2Lin) {
  1196. // Revert to prototype functions
  1197. delete axis.val2lin;
  1198. delete axis.lin2val;
  1199. }
  1200. if (isBroken) {
  1201. axis.userOptions.ordinal = false;
  1202. axis.val2lin = breakVal2Lin;
  1203. axis.lin2val = breakLin2Val;
  1204. axis.setExtremes = function (newMin, newMax, redraw, animation, eventArguments) {
  1205. // If trying to set extremes inside a break, extend min to after,
  1206. // and max to before the break ( #3857 )
  1207. if (this.isBroken) {
  1208. var axisBreak, breaks = this.options.breaks;
  1209. while ((axisBreak = findBreakAt(newMin, breaks))) {
  1210. newMin = axisBreak.to;
  1211. }
  1212. while ((axisBreak = findBreakAt(newMax, breaks))) {
  1213. newMax = axisBreak.from;
  1214. }
  1215. // If both min and max is within the same break.
  1216. if (newMax < newMin) {
  1217. newMax = newMin;
  1218. }
  1219. }
  1220. Axis.prototype.setExtremes.call(this, newMin, newMax, redraw, animation, eventArguments);
  1221. };
  1222. axis.setAxisTranslation = function (saveOld) {
  1223. Axis.prototype.setAxisTranslation.call(this, saveOld);
  1224. this.unitLength = null;
  1225. if (this.isBroken) {
  1226. var breaks = axis.options.breaks,
  1227. // Temporary one:
  1228. breakArrayT = [], breakArray = [], length = 0, inBrk, repeat, min = axis.userMin || axis.min, max = axis.userMax || axis.max, pointRangePadding = pick(axis.pointRangePadding, 0), start, i;
  1229. // Min & max check (#4247)
  1230. breaks.forEach(function (brk) {
  1231. repeat = brk.repeat || Infinity;
  1232. if (axis.isInBreak(brk, min)) {
  1233. min +=
  1234. (brk.to % repeat) -
  1235. (min % repeat);
  1236. }
  1237. if (axis.isInBreak(brk, max)) {
  1238. max -=
  1239. (max % repeat) -
  1240. (brk.from % repeat);
  1241. }
  1242. });
  1243. // Construct an array holding all breaks in the axis
  1244. breaks.forEach(function (brk) {
  1245. start = brk.from;
  1246. repeat = brk.repeat || Infinity;
  1247. while (start - repeat > min) {
  1248. start -= repeat;
  1249. }
  1250. while (start < min) {
  1251. start += repeat;
  1252. }
  1253. for (i = start; i < max; i += repeat) {
  1254. breakArrayT.push({
  1255. value: i,
  1256. move: 'in'
  1257. });
  1258. breakArrayT.push({
  1259. value: i + (brk.to - brk.from),
  1260. move: 'out',
  1261. size: brk.breakSize
  1262. });
  1263. }
  1264. });
  1265. breakArrayT.sort(function (a, b) {
  1266. return ((a.value === b.value) ?
  1267. ((a.move === 'in' ? 0 : 1) -
  1268. (b.move === 'in' ? 0 : 1)) :
  1269. a.value - b.value);
  1270. });
  1271. // Simplify the breaks
  1272. inBrk = 0;
  1273. start = min;
  1274. breakArrayT.forEach(function (brk) {
  1275. inBrk += (brk.move === 'in' ? 1 : -1);
  1276. if (inBrk === 1 && brk.move === 'in') {
  1277. start = brk.value;
  1278. }
  1279. if (inBrk === 0) {
  1280. breakArray.push({
  1281. from: start,
  1282. to: brk.value,
  1283. len: brk.value - start - (brk.size || 0)
  1284. });
  1285. length += brk.value - start - (brk.size || 0);
  1286. }
  1287. });
  1288. axis.breakArray = breakArray;
  1289. // Used with staticScale, and below, the actual axis length when
  1290. // breaks are substracted.
  1291. axis.unitLength =
  1292. max - min - length + pointRangePadding;
  1293. fireEvent(axis, 'afterBreaks');
  1294. if (axis.staticScale) {
  1295. axis.transA = axis.staticScale;
  1296. }
  1297. else if (axis.unitLength) {
  1298. axis.transA *=
  1299. (max - axis.min + pointRangePadding) /
  1300. axis.unitLength;
  1301. }
  1302. if (pointRangePadding) {
  1303. axis.minPixelPadding =
  1304. axis.transA * axis.minPointOffset;
  1305. }
  1306. axis.min = min;
  1307. axis.max = max;
  1308. }
  1309. };
  1310. }
  1311. if (pick(redraw, true)) {
  1312. this.chart.redraw();
  1313. }
  1314. };
  1315. addEvent(Series, 'afterGeneratePoints', function () {
  1316. var _a = this, isDirty = _a.isDirty, connectNulls = _a.options.connectNulls, points = _a.points, xAxis = _a.xAxis, yAxis = _a.yAxis;
  1317. /* Set, or reset visibility of the points. Axis.setBreaks marks the series
  1318. as isDirty */
  1319. if (isDirty) {
  1320. var i = points.length;
  1321. while (i--) {
  1322. var point = points[i];
  1323. // Respect nulls inside the break (#4275)
  1324. var nullGap = point.y === null && connectNulls === false;
  1325. var isPointInBreak = (!nullGap &&
  1326. (xAxis && xAxis.isInAnyBreak(point.x, true) ||
  1327. yAxis && yAxis.isInAnyBreak(point.y, true)));
  1328. // Set point.visible if in any break.
  1329. // If not in break, reset visible to original value.
  1330. point.visible = isPointInBreak ?
  1331. false :
  1332. point.options.visible !== false;
  1333. }
  1334. }
  1335. });
  1336. addEvent(Series, 'afterRender', function drawPointsWrapped() {
  1337. this.drawBreaks(this.xAxis, ['x']);
  1338. this.drawBreaks(this.yAxis, pick(this.pointArrayMap, ['y']));
  1339. });
  1340. /* eslint-enable no-invalid-this */
  1341. H.Series.prototype.drawBreaks = function (axis, keys) {
  1342. var series = this, points = series.points, breaks, threshold, eventName, y;
  1343. if (!axis) {
  1344. return; // #5950
  1345. }
  1346. keys.forEach(function (key) {
  1347. breaks = axis.breakArray || [];
  1348. threshold = axis.isXAxis ?
  1349. axis.min :
  1350. pick(series.options.threshold, axis.min);
  1351. points.forEach(function (point) {
  1352. y = pick(point['stack' + key.toUpperCase()], point[key]);
  1353. breaks.forEach(function (brk) {
  1354. eventName = false;
  1355. if ((threshold < brk.from &&
  1356. y > brk.to) ||
  1357. (threshold > brk.from &&
  1358. y < brk.from)) {
  1359. eventName = 'pointBreak';
  1360. }
  1361. else if ((threshold < brk.from &&
  1362. y > brk.from &&
  1363. y < brk.to) ||
  1364. (threshold > brk.from &&
  1365. y > brk.to &&
  1366. y < brk.from)) {
  1367. eventName = 'pointInBreak';
  1368. }
  1369. if (eventName) {
  1370. fireEvent(axis, eventName, { point: point, brk: brk });
  1371. }
  1372. });
  1373. });
  1374. });
  1375. };
  1376. /**
  1377. * Extend getGraphPath by identifying gaps in the data so that we can draw a gap
  1378. * in the line or area. This was moved from ordinal axis module to broken axis
  1379. * module as of #5045.
  1380. *
  1381. * @private
  1382. * @function Highcharts.Series#gappedPath
  1383. *
  1384. * @return {Highcharts.SVGPathArray}
  1385. * Gapped path
  1386. */
  1387. H.Series.prototype.gappedPath = function () {
  1388. var currentDataGrouping = this.currentDataGrouping, groupingSize = currentDataGrouping && currentDataGrouping.gapSize, gapSize = this.options.gapSize, points = this.points.slice(), i = points.length - 1, yAxis = this.yAxis, stack;
  1389. /**
  1390. * Defines when to display a gap in the graph, together with the
  1391. * [gapUnit](plotOptions.series.gapUnit) option.
  1392. *
  1393. * In case when `dataGrouping` is enabled, points can be grouped into a
  1394. * larger time span. This can make the grouped points to have a greater
  1395. * distance than the absolute value of `gapSize` property, which will result
  1396. * in disappearing graph completely. To prevent this situation the mentioned
  1397. * distance between grouped points is used instead of previously defined
  1398. * `gapSize`.
  1399. *
  1400. * In practice, this option is most often used to visualize gaps in
  1401. * time series. In a stock chart, intraday data is available for daytime
  1402. * hours, while gaps will appear in nights and weekends.
  1403. *
  1404. * @see [gapUnit](plotOptions.series.gapUnit)
  1405. * @see [xAxis.breaks](#xAxis.breaks)
  1406. *
  1407. * @sample {highstock} stock/plotoptions/series-gapsize/
  1408. * Setting the gap size to 2 introduces gaps for weekends in daily
  1409. * datasets.
  1410. *
  1411. * @type {number}
  1412. * @default 0
  1413. * @product highstock
  1414. * @requires modules/broken-axis
  1415. * @apioption plotOptions.series.gapSize
  1416. */
  1417. /**
  1418. * Together with [gapSize](plotOptions.series.gapSize), this option defines
  1419. * where to draw gaps in the graph.
  1420. *
  1421. * When the `gapUnit` is `relative` (default), a gap size of 5 means
  1422. * that if the distance between two points is greater than five times
  1423. * that of the two closest points, the graph will be broken.
  1424. *
  1425. * When the `gapUnit` is `value`, the gap is based on absolute axis values,
  1426. * which on a datetime axis is milliseconds. This also applies to the
  1427. * navigator series that inherits gap options from the base series.
  1428. *
  1429. * @see [gapSize](plotOptions.series.gapSize)
  1430. *
  1431. * @type {string}
  1432. * @default relative
  1433. * @since 5.0.13
  1434. * @product highstock
  1435. * @validvalue ["relative", "value"]
  1436. * @requires modules/broken-axis
  1437. * @apioption plotOptions.series.gapUnit
  1438. */
  1439. if (gapSize && i > 0) { // #5008
  1440. // Gap unit is relative
  1441. if (this.options.gapUnit !== 'value') {
  1442. gapSize *= this.basePointRange;
  1443. }
  1444. // Setting a new gapSize in case dataGrouping is enabled (#7686)
  1445. if (groupingSize &&
  1446. groupingSize > gapSize &&
  1447. // Except when DG is forced (e.g. from other series)
  1448. // and has lower granularity than actual points (#11351)
  1449. groupingSize >= this.basePointRange) {
  1450. gapSize = groupingSize;
  1451. }
  1452. // extension for ordinal breaks
  1453. var current = void 0, next = void 0;
  1454. while (i--) {
  1455. // Reassign next if it is not visible
  1456. if (!(next && next.visible !== false)) {
  1457. next = points[i + 1];
  1458. }
  1459. current = points[i];
  1460. // Skip iteration if one of the points is not visible
  1461. if (next.visible === false || current.visible === false) {
  1462. continue;
  1463. }
  1464. if (next.x - current.x > gapSize) {
  1465. var xRange = (current.x + next.x) / 2;
  1466. points.splice(// insert after this one
  1467. i + 1, 0, {
  1468. isNull: true,
  1469. x: xRange
  1470. });
  1471. // For stacked chart generate empty stack items, #6546
  1472. if (this.options.stacking) {
  1473. stack = yAxis.stacks[this.stackKey][xRange] =
  1474. new H.StackItem(yAxis, yAxis.options
  1475. .stackLabels, false, xRange, this.stack);
  1476. stack.total = 0;
  1477. }
  1478. }
  1479. // Assign current to next for the upcoming iteration
  1480. next = current;
  1481. }
  1482. }
  1483. // Call base method
  1484. return this.getGraphPath(points);
  1485. };
  1486. });
  1487. _registerModule(_modules, 'parts-gantt/TreeGrid.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js'], _modules['parts-gantt/Tree.js'], _modules['mixins/tree-series.js']], function (H, U, Tree, mixinTreeSeries) {
  1488. /* *
  1489. *
  1490. * (c) 2016 Highsoft AS
  1491. * Authors: Jon Arild Nygard
  1492. *
  1493. * License: www.highcharts.com/license
  1494. *
  1495. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1496. *
  1497. * */
  1498. /* eslint no-console: 0 */
  1499. var defined = U.defined, extend = U.extend, isNumber = U.isNumber, isString = U.isString, pick = U.pick, wrap = U.wrap;
  1500. var addEvent = H.addEvent, argsToArray = function (args) {
  1501. return Array.prototype.slice.call(args, 1);
  1502. }, find = H.find, fireEvent = H.fireEvent, getLevelOptions = mixinTreeSeries.getLevelOptions, merge = H.merge, isBoolean = function (x) {
  1503. return typeof x === 'boolean';
  1504. }, isObject = function (x) {
  1505. // Always use strict mode.
  1506. return U.isObject(x, true);
  1507. }, GridAxis = H.Axis, GridAxisTick = H.Tick;
  1508. var override = function (obj, methods) {
  1509. var method, func;
  1510. for (method in methods) {
  1511. if (Object.hasOwnProperty.call(methods, method)) {
  1512. func = methods[method];
  1513. wrap(obj, method, func);
  1514. }
  1515. }
  1516. };
  1517. var getBreakFromNode = function (node, max) {
  1518. var from = node.collapseStart, to = node.collapseEnd;
  1519. // In broken-axis, the axis.max is minimized until it is not within a break.
  1520. // Therefore, if break.to is larger than axis.max, the axis.to should not
  1521. // add the 0.5 axis.tickMarkOffset, to avoid adding a break larger than
  1522. // axis.max
  1523. // TODO consider simplifying broken-axis and this might solve itself
  1524. if (to >= max) {
  1525. from -= 0.5;
  1526. }
  1527. return {
  1528. from: from,
  1529. to: to,
  1530. showPoints: false
  1531. };
  1532. };
  1533. /**
  1534. * Creates a list of positions for the ticks on the axis. Filters out positions
  1535. * that are outside min and max, or is inside an axis break.
  1536. *
  1537. * @private
  1538. * @function getTickPositions
  1539. *
  1540. * @param {Highcharts.Axis} axis
  1541. * The Axis to get the tick positions from.
  1542. *
  1543. * @return {Array<number>}
  1544. * List of positions.
  1545. */
  1546. var getTickPositions = function (axis) {
  1547. return Object.keys(axis.mapOfPosToGridNode).reduce(function (arr, key) {
  1548. var pos = +key;
  1549. if (axis.min <= pos &&
  1550. axis.max >= pos &&
  1551. !axis.isInAnyBreak(pos)) {
  1552. arr.push(pos);
  1553. }
  1554. return arr;
  1555. }, []);
  1556. };
  1557. /**
  1558. * Check if a node is collapsed.
  1559. *
  1560. * @private
  1561. * @function isCollapsed
  1562. *
  1563. * @param {Highcharts.Axis} axis
  1564. * The axis to check against.
  1565. *
  1566. * @param {object} node
  1567. * The node to check if is collapsed.
  1568. *
  1569. * @param {number} pos
  1570. * The tick position to collapse.
  1571. *
  1572. * @return {boolean}
  1573. * Returns true if collapsed, false if expanded.
  1574. */
  1575. var isCollapsed = function (axis, node) {
  1576. var breaks = (axis.options.breaks || []), obj = getBreakFromNode(node, axis.max);
  1577. return breaks.some(function (b) {
  1578. return b.from === obj.from && b.to === obj.to;
  1579. });
  1580. };
  1581. /**
  1582. * Calculates the new axis breaks to collapse a node.
  1583. *
  1584. * @private
  1585. * @function collapse
  1586. *
  1587. * @param {Highcharts.Axis} axis
  1588. * The axis to check against.
  1589. *
  1590. * @param {object} node
  1591. * The node to collapse.
  1592. *
  1593. * @param {number} pos
  1594. * The tick position to collapse.
  1595. *
  1596. * @return {Array<object>}
  1597. * Returns an array of the new breaks for the axis.
  1598. */
  1599. var collapse = function (axis, node) {
  1600. var breaks = (axis.options.breaks || []), obj = getBreakFromNode(node, axis.max);
  1601. breaks.push(obj);
  1602. return breaks;
  1603. };
  1604. /**
  1605. * Calculates the new axis breaks to expand a node.
  1606. *
  1607. * @private
  1608. * @function expand
  1609. *
  1610. * @param {Highcharts.Axis} axis
  1611. * The axis to check against.
  1612. *
  1613. * @param {object} node
  1614. * The node to expand.
  1615. *
  1616. * @param {number} pos
  1617. * The tick position to expand.
  1618. *
  1619. * @return {Array<object>}
  1620. * Returns an array of the new breaks for the axis.
  1621. */
  1622. var expand = function (axis, node) {
  1623. var breaks = (axis.options.breaks || []), obj = getBreakFromNode(node, axis.max);
  1624. // Remove the break from the axis breaks array.
  1625. return breaks.reduce(function (arr, b) {
  1626. if (b.to !== obj.to || b.from !== obj.from) {
  1627. arr.push(b);
  1628. }
  1629. return arr;
  1630. }, []);
  1631. };
  1632. /* eslint-disable valid-jsdoc */
  1633. /**
  1634. * Calculates the new axis breaks after toggling the collapse/expand state of a
  1635. * node. If it is collapsed it will be expanded, and if it is exapended it will
  1636. * be collapsed.
  1637. *
  1638. * @private
  1639. * @function toggleCollapse
  1640. *
  1641. * @param {Highcharts.Axis} axis
  1642. * The axis to check against.
  1643. *
  1644. * @param {object} node
  1645. * The node to toggle.
  1646. *
  1647. * @return {Array<object>}
  1648. * Returns an array of the new breaks for the axis.
  1649. */
  1650. var toggleCollapse = function (axis, node) {
  1651. return (isCollapsed(axis, node) ?
  1652. expand(axis, node) :
  1653. collapse(axis, node));
  1654. };
  1655. var renderLabelIcon = function (tick, params) {
  1656. var icon = tick.labelIcon, isNew = !icon, renderer = params.renderer, labelBox = params.xy, options = params.options, width = options.width, height = options.height, iconCenter = {
  1657. x: labelBox.x - (width / 2) - options.padding,
  1658. y: labelBox.y - (height / 2)
  1659. }, rotation = params.collapsed ? 90 : 180, shouldRender = params.show && isNumber(iconCenter.y);
  1660. if (isNew) {
  1661. tick.labelIcon = icon = renderer
  1662. .path(renderer.symbols[options.type](options.x, options.y, width, height))
  1663. .addClass('highcharts-label-icon')
  1664. .add(params.group);
  1665. }
  1666. // Set the new position, and show or hide
  1667. if (!shouldRender) {
  1668. icon.attr({ y: -9999 }); // #1338
  1669. }
  1670. // Presentational attributes
  1671. if (!renderer.styledMode) {
  1672. icon
  1673. .attr({
  1674. 'stroke-width': 1,
  1675. 'fill': pick(params.color, '#666666')
  1676. })
  1677. .css({
  1678. cursor: 'pointer',
  1679. stroke: options.lineColor,
  1680. strokeWidth: options.lineWidth
  1681. });
  1682. }
  1683. // Update the icon positions
  1684. icon[isNew ? 'attr' : 'animate']({
  1685. translateX: iconCenter.x,
  1686. translateY: iconCenter.y,
  1687. rotation: rotation
  1688. });
  1689. };
  1690. var onTickHover = function (label) {
  1691. label.addClass('highcharts-treegrid-node-active');
  1692. if (!label.renderer.styledMode) {
  1693. label.css({
  1694. textDecoration: 'underline'
  1695. });
  1696. }
  1697. };
  1698. var onTickHoverExit = function (label, options) {
  1699. var css = defined(options.style) ? options.style : {};
  1700. label.removeClass('highcharts-treegrid-node-active');
  1701. if (!label.renderer.styledMode) {
  1702. label.css({
  1703. textDecoration: css.textDecoration
  1704. });
  1705. }
  1706. };
  1707. /**
  1708. * Creates a tree structure of the data, and the treegrid. Calculates
  1709. * categories, and y-values of points based on the tree.
  1710. *
  1711. * @private
  1712. * @function getTreeGridFromData
  1713. *
  1714. * @param {Array<*>} data
  1715. * All the data points to display in the axis.
  1716. *
  1717. * @param {boolean} uniqueNames
  1718. * Wether or not the data node with the same name should share grid cell.
  1719. * If true they do share cell. False by default.
  1720. *
  1721. * @param {number} numberOfSeries
  1722. *
  1723. * @return {object}
  1724. * Returns an object containing categories, mapOfIdToNode,
  1725. * mapOfPosToGridNode, and tree.
  1726. *
  1727. * @todo There should be only one point per line.
  1728. * @todo It should be optional to have one category per point, or merge cells
  1729. * @todo Add unit-tests.
  1730. */
  1731. var getTreeGridFromData = function (data, uniqueNames, numberOfSeries) {
  1732. var categories = [], collapsedNodes = [], mapOfIdToNode = {}, mapOfPosToGridNode = {}, posIterator = -1, uniqueNamesEnabled = isBoolean(uniqueNames) ? uniqueNames : false, tree, treeParams, updateYValuesAndTickPos;
  1733. // Build the tree from the series data.
  1734. treeParams = {
  1735. // After the children has been created.
  1736. after: function (node) {
  1737. var gridNode = mapOfPosToGridNode[node.pos], height = 0, descendants = 0;
  1738. gridNode.children.forEach(function (child) {
  1739. descendants += child.descendants + 1;
  1740. height = Math.max(child.height + 1, height);
  1741. });
  1742. gridNode.descendants = descendants;
  1743. gridNode.height = height;
  1744. if (gridNode.collapsed) {
  1745. collapsedNodes.push(gridNode);
  1746. }
  1747. },
  1748. // Before the children has been created.
  1749. before: function (node) {
  1750. var data = isObject(node.data) ? node.data : {}, name = isString(data.name) ? data.name : '', parentNode = mapOfIdToNode[node.parent], parentGridNode = (isObject(parentNode) ?
  1751. mapOfPosToGridNode[parentNode.pos] :
  1752. null), hasSameName = function (x) {
  1753. return x.name === name;
  1754. }, gridNode, pos;
  1755. // If not unique names, look for a sibling node with the same name.
  1756. if (uniqueNamesEnabled &&
  1757. isObject(parentGridNode) &&
  1758. !!(gridNode = find(parentGridNode.children, hasSameName))) {
  1759. // If if there is a gridNode with the same name, reuse position.
  1760. pos = gridNode.pos;
  1761. // Add data node to list of nodes in the grid node.
  1762. gridNode.nodes.push(node);
  1763. }
  1764. else {
  1765. // If it is a new grid node, increment position.
  1766. pos = posIterator++;
  1767. }
  1768. // Add new grid node to map.
  1769. if (!mapOfPosToGridNode[pos]) {
  1770. mapOfPosToGridNode[pos] = gridNode = {
  1771. depth: parentGridNode ? parentGridNode.depth + 1 : 0,
  1772. name: name,
  1773. nodes: [node],
  1774. children: [],
  1775. pos: pos
  1776. };
  1777. // If not root, then add name to categories.
  1778. if (pos !== -1) {
  1779. categories.push(name);
  1780. }
  1781. // Add name to list of children.
  1782. if (isObject(parentGridNode)) {
  1783. parentGridNode.children.push(gridNode);
  1784. }
  1785. }
  1786. // Add data node to map
  1787. if (isString(node.id)) {
  1788. mapOfIdToNode[node.id] = node;
  1789. }
  1790. // If one of the points are collapsed, then start the grid node in
  1791. // collapsed state.
  1792. if (data.collapsed === true) {
  1793. gridNode.collapsed = true;
  1794. }
  1795. // Assign pos to data node
  1796. node.pos = pos;
  1797. }
  1798. };
  1799. updateYValuesAndTickPos = function (map, numberOfSeries) {
  1800. var setValues = function (gridNode, start, result) {
  1801. var nodes = gridNode.nodes, end = start + (start === -1 ? 0 : numberOfSeries - 1), diff = (end - start) / 2, padding = 0.5, pos = start + diff;
  1802. nodes.forEach(function (node) {
  1803. var data = node.data;
  1804. if (isObject(data)) {
  1805. // Update point
  1806. data.y = start + data.seriesIndex;
  1807. // Remove the property once used
  1808. delete data.seriesIndex;
  1809. }
  1810. node.pos = pos;
  1811. });
  1812. result[pos] = gridNode;
  1813. gridNode.pos = pos;
  1814. gridNode.tickmarkOffset = diff + padding;
  1815. gridNode.collapseStart = end + padding;
  1816. gridNode.children.forEach(function (child) {
  1817. setValues(child, end + 1, result);
  1818. end = child.collapseEnd - padding;
  1819. });
  1820. // Set collapseEnd to the end of the last child node.
  1821. gridNode.collapseEnd = end + padding;
  1822. return result;
  1823. };
  1824. return setValues(map['-1'], -1, {});
  1825. };
  1826. // Create tree from data
  1827. tree = Tree.getTree(data, treeParams);
  1828. // Update y values of data, and set calculate tick positions.
  1829. mapOfPosToGridNode = updateYValuesAndTickPos(mapOfPosToGridNode, numberOfSeries);
  1830. // Return the resulting data.
  1831. return {
  1832. categories: categories,
  1833. mapOfIdToNode: mapOfIdToNode,
  1834. mapOfPosToGridNode: mapOfPosToGridNode,
  1835. collapsedNodes: collapsedNodes,
  1836. tree: tree
  1837. };
  1838. };
  1839. /**
  1840. * Builds the tree of categories and calculates its positions.
  1841. * @private
  1842. * @param {object} e Event object
  1843. * @param {object} e.target The chart instance which the event was fired on.
  1844. * @param {object[]} e.target.axes The axes of the chart.
  1845. */
  1846. var onBeforeRender = function (e) {
  1847. var chart = e.target, axes = chart.axes;
  1848. axes
  1849. .filter(function (axis) {
  1850. return axis.options.type === 'treegrid';
  1851. })
  1852. .forEach(function (axis) {
  1853. var options = axis.options || {}, labelOptions = options.labels, removeFoundExtremesEvent, uniqueNames = options.uniqueNames, numberOfSeries = 0, isDirty, data, treeGrid;
  1854. // Check whether any of series is rendering for the first time,
  1855. // visibility has changed, or its data is dirty,
  1856. // and only then update. #10570, #10580
  1857. // Also check if mapOfPosToGridNode exists. #10887
  1858. isDirty = (!axis.mapOfPosToGridNode ||
  1859. axis.series.some(function (series) {
  1860. return !series.hasRendered ||
  1861. series.isDirtyData ||
  1862. series.isDirty;
  1863. }));
  1864. if (isDirty) {
  1865. // Concatenate data from all series assigned to this axis.
  1866. data = axis.series.reduce(function (arr, s) {
  1867. if (s.visible) {
  1868. // Push all data to array
  1869. s.options.data.forEach(function (data) {
  1870. if (isObject(data)) {
  1871. // Set series index on data. Removed again after
  1872. // use.
  1873. data.seriesIndex = numberOfSeries;
  1874. arr.push(data);
  1875. }
  1876. });
  1877. // Increment series index
  1878. if (uniqueNames === true) {
  1879. numberOfSeries++;
  1880. }
  1881. }
  1882. return arr;
  1883. }, []);
  1884. // setScale is fired after all the series is initialized,
  1885. // which is an ideal time to update the axis.categories.
  1886. treeGrid = getTreeGridFromData(data, uniqueNames, (uniqueNames === true) ? numberOfSeries : 1);
  1887. // Assign values to the axis.
  1888. axis.categories = treeGrid.categories;
  1889. axis.mapOfPosToGridNode =
  1890. treeGrid.mapOfPosToGridNode;
  1891. axis.hasNames = true;
  1892. axis.tree = treeGrid.tree;
  1893. // Update yData now that we have calculated the y values
  1894. axis.series.forEach(function (series) {
  1895. var data = series.options.data.map(function (d) {
  1896. return isObject(d) ? merge(d) : d;
  1897. });
  1898. // Avoid destroying points when series is not visible
  1899. if (series.visible) {
  1900. series.setData(data, false);
  1901. }
  1902. });
  1903. // Calculate the label options for each level in the tree.
  1904. axis.mapOptionsToLevel =
  1905. getLevelOptions({
  1906. defaults: labelOptions,
  1907. from: 1,
  1908. levels: labelOptions.levels,
  1909. to: axis.tree.height
  1910. });
  1911. // Collapse all the nodes belonging to a point where collapsed
  1912. // equals true. Only do this on init.
  1913. // Can be called from beforeRender, if getBreakFromNode removes
  1914. // its dependency on axis.max.
  1915. if (e.type === 'beforeRender') {
  1916. removeFoundExtremesEvent =
  1917. H.addEvent(axis, 'foundExtremes', function () {
  1918. treeGrid.collapsedNodes.forEach(function (node) {
  1919. var breaks = collapse(axis, node);
  1920. axis.setBreaks(breaks, false);
  1921. });
  1922. removeFoundExtremesEvent();
  1923. });
  1924. }
  1925. }
  1926. });
  1927. };
  1928. override(GridAxis.prototype, {
  1929. init: function (proceed, chart, userOptions) {
  1930. var axis = this, isTreeGrid = userOptions.type === 'treegrid';
  1931. // Set default and forced options for TreeGrid
  1932. if (isTreeGrid) {
  1933. // Add event for updating the categories of a treegrid.
  1934. // NOTE Preferably these events should be set on the axis.
  1935. addEvent(chart, 'beforeRender', onBeforeRender);
  1936. addEvent(chart, 'beforeRedraw', onBeforeRender);
  1937. userOptions = merge({
  1938. // Default options
  1939. grid: {
  1940. enabled: true
  1941. },
  1942. // TODO: add support for align in treegrid.
  1943. labels: {
  1944. align: 'left',
  1945. /**
  1946. * Set options on specific levels in a tree grid axis. Takes
  1947. * precedence over labels options.
  1948. *
  1949. * @sample {gantt} gantt/treegrid-axis/labels-levels
  1950. * Levels on TreeGrid Labels
  1951. *
  1952. * @type {Array<*>}
  1953. * @product gantt
  1954. * @apioption yAxis.labels.levels
  1955. *
  1956. * @private
  1957. */
  1958. levels: [{
  1959. /**
  1960. * Specify the level which the options within this object
  1961. * applies to.
  1962. *
  1963. * @type {number}
  1964. * @product gantt
  1965. * @apioption yAxis.labels.levels.level
  1966. *
  1967. * @private
  1968. */
  1969. level: void 0
  1970. }, {
  1971. level: 1,
  1972. /**
  1973. * @type {Highcharts.CSSObject}
  1974. * @product gantt
  1975. * @apioption yAxis.labels.levels.style
  1976. *
  1977. * @private
  1978. */
  1979. style: {
  1980. /** @ignore-option */
  1981. fontWeight: 'bold'
  1982. }
  1983. }],
  1984. /**
  1985. * The symbol for the collapse and expand icon in a
  1986. * treegrid.
  1987. *
  1988. * @product gantt
  1989. * @optionparent yAxis.labels.symbol
  1990. *
  1991. * @private
  1992. */
  1993. symbol: {
  1994. /**
  1995. * The symbol type. Points to a definition function in
  1996. * the `Highcharts.Renderer.symbols` collection.
  1997. *
  1998. * @type {Highcharts.SymbolKeyValue}
  1999. *
  2000. * @private
  2001. */
  2002. type: 'triangle',
  2003. x: -5,
  2004. y: -5,
  2005. height: 10,
  2006. width: 10,
  2007. padding: 5
  2008. }
  2009. },
  2010. uniqueNames: false
  2011. }, userOptions, {
  2012. // Forced options
  2013. reversed: true,
  2014. // grid.columns is not supported in treegrid
  2015. grid: {
  2016. columns: void 0
  2017. }
  2018. });
  2019. }
  2020. // Now apply the original function with the original arguments,
  2021. // which are sliced off this function's arguments
  2022. proceed.apply(axis, [chart, userOptions]);
  2023. if (isTreeGrid) {
  2024. axis.hasNames = true;
  2025. axis.options.showLastLabel = true;
  2026. }
  2027. },
  2028. /**
  2029. * Override to add indentation to axis.maxLabelDimensions.
  2030. *
  2031. * @private
  2032. * @function Highcharts.GridAxis#getMaxLabelDimensions
  2033. *
  2034. * @param {Function} proceed
  2035. * The original function
  2036. */
  2037. getMaxLabelDimensions: function (proceed) {
  2038. var axis = this, options = axis.options, labelOptions = options && options.labels, indentation = (labelOptions && isNumber(labelOptions.indentation) ?
  2039. options.labels.indentation :
  2040. 0), retVal = proceed.apply(axis, argsToArray(arguments)), isTreeGrid = axis.options.type === 'treegrid', treeDepth;
  2041. if (isTreeGrid && this.mapOfPosToGridNode) {
  2042. treeDepth = axis.mapOfPosToGridNode[-1].height;
  2043. retVal.width += indentation * (treeDepth - 1);
  2044. }
  2045. return retVal;
  2046. },
  2047. /**
  2048. * Generates a tick for initial positioning.
  2049. *
  2050. * @private
  2051. * @function Highcharts.GridAxis#generateTick
  2052. *
  2053. * @param {Function} proceed
  2054. * The original generateTick function.
  2055. *
  2056. * @param {number} pos
  2057. * The tick position in axis values.
  2058. */
  2059. generateTick: function (proceed, pos) {
  2060. var axis = this, mapOptionsToLevel = (isObject(axis.mapOptionsToLevel) ? axis.mapOptionsToLevel : {}), isTreeGrid = axis.options.type === 'treegrid', ticks = axis.ticks, tick = ticks[pos], levelOptions, options, gridNode;
  2061. if (isTreeGrid) {
  2062. gridNode = axis.mapOfPosToGridNode[pos];
  2063. levelOptions = mapOptionsToLevel[gridNode.depth];
  2064. if (levelOptions) {
  2065. options = {
  2066. labels: levelOptions
  2067. };
  2068. }
  2069. if (!tick) {
  2070. ticks[pos] = tick =
  2071. new GridAxisTick(axis, pos, null, void 0, {
  2072. category: gridNode.name,
  2073. tickmarkOffset: gridNode.tickmarkOffset,
  2074. options: options
  2075. });
  2076. }
  2077. else {
  2078. // update labels depending on tick interval
  2079. tick.parameters.category = gridNode.name;
  2080. tick.options = options;
  2081. tick.addLabel();
  2082. }
  2083. }
  2084. else {
  2085. proceed.apply(axis, argsToArray(arguments));
  2086. }
  2087. },
  2088. /**
  2089. * Set the tick positions, tickInterval, axis min and max.
  2090. *
  2091. * @private
  2092. * @function Highcharts.GridAxis#setTickInterval
  2093. *
  2094. * @param {Function} proceed
  2095. * The original setTickInterval function.
  2096. */
  2097. setTickInterval: function (proceed) {
  2098. var axis = this, options = axis.options, isTreeGrid = options.type === 'treegrid';
  2099. if (isTreeGrid) {
  2100. axis.min = pick(axis.userMin, options.min, axis.dataMin);
  2101. axis.max = pick(axis.userMax, options.max, axis.dataMax);
  2102. fireEvent(axis, 'foundExtremes');
  2103. // setAxisTranslation modifies the min and max according to
  2104. // axis breaks.
  2105. axis.setAxisTranslation(true);
  2106. axis.tickmarkOffset = 0.5;
  2107. axis.tickInterval = 1;
  2108. axis.tickPositions = this.mapOfPosToGridNode ?
  2109. getTickPositions(axis) :
  2110. [];
  2111. }
  2112. else {
  2113. proceed.apply(axis, argsToArray(arguments));
  2114. }
  2115. }
  2116. });
  2117. override(GridAxisTick.prototype, {
  2118. getLabelPosition: function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) {
  2119. var tick = this, lbOptions = pick(tick.options && tick.options.labels, labelOptions), pos = tick.pos, axis = tick.axis, options = axis.options, isTreeGrid = options.type === 'treegrid', result = proceed.apply(tick, [x, y, label, horiz, lbOptions, tickmarkOffset, index, step]), symbolOptions, indentation, mapOfPosToGridNode, node, level;
  2120. if (isTreeGrid) {
  2121. symbolOptions = (lbOptions && isObject(lbOptions.symbol) ?
  2122. lbOptions.symbol :
  2123. {});
  2124. indentation = (lbOptions && isNumber(lbOptions.indentation) ?
  2125. lbOptions.indentation :
  2126. 0);
  2127. mapOfPosToGridNode = axis.mapOfPosToGridNode;
  2128. node = mapOfPosToGridNode && mapOfPosToGridNode[pos];
  2129. level = (node && node.depth) || 1;
  2130. result.x += (
  2131. // Add space for symbols
  2132. ((symbolOptions.width) + (symbolOptions.padding * 2)) +
  2133. // Apply indentation
  2134. ((level - 1) * indentation));
  2135. }
  2136. return result;
  2137. },
  2138. renderLabel: function (proceed) {
  2139. var tick = this, pos = tick.pos, axis = tick.axis, label = tick.label, mapOfPosToGridNode = axis.mapOfPosToGridNode, options = axis.options, labelOptions = pick(tick.options && tick.options.labels, options && options.labels), symbolOptions = (labelOptions && isObject(labelOptions.symbol) ?
  2140. labelOptions.symbol :
  2141. {}), node = mapOfPosToGridNode && mapOfPosToGridNode[pos], level = node && node.depth, isTreeGrid = options.type === 'treegrid', hasLabel = !!(label && label.element), shouldRender = axis.tickPositions.indexOf(pos) > -1, prefixClassName = 'highcharts-treegrid-node-', collapsed, addClassName, removeClassName, styledMode = axis.chart.styledMode;
  2142. if (isTreeGrid && node) {
  2143. // Add class name for hierarchical styling.
  2144. if (hasLabel) {
  2145. label.addClass(prefixClassName + 'level-' + level);
  2146. }
  2147. }
  2148. proceed.apply(tick, argsToArray(arguments));
  2149. if (isTreeGrid && node && hasLabel && node.descendants > 0) {
  2150. collapsed = isCollapsed(axis, node);
  2151. renderLabelIcon(tick, {
  2152. color: !styledMode && label.styles.color,
  2153. collapsed: collapsed,
  2154. group: label.parentGroup,
  2155. options: symbolOptions,
  2156. renderer: label.renderer,
  2157. show: shouldRender,
  2158. xy: label.xy
  2159. });
  2160. // Add class name for the node.
  2161. addClassName = prefixClassName +
  2162. (collapsed ? 'collapsed' : 'expanded');
  2163. removeClassName = prefixClassName +
  2164. (collapsed ? 'expanded' : 'collapsed');
  2165. label
  2166. .addClass(addClassName)
  2167. .removeClass(removeClassName);
  2168. if (!styledMode) {
  2169. label.css({
  2170. cursor: 'pointer'
  2171. });
  2172. }
  2173. // Add events to both label text and icon
  2174. [label, tick.labelIcon].forEach(function (object) {
  2175. if (!object.attachedTreeGridEvents) {
  2176. // On hover
  2177. H.addEvent(object.element, 'mouseover', function () {
  2178. onTickHover(label);
  2179. });
  2180. // On hover out
  2181. H.addEvent(object.element, 'mouseout', function () {
  2182. onTickHoverExit(label, labelOptions);
  2183. });
  2184. H.addEvent(object.element, 'click', function () {
  2185. tick.toggleCollapse();
  2186. });
  2187. object.attachedTreeGridEvents = true;
  2188. }
  2189. });
  2190. }
  2191. }
  2192. });
  2193. extend(GridAxisTick.prototype, /** @lends Highcharts.Tick.prototype */ {
  2194. /**
  2195. * Collapse the grid cell. Used when axis is of type treegrid.
  2196. *
  2197. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  2198. *
  2199. * @private
  2200. * @function Highcharts.Tick#collapse
  2201. *
  2202. * @param {boolean} [redraw=true]
  2203. * Whether to redraw the chart or wait for an explicit call to
  2204. * {@link Highcharts.Chart#redraw}
  2205. */
  2206. collapse: function (redraw) {
  2207. var tick = this, axis = tick.axis, pos = tick.pos, node = axis.mapOfPosToGridNode[pos], breaks = collapse(axis, node);
  2208. axis.setBreaks(breaks, pick(redraw, true));
  2209. },
  2210. /**
  2211. * Expand the grid cell. Used when axis is of type treegrid.
  2212. *
  2213. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  2214. *
  2215. * @private
  2216. * @function Highcharts.Tick#expand
  2217. *
  2218. * @param {boolean} [redraw=true]
  2219. * Whether to redraw the chart or wait for an explicit call to
  2220. * {@link Highcharts.Chart#redraw}
  2221. */
  2222. expand: function (redraw) {
  2223. var tick = this, axis = tick.axis, pos = tick.pos, node = axis.mapOfPosToGridNode[pos], breaks = expand(axis, node);
  2224. axis.setBreaks(breaks, pick(redraw, true));
  2225. },
  2226. /**
  2227. * Toggle the collapse/expand state of the grid cell. Used when axis is of
  2228. * type treegrid.
  2229. *
  2230. * @see gantt/treegrid-axis/collapsed-dynamically/demo.js
  2231. *
  2232. * @private
  2233. * @function Highcharts.Tick#toggleCollapse
  2234. *
  2235. * @param {boolean} [redraw=true]
  2236. * Whether to redraw the chart or wait for an explicit call to
  2237. * {@link Highcharts.Chart#redraw}
  2238. */
  2239. toggleCollapse: function (redraw) {
  2240. var tick = this, axis = tick.axis, pos = tick.pos, node = axis.mapOfPosToGridNode[pos], breaks = toggleCollapse(axis, node);
  2241. axis.setBreaks(breaks, pick(redraw, true));
  2242. }
  2243. });
  2244. // Make utility functions available for testing.
  2245. GridAxis.prototype.utils = {
  2246. getNode: Tree.getNode
  2247. };
  2248. });
  2249. _registerModule(_modules, 'masters/modules/treegrid.src.js', [], function () {
  2250. });
  2251. }));