stock-tools-gui.js 47 KB

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