/* * * * GUI generator for Stock tools * * (c) 2009-2017 Sebastian Bochan * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import H from '../parts/Globals.js'; import U from '../parts/Utilities.js'; import NavigationBindings from '../annotations/navigationBindings.js'; 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; var win = H.win, DIV = 'div', SPAN = 'span', UL = 'ul', LI = 'li', PREFIX = 'highcharts-', activeClass = PREFIX + 'active'; H.setOptions({ /** * @optionparent lang */ lang: { /** * Configure the stockTools GUI titles(hints) in the chart. Requires * the `stock-tools.js` module to be loaded. * * @product highstock * @since 7.0.0 */ stockTools: { gui: { // Main buttons: simpleShapes: 'Simple shapes', lines: 'Lines', crookedLines: 'Crooked lines', measure: 'Measure', advanced: 'Advanced', toggleAnnotations: 'Toggle annotations', verticalLabels: 'Vertical labels', flags: 'Flags', zoomChange: 'Zoom change', typeChange: 'Type change', saveChart: 'Save chart', indicators: 'Indicators', currentPriceIndicator: 'Current Price Indicators', // Other features: zoomX: 'Zoom X', zoomY: 'Zoom Y', zoomXY: 'Zooom XY', fullScreen: 'Fullscreen', typeOHLC: 'OHLC', typeLine: 'Line', typeCandlestick: 'Candlestick', // Basic shapes: circle: 'Circle', label: 'Label', rectangle: 'Rectangle', // Flags: flagCirclepin: 'Flag circle', flagDiamondpin: 'Flag diamond', flagSquarepin: 'Flag square', flagSimplepin: 'Flag simple', // Measures: measureXY: 'Measure XY', measureX: 'Measure X', measureY: 'Measure Y', // Segment, ray and line: segment: 'Segment', arrowSegment: 'Arrow segment', ray: 'Ray', arrowRay: 'Arrow ray', line: 'Line', arrowLine: 'Arrow line', horizontalLine: 'Horizontal line', verticalLine: 'Vertical line', infinityLine: 'Infinity line', // Crooked lines: crooked3: 'Crooked 3 line', crooked5: 'Crooked 5 line', elliott3: 'Elliott 3 line', elliott5: 'Elliott 5 line', // Counters: verticalCounter: 'Vertical counter', verticalLabel: 'Vertical label', verticalArrow: 'Vertical arrow', // Advanced: fibonacci: 'Fibonacci', pitchfork: 'Pitchfork', parallelChannel: 'Parallel channel' } }, navigation: { popup: { // Annotations: circle: 'Circle', rectangle: 'Rectangle', label: 'Label', segment: 'Segment', arrowSegment: 'Arrow segment', ray: 'Ray', arrowRay: 'Arrow ray', line: 'Line', arrowLine: 'Arrow line', horizontalLine: 'Horizontal line', verticalLine: 'Vertical line', crooked3: 'Crooked 3 line', crooked5: 'Crooked 5 line', elliott3: 'Elliott 3 line', elliott5: 'Elliott 5 line', verticalCounter: 'Vertical counter', verticalLabel: 'Vertical label', verticalArrow: 'Vertical arrow', fibonacci: 'Fibonacci', pitchfork: 'Pitchfork', parallelChannel: 'Parallel channel', infinityLine: 'Infinity line', measure: 'Measure', measureXY: 'Measure XY', measureX: 'Measure X', measureY: 'Measure Y', // Flags: flags: 'Flags', // GUI elements: addButton: 'add', saveButton: 'save', editButton: 'edit', removeButton: 'remove', series: 'Series', volume: 'Volume', connector: 'Connector', // Field names: innerBackground: 'Inner background', outerBackground: 'Outer background', crosshairX: 'Crosshair X', crosshairY: 'Crosshair Y', tunnel: 'Tunnel', background: 'Background' } } }, /** * Configure the stockTools gui strings in the chart. Requires the * [stockTools module]() to be loaded. For a description of the module * and information on its features, see [Highcharts StockTools](). * * @product highstock * * @sample stock/demo/stock-tools-gui Stock Tools GUI * * @sample stock/demo/stock-tools-custom-gui Stock Tools customized GUI * * @since 7.0.0 * @optionparent stockTools */ stockTools: { /** * Definitions of buttons in Stock Tools GUI. */ gui: { /** * Path where Highcharts will look for icons. Change this to use * icons from a different server. * * Since 7.1.3 use [iconsURL](#navigation.iconsURL) for popup and * stock tools. * * @deprecated * @apioption stockTools.gui.iconsURL * */ /** * Enable or disable the stockTools gui. */ enabled: true, /** * A CSS class name to apply to the stocktools' div, * allowing unique CSS styling for each chart. */ className: 'highcharts-bindings-wrapper', /** * A CSS class name to apply to the container of buttons, * allowing unique CSS styling for each chart. */ toolbarClassName: 'stocktools-toolbar', /** * A collection of strings pointing to config options for the * toolbar items. Each name refers to unique key from definitions * object. * * @default [ * 'indicators', * 'separator', * 'simpleShapes', * 'lines', * 'crookedLines', * 'measure', * 'advanced', * 'toggleAnnotations', * 'separator', * 'verticalLabels', * 'flags', * 'separator', * 'zoomChange', * 'fullScreen', * 'typeChange', * 'separator', * 'currentPriceIndicator', * 'saveChart' * ] */ buttons: [ 'indicators', 'separator', 'simpleShapes', 'lines', 'crookedLines', 'measure', 'advanced', 'toggleAnnotations', 'separator', 'verticalLabels', 'flags', 'separator', 'zoomChange', 'fullScreen', 'typeChange', 'separator', 'currentPriceIndicator', 'saveChart' ], /** * An options object of the buttons definitions. Each name refers to * unique key from buttons array. */ definitions: { separator: { /** * A predefined background symbol for the button. */ symbol: 'separator.svg' }, simpleShapes: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'label', * 'circle', * 'rectangle' * ] * */ items: [ 'label', 'circle', 'rectangle' ], circle: { /** * A predefined background symbol for the button. * * @type {string} * */ symbol: 'circle.svg' }, rectangle: { /** * A predefined background symbol for the button. * * @type {string} * */ symbol: 'rectangle.svg' }, label: { /** * A predefined background symbol for the button. * * @type {string} * */ symbol: 'label.svg' } }, flags: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'flagCirclepin', * 'flagDiamondpin', * 'flagSquarepin', * 'flagSimplepin' * ] * */ items: [ 'flagCirclepin', 'flagDiamondpin', 'flagSquarepin', 'flagSimplepin' ], flagSimplepin: { /** * A predefined background symbol for the button. * * @type {string} * */ symbol: 'flag-basic.svg' }, flagDiamondpin: { /** * A predefined background symbol for the button. * * @type {string} * */ symbol: 'flag-diamond.svg' }, flagSquarepin: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'flag-trapeze.svg' }, flagCirclepin: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'flag-elipse.svg' } }, lines: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'segment', * 'arrowSegment', * 'ray', * 'arrowRay', * 'line', * 'arrowLine', * 'horizontalLine', * 'verticalLine' * ] */ items: [ 'segment', 'arrowSegment', 'ray', 'arrowRay', 'line', 'arrowLine', 'horizontalLine', 'verticalLine' ], segment: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'segment.svg' }, arrowSegment: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'arrow-segment.svg' }, ray: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'ray.svg' }, arrowRay: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'arrow-ray.svg' }, line: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'line.svg' }, arrowLine: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'arrow-line.svg' }, verticalLine: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'vertical-line.svg' }, horizontalLine: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'horizontal-line.svg' } }, crookedLines: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'elliott3', * 'elliott5', * 'crooked3', * 'crooked5' * ] * */ items: [ 'elliott3', 'elliott5', 'crooked3', 'crooked5' ], crooked3: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'crooked-3.svg' }, crooked5: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'crooked-5.svg' }, elliott3: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'elliott-3.svg' }, elliott5: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'elliott-5.svg' } }, verticalLabels: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'verticalCounter', * 'verticalLabel', * 'verticalArrow' * ] */ items: [ 'verticalCounter', 'verticalLabel', 'verticalArrow' ], verticalCounter: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'vertical-counter.svg' }, verticalLabel: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'vertical-label.svg' }, verticalArrow: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'vertical-arrow.svg' } }, advanced: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'fibonacci', * 'pitchfork', * 'parallelChannel' * ] */ items: [ 'fibonacci', 'pitchfork', 'parallelChannel' ], pitchfork: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'pitchfork.svg' }, fibonacci: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'fibonacci.svg' }, parallelChannel: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'parallel-channel.svg' } }, measure: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'measureXY', * 'measureX', * 'measureY' * ] */ items: [ 'measureXY', 'measureX', 'measureY' ], measureX: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'measure-x.svg' }, measureY: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'measure-y.svg' }, measureXY: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'measure-xy.svg' } }, toggleAnnotations: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'annotations-visible.svg' }, currentPriceIndicator: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'current-price-show.svg' }, indicators: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'indicators.svg' }, zoomChange: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'zoomX', * 'zoomY', * 'zoomXY' * ] */ items: [ 'zoomX', 'zoomY', 'zoomXY' ], zoomX: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'zoom-x.svg' }, zoomY: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'zoom-y.svg' }, zoomXY: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'zoom-xy.svg' } }, typeChange: { /** * A collection of strings pointing to config options for * the items. * * @type {array} * @default [ * 'typeOHLC', * 'typeLine', * 'typeCandlestick' * ] */ items: [ 'typeOHLC', 'typeLine', 'typeCandlestick' ], typeOHLC: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'series-ohlc.svg' }, typeLine: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'series-line.svg' }, typeCandlestick: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'series-candlestick.svg' } }, fullScreen: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'fullscreen.svg' }, saveChart: { /** * A predefined background symbol for the button. * * @type {string} */ symbol: 'save-chart.svg' } } } } }); /* eslint-disable no-invalid-this, valid-jsdoc */ // Run HTML generator addEvent(H.Chart, 'afterGetContainer', function () { this.setStockTools(); }); addEvent(H.Chart, 'getMargins', function () { var listWrapper = this.stockTools && this.stockTools.listWrapper, offsetWidth = listWrapper && ((listWrapper.startWidth + getStyle(listWrapper, 'padding-left') + getStyle(listWrapper, 'padding-right')) || listWrapper.offsetWidth); if (offsetWidth && offsetWidth < this.plotWidth) { this.plotLeft += offsetWidth; } }); addEvent(H.Chart, 'destroy', function () { if (this.stockTools) { this.stockTools.destroy(); } }); addEvent(H.Chart, 'redraw', function () { if (this.stockTools && this.stockTools.guiEnabled) { this.stockTools.redraw(); } }); /** * Toolbar Class * @private * @constructor * @param {Object} - options of toolbar * @param {Chart} - Reference to chart */ var Toolbar = /** @class */ (function () { function Toolbar(options, langOptions, chart) { this.arrowDown = void 0; this.arrowUp = void 0; this.arrowWrapper = void 0; this.listWrapper = void 0; this.showhideBtn = void 0; this.submenu = void 0; this.toolbar = void 0; this.wrapper = void 0; this.chart = chart; this.options = options; this.lang = langOptions; // set url for icons. this.iconsURL = this.getIconsURL(); this.guiEnabled = options.enabled; this.visible = pick(options.visible, true); this.placed = pick(options.placed, false); // General events collection which should be removed upon // destroy/update: this.eventsToUnbind = []; if (this.guiEnabled) { this.createHTML(); this.init(); this.showHideNavigatorion(); } fireEvent(this, 'afterInit'); } /** * Initialize the toolbar. Create buttons and submenu for each option * defined in `stockTools.gui`. * @private */ Toolbar.prototype.init = function () { 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; // create buttons buttons.forEach(function (btnName) { button = _self.addButton(toolbar, defs, btnName, lang); _self.eventsToUnbind.push(addEvent(button.buttonWrapper, 'click', function () { _self.eraseActiveButtons(allButtons, button.buttonWrapper); })); if (isArray(defs[btnName].items)) { // create submenu buttons addSubmenu.call(_self, button, defs[btnName]); } }); }; /** * Create submenu (list of buttons) for the option. In example main button * is Line, in submenu will be buttons with types of lines. * @private * @param {Highcharts.Dictionary} * button which has submenu * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions} * list of all buttons */ Toolbar.prototype.addSubmenu = function (parentBtn, button) { 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; // create submenu container this.submenu = submenuWrapper = createElement(UL, { className: PREFIX + 'submenu-wrapper' }, null, buttonWrapper); // create submenu buttons and select the first one this.addSubmenuItems(buttonWrapper, button); // show / hide submenu _self.eventsToUnbind.push(addEvent(submenuArrow, 'click', function (e) { e.stopPropagation(); // Erase active class on all other buttons _self.eraseActiveButtons(allButtons, buttonWrapper); // hide menu if (buttonWrapper.className.indexOf(PREFIX + 'current') >= 0) { menuWrapper.style.width = menuWrapper.startWidth + 'px'; buttonWrapper.classList.remove(PREFIX + 'current'); submenuWrapper.style.display = 'none'; } else { // show menu // to calculate height of element submenuWrapper.style.display = 'block'; topMargin = submenuWrapper.offsetHeight - buttonWrapper.offsetHeight - 3; // calculate position of submenu in the box // if submenu is inside, reset top margin if ( // cut on the bottom !(submenuWrapper.offsetHeight + buttonWrapper.offsetTop > wrapper.offsetHeight && // cut on the top buttonWrapper.offsetTop > topMargin)) { topMargin = 0; } // apply calculated styles css(submenuWrapper, { top: -topMargin + 'px', left: buttonWidth + 3 + 'px' }); buttonWrapper.className += ' ' + PREFIX + 'current'; menuWrapper.startWidth = wrapper.offsetWidth; menuWrapper.style.width = menuWrapper.startWidth + getStyle(menuWrapper, 'padding-left') + submenuWrapper.offsetWidth + 3 + 'px'; } })); }; /** * Create buttons in submenu * @private * @param {Highcharts.HTMLDOMElement} * button where submenu is placed * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions} * list of all buttons options * */ Toolbar.prototype.addSubmenuItems = function (buttonWrapper, button) { var _self = this, submenuWrapper = this.submenu, lang = this.lang, menuWrapper = this.listWrapper, items = button.items, firstSubmenuItem, submenuBtn; // add items to submenu items.forEach(function (btnName) { // add buttons to submenu submenuBtn = _self.addButton(submenuWrapper, button, btnName, lang); _self.eventsToUnbind.push(addEvent(submenuBtn.mainButton, 'click', function () { _self.switchSymbol(this, buttonWrapper, true); menuWrapper.style.width = menuWrapper.startWidth + 'px'; submenuWrapper.style.display = 'none'; })); }); // select first submenu item firstSubmenuItem = submenuWrapper .querySelectorAll('li > .' + PREFIX + 'menu-item-btn')[0]; // replace current symbol, in main button, with submenu's button style _self.switchSymbol(firstSubmenuItem, false); }; /* * Erase active class on all other buttons. * * @param {Array} - Array of HTML buttons * @param {HTMLDOMElement} - Current HTML button * */ Toolbar.prototype.eraseActiveButtons = function (buttons, currentButton, submenuItems) { [].forEach.call(buttons, function (btn) { if (btn !== currentButton) { btn.classList.remove(PREFIX + 'current'); btn.classList.remove(PREFIX + 'active'); submenuItems = btn.querySelectorAll('.' + PREFIX + 'submenu-wrapper'); // hide submenu if (submenuItems.length > 0) { submenuItems[0].style.display = 'none'; } } }); }; /** * Create single button. Consist of HTML elements `li`, `span`, and (if * exists) submenu container. * @private * @param {Highcharts.HTMLDOMElement} target * HTML reference, where button should be added * @param {Highcharts.StockToolsGuiDefinitionsButtonsOptions|Highcharts.StockToolsGuiDefinitionsOptions} options * All options, by btnName refer to particular button * @param {string} btnName * of functionality mapped for specific class * @param {Highcharts.Dictionary} lang * All titles, by btnName refer to particular button * @return {Object} - references to all created HTML elements */ Toolbar.prototype.addButton = function (target, options, btnName, lang) { if (lang === void 0) { lang = {}; } var btnOptions = options[btnName], items = btnOptions.items, classMapping = Toolbar.prototype.classMapping, userClassName = btnOptions.className || '', mainButton, submenuArrow, buttonWrapper; // main button wrapper buttonWrapper = createElement(LI, { className: pick(classMapping[btnName], '') + ' ' + userClassName, title: lang[btnName] || btnName }, null, target); // single button mainButton = createElement(SPAN, { className: PREFIX + 'menu-item-btn' }, null, buttonWrapper); // submenu if (items && items.length) { // arrow is a hook to show / hide submenu submenuArrow = createElement(SPAN, { className: PREFIX + 'submenu-item-arrow ' + PREFIX + 'arrow-right' }, null, buttonWrapper); submenuArrow.style['background-image'] = 'url(' + this.iconsURL + 'arrow-bottom.svg)'; } else { mainButton.style['background-image'] = 'url(' + this.iconsURL + btnOptions.symbol + ')'; } return { buttonWrapper: buttonWrapper, mainButton: mainButton, submenuArrow: submenuArrow }; }; /* * Create navigation's HTML elements: container and arrows. * */ Toolbar.prototype.addNavigation = function () { var stockToolbar = this, wrapper = stockToolbar.wrapper; // arrow wrapper stockToolbar.arrowWrapper = createElement(DIV, { className: PREFIX + 'arrow-wrapper' }); stockToolbar.arrowUp = createElement(DIV, { className: PREFIX + 'arrow-up' }, null, stockToolbar.arrowWrapper); stockToolbar.arrowUp.style['background-image'] = 'url(' + this.iconsURL + 'arrow-right.svg)'; stockToolbar.arrowDown = createElement(DIV, { className: PREFIX + 'arrow-down' }, null, stockToolbar.arrowWrapper); stockToolbar.arrowDown.style['background-image'] = 'url(' + this.iconsURL + 'arrow-right.svg)'; wrapper.insertBefore(stockToolbar.arrowWrapper, wrapper.childNodes[0]); // attach scroll events stockToolbar.scrollButtons(); }; /* * Add events to navigation (two arrows) which allows user to scroll * top/down GUI buttons, if container's height is not enough. * */ Toolbar.prototype.scrollButtons = function () { var targetY = 0, _self = this, wrapper = _self.wrapper, toolbar = _self.toolbar, step = 0.1 * wrapper.offsetHeight; // 0.1 = 10% _self.eventsToUnbind.push(addEvent(_self.arrowUp, 'click', function () { if (targetY > 0) { targetY -= step; toolbar.style['margin-top'] = -targetY + 'px'; } })); _self.eventsToUnbind.push(addEvent(_self.arrowDown, 'click', function () { if (wrapper.offsetHeight + targetY <= toolbar.offsetHeight + step) { targetY += step; toolbar.style['margin-top'] = -targetY + 'px'; } })); }; /* * Create stockTools HTML main elements. * */ Toolbar.prototype.createHTML = function () { var stockToolbar = this, chart = stockToolbar.chart, guiOptions = stockToolbar.options, container = chart.container, navigation = chart.options.navigation, bindingsClassName = navigation && navigation.bindingsClassName, listWrapper, toolbar, wrapper; // create main container stockToolbar.wrapper = wrapper = createElement(DIV, { className: PREFIX + 'stocktools-wrapper ' + guiOptions.className + ' ' + bindingsClassName }); container.parentNode.insertBefore(wrapper, container); // toolbar stockToolbar.toolbar = toolbar = createElement(UL, { className: PREFIX + 'stocktools-toolbar ' + guiOptions.toolbarClassName }); // add container for list of buttons stockToolbar.listWrapper = listWrapper = createElement(DIV, { className: PREFIX + 'menu-wrapper' }); wrapper.insertBefore(listWrapper, wrapper.childNodes[0]); listWrapper.insertBefore(toolbar, listWrapper.childNodes[0]); stockToolbar.showHideToolbar(); // add navigation which allows user to scroll down / top GUI buttons stockToolbar.addNavigation(); }; /** * Function called in redraw verifies if the navigation should be visible. * @private */ Toolbar.prototype.showHideNavigatorion = function () { // arrows // 50px space for arrows if (this.visible && this.toolbar.offsetHeight > (this.wrapper.offsetHeight - 50)) { this.arrowWrapper.style.display = 'block'; } else { // reset margin if whole toolbar is visible this.toolbar.style.marginTop = '0px'; // hide arrows this.arrowWrapper.style.display = 'none'; } }; /** * Create button which shows or hides GUI toolbar. * @private */ Toolbar.prototype.showHideToolbar = function () { var stockToolbar = this, chart = this.chart, wrapper = stockToolbar.wrapper, toolbar = this.listWrapper, submenu = this.submenu, visible = this.visible, showhideBtn; // Show hide toolbar this.showhideBtn = showhideBtn = createElement(DIV, { className: PREFIX + 'toggle-toolbar ' + PREFIX + 'arrow-left' }, null, wrapper); showhideBtn.style['background-image'] = 'url(' + this.iconsURL + 'arrow-right.svg)'; if (!visible) { // hide if (submenu) { submenu.style.display = 'none'; } showhideBtn.style.left = '0px'; stockToolbar.visible = visible = false; toolbar.classList.add(PREFIX + 'hide'); showhideBtn.classList.toggle(PREFIX + 'arrow-right'); wrapper.style.height = showhideBtn.offsetHeight + 'px'; } else { wrapper.style.height = '100%'; showhideBtn.style.top = getStyle(toolbar, 'padding-top') + 'px'; showhideBtn.style.left = (wrapper.offsetWidth + getStyle(toolbar, 'padding-left')) + 'px'; } // Toggle menu stockToolbar.eventsToUnbind.push(addEvent(showhideBtn, 'click', function () { chart.update({ stockTools: { gui: { visible: !visible, placed: true } } }); })); }; /* * In main GUI button, replace icon and class with submenu button's * class / symbol. * * @param {HTMLDOMElement} - submenu button * @param {Boolean} - true or false * */ Toolbar.prototype.switchSymbol = function (button, redraw) { var buttonWrapper = button.parentNode, buttonWrapperClass = buttonWrapper.classList.value, // main button in first level og GUI mainNavButton = buttonWrapper.parentNode.parentNode; // set class mainNavButton.className = ''; if (buttonWrapperClass) { mainNavButton.classList.add(buttonWrapperClass.trim()); } // set icon mainNavButton .querySelectorAll('.' + PREFIX + 'menu-item-btn')[0] .style['background-image'] = button.style['background-image']; // set active class if (redraw) { this.selectButton(mainNavButton); } }; /* * Set select state (active class) on button. * * @param {HTMLDOMElement} - button * */ Toolbar.prototype.selectButton = function (button) { if (button.className.indexOf(activeClass) >= 0) { button.classList.remove(activeClass); } else { button.classList.add(activeClass); } }; /* * Remove active class from all buttons except defined. * * @param {HTMLDOMElement} - button which should not be deactivated * */ Toolbar.prototype.unselectAllButtons = function (button) { var activeButtons = button.parentNode .querySelectorAll('.' + activeClass); [].forEach.call(activeButtons, function (activeBtn) { if (activeBtn !== button) { activeBtn.classList.remove(activeClass); } }); }; /* * Update GUI with given options. * * @param {Object} - general options for Stock Tools */ Toolbar.prototype.update = function (options) { merge(true, this.chart.options.stockTools, options); this.destroy(); this.chart.setStockTools(options); // If Stock Tools are updated, then bindings should be updated too: if (this.chart.navigationBindings) { this.chart.navigationBindings.update(); } }; /** * Destroy all HTML GUI elements. * @private */ Toolbar.prototype.destroy = function () { var stockToolsDiv = this.wrapper, parent = stockToolsDiv && stockToolsDiv.parentNode; this.eventsToUnbind.forEach(function (unbinder) { unbinder(); }); // Remove the empty element if (parent) { parent.removeChild(stockToolsDiv); } // redraw this.chart.isDirtyBox = true; this.chart.redraw(); }; /** * Redraw, GUI requires to verify if the navigation should be visible. * @private */ Toolbar.prototype.redraw = function () { this.showHideNavigatorion(); }; Toolbar.prototype.getIconsURL = function () { return this.chart.options.navigation.iconsURL || this.options.iconsURL || 'https://code.highcharts.com/8.1.0/gfx/stock-icons/'; }; return Toolbar; }()); /** * Mapping JSON fields to CSS classes. * @private */ Toolbar.prototype.classMapping = { circle: PREFIX + 'circle-annotation', rectangle: PREFIX + 'rectangle-annotation', label: PREFIX + 'label-annotation', segment: PREFIX + 'segment', arrowSegment: PREFIX + 'arrow-segment', ray: PREFIX + 'ray', arrowRay: PREFIX + 'arrow-ray', line: PREFIX + 'infinity-line', arrowLine: PREFIX + 'arrow-infinity-line', verticalLine: PREFIX + 'vertical-line', horizontalLine: PREFIX + 'horizontal-line', crooked3: PREFIX + 'crooked3', crooked5: PREFIX + 'crooked5', elliott3: PREFIX + 'elliott3', elliott5: PREFIX + 'elliott5', pitchfork: PREFIX + 'pitchfork', fibonacci: PREFIX + 'fibonacci', parallelChannel: PREFIX + 'parallel-channel', measureX: PREFIX + 'measure-x', measureY: PREFIX + 'measure-y', measureXY: PREFIX + 'measure-xy', verticalCounter: PREFIX + 'vertical-counter', verticalLabel: PREFIX + 'vertical-label', verticalArrow: PREFIX + 'vertical-arrow', currentPriceIndicator: PREFIX + 'current-price-indicator', indicators: PREFIX + 'indicators', flagCirclepin: PREFIX + 'flag-circlepin', flagDiamondpin: PREFIX + 'flag-diamondpin', flagSquarepin: PREFIX + 'flag-squarepin', flagSimplepin: PREFIX + 'flag-simplepin', zoomX: PREFIX + 'zoom-x', zoomY: PREFIX + 'zoom-y', zoomXY: PREFIX + 'zoom-xy', typeLine: PREFIX + 'series-type-line', typeOHLC: PREFIX + 'series-type-ohlc', typeCandlestick: PREFIX + 'series-type-candlestick', fullScreen: PREFIX + 'full-screen', toggleAnnotations: PREFIX + 'toggle-annotations', saveChart: PREFIX + 'save-chart', separator: PREFIX + 'separator' }; extend(H.Chart.prototype, { /** * Verify if Toolbar should be added. * @private * @param {Highcharts.StockToolsOptions} - chart options * @return {void} */ setStockTools: function (options) { var chartOptions = this.options, lang = chartOptions.lang, guiOptions = merge(chartOptions.stockTools && chartOptions.stockTools.gui, options && options.gui), langOptions = lang.stockTools && lang.stockTools.gui; this.stockTools = new H.Toolbar(guiOptions, langOptions, this); if (this.stockTools.guiEnabled) { this.isDirtyBox = true; } } }); // Comunication with bindings: addEvent(NavigationBindings, 'selectButton', function (event) { var button = event.button, className = PREFIX + 'submenu-wrapper', gui = this.chart.stockTools; if (gui && gui.guiEnabled) { // Unslect other active buttons gui.unselectAllButtons(event.button); // If clicked on a submenu, select state for it's parent if (button.parentNode.className.indexOf(className) >= 0) { button = button.parentNode.parentNode; } // Set active class on the current button gui.selectButton(button); } }); addEvent(NavigationBindings, 'deselectButton', function (event) { var button = event.button, className = PREFIX + 'submenu-wrapper', gui = this.chart.stockTools; if (gui && gui.guiEnabled) { // If deselecting a button from a submenu, select state for it's parent if (button.parentNode.className.indexOf(className) >= 0) { button = button.parentNode.parentNode; } gui.selectButton(button); } }); H.Toolbar = Toolbar; export default H.Toolbar;