| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456 | /** * GUI generator for Stock tools * * (c) 2009-2017 Sebastian Bochan * * License: www.highcharts.com/license */'use strict';import H from '../parts/Globals.js';var addEvent = H.addEvent,    createElement = H.createElement,    pick = H.pick,    isArray = H.isArray,    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';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         * @type            {Object}         */        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     * @type {Object}     * @optionparent stockTools     */    stockTools: {        /**         * Definitions of buttons in Stock Tools GUI.         */        gui: {            /**             * Enable or disable the stockTools gui.             *             * @type      {boolean}             * @default true             */            enabled: true,            /**             * A CSS class name to apply to the stocktools' div,             * allowing unique CSS styling for each chart.             *             * @type      {string}             * @default 'highcharts-bindings-wrapper'             *             */            className: 'highcharts-bindings-wrapper',            /**             * A CSS class name to apply to the container of buttons,             * allowing unique CSS styling for each chart.             *             * @type      {string}             * @default 'stocktools-toolbar'             *             */            toolbarClassName: 'stocktools-toolbar',            /**             * Path where Highcharts will look for icons. Change this to use             * icons from a different server.             */            iconsURL: 'https://code.highcharts.com/@product.version@/gfx/stock-icons/',            /**             * A collection of strings pointing to config options for the             * toolbar items. Each name refers to unique key from definitions             * object.             *             * @type      {array}             *             * @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.             *             * @type      {object}             *             */            definitions: {                separator: {                    /**                     * A predefined background symbol for the button.                     *                     * @type   {string}                     */                    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'                }            }        }    }});// Run HTML generatoraddEvent(H.Chart, 'afterGetContainer', function () {    this.setStockTools();});addEvent(H.Chart, 'getMargins', function () {    var offsetWidth = (        this.stockTools &&        this.stockTools.listWrapper &&        this.stockTools.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 * * @param {Object} - options of toolbar * @param {Chart} - Reference to chart * */H.Toolbar = function (options, langOptions, chart) {    this.chart = chart;    this.options = options;    this.lang = langOptions;    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');};H.extend(H.Chart.prototype, {    /*     * Verify if Toolbar should be added.     *     * @param {Object} - chart options     *     */    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;        }    }});H.Toolbar.prototype = {    /*     * Initialize the toolbar. Create buttons and submenu for each option     * defined in `stockTools.gui`.     *     */    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,            inIframe = this.inIframe(),            button;        // create buttons        buttons.forEach(function (btnName) {            button = _self.addButton(toolbar, defs, btnName, lang);            if (inIframe && btnName === 'fullScreen') {                button.buttonWrapper.className += ' ' + PREFIX + 'disabled-btn';            }            ['click', 'touchstart'].forEach(function (eventName) {                addEvent(button.buttonWrapper, eventName, 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.     *     * @param {Object} - button which has submenu     * @param {Array} - list of all buttons     *     */    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        ['click', 'touchstart'].forEach(function (eventName) {            addEvent(submenuArrow, eventName, 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 +                                    H.getStyle(menuWrapper, 'padding-left') +                                    submenuWrapper.offsetWidth + 3 + 'px';                }            });        });    },    /*     * Create buttons in submenu     *     * @param {HTMLDOMElement} - button where submenu is placed     * @param {Array} - list of all buttons options     *     */    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            );            ['click', 'touchstart'].forEach(function (eventName) {                addEvent(submenuBtn.mainButton, eventName, 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     *     */    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 `<li>` , `<span>` and (if exists)     * submenu container.     *     * @param {HTMLDOMElement} - HTML reference, where button should be added     * @param {Object} - all options, by btnName refer to particular button     * @param {String} - name of functionality mapped for specific class     * @param {Object} - All titles, by btnName refer to particular button     *     * @return {Object} - references to all created HTML elements     */    addButton: function (target, options, btnName, lang) {        var guiOptions = this.options,            btnOptions = options[btnName],            items = btnOptions.items,            classMapping = H.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 > 1) {            // arrow is a hook to show / hide submenu            submenuArrow = createElement(SPAN, {                className: PREFIX + 'submenu-item-arrow ' +                    PREFIX + 'arrow-right'            }, null, buttonWrapper);        } else {            mainButton.style['background-image'] = 'url(' +                guiOptions.iconsURL + btnOptions.symbol + ')';        }        return {            buttonWrapper: buttonWrapper,            mainButton: mainButton,            submenuArrow: submenuArrow        };    },    /*     * Create navigation's HTML elements: container and arrows.     *     */    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.arrowDown = createElement(DIV, {            className: PREFIX + 'arrow-down'        }, null, stockToolbar.arrowWrapper);        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.     *     */    scrollButtons: function () {        var targetY = 0,            _self = this,            wrapper = _self.wrapper,            toolbar = _self.toolbar,            step = 0.1 * wrapper.offsetHeight; // 0.1 = 10%        ['click', 'touchstart'].forEach(function (eventName) {            addEvent(_self.arrowUp, eventName, function () {                if (targetY > 0) {                    targetY -= step;                    toolbar.style['margin-top'] = -targetY + 'px';                }            });            addEvent(_self.arrowDown, eventName, function () {                if (                    wrapper.offsetHeight + targetY <=                    toolbar.offsetHeight + step                ) {                    targetY += step;                    toolbar.style['margin-top'] = -targetY + 'px';                }            });        });    },    /*     * Create stockTools HTML main elements.     *     */    createHTML: function () {        var stockToolbar = this,            chart = stockToolbar.chart,            guiOptions = stockToolbar.options,            container = chart.container,            listWrapper,            toolbar,            wrapper;        // create main container        stockToolbar.wrapper = wrapper = createElement(DIV, {            className: PREFIX + 'stocktools-wrapper ' +                    guiOptions.className        });        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.     *     */    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.     *     */    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);        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');        } else {            showhideBtn.style.top = H.getStyle(toolbar, 'padding-top') + 'px';            showhideBtn.style.left = (                wrapper.offsetWidth +                H.getStyle(toolbar, 'padding-left')            ) + 'px';        }        // toggle menu        ['click', 'touchstart'].forEach(function (eventName) {            addEvent(showhideBtn, eventName, 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     *     */    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     *     */    selectButton: function (btn) {        if (btn.className.indexOf(activeClass) >= 0) {            btn.classList.remove(activeClass);        } else {            btn.classList.add(activeClass);        }    },    /*     * Remove active class from all buttons except defined.     *     * @param {HTMLDOMElement} - button which should not be deactivated     *     */    unselectAllButtons: function (btn) {        var activeButtons = btn.parentNode.querySelectorAll('.' + activeClass);        [].forEach.call(activeButtons, function (activeBtn) {            if (activeBtn !== btn) {                activeBtn.classList.remove(activeClass);            }        });    },    /*     * Verify if chart is in iframe.     *     * @return {Object} - elements translations.     */    inIframe: function () {        try {            return win.self !== win.top;        } catch (e) {            return true;        }    },    /*     * Update GUI with given options.     *     * @param {Object} - general options for Stock Tools     */    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.     *     */    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.     *     */    redraw: function () {        this.showHideNavigatorion();    },    /*     * Mapping JSON fields to CSS classes.     *     */    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'    }};// Comunication with bindings:addEvent(H.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(H.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);    }});
 |