StockToolsBindings.js 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861
  1. /**
  2. *
  3. * Events generator for Stock tools
  4. *
  5. * (c) 2009-2021 Paweł Fus
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import H from '../Core/Globals.js';
  14. import NavigationBindings from '../Extensions/Annotations/NavigationBindings.js';
  15. import D from '../Core/DefaultOptions.js';
  16. var getOptions = D.getOptions, setOptions = D.setOptions;
  17. import Series from '../Core/Series/Series.js';
  18. import U from '../Core/Utilities.js';
  19. import palette from '../Core/Color/Palette.js';
  20. var correctFloat = U.correctFloat, defined = U.defined, extend = U.extend, fireEvent = U.fireEvent, isNumber = U.isNumber, merge = U.merge, pick = U.pick, uniqueKey = U.uniqueKey;
  21. var bindingsUtils = NavigationBindings.prototype.utils, PREFIX = 'highcharts-';
  22. /* eslint-disable no-invalid-this, valid-jsdoc */
  23. /**
  24. * Generates function which will add a flag series using modal in GUI.
  25. * Method fires an event "showPopup" with config:
  26. * `{type, options, callback}`.
  27. *
  28. * Example: NavigationBindings.utils.addFlagFromForm('url(...)') - will
  29. * generate function that shows modal in GUI.
  30. *
  31. * @private
  32. * @function bindingsUtils.addFlagFromForm
  33. *
  34. * @param {Highcharts.FlagsShapeValue} type
  35. * Type of flag series, e.g. "squarepin"
  36. *
  37. * @return {Function}
  38. * Callback to be used in `start` callback
  39. */
  40. bindingsUtils.addFlagFromForm = function (type) {
  41. return function (e) {
  42. var navigation = this, chart = navigation.chart, toolbar = chart.stockTools, getFieldType = bindingsUtils.getFieldType, point = bindingsUtils.attractToPoint(e, chart), pointConfig, seriesOptions;
  43. if (!point) {
  44. return;
  45. }
  46. pointConfig = {
  47. x: point.x,
  48. y: point.y
  49. };
  50. seriesOptions = {
  51. type: 'flags',
  52. onSeries: point.series.id,
  53. shape: type,
  54. data: [pointConfig],
  55. xAxis: point.xAxis,
  56. yAxis: point.yAxis,
  57. point: {
  58. events: {
  59. click: function () {
  60. var point = this, options = point.options;
  61. fireEvent(navigation, 'showPopup', {
  62. point: point,
  63. formType: 'annotation-toolbar',
  64. options: {
  65. langKey: 'flags',
  66. type: 'flags',
  67. title: [
  68. options.title,
  69. getFieldType(options.title)
  70. ],
  71. name: [
  72. options.name,
  73. getFieldType(options.name)
  74. ]
  75. },
  76. onSubmit: function (updated) {
  77. if (updated.actionType === 'remove') {
  78. point.remove();
  79. }
  80. else {
  81. point.update(navigation.fieldsToOptions(updated.fields, {}));
  82. }
  83. }
  84. });
  85. }
  86. }
  87. }
  88. };
  89. if (!toolbar || !toolbar.guiEnabled) {
  90. chart.addSeries(seriesOptions);
  91. }
  92. fireEvent(navigation, 'showPopup', {
  93. formType: 'flag',
  94. // Enabled options:
  95. options: {
  96. langKey: 'flags',
  97. type: 'flags',
  98. title: ['A', getFieldType('A')],
  99. name: ['Flag A', getFieldType('Flag A')]
  100. },
  101. // Callback on submit:
  102. onSubmit: function (data) {
  103. navigation.fieldsToOptions(data.fields, seriesOptions.data[0]);
  104. chart.addSeries(seriesOptions);
  105. }
  106. });
  107. };
  108. };
  109. bindingsUtils.manageIndicators = function (data) {
  110. var navigation = this, chart = navigation.chart, seriesConfig = {
  111. linkedTo: data.linkedTo,
  112. type: data.type
  113. }, indicatorsWithVolume = [
  114. 'ad',
  115. 'cmf',
  116. 'klinger',
  117. 'mfi',
  118. 'obv',
  119. 'vbp',
  120. 'vwap'
  121. ], indicatorsWithAxes = [
  122. 'ad',
  123. 'atr',
  124. 'cci',
  125. 'cmf',
  126. 'disparityindex',
  127. 'cmo',
  128. 'dmi',
  129. 'macd',
  130. 'mfi',
  131. 'roc',
  132. 'rsi',
  133. 'ao',
  134. 'aroon',
  135. 'aroonoscillator',
  136. 'trix',
  137. 'apo',
  138. 'dpo',
  139. 'ppo',
  140. 'natr',
  141. 'obv',
  142. 'williamsr',
  143. 'stochastic',
  144. 'slowstochastic',
  145. 'linearRegression',
  146. 'linearRegressionSlope',
  147. 'linearRegressionIntercept',
  148. 'linearRegressionAngle',
  149. 'klinger'
  150. ], yAxis, parentSeries, defaultOptions, series;
  151. if (data.actionType === 'edit') {
  152. navigation.fieldsToOptions(data.fields, seriesConfig);
  153. series = chart.get(data.seriesId);
  154. if (series) {
  155. series.update(seriesConfig, false);
  156. }
  157. }
  158. else if (data.actionType === 'remove') {
  159. series = chart.get(data.seriesId);
  160. if (series) {
  161. yAxis = series.yAxis;
  162. if (series.linkedSeries) {
  163. series.linkedSeries.forEach(function (linkedSeries) {
  164. linkedSeries.remove(false);
  165. });
  166. }
  167. series.remove(false);
  168. if (indicatorsWithAxes.indexOf(series.type) >= 0) {
  169. var removedYAxisHeight = yAxis.options.height;
  170. yAxis.remove(false);
  171. navigation.resizeYAxes(removedYAxisHeight);
  172. }
  173. }
  174. }
  175. else {
  176. seriesConfig.id = uniqueKey();
  177. navigation.fieldsToOptions(data.fields, seriesConfig);
  178. parentSeries = chart.get(seriesConfig.linkedTo);
  179. defaultOptions = getOptions().plotOptions;
  180. // Make sure that indicator uses the SUM approx if SUM approx is used
  181. // by parent series (#13950).
  182. if (typeof parentSeries !== 'undefined' &&
  183. parentSeries instanceof Series &&
  184. parentSeries.getDGApproximation() === 'sum' &&
  185. // If indicator has defined approx type, use it (e.g. "ranges")
  186. !defined(defaultOptions && defaultOptions[seriesConfig.type] &&
  187. defaultOptions.dataGrouping &&
  188. defaultOptions.dataGrouping.approximation)) {
  189. seriesConfig.dataGrouping = {
  190. approximation: 'sum'
  191. };
  192. }
  193. if (indicatorsWithAxes.indexOf(data.type) >= 0) {
  194. yAxis = chart.addAxis({
  195. id: uniqueKey(),
  196. offset: 0,
  197. opposite: true,
  198. title: {
  199. text: ''
  200. },
  201. tickPixelInterval: 40,
  202. showLastLabel: false,
  203. labels: {
  204. align: 'left',
  205. y: -2
  206. }
  207. }, false, false);
  208. seriesConfig.yAxis = yAxis.options.id;
  209. navigation.resizeYAxes();
  210. }
  211. else {
  212. seriesConfig.yAxis = chart.get(data.linkedTo).options.yAxis;
  213. }
  214. if (indicatorsWithVolume.indexOf(data.type) >= 0) {
  215. seriesConfig.params.volumeSeriesID = chart.series.filter(function (series) {
  216. return series.options.type === 'column';
  217. })[0].options.id;
  218. }
  219. chart.addSeries(seriesConfig, false);
  220. }
  221. fireEvent(navigation, 'deselectButton', {
  222. button: navigation.selectedButtonElement
  223. });
  224. chart.redraw();
  225. };
  226. /**
  227. * Update height for an annotation. Height is calculated as a difference
  228. * between last point in `typeOptions` and current position. It's a value,
  229. * not pixels height.
  230. *
  231. * @private
  232. * @function bindingsUtils.updateHeight
  233. *
  234. * @param {Highcharts.PointerEventObject} e
  235. * normalized browser event
  236. *
  237. * @param {Highcharts.Annotation} annotation
  238. * Annotation to be updated
  239. *
  240. * @return {void}
  241. */
  242. bindingsUtils.updateHeight = function (e, annotation) {
  243. var coordsY = this.utils.getAssignedAxis(this.chart.pointer.getCoordinates(e).yAxis);
  244. if (coordsY) {
  245. annotation.update({
  246. typeOptions: {
  247. height: coordsY.value -
  248. annotation.options.typeOptions.points[1].y
  249. }
  250. });
  251. }
  252. };
  253. // @todo
  254. // Consider using getHoverData(), but always kdTree (columns?)
  255. bindingsUtils.attractToPoint = function (e, chart) {
  256. var coords = chart.pointer.getCoordinates(e), coordsX, coordsY, distX = Number.MAX_VALUE, closestPoint, x, y;
  257. if (chart.navigationBindings) {
  258. coordsX = chart.navigationBindings.utils.getAssignedAxis(coords.xAxis);
  259. coordsY = chart.navigationBindings.utils.getAssignedAxis(coords.yAxis);
  260. }
  261. // Exit if clicked out of axes area.
  262. if (!coordsX || !coordsY) {
  263. return;
  264. }
  265. x = coordsX.value;
  266. y = coordsY.value;
  267. // Search by 'x' but only in yAxis' series.
  268. coordsY.axis.series.forEach(function (series) {
  269. if (series.points) {
  270. series.points.forEach(function (point) {
  271. if (point && distX > Math.abs(point.x - x)) {
  272. distX = Math.abs(point.x - x);
  273. closestPoint = point;
  274. }
  275. });
  276. }
  277. });
  278. if (closestPoint && closestPoint.x && closestPoint.y) {
  279. return {
  280. x: closestPoint.x,
  281. y: closestPoint.y,
  282. below: y < closestPoint.y,
  283. series: closestPoint.series,
  284. xAxis: closestPoint.series.xAxis.options.index || 0,
  285. yAxis: closestPoint.series.yAxis.options.index || 0
  286. };
  287. }
  288. };
  289. /**
  290. * Shorthand to check if given yAxis comes from navigator.
  291. *
  292. * @private
  293. * @function bindingsUtils.isNotNavigatorYAxis
  294. *
  295. * @param {Highcharts.Axis} axis
  296. * Axis to check.
  297. *
  298. * @return {boolean}
  299. * True, if axis comes from navigator.
  300. */
  301. bindingsUtils.isNotNavigatorYAxis = function (axis) {
  302. return axis.userOptions.className !== PREFIX + 'navigator-yaxis';
  303. };
  304. /**
  305. * Check if any of the price indicators are enabled.
  306. * @private
  307. * @function bindingsUtils.isLastPriceEnabled
  308. *
  309. * @param {array} series
  310. * Array of series.
  311. *
  312. * @return {boolean}
  313. * Tells which indicator is enabled.
  314. */
  315. bindingsUtils.isPriceIndicatorEnabled = function (series) {
  316. return series.some(function (s) { return s.lastVisiblePrice || s.lastPrice; });
  317. };
  318. /**
  319. * Update each point after specified index, most of the annotations use
  320. * this. For example crooked line: logic behind updating each point is the
  321. * same, only index changes when adding an annotation.
  322. *
  323. * Example: NavigationBindings.utils.updateNthPoint(1) - will generate
  324. * function that updates all consecutive points except point with index=0.
  325. *
  326. * @private
  327. * @function bindingsUtils.updateNthPoint
  328. *
  329. * @param {number} startIndex
  330. * Index from each point should udpated
  331. *
  332. * @return {Function}
  333. * Callback to be used in steps array
  334. */
  335. bindingsUtils.updateNthPoint = function (startIndex) {
  336. return function (e, annotation) {
  337. var options = annotation.options.typeOptions, coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  338. if (coordsX && coordsY) {
  339. options.points.forEach(function (point, index) {
  340. if (index >= startIndex) {
  341. point.x = coordsX.value;
  342. point.y = coordsY.value;
  343. }
  344. });
  345. annotation.update({
  346. typeOptions: {
  347. points: options.points
  348. }
  349. });
  350. }
  351. };
  352. };
  353. // Extends NavigationBindigs to support indicators and resizers:
  354. extend(NavigationBindings.prototype, {
  355. /* eslint-disable valid-jsdoc */
  356. /**
  357. * Get current positions for all yAxes. If new axis does not have position,
  358. * returned is default height and last available top place.
  359. *
  360. * @private
  361. * @function Highcharts.NavigationBindings#getYAxisPositions
  362. *
  363. * @param {Array<Highcharts.Axis>} yAxes
  364. * Array of yAxes available in the chart.
  365. *
  366. * @param {number} plotHeight
  367. * Available height in the chart.
  368. *
  369. * @param {number} defaultHeight
  370. * Default height in percents.
  371. *
  372. * @param {string} removedYAxisHeight
  373. * Height of the removed yAxis in percents.
  374. *
  375. * @return {Highcharts.YAxisPositions}
  376. * An object containing an array of calculated positions
  377. * in percentages. Format: `{top: Number, height: Number}`
  378. * and maximum value of top + height of axes.
  379. */
  380. getYAxisPositions: function (yAxes, plotHeight, defaultHeight, removedYAxisHeight) {
  381. var positions, allAxesHeight = 0, previousAxisHeight, removedHeight;
  382. /** @private */
  383. function isPercentage(prop) {
  384. return defined(prop) && !isNumber(prop) && prop.match('%');
  385. }
  386. if (removedYAxisHeight) {
  387. removedHeight = correctFloat((parseFloat(removedYAxisHeight) / 100));
  388. }
  389. positions = yAxes.map(function (yAxis, index) {
  390. var height = correctFloat(isPercentage(yAxis.options.height) ?
  391. parseFloat(yAxis.options.height) / 100 :
  392. yAxis.height / plotHeight), top = correctFloat(isPercentage(yAxis.options.top) ?
  393. parseFloat(yAxis.options.top) / 100 :
  394. (yAxis.top - yAxis.chart.plotTop) / plotHeight);
  395. // New axis' height is NaN so we can check if
  396. // the axis is newly created this way
  397. if (!removedHeight) {
  398. if (!isNumber(height)) {
  399. // Check if the previous axis is the
  400. // indicator axis (every indicator inherits from sma)
  401. height = yAxes[index - 1].series.every(function (s) { return s.is('sma'); }) ?
  402. previousAxisHeight : defaultHeight / 100;
  403. }
  404. if (!isNumber(top)) {
  405. top = allAxesHeight;
  406. }
  407. previousAxisHeight = height;
  408. allAxesHeight = correctFloat(Math.max(allAxesHeight, (top || 0) + (height || 0)));
  409. }
  410. else {
  411. if (top <= allAxesHeight) {
  412. allAxesHeight = correctFloat(Math.max(allAxesHeight, (top || 0) + (height || 0)));
  413. }
  414. else {
  415. top = correctFloat(top - removedHeight);
  416. allAxesHeight = correctFloat(allAxesHeight + height);
  417. }
  418. }
  419. return {
  420. height: height * 100,
  421. top: top * 100
  422. };
  423. });
  424. return { positions: positions, allAxesHeight: allAxesHeight };
  425. },
  426. /**
  427. * Get current resize options for each yAxis. Note that each resize is
  428. * linked to the next axis, except the last one which shouldn't affect
  429. * axes in the navigator. Because indicator can be removed with it's yAxis
  430. * in the middle of yAxis array, we need to bind closest yAxes back.
  431. *
  432. * @private
  433. * @function Highcharts.NavigationBindings#getYAxisResizers
  434. *
  435. * @param {Array<Highcharts.Axis>} yAxes
  436. * Array of yAxes available in the chart
  437. *
  438. * @return {Array<object>}
  439. * An array of resizer options.
  440. * Format: `{enabled: Boolean, controlledAxis: { next: [String]}}`
  441. */
  442. getYAxisResizers: function (yAxes) {
  443. var resizers = [];
  444. yAxes.forEach(function (_yAxis, index) {
  445. var nextYAxis = yAxes[index + 1];
  446. // We have next axis, bind them:
  447. if (nextYAxis) {
  448. resizers[index] = {
  449. enabled: true,
  450. controlledAxis: {
  451. next: [
  452. pick(nextYAxis.options.id, nextYAxis.options.index)
  453. ]
  454. }
  455. };
  456. }
  457. else {
  458. // Remove binding:
  459. resizers[index] = {
  460. enabled: false
  461. };
  462. }
  463. });
  464. return resizers;
  465. },
  466. /**
  467. * Resize all yAxes (except navigator) to fit the plotting height. Method
  468. * checks if new axis is added, if the new axis will fit under previous
  469. * axes it is placed there. If not, current plot area is scaled
  470. * to make room for new axis.
  471. *
  472. * If axis is removed, the current plot area streaches to fit into 100%
  473. * of the plot area.
  474. *
  475. * @private
  476. * @function Highcharts.NavigationBindings#resizeYAxes
  477. * @param {string} [removedYAxisHeight]
  478. *
  479. *
  480. */
  481. resizeYAxes: function (removedYAxisHeight) {
  482. // The height of the new axis before rescalling. In %, but as a number.
  483. var defaultHeight = 20;
  484. var chart = this.chart,
  485. // Only non-navigator axes
  486. yAxes = chart.yAxis.filter(bindingsUtils.isNotNavigatorYAxis), plotHeight = chart.plotHeight,
  487. // Gather current heights (in %)
  488. _a = this.getYAxisPositions(yAxes, plotHeight, defaultHeight, removedYAxisHeight), positions = _a.positions, allAxesHeight = _a.allAxesHeight, resizers = this.getYAxisResizers(yAxes);
  489. // check if the axis is being either added or removed and
  490. // if the new indicator axis will fit under existing axes.
  491. // if so, there is no need to scale them.
  492. if (!removedYAxisHeight &&
  493. allAxesHeight <= correctFloat(0.8 + defaultHeight / 100)) {
  494. positions[positions.length - 1] = {
  495. height: defaultHeight,
  496. top: correctFloat(allAxesHeight * 100 - defaultHeight)
  497. };
  498. }
  499. else {
  500. positions.forEach(function (position) {
  501. position.height = (position.height / (allAxesHeight * 100)) * 100;
  502. position.top = (position.top / (allAxesHeight * 100)) * 100;
  503. });
  504. }
  505. positions.forEach(function (position, index) {
  506. yAxes[index].update({
  507. height: position.height + '%',
  508. top: position.top + '%',
  509. resize: resizers[index],
  510. offset: 0
  511. }, false);
  512. });
  513. },
  514. /**
  515. * Utility to modify calculated positions according to the remaining/needed
  516. * space. Later, these positions are used in `yAxis.update({ top, height })`
  517. *
  518. * @private
  519. * @function Highcharts.NavigationBindings#recalculateYAxisPositions
  520. * @param {Array<Highcharts.Dictionary<number>>} positions
  521. * Default positions of all yAxes.
  522. * @param {number} changedSpace
  523. * How much space should be added or removed.
  524. * @param {boolean} modifyHeight
  525. * Update only `top` or both `top` and `height`.
  526. * @param {number} adder
  527. * `-1` or `1`, to determine whether we should add or remove space.
  528. *
  529. * @return {Array<object>}
  530. * Modified positions,
  531. */
  532. recalculateYAxisPositions: function (positions, changedSpace, modifyHeight, adder) {
  533. positions.forEach(function (position, index) {
  534. var prevPosition = positions[index - 1];
  535. position.top = !prevPosition ? 0 :
  536. correctFloat(prevPosition.height + prevPosition.top);
  537. if (modifyHeight) {
  538. position.height = correctFloat(position.height + adder * changedSpace);
  539. }
  540. });
  541. return positions;
  542. }
  543. /* eslint-enable valid-jsdoc */
  544. });
  545. /**
  546. * @type {Highcharts.Dictionary<Highcharts.NavigationBindingsOptionsObject>}
  547. * @since 7.0.0
  548. * @optionparent navigation.bindings
  549. */
  550. var stockToolsBindings = {
  551. // Line type annotations:
  552. /**
  553. * A segment annotation bindings. Includes `start` and one event in `steps`
  554. * array.
  555. *
  556. * @type {Highcharts.NavigationBindingsOptionsObject}
  557. * @product highstock
  558. * @default {"className": "highcharts-segment", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  559. */
  560. segment: {
  561. /** @ignore-option */
  562. className: 'highcharts-segment',
  563. // eslint-disable-next-line valid-jsdoc
  564. /** @ignore-option */
  565. start: function (e) {
  566. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  567. // Exit if clicked out of axes area
  568. if (!coordsX || !coordsY) {
  569. return;
  570. }
  571. options = merge({
  572. langKey: 'segment',
  573. type: 'crookedLine',
  574. typeOptions: {
  575. xAxis: coordsX.axis.options.index,
  576. yAxis: coordsY.axis.options.index,
  577. points: [{
  578. x: coordsX.value,
  579. y: coordsY.value
  580. }, {
  581. x: coordsX.value,
  582. y: coordsY.value
  583. }]
  584. }
  585. }, navigation.annotationsOptions, navigation.bindings.segment.annotationsOptions);
  586. return this.chart.addAnnotation(options);
  587. },
  588. /** @ignore-option */
  589. steps: [
  590. bindingsUtils.updateNthPoint(1)
  591. ]
  592. },
  593. /**
  594. * A segment with an arrow annotation bindings. Includes `start` and one
  595. * event in `steps` array.
  596. *
  597. * @type {Highcharts.NavigationBindingsOptionsObject}
  598. * @product highstock
  599. * @default {"className": "highcharts-arrow-segment", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  600. */
  601. arrowSegment: {
  602. /** @ignore-option */
  603. className: 'highcharts-arrow-segment',
  604. // eslint-disable-next-line valid-jsdoc
  605. /** @ignore-option */
  606. start: function (e) {
  607. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  608. // Exit if clicked out of axes area
  609. if (!coordsX || !coordsY) {
  610. return;
  611. }
  612. options = merge({
  613. langKey: 'arrowSegment',
  614. type: 'crookedLine',
  615. typeOptions: {
  616. line: {
  617. markerEnd: 'arrow'
  618. },
  619. xAxis: coordsX.axis.options.index,
  620. yAxis: coordsY.axis.options.index,
  621. points: [{
  622. x: coordsX.value,
  623. y: coordsY.value
  624. }, {
  625. x: coordsX.value,
  626. y: coordsY.value
  627. }]
  628. }
  629. }, navigation.annotationsOptions, navigation.bindings.arrowSegment.annotationsOptions);
  630. return this.chart.addAnnotation(options);
  631. },
  632. /** @ignore-option */
  633. steps: [
  634. bindingsUtils.updateNthPoint(1)
  635. ]
  636. },
  637. /**
  638. * A ray annotation bindings. Includes `start` and one event in `steps`
  639. * array.
  640. *
  641. * @type {Highcharts.NavigationBindingsOptionsObject}
  642. * @product highstock
  643. * @default {"className": "highcharts-ray", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  644. */
  645. ray: {
  646. /** @ignore-option */
  647. className: 'highcharts-ray',
  648. // eslint-disable-next-line valid-jsdoc
  649. /** @ignore-option */
  650. start: function (e) {
  651. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  652. // Exit if clicked out of axes area
  653. if (!coordsX || !coordsY) {
  654. return;
  655. }
  656. options = merge({
  657. langKey: 'ray',
  658. type: 'infinityLine',
  659. typeOptions: {
  660. type: 'ray',
  661. xAxis: coordsX.axis.options.index,
  662. yAxis: coordsY.axis.options.index,
  663. points: [{
  664. x: coordsX.value,
  665. y: coordsY.value
  666. }, {
  667. x: coordsX.value,
  668. y: coordsY.value
  669. }]
  670. }
  671. }, navigation.annotationsOptions, navigation.bindings.ray.annotationsOptions);
  672. return this.chart.addAnnotation(options);
  673. },
  674. /** @ignore-option */
  675. steps: [
  676. bindingsUtils.updateNthPoint(1)
  677. ]
  678. },
  679. /**
  680. * A ray with an arrow annotation bindings. Includes `start` and one event
  681. * in `steps` array.
  682. *
  683. * @type {Highcharts.NavigationBindingsOptionsObject}
  684. * @product highstock
  685. * @default {"className": "highcharts-arrow-ray", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  686. */
  687. arrowRay: {
  688. /** @ignore-option */
  689. className: 'highcharts-arrow-ray',
  690. // eslint-disable-next-line valid-jsdoc
  691. /** @ignore-option */
  692. start: function (e) {
  693. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  694. // Exit if clicked out of axes area
  695. if (!coordsX || !coordsY) {
  696. return;
  697. }
  698. options = merge({
  699. langKey: 'arrowRay',
  700. type: 'infinityLine',
  701. typeOptions: {
  702. type: 'ray',
  703. line: {
  704. markerEnd: 'arrow'
  705. },
  706. xAxis: coordsX.axis.options.index,
  707. yAxis: coordsY.axis.options.index,
  708. points: [{
  709. x: coordsX.value,
  710. y: coordsY.value
  711. }, {
  712. x: coordsX.value,
  713. y: coordsY.value
  714. }]
  715. }
  716. }, navigation.annotationsOptions, navigation.bindings.arrowRay.annotationsOptions);
  717. return this.chart.addAnnotation(options);
  718. },
  719. /** @ignore-option */
  720. steps: [
  721. bindingsUtils.updateNthPoint(1)
  722. ]
  723. },
  724. /**
  725. * A line annotation. Includes `start` and one event in `steps` array.
  726. *
  727. * @type {Highcharts.NavigationBindingsOptionsObject}
  728. * @product highstock
  729. * @default {"className": "highcharts-infinity-line", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  730. */
  731. infinityLine: {
  732. /** @ignore-option */
  733. className: 'highcharts-infinity-line',
  734. // eslint-disable-next-line valid-jsdoc
  735. /** @ignore-option */
  736. start: function (e) {
  737. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  738. // Exit if clicked out of axes area
  739. if (!coordsX || !coordsY) {
  740. return;
  741. }
  742. options = merge({
  743. langKey: 'infinityLine',
  744. type: 'infinityLine',
  745. typeOptions: {
  746. type: 'line',
  747. xAxis: coordsX.axis.options.index,
  748. yAxis: coordsY.axis.options.index,
  749. points: [{
  750. x: coordsX.value,
  751. y: coordsY.value
  752. }, {
  753. x: coordsX.value,
  754. y: coordsY.value
  755. }]
  756. }
  757. }, navigation.annotationsOptions, navigation.bindings.infinityLine.annotationsOptions);
  758. return this.chart.addAnnotation(options);
  759. },
  760. /** @ignore-option */
  761. steps: [
  762. bindingsUtils.updateNthPoint(1)
  763. ]
  764. },
  765. /**
  766. * A line with arrow annotation. Includes `start` and one event in `steps`
  767. * array.
  768. *
  769. * @type {Highcharts.NavigationBindingsOptionsObject}
  770. * @product highstock
  771. * @default {"className": "highcharts-arrow-infinity-line", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  772. */
  773. arrowInfinityLine: {
  774. /** @ignore-option */
  775. className: 'highcharts-arrow-infinity-line',
  776. // eslint-disable-next-line valid-jsdoc
  777. /** @ignore-option */
  778. start: function (e) {
  779. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  780. // Exit if clicked out of axes area
  781. if (!coordsX || !coordsY) {
  782. return;
  783. }
  784. options = merge({
  785. langKey: 'arrowInfinityLine',
  786. type: 'infinityLine',
  787. typeOptions: {
  788. type: 'line',
  789. line: {
  790. markerEnd: 'arrow'
  791. },
  792. xAxis: coordsX.axis.options.index,
  793. yAxis: coordsY.axis.options.index,
  794. points: [{
  795. x: coordsX.value,
  796. y: coordsY.value
  797. }, {
  798. x: coordsX.value,
  799. y: coordsY.value
  800. }]
  801. }
  802. }, navigation.annotationsOptions, navigation.bindings.arrowInfinityLine.annotationsOptions);
  803. return this.chart.addAnnotation(options);
  804. },
  805. /** @ignore-option */
  806. steps: [
  807. bindingsUtils.updateNthPoint(1)
  808. ]
  809. },
  810. /**
  811. * A horizontal line annotation. Includes `start` event.
  812. *
  813. * @type {Highcharts.NavigationBindingsOptionsObject}
  814. * @product highstock
  815. * @default {"className": "highcharts-horizontal-line", "start": function() {}, "annotationsOptions": {}}
  816. */
  817. horizontalLine: {
  818. /** @ignore-option */
  819. className: 'highcharts-horizontal-line',
  820. // eslint-disable-next-line valid-jsdoc
  821. /** @ignore-option */
  822. start: function (e) {
  823. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  824. // Exit if clicked out of axes area
  825. if (!coordsX || !coordsY) {
  826. return;
  827. }
  828. options = merge({
  829. langKey: 'horizontalLine',
  830. type: 'infinityLine',
  831. draggable: 'y',
  832. typeOptions: {
  833. type: 'horizontalLine',
  834. xAxis: coordsX.axis.options.index,
  835. yAxis: coordsY.axis.options.index,
  836. points: [{
  837. x: coordsX.value,
  838. y: coordsY.value
  839. }]
  840. }
  841. }, navigation.annotationsOptions, navigation.bindings.horizontalLine.annotationsOptions);
  842. this.chart.addAnnotation(options);
  843. }
  844. },
  845. /**
  846. * A vertical line annotation. Includes `start` event.
  847. *
  848. * @type {Highcharts.NavigationBindingsOptionsObject}
  849. * @product highstock
  850. * @default {"className": "highcharts-vertical-line", "start": function() {}, "annotationsOptions": {}}
  851. */
  852. verticalLine: {
  853. /** @ignore-option */
  854. className: 'highcharts-vertical-line',
  855. // eslint-disable-next-line valid-jsdoc
  856. /** @ignore-option */
  857. start: function (e) {
  858. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis), navigation = this.chart.options.navigation, options;
  859. // Exit if clicked out of axes area
  860. if (!coordsX || !coordsY) {
  861. return;
  862. }
  863. options = merge({
  864. langKey: 'verticalLine',
  865. type: 'infinityLine',
  866. draggable: 'x',
  867. typeOptions: {
  868. type: 'verticalLine',
  869. xAxis: coordsX.axis.options.index,
  870. yAxis: coordsY.axis.options.index,
  871. points: [{
  872. x: coordsX.value,
  873. y: coordsY.value
  874. }]
  875. }
  876. }, navigation.annotationsOptions, navigation.bindings.verticalLine.annotationsOptions);
  877. this.chart.addAnnotation(options);
  878. }
  879. },
  880. /**
  881. * Crooked line (three points) annotation bindings. Includes `start` and two
  882. * events in `steps` (for second and third points in crooked line) array.
  883. *
  884. * @type {Highcharts.NavigationBindingsOptionsObject}
  885. * @product highstock
  886. * @default {"className": "highcharts-crooked3", "start": function() {}, "steps": [function() {}, function() {}], "annotationsOptions": {}}
  887. */
  888. // Crooked Line type annotations:
  889. crooked3: {
  890. /** @ignore-option */
  891. className: 'highcharts-crooked3',
  892. // eslint-disable-next-line valid-jsdoc
  893. /** @ignore-option */
  894. start: function (e) {
  895. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  896. // Exit if clicked out of axes area
  897. if (!coordsX || !coordsY) {
  898. return;
  899. }
  900. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  901. langKey: 'crooked3',
  902. type: 'crookedLine',
  903. typeOptions: {
  904. xAxis: coordsX.axis.options.index,
  905. yAxis: coordsY.axis.options.index,
  906. points: [
  907. { x: x, y: y },
  908. { x: x, y: y },
  909. { x: x, y: y }
  910. ]
  911. }
  912. }, navigation.annotationsOptions, navigation.bindings.crooked3.annotationsOptions);
  913. return this.chart.addAnnotation(options);
  914. },
  915. /** @ignore-option */
  916. steps: [
  917. bindingsUtils.updateNthPoint(1),
  918. bindingsUtils.updateNthPoint(2)
  919. ]
  920. },
  921. /**
  922. * Crooked line (five points) annotation bindings. Includes `start` and four
  923. * events in `steps` (for all consequent points in crooked line) array.
  924. *
  925. * @type {Highcharts.NavigationBindingsOptionsObject}
  926. * @product highstock
  927. * @default {"className": "highcharts-crooked3", "start": function() {}, "steps": [function() {}, function() {}, function() {}, function() {}], "annotationsOptions": {}}
  928. */
  929. crooked5: {
  930. /** @ignore-option */
  931. className: 'highcharts-crooked5',
  932. // eslint-disable-next-line valid-jsdoc
  933. /** @ignore-option */
  934. start: function (e) {
  935. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  936. // Exit if clicked out of axes area
  937. if (!coordsX || !coordsY) {
  938. return;
  939. }
  940. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  941. langKey: 'crookedLine',
  942. type: 'crookedLine',
  943. typeOptions: {
  944. xAxis: coordsX.axis.options.index,
  945. yAxis: coordsY.axis.options.index,
  946. points: [
  947. { x: x, y: y },
  948. { x: x, y: y },
  949. { x: x, y: y },
  950. { x: x, y: y },
  951. { x: x, y: y }
  952. ]
  953. }
  954. }, navigation.annotationsOptions, navigation.bindings.crooked5.annotationsOptions);
  955. return this.chart.addAnnotation(options);
  956. },
  957. /** @ignore-option */
  958. steps: [
  959. bindingsUtils.updateNthPoint(1),
  960. bindingsUtils.updateNthPoint(2),
  961. bindingsUtils.updateNthPoint(3),
  962. bindingsUtils.updateNthPoint(4)
  963. ]
  964. },
  965. /**
  966. * Elliott wave (three points) annotation bindings. Includes `start` and two
  967. * events in `steps` (for second and third points) array.
  968. *
  969. * @type {Highcharts.NavigationBindingsOptionsObject}
  970. * @product highstock
  971. * @default {"className": "highcharts-elliott3", "start": function() {}, "steps": [function() {}, function() {}], "annotationsOptions": {}}
  972. */
  973. elliott3: {
  974. /** @ignore-option */
  975. className: 'highcharts-elliott3',
  976. // eslint-disable-next-line valid-jsdoc
  977. /** @ignore-option */
  978. start: function (e) {
  979. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  980. // Exit if clicked out of axes area
  981. if (!coordsX || !coordsY) {
  982. return;
  983. }
  984. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  985. langKey: 'elliott3',
  986. type: 'elliottWave',
  987. typeOptions: {
  988. xAxis: coordsX.axis.options.index,
  989. yAxis: coordsY.axis.options.index,
  990. points: [
  991. { x: x, y: y },
  992. { x: x, y: y },
  993. { x: x, y: y },
  994. { x: x, y: y }
  995. ]
  996. },
  997. labelOptions: {
  998. style: {
  999. color: palette.neutralColor60
  1000. }
  1001. }
  1002. }, navigation.annotationsOptions, navigation.bindings.elliott3.annotationsOptions);
  1003. return this.chart.addAnnotation(options);
  1004. },
  1005. /** @ignore-option */
  1006. steps: [
  1007. bindingsUtils.updateNthPoint(1),
  1008. bindingsUtils.updateNthPoint(2),
  1009. bindingsUtils.updateNthPoint(3)
  1010. ]
  1011. },
  1012. /**
  1013. * Elliott wave (five points) annotation bindings. Includes `start` and four
  1014. * event in `steps` (for all consequent points in Elliott wave) array.
  1015. *
  1016. * @type {Highcharts.NavigationBindingsOptionsObject}
  1017. * @product highstock
  1018. * @default {"className": "highcharts-elliott3", "start": function() {}, "steps": [function() {}, function() {}, function() {}, function() {}], "annotationsOptions": {}}
  1019. */
  1020. elliott5: {
  1021. /** @ignore-option */
  1022. className: 'highcharts-elliott5',
  1023. // eslint-disable-next-line valid-jsdoc
  1024. /** @ignore-option */
  1025. start: function (e) {
  1026. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  1027. // Exit if clicked out of axes area
  1028. if (!coordsX || !coordsY) {
  1029. return;
  1030. }
  1031. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  1032. langKey: 'elliott5',
  1033. type: 'elliottWave',
  1034. typeOptions: {
  1035. xAxis: coordsX.axis.options.index,
  1036. yAxis: coordsY.axis.options.index,
  1037. points: [
  1038. { x: x, y: y },
  1039. { x: x, y: y },
  1040. { x: x, y: y },
  1041. { x: x, y: y },
  1042. { x: x, y: y },
  1043. { x: x, y: y }
  1044. ]
  1045. },
  1046. labelOptions: {
  1047. style: {
  1048. color: palette.neutralColor60
  1049. }
  1050. }
  1051. }, navigation.annotationsOptions, navigation.bindings.elliott5.annotationsOptions);
  1052. return this.chart.addAnnotation(options);
  1053. },
  1054. /** @ignore-option */
  1055. steps: [
  1056. bindingsUtils.updateNthPoint(1),
  1057. bindingsUtils.updateNthPoint(2),
  1058. bindingsUtils.updateNthPoint(3),
  1059. bindingsUtils.updateNthPoint(4),
  1060. bindingsUtils.updateNthPoint(5)
  1061. ]
  1062. },
  1063. /**
  1064. * A measure (x-dimension) annotation bindings. Includes `start` and one
  1065. * event in `steps` array.
  1066. *
  1067. * @type {Highcharts.NavigationBindingsOptionsObject}
  1068. * @product highstock
  1069. * @default {"className": "highcharts-measure-x", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  1070. */
  1071. measureX: {
  1072. /** @ignore-option */
  1073. className: 'highcharts-measure-x',
  1074. // eslint-disable-next-line valid-jsdoc
  1075. /** @ignore-option */
  1076. start: function (e) {
  1077. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  1078. // Exit if clicked out of axes area
  1079. if (!coordsX || !coordsY) {
  1080. return;
  1081. }
  1082. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  1083. langKey: 'measure',
  1084. type: 'measure',
  1085. typeOptions: {
  1086. selectType: 'x',
  1087. xAxis: coordsX.axis.options.index,
  1088. yAxis: coordsY.axis.options.index,
  1089. point: { x: x, y: y },
  1090. crosshairX: {
  1091. strokeWidth: 1,
  1092. stroke: palette.neutralColor100
  1093. },
  1094. crosshairY: {
  1095. enabled: false,
  1096. strokeWidth: 0,
  1097. stroke: palette.neutralColor100
  1098. },
  1099. background: {
  1100. width: 0,
  1101. height: 0,
  1102. strokeWidth: 0,
  1103. stroke: palette.backgroundColor
  1104. }
  1105. },
  1106. labelOptions: {
  1107. style: {
  1108. color: palette.neutralColor60
  1109. }
  1110. }
  1111. }, navigation.annotationsOptions, navigation.bindings.measureX.annotationsOptions);
  1112. return this.chart.addAnnotation(options);
  1113. },
  1114. /** @ignore-option */
  1115. steps: [
  1116. bindingsUtils.updateRectSize
  1117. ]
  1118. },
  1119. /**
  1120. * A measure (y-dimension) annotation bindings. Includes `start` and one
  1121. * event in `steps` array.
  1122. *
  1123. * @type {Highcharts.NavigationBindingsOptionsObject}
  1124. * @product highstock
  1125. * @default {"className": "highcharts-measure-y", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  1126. */
  1127. measureY: {
  1128. /** @ignore-option */
  1129. className: 'highcharts-measure-y',
  1130. // eslint-disable-next-line valid-jsdoc
  1131. /** @ignore-option */
  1132. start: function (e) {
  1133. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  1134. // Exit if clicked out of axes area
  1135. if (!coordsX || !coordsY) {
  1136. return;
  1137. }
  1138. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  1139. langKey: 'measure',
  1140. type: 'measure',
  1141. typeOptions: {
  1142. selectType: 'y',
  1143. xAxis: coordsX.axis.options.index,
  1144. yAxis: coordsY.axis.options.index,
  1145. point: { x: x, y: y },
  1146. crosshairX: {
  1147. enabled: false,
  1148. strokeWidth: 0,
  1149. stroke: palette.neutralColor100
  1150. },
  1151. crosshairY: {
  1152. strokeWidth: 1,
  1153. stroke: palette.neutralColor100
  1154. },
  1155. background: {
  1156. width: 0,
  1157. height: 0,
  1158. strokeWidth: 0,
  1159. stroke: palette.backgroundColor
  1160. }
  1161. },
  1162. labelOptions: {
  1163. style: {
  1164. color: palette.neutralColor60
  1165. }
  1166. }
  1167. }, navigation.annotationsOptions, navigation.bindings.measureY.annotationsOptions);
  1168. return this.chart.addAnnotation(options);
  1169. },
  1170. /** @ignore-option */
  1171. steps: [
  1172. bindingsUtils.updateRectSize
  1173. ]
  1174. },
  1175. /**
  1176. * A measure (xy-dimension) annotation bindings. Includes `start` and one
  1177. * event in `steps` array.
  1178. *
  1179. * @type {Highcharts.NavigationBindingsOptionsObject}
  1180. * @product highstock
  1181. * @default {"className": "highcharts-measure-xy", "start": function() {}, "steps": [function() {}], "annotationsOptions": {}}
  1182. */
  1183. measureXY: {
  1184. /** @ignore-option */
  1185. className: 'highcharts-measure-xy',
  1186. // eslint-disable-next-line valid-jsdoc
  1187. /** @ignore-option */
  1188. start: function (e) {
  1189. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  1190. // Exit if clicked out of axes area
  1191. if (!coordsX || !coordsY) {
  1192. return;
  1193. }
  1194. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  1195. langKey: 'measure',
  1196. type: 'measure',
  1197. typeOptions: {
  1198. selectType: 'xy',
  1199. xAxis: coordsX.axis.options.index,
  1200. yAxis: coordsY.axis.options.index,
  1201. point: { x: x, y: y },
  1202. background: {
  1203. width: 0,
  1204. height: 0,
  1205. strokeWidth: 10
  1206. },
  1207. crosshairX: {
  1208. strokeWidth: 1,
  1209. stroke: palette.neutralColor100
  1210. },
  1211. crosshairY: {
  1212. strokeWidth: 1,
  1213. stroke: palette.neutralColor100
  1214. }
  1215. },
  1216. labelOptions: {
  1217. style: {
  1218. color: palette.neutralColor60
  1219. }
  1220. }
  1221. }, navigation.annotationsOptions, navigation.bindings.measureXY.annotationsOptions);
  1222. return this.chart.addAnnotation(options);
  1223. },
  1224. /** @ignore-option */
  1225. steps: [
  1226. bindingsUtils.updateRectSize
  1227. ]
  1228. },
  1229. // Advanced type annotations:
  1230. /**
  1231. * A fibonacci annotation bindings. Includes `start` and two events in
  1232. * `steps` array (updates second point, then height).
  1233. *
  1234. * @type {Highcharts.NavigationBindingsOptionsObject}
  1235. * @product highstock
  1236. * @default {"className": "highcharts-fibonacci", "start": function() {}, "steps": [function() {}, function() {}], "annotationsOptions": {}}
  1237. */
  1238. fibonacci: {
  1239. /** @ignore-option */
  1240. className: 'highcharts-fibonacci',
  1241. // eslint-disable-next-line valid-jsdoc
  1242. /** @ignore-option */
  1243. start: function (e) {
  1244. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  1245. // Exit if clicked out of axes area
  1246. if (!coordsX || !coordsY) {
  1247. return;
  1248. }
  1249. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  1250. langKey: 'fibonacci',
  1251. type: 'fibonacci',
  1252. typeOptions: {
  1253. xAxis: coordsX.axis.options.index,
  1254. yAxis: coordsY.axis.options.index,
  1255. points: [
  1256. { x: x, y: y },
  1257. { x: x, y: y }
  1258. ]
  1259. },
  1260. labelOptions: {
  1261. style: {
  1262. color: palette.neutralColor60
  1263. }
  1264. }
  1265. }, navigation.annotationsOptions, navigation.bindings.fibonacci.annotationsOptions);
  1266. return this.chart.addAnnotation(options);
  1267. },
  1268. /** @ignore-option */
  1269. steps: [
  1270. bindingsUtils.updateNthPoint(1),
  1271. bindingsUtils.updateHeight
  1272. ]
  1273. },
  1274. /**
  1275. * A parallel channel (tunnel) annotation bindings. Includes `start` and
  1276. * two events in `steps` array (updates second point, then height).
  1277. *
  1278. * @type {Highcharts.NavigationBindingsOptionsObject}
  1279. * @product highstock
  1280. * @default {"className": "highcharts-parallel-channel", "start": function() {}, "steps": [function() {}, function() {}], "annotationsOptions": {}}
  1281. */
  1282. parallelChannel: {
  1283. /** @ignore-option */
  1284. className: 'highcharts-parallel-channel',
  1285. // eslint-disable-next-line valid-jsdoc
  1286. /** @ignore-option */
  1287. start: function (e) {
  1288. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  1289. // Exit if clicked out of axes area
  1290. if (!coordsX || !coordsY) {
  1291. return;
  1292. }
  1293. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  1294. langKey: 'parallelChannel',
  1295. type: 'tunnel',
  1296. typeOptions: {
  1297. xAxis: coordsX.axis.options.index,
  1298. yAxis: coordsY.axis.options.index,
  1299. points: [
  1300. { x: x, y: y },
  1301. { x: x, y: y }
  1302. ]
  1303. }
  1304. }, navigation.annotationsOptions, navigation.bindings.parallelChannel.annotationsOptions);
  1305. return this.chart.addAnnotation(options);
  1306. },
  1307. /** @ignore-option */
  1308. steps: [
  1309. bindingsUtils.updateNthPoint(1),
  1310. bindingsUtils.updateHeight
  1311. ]
  1312. },
  1313. /**
  1314. * An Andrew's pitchfork annotation bindings. Includes `start` and two
  1315. * events in `steps` array (sets second and third control points).
  1316. *
  1317. * @type {Highcharts.NavigationBindingsOptionsObject}
  1318. * @product highstock
  1319. * @default {"className": "highcharts-pitchfork", "start": function() {}, "steps": [function() {}, function() {}], "annotationsOptions": {}}
  1320. */
  1321. pitchfork: {
  1322. /** @ignore-option */
  1323. className: 'highcharts-pitchfork',
  1324. // eslint-disable-next-line valid-jsdoc
  1325. /** @ignore-option */
  1326. start: function (e) {
  1327. var coords = this.chart.pointer.getCoordinates(e), coordsX = this.utils.getAssignedAxis(coords.xAxis), coordsY = this.utils.getAssignedAxis(coords.yAxis);
  1328. // Exit if clicked out of axes area
  1329. if (!coordsX || !coordsY) {
  1330. return;
  1331. }
  1332. var x = coordsX.value, y = coordsY.value, navigation = this.chart.options.navigation, options = merge({
  1333. langKey: 'pitchfork',
  1334. type: 'pitchfork',
  1335. typeOptions: {
  1336. xAxis: coordsX.axis.options.index,
  1337. yAxis: coordsY.axis.options.index,
  1338. points: [{
  1339. x: coordsX.value,
  1340. y: coordsY.value,
  1341. controlPoint: {
  1342. style: {
  1343. fill: palette.negativeColor
  1344. }
  1345. }
  1346. }, { x: x, y: y },
  1347. { x: x, y: y }],
  1348. innerBackground: {
  1349. fill: 'rgba(100, 170, 255, 0.8)'
  1350. }
  1351. },
  1352. shapeOptions: {
  1353. strokeWidth: 2
  1354. }
  1355. }, navigation.annotationsOptions, navigation.bindings.pitchfork.annotationsOptions);
  1356. return this.chart.addAnnotation(options);
  1357. },
  1358. /** @ignore-option */
  1359. steps: [
  1360. bindingsUtils.updateNthPoint(1),
  1361. bindingsUtils.updateNthPoint(2)
  1362. ]
  1363. },
  1364. // Labels with arrow and auto increments
  1365. /**
  1366. * A vertical counter annotation bindings. Includes `start` event. On click,
  1367. * finds the closest point and marks it with a numeric annotation -
  1368. * incrementing counter on each add.
  1369. *
  1370. * @type {Highcharts.NavigationBindingsOptionsObject}
  1371. * @product highstock
  1372. * @default {"className": "highcharts-vertical-counter", "start": function() {}, "annotationsOptions": {}}
  1373. */
  1374. verticalCounter: {
  1375. /** @ignore-option */
  1376. className: 'highcharts-vertical-counter',
  1377. // eslint-disable-next-line valid-jsdoc
  1378. /** @ignore-option */
  1379. start: function (e) {
  1380. var closestPoint = bindingsUtils.attractToPoint(e, this.chart), navigation = this.chart.options.navigation, options, annotation;
  1381. // Exit if clicked out of axes area
  1382. if (!closestPoint) {
  1383. return;
  1384. }
  1385. this.verticalCounter = this.verticalCounter || 0;
  1386. options = merge({
  1387. langKey: 'verticalCounter',
  1388. type: 'verticalLine',
  1389. typeOptions: {
  1390. point: {
  1391. x: closestPoint.x,
  1392. y: closestPoint.y,
  1393. xAxis: closestPoint.xAxis,
  1394. yAxis: closestPoint.yAxis
  1395. },
  1396. label: {
  1397. offset: closestPoint.below ? 40 : -40,
  1398. text: this.verticalCounter.toString()
  1399. }
  1400. },
  1401. labelOptions: {
  1402. style: {
  1403. color: palette.neutralColor60,
  1404. fontSize: '11px'
  1405. }
  1406. },
  1407. shapeOptions: {
  1408. stroke: 'rgba(0, 0, 0, 0.75)',
  1409. strokeWidth: 1
  1410. }
  1411. }, navigation.annotationsOptions, navigation.bindings.verticalCounter.annotationsOptions);
  1412. annotation = this.chart.addAnnotation(options);
  1413. this.verticalCounter++;
  1414. annotation.options.events.click.call(annotation, {});
  1415. }
  1416. },
  1417. /**
  1418. * A vertical arrow annotation bindings. Includes `start` event. On click,
  1419. * finds the closest point and marks it with an arrow and a label with
  1420. * value.
  1421. *
  1422. * @type {Highcharts.NavigationBindingsOptionsObject}
  1423. * @product highstock
  1424. * @default {"className": "highcharts-vertical-label", "start": function() {}, "annotationsOptions": {}}
  1425. */
  1426. verticalLabel: {
  1427. /** @ignore-option */
  1428. className: 'highcharts-vertical-label',
  1429. // eslint-disable-next-line valid-jsdoc
  1430. /** @ignore-option */
  1431. start: function (e) {
  1432. var closestPoint = bindingsUtils.attractToPoint(e, this.chart), navigation = this.chart.options.navigation, options, annotation;
  1433. // Exit if clicked out of axes area
  1434. if (!closestPoint) {
  1435. return;
  1436. }
  1437. options = merge({
  1438. langKey: 'verticalLabel',
  1439. type: 'verticalLine',
  1440. typeOptions: {
  1441. point: {
  1442. x: closestPoint.x,
  1443. y: closestPoint.y,
  1444. xAxis: closestPoint.xAxis,
  1445. yAxis: closestPoint.yAxis
  1446. },
  1447. label: {
  1448. offset: closestPoint.below ? 40 : -40
  1449. }
  1450. },
  1451. labelOptions: {
  1452. style: {
  1453. color: palette.neutralColor60,
  1454. fontSize: '11px'
  1455. }
  1456. },
  1457. shapeOptions: {
  1458. stroke: 'rgba(0, 0, 0, 0.75)',
  1459. strokeWidth: 1
  1460. }
  1461. }, navigation.annotationsOptions, navigation.bindings.verticalLabel.annotationsOptions);
  1462. annotation = this.chart.addAnnotation(options);
  1463. annotation.options.events.click.call(annotation, {});
  1464. }
  1465. },
  1466. /**
  1467. * A vertical arrow annotation bindings. Includes `start` event. On click,
  1468. * finds the closest point and marks it with an arrow.
  1469. * `${palette.positiveColor}` is the color of the arrow when
  1470. * pointing from above and `${palette.negativeColor}`
  1471. * when pointing from below the point.
  1472. *
  1473. * @type {Highcharts.NavigationBindingsOptionsObject}
  1474. * @product highstock
  1475. * @default {"className": "highcharts-vertical-arrow", "start": function() {}, "annotationsOptions": {}}
  1476. */
  1477. verticalArrow: {
  1478. /** @ignore-option */
  1479. className: 'highcharts-vertical-arrow',
  1480. // eslint-disable-next-line valid-jsdoc
  1481. /** @ignore-option */
  1482. start: function (e) {
  1483. var closestPoint = bindingsUtils.attractToPoint(e, this.chart), navigation = this.chart.options.navigation, options, annotation;
  1484. // Exit if clicked out of axes area
  1485. if (!closestPoint) {
  1486. return;
  1487. }
  1488. options = merge({
  1489. langKey: 'verticalArrow',
  1490. type: 'verticalLine',
  1491. typeOptions: {
  1492. point: {
  1493. x: closestPoint.x,
  1494. y: closestPoint.y,
  1495. xAxis: closestPoint.xAxis,
  1496. yAxis: closestPoint.yAxis
  1497. },
  1498. label: {
  1499. offset: closestPoint.below ? 40 : -40,
  1500. format: ' '
  1501. },
  1502. connector: {
  1503. fill: 'none',
  1504. stroke: closestPoint.below ?
  1505. palette.negativeColor : palette.positiveColor
  1506. }
  1507. },
  1508. shapeOptions: {
  1509. stroke: 'rgba(0, 0, 0, 0.75)',
  1510. strokeWidth: 1
  1511. }
  1512. }, navigation.annotationsOptions, navigation.bindings.verticalArrow.annotationsOptions);
  1513. annotation = this.chart.addAnnotation(options);
  1514. annotation.options.events.click.call(annotation, {});
  1515. }
  1516. },
  1517. // Flag types:
  1518. /**
  1519. * A flag series bindings. Includes `start` event. On click, finds the
  1520. * closest point and marks it with a flag with `'circlepin'` shape.
  1521. *
  1522. * @type {Highcharts.NavigationBindingsOptionsObject}
  1523. * @product highstock
  1524. * @default {"className": "highcharts-flag-circlepin", "start": function() {}}
  1525. */
  1526. flagCirclepin: {
  1527. /** @ignore-option */
  1528. className: 'highcharts-flag-circlepin',
  1529. /** @ignore-option */
  1530. start: bindingsUtils.addFlagFromForm('circlepin')
  1531. },
  1532. /**
  1533. * A flag series bindings. Includes `start` event. On click, finds the
  1534. * closest point and marks it with a flag with `'diamondpin'` shape.
  1535. *
  1536. * @type {Highcharts.NavigationBindingsOptionsObject}
  1537. * @product highstock
  1538. * @default {"className": "highcharts-flag-diamondpin", "start": function() {}}
  1539. */
  1540. flagDiamondpin: {
  1541. /** @ignore-option */
  1542. className: 'highcharts-flag-diamondpin',
  1543. /** @ignore-option */
  1544. start: bindingsUtils.addFlagFromForm('flag')
  1545. },
  1546. /**
  1547. * A flag series bindings. Includes `start` event.
  1548. * On click, finds the closest point and marks it with a flag with
  1549. * `'squarepin'` shape.
  1550. *
  1551. * @type {Highcharts.NavigationBindingsOptionsObject}
  1552. * @product highstock
  1553. * @default {"className": "highcharts-flag-squarepin", "start": function() {}}
  1554. */
  1555. flagSquarepin: {
  1556. /** @ignore-option */
  1557. className: 'highcharts-flag-squarepin',
  1558. /** @ignore-option */
  1559. start: bindingsUtils.addFlagFromForm('squarepin')
  1560. },
  1561. /**
  1562. * A flag series bindings. Includes `start` event.
  1563. * On click, finds the closest point and marks it with a flag without pin
  1564. * shape.
  1565. *
  1566. * @type {Highcharts.NavigationBindingsOptionsObject}
  1567. * @product highstock
  1568. * @default {"className": "highcharts-flag-simplepin", "start": function() {}}
  1569. */
  1570. flagSimplepin: {
  1571. /** @ignore-option */
  1572. className: 'highcharts-flag-simplepin',
  1573. /** @ignore-option */
  1574. start: bindingsUtils.addFlagFromForm('nopin')
  1575. },
  1576. // Other tools:
  1577. /**
  1578. * Enables zooming in xAxis on a chart. Includes `start` event which
  1579. * changes [chart.zoomType](#chart.zoomType).
  1580. *
  1581. * @type {Highcharts.NavigationBindingsOptionsObject}
  1582. * @product highstock
  1583. * @default {"className": "highcharts-zoom-x", "init": function() {}}
  1584. */
  1585. zoomX: {
  1586. /** @ignore-option */
  1587. className: 'highcharts-zoom-x',
  1588. // eslint-disable-next-line valid-jsdoc
  1589. /** @ignore-option */
  1590. init: function (button) {
  1591. this.chart.update({
  1592. chart: {
  1593. zoomType: 'x'
  1594. }
  1595. });
  1596. fireEvent(this, 'deselectButton', { button: button });
  1597. }
  1598. },
  1599. /**
  1600. * Enables zooming in yAxis on a chart. Includes `start` event which
  1601. * changes [chart.zoomType](#chart.zoomType).
  1602. *
  1603. * @type {Highcharts.NavigationBindingsOptionsObject}
  1604. * @product highstock
  1605. * @default {"className": "highcharts-zoom-y", "init": function() {}}
  1606. */
  1607. zoomY: {
  1608. /** @ignore-option */
  1609. className: 'highcharts-zoom-y',
  1610. // eslint-disable-next-line valid-jsdoc
  1611. /** @ignore-option */
  1612. init: function (button) {
  1613. this.chart.update({
  1614. chart: {
  1615. zoomType: 'y'
  1616. }
  1617. });
  1618. fireEvent(this, 'deselectButton', { button: button });
  1619. }
  1620. },
  1621. /**
  1622. * Enables zooming in xAxis and yAxis on a chart. Includes `start` event
  1623. * which changes [chart.zoomType](#chart.zoomType).
  1624. *
  1625. * @type {Highcharts.NavigationBindingsOptionsObject}
  1626. * @product highstock
  1627. * @default {"className": "highcharts-zoom-xy", "init": function() {}}
  1628. */
  1629. zoomXY: {
  1630. /** @ignore-option */
  1631. className: 'highcharts-zoom-xy',
  1632. // eslint-disable-next-line valid-jsdoc
  1633. /** @ignore-option */
  1634. init: function (button) {
  1635. this.chart.update({
  1636. chart: {
  1637. zoomType: 'xy'
  1638. }
  1639. });
  1640. fireEvent(this, 'deselectButton', { button: button });
  1641. }
  1642. },
  1643. /**
  1644. * Changes main series to `'line'` type.
  1645. *
  1646. * @type {Highcharts.NavigationBindingsOptionsObject}
  1647. * @product highstock
  1648. * @default {"className": "highcharts-series-type-line", "init": function() {}}
  1649. */
  1650. seriesTypeLine: {
  1651. /** @ignore-option */
  1652. className: 'highcharts-series-type-line',
  1653. // eslint-disable-next-line valid-jsdoc
  1654. /** @ignore-option */
  1655. init: function (button) {
  1656. this.chart.series[0].update({
  1657. type: 'line',
  1658. useOhlcData: true
  1659. });
  1660. fireEvent(this, 'deselectButton', { button: button });
  1661. }
  1662. },
  1663. /**
  1664. * Changes main series to `'ohlc'` type.
  1665. *
  1666. * @type {Highcharts.NavigationBindingsOptionsObject}
  1667. * @product highstock
  1668. * @default {"className": "highcharts-series-type-ohlc", "init": function() {}}
  1669. */
  1670. seriesTypeOhlc: {
  1671. /** @ignore-option */
  1672. className: 'highcharts-series-type-ohlc',
  1673. // eslint-disable-next-line valid-jsdoc
  1674. /** @ignore-option */
  1675. init: function (button) {
  1676. this.chart.series[0].update({
  1677. type: 'ohlc'
  1678. });
  1679. fireEvent(this, 'deselectButton', { button: button });
  1680. }
  1681. },
  1682. /**
  1683. * Changes main series to `'candlestick'` type.
  1684. *
  1685. * @type {Highcharts.NavigationBindingsOptionsObject}
  1686. * @product highstock
  1687. * @default {"className": "highcharts-series-type-candlestick", "init": function() {}}
  1688. */
  1689. seriesTypeCandlestick: {
  1690. /** @ignore-option */
  1691. className: 'highcharts-series-type-candlestick',
  1692. // eslint-disable-next-line valid-jsdoc
  1693. /** @ignore-option */
  1694. init: function (button) {
  1695. this.chart.series[0].update({
  1696. type: 'candlestick'
  1697. });
  1698. fireEvent(this, 'deselectButton', { button: button });
  1699. }
  1700. },
  1701. /**
  1702. * Displays chart in fullscreen.
  1703. *
  1704. * **Note**: Fullscreen is not supported on iPhone due to iOS limitations.
  1705. *
  1706. * @type {Highcharts.NavigationBindingsOptionsObject}
  1707. * @product highstock
  1708. * @default {"className": "noDataState": "normal", "highcharts-full-screen", "init": function() {}}
  1709. */
  1710. fullScreen: {
  1711. /** @ignore-option */
  1712. className: 'highcharts-full-screen',
  1713. noDataState: 'normal',
  1714. // eslint-disable-next-line valid-jsdoc
  1715. /** @ignore-option */
  1716. init: function (button) {
  1717. this.chart.fullscreen.toggle();
  1718. fireEvent(this, 'deselectButton', { button: button });
  1719. }
  1720. },
  1721. /**
  1722. * Hides/shows two price indicators:
  1723. * - last price in the dataset
  1724. * - last price in the selected range
  1725. *
  1726. * @type {Highcharts.NavigationBindingsOptionsObject}
  1727. * @product highstock
  1728. * @default {"className": "highcharts-current-price-indicator", "init": function() {}}
  1729. */
  1730. currentPriceIndicator: {
  1731. /** @ignore-option */
  1732. className: 'highcharts-current-price-indicator',
  1733. // eslint-disable-next-line valid-jsdoc
  1734. /** @ignore-option */
  1735. init: function (button) {
  1736. var chart = this.chart, series = chart.series, gui = chart.stockTools, priceIndicatorEnabled = bindingsUtils.isPriceIndicatorEnabled(chart.series);
  1737. if (gui && gui.guiEnabled) {
  1738. series.forEach(function (series) {
  1739. series.update({
  1740. lastPrice: { enabled: !priceIndicatorEnabled },
  1741. lastVisiblePrice: { enabled: !priceIndicatorEnabled, label: { enabled: true } }
  1742. }, false);
  1743. });
  1744. chart.redraw();
  1745. }
  1746. fireEvent(this, 'deselectButton', { button: button });
  1747. }
  1748. },
  1749. /**
  1750. * Indicators bindings. Includes `init` event to show a popup.
  1751. *
  1752. * Note: In order to show base series from the chart in the popup's
  1753. * dropdown each series requires
  1754. * [series.id](https://api.highcharts.com/highstock/series.line.id) to be
  1755. * defined.
  1756. *
  1757. * @type {Highcharts.NavigationBindingsOptionsObject}
  1758. * @product highstock
  1759. * @default {"className": "highcharts-indicators", "init": function() {}}
  1760. */
  1761. indicators: {
  1762. /** @ignore-option */
  1763. className: 'highcharts-indicators',
  1764. // eslint-disable-next-line valid-jsdoc
  1765. /** @ignore-option */
  1766. init: function () {
  1767. var navigation = this;
  1768. fireEvent(navigation, 'showPopup', {
  1769. formType: 'indicators',
  1770. options: {},
  1771. // Callback on submit:
  1772. onSubmit: function (data) {
  1773. navigation.utils.manageIndicators.call(navigation, data);
  1774. }
  1775. });
  1776. }
  1777. },
  1778. /**
  1779. * Hides/shows all annotations on a chart.
  1780. *
  1781. * @type {Highcharts.NavigationBindingsOptionsObject}
  1782. * @product highstock
  1783. * @default {"className": "highcharts-toggle-annotations", "init": function() {}}
  1784. */
  1785. toggleAnnotations: {
  1786. /** @ignore-option */
  1787. className: 'highcharts-toggle-annotations',
  1788. // eslint-disable-next-line valid-jsdoc
  1789. /** @ignore-option */
  1790. init: function (button) {
  1791. var chart = this.chart, gui = chart.stockTools, iconsURL = gui.getIconsURL();
  1792. this.toggledAnnotations = !this.toggledAnnotations;
  1793. (chart.annotations || []).forEach(function (annotation) {
  1794. annotation.setVisibility(!this.toggledAnnotations);
  1795. }, this);
  1796. if (gui && gui.guiEnabled) {
  1797. if (this.toggledAnnotations) {
  1798. button.firstChild.style['background-image'] =
  1799. 'url("' + iconsURL +
  1800. 'annotations-hidden.svg")';
  1801. }
  1802. else {
  1803. button.firstChild.style['background-image'] =
  1804. 'url("' + iconsURL +
  1805. 'annotations-visible.svg")';
  1806. }
  1807. }
  1808. fireEvent(this, 'deselectButton', { button: button });
  1809. }
  1810. },
  1811. /**
  1812. * Save a chart in localStorage under `highcharts-chart` key.
  1813. * Stored items:
  1814. * - annotations
  1815. * - indicators (with yAxes)
  1816. * - flags
  1817. *
  1818. * @type {Highcharts.NavigationBindingsOptionsObject}
  1819. * @product highstock
  1820. * @default {"className": "highcharts-save-chart", "noDataState": "normal", "init": function() {}}
  1821. */
  1822. saveChart: {
  1823. /** @ignore-option */
  1824. className: 'highcharts-save-chart',
  1825. noDataState: 'normal',
  1826. // eslint-disable-next-line valid-jsdoc
  1827. /** @ignore-option */
  1828. init: function (button) {
  1829. var navigation = this, chart = navigation.chart, annotations = [], indicators = [], flags = [], yAxes = [];
  1830. chart.annotations.forEach(function (annotation, index) {
  1831. annotations[index] = annotation.userOptions;
  1832. });
  1833. chart.series.forEach(function (series) {
  1834. if (series.is('sma')) {
  1835. indicators.push(series.userOptions);
  1836. }
  1837. else if (series.type === 'flags') {
  1838. flags.push(series.userOptions);
  1839. }
  1840. });
  1841. chart.yAxis.forEach(function (yAxis) {
  1842. if (bindingsUtils.isNotNavigatorYAxis(yAxis)) {
  1843. yAxes.push(yAxis.options);
  1844. }
  1845. });
  1846. H.win.localStorage.setItem(PREFIX + 'chart', JSON.stringify({
  1847. annotations: annotations,
  1848. indicators: indicators,
  1849. flags: flags,
  1850. yAxes: yAxes
  1851. }));
  1852. fireEvent(this, 'deselectButton', { button: button });
  1853. }
  1854. }
  1855. };
  1856. setOptions({
  1857. navigation: {
  1858. bindings: stockToolsBindings
  1859. }
  1860. });
  1861. NavigationBindings.prototype.utils = merge(bindingsUtils, NavigationBindings.prototype.utils);