treegrid.src.js 92 KB

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