StockToolsGui.js 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  1. /* *
  2. *
  3. * GUI generator for Stock tools
  4. *
  5. * (c) 2009-2017 Sebastian Bochan
  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 Chart from '../Core/Chart/Chart.js';
  14. import H from '../Core/Globals.js';
  15. import NavigationBindings from '../Extensions/Annotations/NavigationBindings.js';
  16. import U from '../Core/Utilities.js';
  17. 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;
  18. var DIV = 'div', SPAN = 'span', UL = 'ul', LI = 'li', PREFIX = 'highcharts-', activeClass = PREFIX + 'active';
  19. setOptions({
  20. /**
  21. * @optionparent lang
  22. */
  23. lang: {
  24. /**
  25. * Configure the stockTools GUI titles(hints) in the chart. Requires
  26. * the `stock-tools.js` module to be loaded.
  27. *
  28. * @product highstock
  29. * @since 7.0.0
  30. */
  31. stockTools: {
  32. gui: {
  33. // Main buttons:
  34. simpleShapes: 'Simple shapes',
  35. lines: 'Lines',
  36. crookedLines: 'Crooked lines',
  37. measure: 'Measure',
  38. advanced: 'Advanced',
  39. toggleAnnotations: 'Toggle annotations',
  40. verticalLabels: 'Vertical labels',
  41. flags: 'Flags',
  42. zoomChange: 'Zoom change',
  43. typeChange: 'Type change',
  44. saveChart: 'Save chart',
  45. indicators: 'Indicators',
  46. currentPriceIndicator: 'Current Price Indicators',
  47. // Other features:
  48. zoomX: 'Zoom X',
  49. zoomY: 'Zoom Y',
  50. zoomXY: 'Zooom XY',
  51. fullScreen: 'Fullscreen',
  52. typeOHLC: 'OHLC',
  53. typeLine: 'Line',
  54. typeCandlestick: 'Candlestick',
  55. // Basic shapes:
  56. circle: 'Circle',
  57. label: 'Label',
  58. rectangle: 'Rectangle',
  59. // Flags:
  60. flagCirclepin: 'Flag circle',
  61. flagDiamondpin: 'Flag diamond',
  62. flagSquarepin: 'Flag square',
  63. flagSimplepin: 'Flag simple',
  64. // Measures:
  65. measureXY: 'Measure XY',
  66. measureX: 'Measure X',
  67. measureY: 'Measure Y',
  68. // Segment, ray and line:
  69. segment: 'Segment',
  70. arrowSegment: 'Arrow segment',
  71. ray: 'Ray',
  72. arrowRay: 'Arrow ray',
  73. line: 'Line',
  74. arrowLine: 'Arrow line',
  75. horizontalLine: 'Horizontal line',
  76. verticalLine: 'Vertical line',
  77. infinityLine: 'Infinity line',
  78. // Crooked lines:
  79. crooked3: 'Crooked 3 line',
  80. crooked5: 'Crooked 5 line',
  81. elliott3: 'Elliott 3 line',
  82. elliott5: 'Elliott 5 line',
  83. // Counters:
  84. verticalCounter: 'Vertical counter',
  85. verticalLabel: 'Vertical label',
  86. verticalArrow: 'Vertical arrow',
  87. // Advanced:
  88. fibonacci: 'Fibonacci',
  89. pitchfork: 'Pitchfork',
  90. parallelChannel: 'Parallel channel'
  91. }
  92. },
  93. navigation: {
  94. popup: {
  95. // Annotations:
  96. circle: 'Circle',
  97. rectangle: 'Rectangle',
  98. label: 'Label',
  99. segment: 'Segment',
  100. arrowSegment: 'Arrow segment',
  101. ray: 'Ray',
  102. arrowRay: 'Arrow ray',
  103. line: 'Line',
  104. arrowLine: 'Arrow line',
  105. horizontalLine: 'Horizontal line',
  106. verticalLine: 'Vertical line',
  107. crooked3: 'Crooked 3 line',
  108. crooked5: 'Crooked 5 line',
  109. elliott3: 'Elliott 3 line',
  110. elliott5: 'Elliott 5 line',
  111. verticalCounter: 'Vertical counter',
  112. verticalLabel: 'Vertical label',
  113. verticalArrow: 'Vertical arrow',
  114. fibonacci: 'Fibonacci',
  115. pitchfork: 'Pitchfork',
  116. parallelChannel: 'Parallel channel',
  117. infinityLine: 'Infinity line',
  118. measure: 'Measure',
  119. measureXY: 'Measure XY',
  120. measureX: 'Measure X',
  121. measureY: 'Measure Y',
  122. // Flags:
  123. flags: 'Flags',
  124. // GUI elements:
  125. addButton: 'add',
  126. saveButton: 'save',
  127. editButton: 'edit',
  128. removeButton: 'remove',
  129. series: 'Series',
  130. volume: 'Volume',
  131. connector: 'Connector',
  132. // Field names:
  133. innerBackground: 'Inner background',
  134. outerBackground: 'Outer background',
  135. crosshairX: 'Crosshair X',
  136. crosshairY: 'Crosshair Y',
  137. tunnel: 'Tunnel',
  138. background: 'Background'
  139. }
  140. }
  141. },
  142. /**
  143. * Configure the stockTools gui strings in the chart. Requires the
  144. * [stockTools module]() to be loaded. For a description of the module
  145. * and information on its features, see [Highcharts StockTools]().
  146. *
  147. * @product highstock
  148. *
  149. * @sample stock/demo/stock-tools-gui Stock Tools GUI
  150. *
  151. * @sample stock/demo/stock-tools-custom-gui Stock Tools customized GUI
  152. *
  153. * @since 7.0.0
  154. * @optionparent stockTools
  155. */
  156. stockTools: {
  157. /**
  158. * Definitions of buttons in Stock Tools GUI.
  159. */
  160. gui: {
  161. /**
  162. * Path where Highcharts will look for icons. Change this to use
  163. * icons from a different server.
  164. *
  165. * Since 7.1.3 use [iconsURL](#navigation.iconsURL) for popup and
  166. * stock tools.
  167. *
  168. * @deprecated
  169. * @apioption stockTools.gui.iconsURL
  170. *
  171. */
  172. /**
  173. * Enable or disable the stockTools gui.
  174. */
  175. enabled: true,
  176. /**
  177. * A CSS class name to apply to the stocktools' div,
  178. * allowing unique CSS styling for each chart.
  179. */
  180. className: 'highcharts-bindings-wrapper',
  181. /**
  182. * A CSS class name to apply to the container of buttons,
  183. * allowing unique CSS styling for each chart.
  184. */
  185. toolbarClassName: 'stocktools-toolbar',
  186. /**
  187. * A collection of strings pointing to config options for the
  188. * toolbar items. Each name refers to unique key from definitions
  189. * object.
  190. *
  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(H.Chart, 'afterGetContainer', function () {
  746. this.setStockTools();
  747. });
  748. addEvent(H.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. }
  755. });
  756. addEvent(H.Chart, 'destroy', function () {
  757. if (this.stockTools) {
  758. this.stockTools.destroy();
  759. }
  760. });
  761. addEvent(H.Chart, 'redraw', function () {
  762. if (this.stockTools && this.stockTools.guiEnabled) {
  763. this.stockTools.redraw();
  764. }
  765. });
  766. /**
  767. * Toolbar Class
  768. * @private
  769. * @constructor
  770. * @param {Object} - options of toolbar
  771. * @param {Chart} - Reference to chart
  772. */
  773. var Toolbar = /** @class */ (function () {
  774. function Toolbar(options, langOptions, chart) {
  775. this.arrowDown = void 0;
  776. this.arrowUp = void 0;
  777. this.arrowWrapper = void 0;
  778. this.listWrapper = void 0;
  779. this.showhideBtn = void 0;
  780. this.submenu = void 0;
  781. this.toolbar = void 0;
  782. this.wrapper = void 0;
  783. this.chart = chart;
  784. this.options = options;
  785. this.lang = langOptions;
  786. // set url for icons.
  787. this.iconsURL = this.getIconsURL();
  788. this.guiEnabled = options.enabled;
  789. this.visible = pick(options.visible, true);
  790. this.placed = pick(options.placed, false);
  791. // General events collection which should be removed upon
  792. // destroy/update:
  793. this.eventsToUnbind = [];
  794. if (this.guiEnabled) {
  795. this.createHTML();
  796. this.init();
  797. this.showHideNavigatorion();
  798. }
  799. fireEvent(this, 'afterInit');
  800. }
  801. /**
  802. * Initialize the toolbar. Create buttons and submenu for each option
  803. * defined in `stockTools.gui`.
  804. * @private
  805. */
  806. Toolbar.prototype.init = function () {
  807. 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;
  808. // create buttons
  809. buttons.forEach(function (btnName) {
  810. button = _self.addButton(toolbar, defs, btnName, lang);
  811. _self.eventsToUnbind.push(addEvent(button.buttonWrapper, 'click', function () {
  812. _self.eraseActiveButtons(allButtons, button.buttonWrapper);
  813. }));
  814. if (isArray(defs[btnName].items)) {
  815. // create submenu buttons
  816. addSubmenu.call(_self, button, defs[btnName]);
  817. }
  818. });
  819. };
  820. /**
  821. * Create submenu (list of buttons) for the option. In example main button
  822. * is Line, in submenu will be buttons with types of lines.
  823. * @private
  824. * @param {Highcharts.Dictionary<Highcharts.HTMLDOMElement>}
  825. * button which has submenu
  826. * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions}
  827. * list of all buttons
  828. */
  829. Toolbar.prototype.addSubmenu = function (parentBtn, button) {
  830. 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;
  831. // create submenu container
  832. this.submenu = submenuWrapper = createElement(UL, {
  833. className: PREFIX + 'submenu-wrapper'
  834. }, null, buttonWrapper);
  835. // create submenu buttons and select the first one
  836. this.addSubmenuItems(buttonWrapper, button);
  837. // show / hide submenu
  838. _self.eventsToUnbind.push(addEvent(submenuArrow, 'click', function (e) {
  839. e.stopPropagation();
  840. // Erase active class on all other buttons
  841. _self.eraseActiveButtons(allButtons, buttonWrapper);
  842. // hide menu
  843. if (buttonWrapper.className.indexOf(PREFIX + 'current') >= 0) {
  844. menuWrapper.style.width =
  845. menuWrapper.startWidth + 'px';
  846. buttonWrapper.classList.remove(PREFIX + 'current');
  847. submenuWrapper.style.display = 'none';
  848. }
  849. else {
  850. // show menu
  851. // to calculate height of element
  852. submenuWrapper.style.display = 'block';
  853. topMargin = submenuWrapper.offsetHeight -
  854. buttonWrapper.offsetHeight - 3;
  855. // calculate position of submenu in the box
  856. // if submenu is inside, reset top margin
  857. if (
  858. // cut on the bottom
  859. !(submenuWrapper.offsetHeight +
  860. buttonWrapper.offsetTop >
  861. wrapper.offsetHeight &&
  862. // cut on the top
  863. buttonWrapper.offsetTop > topMargin)) {
  864. topMargin = 0;
  865. }
  866. // apply calculated styles
  867. css(submenuWrapper, {
  868. top: -topMargin + 'px',
  869. left: buttonWidth + 3 + 'px'
  870. });
  871. buttonWrapper.className += ' ' + PREFIX + 'current';
  872. menuWrapper.startWidth = wrapper.offsetWidth;
  873. menuWrapper.style.width = menuWrapper.startWidth +
  874. getStyle(menuWrapper, 'padding-left') +
  875. submenuWrapper.offsetWidth + 3 + 'px';
  876. }
  877. }));
  878. };
  879. /**
  880. * Create buttons in submenu
  881. * @private
  882. * @param {Highcharts.HTMLDOMElement}
  883. * button where submenu is placed
  884. * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions}
  885. * list of all buttons options
  886. *
  887. */
  888. Toolbar.prototype.addSubmenuItems = function (buttonWrapper, button) {
  889. var _self = this, submenuWrapper = this.submenu, lang = this.lang, menuWrapper = this.listWrapper, items = button.items, firstSubmenuItem, submenuBtn;
  890. // add items to submenu
  891. items.forEach(function (btnName) {
  892. // add buttons to submenu
  893. submenuBtn = _self.addButton(submenuWrapper, button, btnName, lang);
  894. _self.eventsToUnbind.push(addEvent(submenuBtn.mainButton, 'click', function () {
  895. _self.switchSymbol(this, buttonWrapper, true);
  896. menuWrapper.style.width =
  897. menuWrapper.startWidth + 'px';
  898. submenuWrapper.style.display = 'none';
  899. }));
  900. });
  901. // select first submenu item
  902. firstSubmenuItem = submenuWrapper
  903. .querySelectorAll('li > .' + PREFIX + 'menu-item-btn')[0];
  904. // replace current symbol, in main button, with submenu's button style
  905. _self.switchSymbol(firstSubmenuItem, false);
  906. };
  907. /*
  908. * Erase active class on all other buttons.
  909. *
  910. * @param {Array} - Array of HTML buttons
  911. * @param {HTMLDOMElement} - Current HTML button
  912. *
  913. */
  914. Toolbar.prototype.eraseActiveButtons = function (buttons, currentButton, submenuItems) {
  915. [].forEach.call(buttons, function (btn) {
  916. if (btn !== currentButton) {
  917. btn.classList.remove(PREFIX + 'current');
  918. btn.classList.remove(PREFIX + 'active');
  919. submenuItems =
  920. btn.querySelectorAll('.' + PREFIX + 'submenu-wrapper');
  921. // hide submenu
  922. if (submenuItems.length > 0) {
  923. submenuItems[0].style.display = 'none';
  924. }
  925. }
  926. });
  927. };
  928. /**
  929. * Create single button. Consist of HTML elements `li`, `span`, and (if
  930. * exists) submenu container.
  931. * @private
  932. * @param {Highcharts.HTMLDOMElement} target
  933. * HTML reference, where button should be added
  934. * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions|Highcharts.StockToolsGuiDefinitionsOptions} options
  935. * All options, by btnName refer to particular button
  936. * @param {string} btnName
  937. * of functionality mapped for specific class
  938. * @param {Highcharts.Dictionary<string>} lang
  939. * All titles, by btnName refer to particular button
  940. * @return {Object} - references to all created HTML elements
  941. */
  942. Toolbar.prototype.addButton = function (target, options, btnName, lang) {
  943. if (lang === void 0) { lang = {}; }
  944. var btnOptions = options[btnName], items = btnOptions.items, classMapping = Toolbar.prototype.classMapping, userClassName = btnOptions.className || '', mainButton, submenuArrow, buttonWrapper;
  945. // main button wrapper
  946. buttonWrapper = createElement(LI, {
  947. className: pick(classMapping[btnName], '') + ' ' + userClassName,
  948. title: lang[btnName] || btnName
  949. }, null, target);
  950. // single button
  951. mainButton = createElement(SPAN, {
  952. className: PREFIX + 'menu-item-btn'
  953. }, null, buttonWrapper);
  954. // submenu
  955. if (items && items.length) {
  956. // arrow is a hook to show / hide submenu
  957. submenuArrow = createElement(SPAN, {
  958. className: PREFIX + 'submenu-item-arrow ' +
  959. PREFIX + 'arrow-right'
  960. }, null, buttonWrapper);
  961. submenuArrow.style['background-image'] = 'url(' +
  962. this.iconsURL + 'arrow-bottom.svg)';
  963. }
  964. else {
  965. mainButton.style['background-image'] = 'url(' +
  966. this.iconsURL + btnOptions.symbol + ')';
  967. }
  968. return {
  969. buttonWrapper: buttonWrapper,
  970. mainButton: mainButton,
  971. submenuArrow: submenuArrow
  972. };
  973. };
  974. /*
  975. * Create navigation's HTML elements: container and arrows.
  976. *
  977. */
  978. Toolbar.prototype.addNavigation = function () {
  979. var stockToolbar = this, wrapper = stockToolbar.wrapper;
  980. // arrow wrapper
  981. stockToolbar.arrowWrapper = createElement(DIV, {
  982. className: PREFIX + 'arrow-wrapper'
  983. });
  984. stockToolbar.arrowUp = createElement(DIV, {
  985. className: PREFIX + 'arrow-up'
  986. }, null, stockToolbar.arrowWrapper);
  987. stockToolbar.arrowUp.style['background-image'] =
  988. 'url(' + this.iconsURL + 'arrow-right.svg)';
  989. stockToolbar.arrowDown = createElement(DIV, {
  990. className: PREFIX + 'arrow-down'
  991. }, null, stockToolbar.arrowWrapper);
  992. stockToolbar.arrowDown.style['background-image'] =
  993. 'url(' + this.iconsURL + 'arrow-right.svg)';
  994. wrapper.insertBefore(stockToolbar.arrowWrapper, wrapper.childNodes[0]);
  995. // attach scroll events
  996. stockToolbar.scrollButtons();
  997. };
  998. /*
  999. * Add events to navigation (two arrows) which allows user to scroll
  1000. * top/down GUI buttons, if container's height is not enough.
  1001. *
  1002. */
  1003. Toolbar.prototype.scrollButtons = function () {
  1004. var targetY = 0, _self = this, wrapper = _self.wrapper, toolbar = _self.toolbar, step = 0.1 * wrapper.offsetHeight; // 0.1 = 10%
  1005. _self.eventsToUnbind.push(addEvent(_self.arrowUp, 'click', function () {
  1006. if (targetY > 0) {
  1007. targetY -= step;
  1008. toolbar.style['margin-top'] = -targetY + 'px';
  1009. }
  1010. }));
  1011. _self.eventsToUnbind.push(addEvent(_self.arrowDown, 'click', function () {
  1012. if (wrapper.offsetHeight + targetY <=
  1013. toolbar.offsetHeight + step) {
  1014. targetY += step;
  1015. toolbar.style['margin-top'] = -targetY + 'px';
  1016. }
  1017. }));
  1018. };
  1019. /*
  1020. * Create stockTools HTML main elements.
  1021. *
  1022. */
  1023. Toolbar.prototype.createHTML = function () {
  1024. var stockToolbar = this, chart = stockToolbar.chart, guiOptions = stockToolbar.options, container = chart.container, navigation = chart.options.navigation, bindingsClassName = navigation && navigation.bindingsClassName, listWrapper, toolbar, wrapper;
  1025. // create main container
  1026. stockToolbar.wrapper = wrapper = createElement(DIV, {
  1027. className: PREFIX + 'stocktools-wrapper ' +
  1028. guiOptions.className + ' ' + bindingsClassName
  1029. });
  1030. container.parentNode.insertBefore(wrapper, container);
  1031. // toolbar
  1032. stockToolbar.toolbar = toolbar = createElement(UL, {
  1033. className: PREFIX + 'stocktools-toolbar ' +
  1034. guiOptions.toolbarClassName
  1035. });
  1036. // add container for list of buttons
  1037. stockToolbar.listWrapper = listWrapper = createElement(DIV, {
  1038. className: PREFIX + 'menu-wrapper'
  1039. });
  1040. wrapper.insertBefore(listWrapper, wrapper.childNodes[0]);
  1041. listWrapper.insertBefore(toolbar, listWrapper.childNodes[0]);
  1042. stockToolbar.showHideToolbar();
  1043. // add navigation which allows user to scroll down / top GUI buttons
  1044. stockToolbar.addNavigation();
  1045. };
  1046. /**
  1047. * Function called in redraw verifies if the navigation should be visible.
  1048. * @private
  1049. */
  1050. Toolbar.prototype.showHideNavigatorion = function () {
  1051. // arrows
  1052. // 50px space for arrows
  1053. if (this.visible &&
  1054. this.toolbar.offsetHeight > (this.wrapper.offsetHeight - 50)) {
  1055. this.arrowWrapper.style.display = 'block';
  1056. }
  1057. else {
  1058. // reset margin if whole toolbar is visible
  1059. this.toolbar.style.marginTop = '0px';
  1060. // hide arrows
  1061. this.arrowWrapper.style.display = 'none';
  1062. }
  1063. };
  1064. /**
  1065. * Create button which shows or hides GUI toolbar.
  1066. * @private
  1067. */
  1068. Toolbar.prototype.showHideToolbar = function () {
  1069. var stockToolbar = this, chart = this.chart, wrapper = stockToolbar.wrapper, toolbar = this.listWrapper, submenu = this.submenu, visible = this.visible, showhideBtn;
  1070. // Show hide toolbar
  1071. this.showhideBtn = showhideBtn = createElement(DIV, {
  1072. className: PREFIX + 'toggle-toolbar ' + PREFIX + 'arrow-left'
  1073. }, null, wrapper);
  1074. showhideBtn.style['background-image'] =
  1075. 'url(' + this.iconsURL + 'arrow-right.svg)';
  1076. if (!visible) {
  1077. // hide
  1078. if (submenu) {
  1079. submenu.style.display = 'none';
  1080. }
  1081. showhideBtn.style.left = '0px';
  1082. stockToolbar.visible = visible = false;
  1083. toolbar.classList.add(PREFIX + 'hide');
  1084. showhideBtn.classList.toggle(PREFIX + 'arrow-right');
  1085. wrapper.style.height = showhideBtn.offsetHeight + 'px';
  1086. }
  1087. else {
  1088. wrapper.style.height = '100%';
  1089. showhideBtn.style.top = getStyle(toolbar, 'padding-top') + 'px';
  1090. showhideBtn.style.left = (wrapper.offsetWidth +
  1091. getStyle(toolbar, 'padding-left')) + 'px';
  1092. }
  1093. // Toggle menu
  1094. stockToolbar.eventsToUnbind.push(addEvent(showhideBtn, 'click', function () {
  1095. chart.update({
  1096. stockTools: {
  1097. gui: {
  1098. visible: !visible,
  1099. placed: true
  1100. }
  1101. }
  1102. });
  1103. }));
  1104. };
  1105. /*
  1106. * In main GUI button, replace icon and class with submenu button's
  1107. * class / symbol.
  1108. *
  1109. * @param {HTMLDOMElement} - submenu button
  1110. * @param {Boolean} - true or false
  1111. *
  1112. */
  1113. Toolbar.prototype.switchSymbol = function (button, redraw) {
  1114. var buttonWrapper = button.parentNode, buttonWrapperClass = buttonWrapper.classList.value,
  1115. // main button in first level og GUI
  1116. mainNavButton = buttonWrapper.parentNode.parentNode;
  1117. // set class
  1118. mainNavButton.className = '';
  1119. if (buttonWrapperClass) {
  1120. mainNavButton.classList.add(buttonWrapperClass.trim());
  1121. }
  1122. // set icon
  1123. mainNavButton
  1124. .querySelectorAll('.' + PREFIX + 'menu-item-btn')[0]
  1125. .style['background-image'] =
  1126. button.style['background-image'];
  1127. // set active class
  1128. if (redraw) {
  1129. this.selectButton(mainNavButton);
  1130. }
  1131. };
  1132. /*
  1133. * Set select state (active class) on button.
  1134. *
  1135. * @param {HTMLDOMElement} - button
  1136. *
  1137. */
  1138. Toolbar.prototype.selectButton = function (button) {
  1139. if (button.className.indexOf(activeClass) >= 0) {
  1140. button.classList.remove(activeClass);
  1141. }
  1142. else {
  1143. button.classList.add(activeClass);
  1144. }
  1145. };
  1146. /*
  1147. * Remove active class from all buttons except defined.
  1148. *
  1149. * @param {HTMLDOMElement} - button which should not be deactivated
  1150. *
  1151. */
  1152. Toolbar.prototype.unselectAllButtons = function (button) {
  1153. var activeButtons = button.parentNode
  1154. .querySelectorAll('.' + activeClass);
  1155. [].forEach.call(activeButtons, function (activeBtn) {
  1156. if (activeBtn !== button) {
  1157. activeBtn.classList.remove(activeClass);
  1158. }
  1159. });
  1160. };
  1161. /*
  1162. * Update GUI with given options.
  1163. *
  1164. * @param {Object} - general options for Stock Tools
  1165. */
  1166. Toolbar.prototype.update = function (options) {
  1167. merge(true, this.chart.options.stockTools, options);
  1168. this.destroy();
  1169. this.chart.setStockTools(options);
  1170. // If Stock Tools are updated, then bindings should be updated too:
  1171. if (this.chart.navigationBindings) {
  1172. this.chart.navigationBindings.update();
  1173. }
  1174. };
  1175. /**
  1176. * Destroy all HTML GUI elements.
  1177. * @private
  1178. */
  1179. Toolbar.prototype.destroy = function () {
  1180. var stockToolsDiv = this.wrapper, parent = stockToolsDiv && stockToolsDiv.parentNode;
  1181. this.eventsToUnbind.forEach(function (unbinder) {
  1182. unbinder();
  1183. });
  1184. // Remove the empty element
  1185. if (parent) {
  1186. parent.removeChild(stockToolsDiv);
  1187. }
  1188. // redraw
  1189. this.chart.isDirtyBox = true;
  1190. this.chart.redraw();
  1191. };
  1192. /**
  1193. * Redraw, GUI requires to verify if the navigation should be visible.
  1194. * @private
  1195. */
  1196. Toolbar.prototype.redraw = function () {
  1197. this.showHideNavigatorion();
  1198. };
  1199. Toolbar.prototype.getIconsURL = function () {
  1200. return this.chart.options.navigation.iconsURL ||
  1201. this.options.iconsURL ||
  1202. 'https://code.highcharts.com/8.2.0/gfx/stock-icons/';
  1203. };
  1204. return Toolbar;
  1205. }());
  1206. /**
  1207. * Mapping JSON fields to CSS classes.
  1208. * @private
  1209. */
  1210. Toolbar.prototype.classMapping = {
  1211. circle: PREFIX + 'circle-annotation',
  1212. rectangle: PREFIX + 'rectangle-annotation',
  1213. label: PREFIX + 'label-annotation',
  1214. segment: PREFIX + 'segment',
  1215. arrowSegment: PREFIX + 'arrow-segment',
  1216. ray: PREFIX + 'ray',
  1217. arrowRay: PREFIX + 'arrow-ray',
  1218. line: PREFIX + 'infinity-line',
  1219. arrowLine: PREFIX + 'arrow-infinity-line',
  1220. verticalLine: PREFIX + 'vertical-line',
  1221. horizontalLine: PREFIX + 'horizontal-line',
  1222. crooked3: PREFIX + 'crooked3',
  1223. crooked5: PREFIX + 'crooked5',
  1224. elliott3: PREFIX + 'elliott3',
  1225. elliott5: PREFIX + 'elliott5',
  1226. pitchfork: PREFIX + 'pitchfork',
  1227. fibonacci: PREFIX + 'fibonacci',
  1228. parallelChannel: PREFIX + 'parallel-channel',
  1229. measureX: PREFIX + 'measure-x',
  1230. measureY: PREFIX + 'measure-y',
  1231. measureXY: PREFIX + 'measure-xy',
  1232. verticalCounter: PREFIX + 'vertical-counter',
  1233. verticalLabel: PREFIX + 'vertical-label',
  1234. verticalArrow: PREFIX + 'vertical-arrow',
  1235. currentPriceIndicator: PREFIX + 'current-price-indicator',
  1236. indicators: PREFIX + 'indicators',
  1237. flagCirclepin: PREFIX + 'flag-circlepin',
  1238. flagDiamondpin: PREFIX + 'flag-diamondpin',
  1239. flagSquarepin: PREFIX + 'flag-squarepin',
  1240. flagSimplepin: PREFIX + 'flag-simplepin',
  1241. zoomX: PREFIX + 'zoom-x',
  1242. zoomY: PREFIX + 'zoom-y',
  1243. zoomXY: PREFIX + 'zoom-xy',
  1244. typeLine: PREFIX + 'series-type-line',
  1245. typeOHLC: PREFIX + 'series-type-ohlc',
  1246. typeCandlestick: PREFIX + 'series-type-candlestick',
  1247. fullScreen: PREFIX + 'full-screen',
  1248. toggleAnnotations: PREFIX + 'toggle-annotations',
  1249. saveChart: PREFIX + 'save-chart',
  1250. separator: PREFIX + 'separator'
  1251. };
  1252. extend(Chart.prototype, {
  1253. /**
  1254. * Verify if Toolbar should be added.
  1255. * @private
  1256. * @param {Highcharts.StockToolsOptions} - chart options
  1257. */
  1258. setStockTools: function (options) {
  1259. var chartOptions = this.options, lang = chartOptions.lang, guiOptions = merge(chartOptions.stockTools && chartOptions.stockTools.gui, options && options.gui), langOptions = lang.stockTools && lang.stockTools.gui;
  1260. this.stockTools = new H.Toolbar(guiOptions, langOptions, this);
  1261. if (this.stockTools.guiEnabled) {
  1262. this.isDirtyBox = true;
  1263. }
  1264. }
  1265. });
  1266. // Comunication with bindings:
  1267. addEvent(NavigationBindings, 'selectButton', function (event) {
  1268. var button = event.button, className = PREFIX + 'submenu-wrapper', gui = this.chart.stockTools;
  1269. if (gui && gui.guiEnabled) {
  1270. // Unslect other active buttons
  1271. gui.unselectAllButtons(event.button);
  1272. // If clicked on a submenu, select state for it's parent
  1273. if (button.parentNode.className.indexOf(className) >= 0) {
  1274. button = button.parentNode.parentNode;
  1275. }
  1276. // Set active class on the current button
  1277. gui.selectButton(button);
  1278. }
  1279. });
  1280. addEvent(NavigationBindings, 'deselectButton', function (event) {
  1281. var button = event.button, className = PREFIX + 'submenu-wrapper', gui = this.chart.stockTools;
  1282. if (gui && gui.guiEnabled) {
  1283. // If deselecting a button from a submenu, select state for it's parent
  1284. if (button.parentNode.className.indexOf(className) >= 0) {
  1285. button = button.parentNode.parentNode;
  1286. }
  1287. gui.selectButton(button);
  1288. }
  1289. });
  1290. H.Toolbar = Toolbar;
  1291. export default H.Toolbar;