StockToolsGui.js 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315
  1. /* *
  2. *
  3. * GUI generator for Stock tools
  4. *
  5. * (c) 2009-2021 Sebastian Bochan
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. import Chart from '../Core/Chart/Chart.js';
  13. import H from '../Core/Globals.js';
  14. import NavigationBindings from '../Extensions/Annotations/NavigationBindings.js';
  15. import U from '../Core/Utilities.js';
  16. var addEvent = U.addEvent, createElement = U.createElement, css = U.css, extend = U.extend, fireEvent = U.fireEvent, getStyle = U.getStyle, isArray = U.isArray, merge = U.merge, pick = U.pick, setOptions = U.setOptions;
  17. var DIV = 'div', SPAN = 'span', UL = 'ul', LI = 'li', PREFIX = 'highcharts-', activeClass = PREFIX + 'active';
  18. setOptions({
  19. /**
  20. * @optionparent lang
  21. */
  22. lang: {
  23. /**
  24. * Configure the stockTools GUI titles(hints) in the chart. Requires
  25. * the `stock-tools.js` module to be loaded.
  26. *
  27. * @product highstock
  28. * @since 7.0.0
  29. */
  30. stockTools: {
  31. gui: {
  32. // Main buttons:
  33. simpleShapes: 'Simple shapes',
  34. lines: 'Lines',
  35. crookedLines: 'Crooked lines',
  36. measure: 'Measure',
  37. advanced: 'Advanced',
  38. toggleAnnotations: 'Toggle annotations',
  39. verticalLabels: 'Vertical labels',
  40. flags: 'Flags',
  41. zoomChange: 'Zoom change',
  42. typeChange: 'Type change',
  43. saveChart: 'Save chart',
  44. indicators: 'Indicators',
  45. currentPriceIndicator: 'Current Price Indicators',
  46. // Other features:
  47. zoomX: 'Zoom X',
  48. zoomY: 'Zoom Y',
  49. zoomXY: 'Zooom XY',
  50. fullScreen: 'Fullscreen',
  51. typeOHLC: 'OHLC',
  52. typeLine: 'Line',
  53. typeCandlestick: 'Candlestick',
  54. // Basic shapes:
  55. circle: 'Circle',
  56. label: 'Label',
  57. rectangle: 'Rectangle',
  58. // Flags:
  59. flagCirclepin: 'Flag circle',
  60. flagDiamondpin: 'Flag diamond',
  61. flagSquarepin: 'Flag square',
  62. flagSimplepin: 'Flag simple',
  63. // Measures:
  64. measureXY: 'Measure XY',
  65. measureX: 'Measure X',
  66. measureY: 'Measure Y',
  67. // Segment, ray and line:
  68. segment: 'Segment',
  69. arrowSegment: 'Arrow segment',
  70. ray: 'Ray',
  71. arrowRay: 'Arrow ray',
  72. line: 'Line',
  73. arrowLine: 'Arrow line',
  74. horizontalLine: 'Horizontal line',
  75. verticalLine: 'Vertical line',
  76. infinityLine: 'Infinity line',
  77. // Crooked lines:
  78. crooked3: 'Crooked 3 line',
  79. crooked5: 'Crooked 5 line',
  80. elliott3: 'Elliott 3 line',
  81. elliott5: 'Elliott 5 line',
  82. // Counters:
  83. verticalCounter: 'Vertical counter',
  84. verticalLabel: 'Vertical label',
  85. verticalArrow: 'Vertical arrow',
  86. // Advanced:
  87. fibonacci: 'Fibonacci',
  88. pitchfork: 'Pitchfork',
  89. parallelChannel: 'Parallel channel'
  90. }
  91. },
  92. navigation: {
  93. popup: {
  94. // Annotations:
  95. circle: 'Circle',
  96. rectangle: 'Rectangle',
  97. label: 'Label',
  98. segment: 'Segment',
  99. arrowSegment: 'Arrow segment',
  100. ray: 'Ray',
  101. arrowRay: 'Arrow ray',
  102. line: 'Line',
  103. arrowLine: 'Arrow line',
  104. horizontalLine: 'Horizontal line',
  105. verticalLine: 'Vertical line',
  106. crooked3: 'Crooked 3 line',
  107. crooked5: 'Crooked 5 line',
  108. elliott3: 'Elliott 3 line',
  109. elliott5: 'Elliott 5 line',
  110. verticalCounter: 'Vertical counter',
  111. verticalLabel: 'Vertical label',
  112. verticalArrow: 'Vertical arrow',
  113. fibonacci: 'Fibonacci',
  114. pitchfork: 'Pitchfork',
  115. parallelChannel: 'Parallel channel',
  116. infinityLine: 'Infinity line',
  117. measure: 'Measure',
  118. measureXY: 'Measure XY',
  119. measureX: 'Measure X',
  120. measureY: 'Measure Y',
  121. // Flags:
  122. flags: 'Flags',
  123. // GUI elements:
  124. addButton: 'add',
  125. saveButton: 'save',
  126. editButton: 'edit',
  127. removeButton: 'remove',
  128. series: 'Series',
  129. volume: 'Volume',
  130. connector: 'Connector',
  131. // Field names:
  132. innerBackground: 'Inner background',
  133. outerBackground: 'Outer background',
  134. crosshairX: 'Crosshair X',
  135. crosshairY: 'Crosshair Y',
  136. tunnel: 'Tunnel',
  137. background: 'Background'
  138. }
  139. }
  140. },
  141. /**
  142. * Configure the stockTools gui strings in the chart. Requires the
  143. * [stockTools module]() to be loaded. For a description of the module
  144. * and information on its features, see [Highcharts StockTools]().
  145. *
  146. * @product highstock
  147. *
  148. * @sample stock/demo/stock-tools-gui Stock Tools GUI
  149. *
  150. * @sample stock/demo/stock-tools-custom-gui Stock Tools customized GUI
  151. *
  152. * @since 7.0.0
  153. * @optionparent stockTools
  154. */
  155. stockTools: {
  156. /**
  157. * Definitions of buttons in Stock Tools GUI.
  158. */
  159. gui: {
  160. /**
  161. * Path where Highcharts will look for icons. Change this to use
  162. * icons from a different server.
  163. *
  164. * Since 7.1.3 use [iconsURL](#navigation.iconsURL) for popup and
  165. * stock tools.
  166. *
  167. * @deprecated
  168. * @apioption stockTools.gui.iconsURL
  169. *
  170. */
  171. /**
  172. * Enable or disable the stockTools gui.
  173. */
  174. enabled: true,
  175. /**
  176. * A CSS class name to apply to the stocktools' div,
  177. * allowing unique CSS styling for each chart.
  178. */
  179. className: 'highcharts-bindings-wrapper',
  180. /**
  181. * A CSS class name to apply to the container of buttons,
  182. * allowing unique CSS styling for each chart.
  183. */
  184. toolbarClassName: 'stocktools-toolbar',
  185. /**
  186. * A collection of strings pointing to config options for the
  187. * toolbar items. Each name refers to a unique key from the
  188. * definitions object.
  189. *
  190. * @type {Array<string>}
  191. * @default [
  192. * 'indicators',
  193. * 'separator',
  194. * 'simpleShapes',
  195. * 'lines',
  196. * 'crookedLines',
  197. * 'measure',
  198. * 'advanced',
  199. * 'toggleAnnotations',
  200. * 'separator',
  201. * 'verticalLabels',
  202. * 'flags',
  203. * 'separator',
  204. * 'zoomChange',
  205. * 'fullScreen',
  206. * 'typeChange',
  207. * 'separator',
  208. * 'currentPriceIndicator',
  209. * 'saveChart'
  210. * ]
  211. */
  212. buttons: [
  213. 'indicators',
  214. 'separator',
  215. 'simpleShapes',
  216. 'lines',
  217. 'crookedLines',
  218. 'measure',
  219. 'advanced',
  220. 'toggleAnnotations',
  221. 'separator',
  222. 'verticalLabels',
  223. 'flags',
  224. 'separator',
  225. 'zoomChange',
  226. 'fullScreen',
  227. 'typeChange',
  228. 'separator',
  229. 'currentPriceIndicator',
  230. 'saveChart'
  231. ],
  232. /**
  233. * An options object of the buttons definitions. Each name refers to
  234. * unique key from buttons array.
  235. */
  236. definitions: {
  237. separator: {
  238. /**
  239. * A predefined background symbol for the button.
  240. */
  241. symbol: 'separator.svg'
  242. },
  243. simpleShapes: {
  244. /**
  245. * A collection of strings pointing to config options for
  246. * the items.
  247. *
  248. * @type {array}
  249. * @default [
  250. * 'label',
  251. * 'circle',
  252. * 'rectangle'
  253. * ]
  254. *
  255. */
  256. items: [
  257. 'label',
  258. 'circle',
  259. 'rectangle'
  260. ],
  261. circle: {
  262. /**
  263. * A predefined background symbol for the button.
  264. *
  265. * @type {string}
  266. *
  267. */
  268. symbol: 'circle.svg'
  269. },
  270. rectangle: {
  271. /**
  272. * A predefined background symbol for the button.
  273. *
  274. * @type {string}
  275. *
  276. */
  277. symbol: 'rectangle.svg'
  278. },
  279. label: {
  280. /**
  281. * A predefined background symbol for the button.
  282. *
  283. * @type {string}
  284. *
  285. */
  286. symbol: 'label.svg'
  287. }
  288. },
  289. flags: {
  290. /**
  291. * A collection of strings pointing to config options for
  292. * the items.
  293. *
  294. * @type {array}
  295. * @default [
  296. * 'flagCirclepin',
  297. * 'flagDiamondpin',
  298. * 'flagSquarepin',
  299. * 'flagSimplepin'
  300. * ]
  301. *
  302. */
  303. items: [
  304. 'flagCirclepin',
  305. 'flagDiamondpin',
  306. 'flagSquarepin',
  307. 'flagSimplepin'
  308. ],
  309. flagSimplepin: {
  310. /**
  311. * A predefined background symbol for the button.
  312. *
  313. * @type {string}
  314. *
  315. */
  316. symbol: 'flag-basic.svg'
  317. },
  318. flagDiamondpin: {
  319. /**
  320. * A predefined background symbol for the button.
  321. *
  322. * @type {string}
  323. *
  324. */
  325. symbol: 'flag-diamond.svg'
  326. },
  327. flagSquarepin: {
  328. /**
  329. * A predefined background symbol for the button.
  330. *
  331. * @type {string}
  332. */
  333. symbol: 'flag-trapeze.svg'
  334. },
  335. flagCirclepin: {
  336. /**
  337. * A predefined background symbol for the button.
  338. *
  339. * @type {string}
  340. */
  341. symbol: 'flag-elipse.svg'
  342. }
  343. },
  344. lines: {
  345. /**
  346. * A collection of strings pointing to config options for
  347. * the items.
  348. *
  349. * @type {array}
  350. * @default [
  351. * 'segment',
  352. * 'arrowSegment',
  353. * 'ray',
  354. * 'arrowRay',
  355. * 'line',
  356. * 'arrowLine',
  357. * 'horizontalLine',
  358. * 'verticalLine'
  359. * ]
  360. */
  361. items: [
  362. 'segment',
  363. 'arrowSegment',
  364. 'ray',
  365. 'arrowRay',
  366. 'line',
  367. 'arrowLine',
  368. 'horizontalLine',
  369. 'verticalLine'
  370. ],
  371. segment: {
  372. /**
  373. * A predefined background symbol for the button.
  374. *
  375. * @type {string}
  376. */
  377. symbol: 'segment.svg'
  378. },
  379. arrowSegment: {
  380. /**
  381. * A predefined background symbol for the button.
  382. *
  383. * @type {string}
  384. */
  385. symbol: 'arrow-segment.svg'
  386. },
  387. ray: {
  388. /**
  389. * A predefined background symbol for the button.
  390. *
  391. * @type {string}
  392. */
  393. symbol: 'ray.svg'
  394. },
  395. arrowRay: {
  396. /**
  397. * A predefined background symbol for the button.
  398. *
  399. * @type {string}
  400. */
  401. symbol: 'arrow-ray.svg'
  402. },
  403. line: {
  404. /**
  405. * A predefined background symbol for the button.
  406. *
  407. * @type {string}
  408. */
  409. symbol: 'line.svg'
  410. },
  411. arrowLine: {
  412. /**
  413. * A predefined background symbol for the button.
  414. *
  415. * @type {string}
  416. */
  417. symbol: 'arrow-line.svg'
  418. },
  419. verticalLine: {
  420. /**
  421. * A predefined background symbol for the button.
  422. *
  423. * @type {string}
  424. */
  425. symbol: 'vertical-line.svg'
  426. },
  427. horizontalLine: {
  428. /**
  429. * A predefined background symbol for the button.
  430. *
  431. * @type {string}
  432. */
  433. symbol: 'horizontal-line.svg'
  434. }
  435. },
  436. crookedLines: {
  437. /**
  438. * A collection of strings pointing to config options for
  439. * the items.
  440. *
  441. * @type {array}
  442. * @default [
  443. * 'elliott3',
  444. * 'elliott5',
  445. * 'crooked3',
  446. * 'crooked5'
  447. * ]
  448. *
  449. */
  450. items: [
  451. 'elliott3',
  452. 'elliott5',
  453. 'crooked3',
  454. 'crooked5'
  455. ],
  456. crooked3: {
  457. /**
  458. * A predefined background symbol for the button.
  459. *
  460. * @type {string}
  461. */
  462. symbol: 'crooked-3.svg'
  463. },
  464. crooked5: {
  465. /**
  466. * A predefined background symbol for the button.
  467. *
  468. * @type {string}
  469. */
  470. symbol: 'crooked-5.svg'
  471. },
  472. elliott3: {
  473. /**
  474. * A predefined background symbol for the button.
  475. *
  476. * @type {string}
  477. */
  478. symbol: 'elliott-3.svg'
  479. },
  480. elliott5: {
  481. /**
  482. * A predefined background symbol for the button.
  483. *
  484. * @type {string}
  485. */
  486. symbol: 'elliott-5.svg'
  487. }
  488. },
  489. verticalLabels: {
  490. /**
  491. * A collection of strings pointing to config options for
  492. * the items.
  493. *
  494. * @type {array}
  495. * @default [
  496. * 'verticalCounter',
  497. * 'verticalLabel',
  498. * 'verticalArrow'
  499. * ]
  500. */
  501. items: [
  502. 'verticalCounter',
  503. 'verticalLabel',
  504. 'verticalArrow'
  505. ],
  506. verticalCounter: {
  507. /**
  508. * A predefined background symbol for the button.
  509. *
  510. * @type {string}
  511. */
  512. symbol: 'vertical-counter.svg'
  513. },
  514. verticalLabel: {
  515. /**
  516. * A predefined background symbol for the button.
  517. *
  518. * @type {string}
  519. */
  520. symbol: 'vertical-label.svg'
  521. },
  522. verticalArrow: {
  523. /**
  524. * A predefined background symbol for the button.
  525. *
  526. * @type {string}
  527. */
  528. symbol: 'vertical-arrow.svg'
  529. }
  530. },
  531. advanced: {
  532. /**
  533. * A collection of strings pointing to config options for
  534. * the items.
  535. *
  536. * @type {array}
  537. * @default [
  538. * 'fibonacci',
  539. * 'pitchfork',
  540. * 'parallelChannel'
  541. * ]
  542. */
  543. items: [
  544. 'fibonacci',
  545. 'pitchfork',
  546. 'parallelChannel'
  547. ],
  548. pitchfork: {
  549. /**
  550. * A predefined background symbol for the button.
  551. *
  552. * @type {string}
  553. */
  554. symbol: 'pitchfork.svg'
  555. },
  556. fibonacci: {
  557. /**
  558. * A predefined background symbol for the button.
  559. *
  560. * @type {string}
  561. */
  562. symbol: 'fibonacci.svg'
  563. },
  564. parallelChannel: {
  565. /**
  566. * A predefined background symbol for the button.
  567. *
  568. * @type {string}
  569. */
  570. symbol: 'parallel-channel.svg'
  571. }
  572. },
  573. measure: {
  574. /**
  575. * A collection of strings pointing to config options for
  576. * the items.
  577. *
  578. * @type {array}
  579. * @default [
  580. * 'measureXY',
  581. * 'measureX',
  582. * 'measureY'
  583. * ]
  584. */
  585. items: [
  586. 'measureXY',
  587. 'measureX',
  588. 'measureY'
  589. ],
  590. measureX: {
  591. /**
  592. * A predefined background symbol for the button.
  593. *
  594. * @type {string}
  595. */
  596. symbol: 'measure-x.svg'
  597. },
  598. measureY: {
  599. /**
  600. * A predefined background symbol for the button.
  601. *
  602. * @type {string}
  603. */
  604. symbol: 'measure-y.svg'
  605. },
  606. measureXY: {
  607. /**
  608. * A predefined background symbol for the button.
  609. *
  610. * @type {string}
  611. */
  612. symbol: 'measure-xy.svg'
  613. }
  614. },
  615. toggleAnnotations: {
  616. /**
  617. * A predefined background symbol for the button.
  618. *
  619. * @type {string}
  620. */
  621. symbol: 'annotations-visible.svg'
  622. },
  623. currentPriceIndicator: {
  624. /**
  625. * A predefined background symbol for the button.
  626. *
  627. * @type {string}
  628. */
  629. symbol: 'current-price-show.svg'
  630. },
  631. indicators: {
  632. /**
  633. * A predefined background symbol for the button.
  634. *
  635. * @type {string}
  636. */
  637. symbol: 'indicators.svg'
  638. },
  639. zoomChange: {
  640. /**
  641. * A collection of strings pointing to config options for
  642. * the items.
  643. *
  644. * @type {array}
  645. * @default [
  646. * 'zoomX',
  647. * 'zoomY',
  648. * 'zoomXY'
  649. * ]
  650. */
  651. items: [
  652. 'zoomX',
  653. 'zoomY',
  654. 'zoomXY'
  655. ],
  656. zoomX: {
  657. /**
  658. * A predefined background symbol for the button.
  659. *
  660. * @type {string}
  661. */
  662. symbol: 'zoom-x.svg'
  663. },
  664. zoomY: {
  665. /**
  666. * A predefined background symbol for the button.
  667. *
  668. * @type {string}
  669. */
  670. symbol: 'zoom-y.svg'
  671. },
  672. zoomXY: {
  673. /**
  674. * A predefined background symbol for the button.
  675. *
  676. * @type {string}
  677. */
  678. symbol: 'zoom-xy.svg'
  679. }
  680. },
  681. typeChange: {
  682. /**
  683. * A collection of strings pointing to config options for
  684. * the items.
  685. *
  686. * @type {array}
  687. * @default [
  688. * 'typeOHLC',
  689. * 'typeLine',
  690. * 'typeCandlestick'
  691. * ]
  692. */
  693. items: [
  694. 'typeOHLC',
  695. 'typeLine',
  696. 'typeCandlestick'
  697. ],
  698. typeOHLC: {
  699. /**
  700. * A predefined background symbol for the button.
  701. *
  702. * @type {string}
  703. */
  704. symbol: 'series-ohlc.svg'
  705. },
  706. typeLine: {
  707. /**
  708. * A predefined background symbol for the button.
  709. *
  710. * @type {string}
  711. */
  712. symbol: 'series-line.svg'
  713. },
  714. typeCandlestick: {
  715. /**
  716. * A predefined background symbol for the button.
  717. *
  718. * @type {string}
  719. */
  720. symbol: 'series-candlestick.svg'
  721. }
  722. },
  723. fullScreen: {
  724. /**
  725. * A predefined background symbol for the button.
  726. *
  727. * @type {string}
  728. */
  729. symbol: 'fullscreen.svg'
  730. },
  731. saveChart: {
  732. /**
  733. * A predefined background symbol for the button.
  734. *
  735. * @type {string}
  736. */
  737. symbol: 'save-chart.svg'
  738. }
  739. }
  740. }
  741. }
  742. });
  743. /* eslint-disable no-invalid-this, valid-jsdoc */
  744. // Run HTML generator
  745. addEvent(Chart, 'afterGetContainer', function () {
  746. this.setStockTools();
  747. });
  748. addEvent(Chart, 'getMargins', function () {
  749. var listWrapper = this.stockTools && this.stockTools.listWrapper, offsetWidth = listWrapper && ((listWrapper.startWidth +
  750. getStyle(listWrapper, 'padding-left') +
  751. getStyle(listWrapper, 'padding-right')) || listWrapper.offsetWidth);
  752. if (offsetWidth && offsetWidth < this.plotWidth) {
  753. this.plotLeft += offsetWidth;
  754. this.spacing[3] += offsetWidth;
  755. }
  756. });
  757. ['beforeRender', 'beforeRedraw'].forEach(function (event) {
  758. addEvent(Chart, event, function () {
  759. if (this.stockTools) {
  760. var listWrapper = this.stockTools.listWrapper, offsetWidth = listWrapper && ((listWrapper.startWidth +
  761. getStyle(listWrapper, 'padding-left') +
  762. getStyle(listWrapper, 'padding-right')) || listWrapper.offsetWidth);
  763. var dirty = false;
  764. if (offsetWidth && offsetWidth < this.plotWidth) {
  765. this.spacingBox.x += offsetWidth;
  766. dirty = true;
  767. }
  768. else if (offsetWidth === 0) {
  769. dirty = true;
  770. }
  771. if (offsetWidth !== this.stockTools.prevOffsetWidth) {
  772. this.stockTools.prevOffsetWidth = offsetWidth;
  773. if (dirty) {
  774. this.isDirtyLegend = true;
  775. }
  776. }
  777. }
  778. });
  779. });
  780. addEvent(Chart, 'destroy', function () {
  781. if (this.stockTools) {
  782. this.stockTools.destroy();
  783. }
  784. });
  785. addEvent(Chart, 'redraw', function () {
  786. if (this.stockTools && this.stockTools.guiEnabled) {
  787. this.stockTools.redraw();
  788. }
  789. });
  790. /**
  791. * Toolbar Class
  792. * @private
  793. * @constructor
  794. * @param {Object} - options of toolbar
  795. * @param {Chart} - Reference to chart
  796. */
  797. var Toolbar = /** @class */ (function () {
  798. function Toolbar(options, langOptions, chart) {
  799. this.arrowDown = void 0;
  800. this.arrowUp = void 0;
  801. this.arrowWrapper = void 0;
  802. this.listWrapper = void 0;
  803. this.showhideBtn = void 0;
  804. this.submenu = void 0;
  805. this.toolbar = void 0;
  806. this.wrapper = void 0;
  807. this.chart = chart;
  808. this.options = options;
  809. this.lang = langOptions;
  810. // set url for icons.
  811. this.iconsURL = this.getIconsURL();
  812. this.guiEnabled = options.enabled;
  813. this.visible = pick(options.visible, true);
  814. this.placed = pick(options.placed, false);
  815. // General events collection which should be removed upon
  816. // destroy/update:
  817. this.eventsToUnbind = [];
  818. if (this.guiEnabled) {
  819. this.createHTML();
  820. this.init();
  821. this.showHideNavigatorion();
  822. }
  823. fireEvent(this, 'afterInit');
  824. }
  825. /**
  826. * Initialize the toolbar. Create buttons and submenu for each option
  827. * defined in `stockTools.gui`.
  828. * @private
  829. */
  830. Toolbar.prototype.init = function () {
  831. var _self = this, lang = this.lang, guiOptions = this.options, toolbar = this.toolbar, addSubmenu = _self.addSubmenu, buttons = guiOptions.buttons, defs = guiOptions.definitions, allButtons = toolbar.childNodes, button;
  832. // create buttons
  833. buttons.forEach(function (btnName) {
  834. button = _self.addButton(toolbar, defs, btnName, lang);
  835. _self.eventsToUnbind.push(addEvent(button.buttonWrapper, 'click', function () {
  836. _self.eraseActiveButtons(allButtons, button.buttonWrapper);
  837. }));
  838. if (isArray(defs[btnName].items)) {
  839. // create submenu buttons
  840. addSubmenu.call(_self, button, defs[btnName]);
  841. }
  842. });
  843. };
  844. /**
  845. * Create submenu (list of buttons) for the option. In example main button
  846. * is Line, in submenu will be buttons with types of lines.
  847. * @private
  848. * @param {Highcharts.Dictionary<Highcharts.HTMLDOMElement>}
  849. * button which has submenu
  850. * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions}
  851. * list of all buttons
  852. */
  853. Toolbar.prototype.addSubmenu = function (parentBtn, button) {
  854. var _self = this, submenuArrow = parentBtn.submenuArrow, buttonWrapper = parentBtn.buttonWrapper, buttonWidth = getStyle(buttonWrapper, 'width'), wrapper = this.wrapper, menuWrapper = this.listWrapper, allButtons = this.toolbar.childNodes, topMargin = 0, submenuWrapper;
  855. // create submenu container
  856. this.submenu = submenuWrapper = createElement(UL, {
  857. className: PREFIX + 'submenu-wrapper'
  858. }, null, buttonWrapper);
  859. // create submenu buttons and select the first one
  860. this.addSubmenuItems(buttonWrapper, button);
  861. // show / hide submenu
  862. _self.eventsToUnbind.push(addEvent(submenuArrow, 'click', function (e) {
  863. e.stopPropagation();
  864. // Erase active class on all other buttons
  865. _self.eraseActiveButtons(allButtons, buttonWrapper);
  866. // hide menu
  867. if (buttonWrapper.className.indexOf(PREFIX + 'current') >= 0) {
  868. menuWrapper.style.width =
  869. menuWrapper.startWidth + 'px';
  870. buttonWrapper.classList.remove(PREFIX + 'current');
  871. submenuWrapper.style.display = 'none';
  872. }
  873. else {
  874. // show menu
  875. // to calculate height of element
  876. submenuWrapper.style.display = 'block';
  877. topMargin = submenuWrapper.offsetHeight -
  878. buttonWrapper.offsetHeight - 3;
  879. // calculate position of submenu in the box
  880. // if submenu is inside, reset top margin
  881. if (
  882. // cut on the bottom
  883. !(submenuWrapper.offsetHeight +
  884. buttonWrapper.offsetTop >
  885. wrapper.offsetHeight &&
  886. // cut on the top
  887. buttonWrapper.offsetTop > topMargin)) {
  888. topMargin = 0;
  889. }
  890. // apply calculated styles
  891. css(submenuWrapper, {
  892. top: -topMargin + 'px',
  893. left: buttonWidth + 3 + 'px'
  894. });
  895. buttonWrapper.className += ' ' + PREFIX + 'current';
  896. menuWrapper.startWidth = wrapper.offsetWidth;
  897. menuWrapper.style.width = menuWrapper.startWidth +
  898. getStyle(menuWrapper, 'padding-left') +
  899. submenuWrapper.offsetWidth + 3 + 'px';
  900. }
  901. }));
  902. };
  903. /**
  904. * Create buttons in submenu
  905. * @private
  906. * @param {Highcharts.HTMLDOMElement}
  907. * button where submenu is placed
  908. * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions}
  909. * list of all buttons options
  910. *
  911. */
  912. Toolbar.prototype.addSubmenuItems = function (buttonWrapper, button) {
  913. var _self = this, submenuWrapper = this.submenu, lang = this.lang, menuWrapper = this.listWrapper, items = button.items, firstSubmenuItem, submenuBtn;
  914. // add items to submenu
  915. items.forEach(function (btnName) {
  916. // add buttons to submenu
  917. submenuBtn = _self.addButton(submenuWrapper, button, btnName, lang);
  918. _self.eventsToUnbind.push(addEvent(submenuBtn.mainButton, 'click', function () {
  919. _self.switchSymbol(this, buttonWrapper, true);
  920. menuWrapper.style.width =
  921. menuWrapper.startWidth + 'px';
  922. submenuWrapper.style.display = 'none';
  923. }));
  924. });
  925. // select first submenu item
  926. firstSubmenuItem = submenuWrapper
  927. .querySelectorAll('li > .' + PREFIX + 'menu-item-btn')[0];
  928. // replace current symbol, in main button, with submenu's button style
  929. _self.switchSymbol(firstSubmenuItem, false);
  930. };
  931. /*
  932. * Erase active class on all other buttons.
  933. *
  934. * @param {Array} - Array of HTML buttons
  935. * @param {HTMLDOMElement} - Current HTML button
  936. *
  937. */
  938. Toolbar.prototype.eraseActiveButtons = function (buttons, currentButton, submenuItems) {
  939. [].forEach.call(buttons, function (btn) {
  940. if (btn !== currentButton) {
  941. btn.classList.remove(PREFIX + 'current');
  942. btn.classList.remove(PREFIX + 'active');
  943. submenuItems =
  944. btn.querySelectorAll('.' + PREFIX + 'submenu-wrapper');
  945. // hide submenu
  946. if (submenuItems.length > 0) {
  947. submenuItems[0].style.display = 'none';
  948. }
  949. }
  950. });
  951. };
  952. /**
  953. * Create single button. Consist of HTML elements `li`, `span`, and (if
  954. * exists) submenu container.
  955. * @private
  956. * @param {Highcharts.HTMLDOMElement} target
  957. * HTML reference, where button should be added
  958. * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions|Highcharts.StockToolsGuiDefinitionsOptions} options
  959. * All options, by btnName refer to particular button
  960. * @param {string} btnName
  961. * of functionality mapped for specific class
  962. * @param {Highcharts.Dictionary<string>} lang
  963. * All titles, by btnName refer to particular button
  964. * @return {Object} - references to all created HTML elements
  965. */
  966. Toolbar.prototype.addButton = function (target, options, btnName, lang) {
  967. if (lang === void 0) { lang = {}; }
  968. var btnOptions = options[btnName], items = btnOptions.items, classMapping = Toolbar.prototype.classMapping, userClassName = btnOptions.className || '', mainButton, submenuArrow, buttonWrapper;
  969. // main button wrapper
  970. buttonWrapper = createElement(LI, {
  971. className: pick(classMapping[btnName], '') + ' ' + userClassName,
  972. title: lang[btnName] || btnName
  973. }, null, target);
  974. // single button
  975. mainButton = createElement(SPAN, {
  976. className: PREFIX + 'menu-item-btn'
  977. }, null, buttonWrapper);
  978. // submenu
  979. if (items && items.length) {
  980. // arrow is a hook to show / hide submenu
  981. submenuArrow = createElement(SPAN, {
  982. className: PREFIX + 'submenu-item-arrow ' +
  983. PREFIX + 'arrow-right'
  984. }, null, buttonWrapper);
  985. submenuArrow.style['background-image'] = 'url(' +
  986. this.iconsURL + 'arrow-bottom.svg)';
  987. }
  988. else {
  989. mainButton.style['background-image'] = 'url(' +
  990. this.iconsURL + btnOptions.symbol + ')';
  991. }
  992. return {
  993. buttonWrapper: buttonWrapper,
  994. mainButton: mainButton,
  995. submenuArrow: submenuArrow
  996. };
  997. };
  998. /*
  999. * Create navigation's HTML elements: container and arrows.
  1000. *
  1001. */
  1002. Toolbar.prototype.addNavigation = function () {
  1003. var stockToolbar = this, wrapper = stockToolbar.wrapper;
  1004. // arrow wrapper
  1005. stockToolbar.arrowWrapper = createElement(DIV, {
  1006. className: PREFIX + 'arrow-wrapper'
  1007. });
  1008. stockToolbar.arrowUp = createElement(DIV, {
  1009. className: PREFIX + 'arrow-up'
  1010. }, null, stockToolbar.arrowWrapper);
  1011. stockToolbar.arrowUp.style['background-image'] =
  1012. 'url(' + this.iconsURL + 'arrow-right.svg)';
  1013. stockToolbar.arrowDown = createElement(DIV, {
  1014. className: PREFIX + 'arrow-down'
  1015. }, null, stockToolbar.arrowWrapper);
  1016. stockToolbar.arrowDown.style['background-image'] =
  1017. 'url(' + this.iconsURL + 'arrow-right.svg)';
  1018. wrapper.insertBefore(stockToolbar.arrowWrapper, wrapper.childNodes[0]);
  1019. // attach scroll events
  1020. stockToolbar.scrollButtons();
  1021. };
  1022. /*
  1023. * Add events to navigation (two arrows) which allows user to scroll
  1024. * top/down GUI buttons, if container's height is not enough.
  1025. *
  1026. */
  1027. Toolbar.prototype.scrollButtons = function () {
  1028. var targetY = 0, _self = this, wrapper = _self.wrapper, toolbar = _self.toolbar, step = 0.1 * wrapper.offsetHeight; // 0.1 = 10%
  1029. _self.eventsToUnbind.push(addEvent(_self.arrowUp, 'click', function () {
  1030. if (targetY > 0) {
  1031. targetY -= step;
  1032. toolbar.style['margin-top'] = -targetY + 'px';
  1033. }
  1034. }));
  1035. _self.eventsToUnbind.push(addEvent(_self.arrowDown, 'click', function () {
  1036. if (wrapper.offsetHeight + targetY <=
  1037. toolbar.offsetHeight + step) {
  1038. targetY += step;
  1039. toolbar.style['margin-top'] = -targetY + 'px';
  1040. }
  1041. }));
  1042. };
  1043. /*
  1044. * Create stockTools HTML main elements.
  1045. *
  1046. */
  1047. Toolbar.prototype.createHTML = function () {
  1048. var stockToolbar = this, chart = stockToolbar.chart, guiOptions = stockToolbar.options, container = chart.container, navigation = chart.options.navigation, bindingsClassName = navigation && navigation.bindingsClassName, listWrapper, toolbar, wrapper;
  1049. // create main container
  1050. stockToolbar.wrapper = wrapper = createElement(DIV, {
  1051. className: PREFIX + 'stocktools-wrapper ' +
  1052. guiOptions.className + ' ' + bindingsClassName
  1053. });
  1054. container.parentNode.insertBefore(wrapper, container);
  1055. // toolbar
  1056. stockToolbar.toolbar = toolbar = createElement(UL, {
  1057. className: PREFIX + 'stocktools-toolbar ' +
  1058. guiOptions.toolbarClassName
  1059. });
  1060. // add container for list of buttons
  1061. stockToolbar.listWrapper = listWrapper = createElement(DIV, {
  1062. className: PREFIX + 'menu-wrapper'
  1063. });
  1064. wrapper.insertBefore(listWrapper, wrapper.childNodes[0]);
  1065. listWrapper.insertBefore(toolbar, listWrapper.childNodes[0]);
  1066. stockToolbar.showHideToolbar();
  1067. // add navigation which allows user to scroll down / top GUI buttons
  1068. stockToolbar.addNavigation();
  1069. };
  1070. /**
  1071. * Function called in redraw verifies if the navigation should be visible.
  1072. * @private
  1073. */
  1074. Toolbar.prototype.showHideNavigatorion = function () {
  1075. // arrows
  1076. // 50px space for arrows
  1077. if (this.visible &&
  1078. this.toolbar.offsetHeight > (this.wrapper.offsetHeight - 50)) {
  1079. this.arrowWrapper.style.display = 'block';
  1080. }
  1081. else {
  1082. // reset margin if whole toolbar is visible
  1083. this.toolbar.style.marginTop = '0px';
  1084. // hide arrows
  1085. this.arrowWrapper.style.display = 'none';
  1086. }
  1087. };
  1088. /**
  1089. * Create button which shows or hides GUI toolbar.
  1090. * @private
  1091. */
  1092. Toolbar.prototype.showHideToolbar = function () {
  1093. var stockToolbar = this, chart = this.chart, wrapper = stockToolbar.wrapper, toolbar = this.listWrapper, submenu = this.submenu, visible = this.visible, showhideBtn;
  1094. // Show hide toolbar
  1095. this.showhideBtn = showhideBtn = createElement(DIV, {
  1096. className: PREFIX + 'toggle-toolbar ' + PREFIX + 'arrow-left'
  1097. }, null, wrapper);
  1098. showhideBtn.style['background-image'] =
  1099. 'url(' + this.iconsURL + 'arrow-right.svg)';
  1100. if (!visible) {
  1101. // hide
  1102. if (submenu) {
  1103. submenu.style.display = 'none';
  1104. }
  1105. showhideBtn.style.left = '0px';
  1106. stockToolbar.visible = visible = false;
  1107. toolbar.classList.add(PREFIX + 'hide');
  1108. showhideBtn.classList.toggle(PREFIX + 'arrow-right');
  1109. wrapper.style.height = showhideBtn.offsetHeight + 'px';
  1110. }
  1111. else {
  1112. wrapper.style.height = '100%';
  1113. showhideBtn.style.top = getStyle(toolbar, 'padding-top') + 'px';
  1114. showhideBtn.style.left = (wrapper.offsetWidth +
  1115. getStyle(toolbar, 'padding-left')) + 'px';
  1116. }
  1117. // Toggle menu
  1118. stockToolbar.eventsToUnbind.push(addEvent(showhideBtn, 'click', function () {
  1119. chart.update({
  1120. stockTools: {
  1121. gui: {
  1122. visible: !visible,
  1123. placed: true
  1124. }
  1125. }
  1126. });
  1127. }));
  1128. };
  1129. /*
  1130. * In main GUI button, replace icon and class with submenu button's
  1131. * class / symbol.
  1132. *
  1133. * @param {HTMLDOMElement} - submenu button
  1134. * @param {Boolean} - true or false
  1135. *
  1136. */
  1137. Toolbar.prototype.switchSymbol = function (button, redraw) {
  1138. var buttonWrapper = button.parentNode, buttonWrapperClass = buttonWrapper.classList.value,
  1139. // main button in first level og GUI
  1140. mainNavButton = buttonWrapper.parentNode.parentNode;
  1141. // set class
  1142. mainNavButton.className = '';
  1143. if (buttonWrapperClass) {
  1144. mainNavButton.classList.add(buttonWrapperClass.trim());
  1145. }
  1146. // set icon
  1147. mainNavButton
  1148. .querySelectorAll('.' + PREFIX + 'menu-item-btn')[0]
  1149. .style['background-image'] =
  1150. button.style['background-image'];
  1151. // set active class
  1152. if (redraw) {
  1153. this.selectButton(mainNavButton);
  1154. }
  1155. };
  1156. /*
  1157. * Set select state (active class) on button.
  1158. *
  1159. * @param {HTMLDOMElement} - button
  1160. *
  1161. */
  1162. Toolbar.prototype.selectButton = function (button) {
  1163. if (button.className.indexOf(activeClass) >= 0) {
  1164. button.classList.remove(activeClass);
  1165. }
  1166. else {
  1167. button.classList.add(activeClass);
  1168. }
  1169. };
  1170. /*
  1171. * Remove active class from all buttons except defined.
  1172. *
  1173. * @param {HTMLDOMElement} - button which should not be deactivated
  1174. *
  1175. */
  1176. Toolbar.prototype.unselectAllButtons = function (button) {
  1177. var activeButtons = button.parentNode
  1178. .querySelectorAll('.' + activeClass);
  1179. [].forEach.call(activeButtons, function (activeBtn) {
  1180. if (activeBtn !== button) {
  1181. activeBtn.classList.remove(activeClass);
  1182. }
  1183. });
  1184. };
  1185. /*
  1186. * Update GUI with given options.
  1187. *
  1188. * @param {Object} - general options for Stock Tools
  1189. */
  1190. Toolbar.prototype.update = function (options) {
  1191. merge(true, this.chart.options.stockTools, options);
  1192. this.destroy();
  1193. this.chart.setStockTools(options);
  1194. // If Stock Tools are updated, then bindings should be updated too:
  1195. if (this.chart.navigationBindings) {
  1196. this.chart.navigationBindings.update();
  1197. }
  1198. };
  1199. /**
  1200. * Destroy all HTML GUI elements.
  1201. * @private
  1202. */
  1203. Toolbar.prototype.destroy = function () {
  1204. var stockToolsDiv = this.wrapper, parent = stockToolsDiv && stockToolsDiv.parentNode;
  1205. this.eventsToUnbind.forEach(function (unbinder) {
  1206. unbinder();
  1207. });
  1208. // Remove the empty element
  1209. if (parent) {
  1210. parent.removeChild(stockToolsDiv);
  1211. }
  1212. // redraw
  1213. this.chart.isDirtyBox = true;
  1214. this.chart.redraw();
  1215. };
  1216. /**
  1217. * Redraw, GUI requires to verify if the navigation should be visible.
  1218. * @private
  1219. */
  1220. Toolbar.prototype.redraw = function () {
  1221. this.showHideNavigatorion();
  1222. };
  1223. Toolbar.prototype.getIconsURL = function () {
  1224. return this.chart.options.navigation.iconsURL ||
  1225. this.options.iconsURL ||
  1226. 'https://code.highcharts.com/9.0.1/gfx/stock-icons/';
  1227. };
  1228. return Toolbar;
  1229. }());
  1230. /**
  1231. * Mapping JSON fields to CSS classes.
  1232. * @private
  1233. */
  1234. Toolbar.prototype.classMapping = {
  1235. circle: PREFIX + 'circle-annotation',
  1236. rectangle: PREFIX + 'rectangle-annotation',
  1237. label: PREFIX + 'label-annotation',
  1238. segment: PREFIX + 'segment',
  1239. arrowSegment: PREFIX + 'arrow-segment',
  1240. ray: PREFIX + 'ray',
  1241. arrowRay: PREFIX + 'arrow-ray',
  1242. line: PREFIX + 'infinity-line',
  1243. arrowLine: PREFIX + 'arrow-infinity-line',
  1244. verticalLine: PREFIX + 'vertical-line',
  1245. horizontalLine: PREFIX + 'horizontal-line',
  1246. crooked3: PREFIX + 'crooked3',
  1247. crooked5: PREFIX + 'crooked5',
  1248. elliott3: PREFIX + 'elliott3',
  1249. elliott5: PREFIX + 'elliott5',
  1250. pitchfork: PREFIX + 'pitchfork',
  1251. fibonacci: PREFIX + 'fibonacci',
  1252. parallelChannel: PREFIX + 'parallel-channel',
  1253. measureX: PREFIX + 'measure-x',
  1254. measureY: PREFIX + 'measure-y',
  1255. measureXY: PREFIX + 'measure-xy',
  1256. verticalCounter: PREFIX + 'vertical-counter',
  1257. verticalLabel: PREFIX + 'vertical-label',
  1258. verticalArrow: PREFIX + 'vertical-arrow',
  1259. currentPriceIndicator: PREFIX + 'current-price-indicator',
  1260. indicators: PREFIX + 'indicators',
  1261. flagCirclepin: PREFIX + 'flag-circlepin',
  1262. flagDiamondpin: PREFIX + 'flag-diamondpin',
  1263. flagSquarepin: PREFIX + 'flag-squarepin',
  1264. flagSimplepin: PREFIX + 'flag-simplepin',
  1265. zoomX: PREFIX + 'zoom-x',
  1266. zoomY: PREFIX + 'zoom-y',
  1267. zoomXY: PREFIX + 'zoom-xy',
  1268. typeLine: PREFIX + 'series-type-line',
  1269. typeOHLC: PREFIX + 'series-type-ohlc',
  1270. typeCandlestick: PREFIX + 'series-type-candlestick',
  1271. fullScreen: PREFIX + 'full-screen',
  1272. toggleAnnotations: PREFIX + 'toggle-annotations',
  1273. saveChart: PREFIX + 'save-chart',
  1274. separator: PREFIX + 'separator'
  1275. };
  1276. extend(Chart.prototype, {
  1277. /**
  1278. * Verify if Toolbar should be added.
  1279. * @private
  1280. * @param {Highcharts.StockToolsOptions} - chart options
  1281. */
  1282. setStockTools: function (options) {
  1283. var chartOptions = this.options, lang = chartOptions.lang, guiOptions = merge(chartOptions.stockTools && chartOptions.stockTools.gui, options && options.gui), langOptions = lang.stockTools && lang.stockTools.gui;
  1284. this.stockTools = new Toolbar(guiOptions, langOptions, this);
  1285. if (this.stockTools.guiEnabled) {
  1286. this.isDirtyBox = true;
  1287. }
  1288. }
  1289. });
  1290. // Comunication with bindings:
  1291. addEvent(NavigationBindings, 'selectButton', function (event) {
  1292. var button = event.button, className = PREFIX + 'submenu-wrapper', gui = this.chart.stockTools;
  1293. if (gui && gui.guiEnabled) {
  1294. // Unslect other active buttons
  1295. gui.unselectAllButtons(event.button);
  1296. // If clicked on a submenu, select state for it's parent
  1297. if (button.parentNode.className.indexOf(className) >= 0) {
  1298. button = button.parentNode.parentNode;
  1299. }
  1300. // Set active class on the current button
  1301. gui.selectButton(button);
  1302. }
  1303. });
  1304. addEvent(NavigationBindings, 'deselectButton', function (event) {
  1305. var button = event.button, className = PREFIX + 'submenu-wrapper', gui = this.chart.stockTools;
  1306. if (gui && gui.guiEnabled) {
  1307. // If deselecting a button from a submenu, select state for it's parent
  1308. if (button.parentNode.className.indexOf(className) >= 0) {
  1309. button = button.parentNode.parentNode;
  1310. }
  1311. gui.selectButton(button);
  1312. }
  1313. });
  1314. H.Toolbar = Toolbar;
  1315. export default H.Toolbar;