| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069 | /** * Popup 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,    objectEach = H.objectEach,    pick = H.pick,    wrap = H.wrap,    isString = H.isString,    isObject = H.isObject,    isArray = H.isArray,    indexFilter = /\d/g,    PREFIX = 'highcharts-',    DIV = 'div',    INPUT = 'input',    LABEL = 'label',    BUTTON = 'button',    SELECT = 'select',    OPTION = 'option',    SPAN = 'span',    UL = 'ul',    LI = 'li',    H3 = 'h3';// onContainerMouseDown blocks internal popup events, due to e.preventDefault.// Related issue #4606wrap(H.Pointer.prototype, 'onContainerMouseDown', function (proceed, e) {    var popupClass = e.target && e.target.className;    // elements is not in popup    if (!(isString(popupClass) &&        popupClass.indexOf(PREFIX + 'popup-field') >= 0)    ) {        proceed.apply(this, Array.prototype.slice.call(arguments, 1));    }});H.Popup = function (parentDiv) {    this.init(parentDiv);};H.Popup.prototype = {    /*     * Initialize the popup. Create base div and add close button.     *     * @param {HTMLDOMElement} - container where popup should be placed     *     * @return {HTMLDOMElement} - return created popup's div     *     */    init: function (parentDiv) {        // create popup div        this.container = createElement(DIV, {            className: PREFIX + 'popup'        }, null, parentDiv);        this.lang = this.getLangpack();        // add close button        this.addCloseBtn();    },    /*     * Create HTML element and attach click event (close popup).     *     */    addCloseBtn: function () {        var _self = this,            closeBtn;        // create close popup btn        closeBtn = createElement(DIV, {            className: PREFIX + 'popup-close'        }, null, this.container);        ['click', 'touchstart'].forEach(function (eventName) {            addEvent(closeBtn, eventName, function () {                _self.closePopup();            });        });    },    /*     * Create two columns (divs) in HTML.     *     * @param {HTMLDOMElement} - container of columns     *     * @return {Object} - reference to two HTML columns     *     */    addColsContainer: function (container) {        var rhsCol,            lhsCol;        // left column        lhsCol = createElement(DIV, {            className: PREFIX + 'popup-lhs-col'        }, null, container);        // right column        rhsCol = createElement(DIV, {            className: PREFIX + 'popup-rhs-col'        }, null, container);        // wrapper content        createElement(DIV, {            className: PREFIX + 'popup-rhs-col-wrapper'        }, null, rhsCol);        return {            lhsCol: lhsCol,            rhsCol: rhsCol        };    },    /*     * Create input with label.     *     * @param {String} - chain of fields i.e params.styles.fontSize     * @param {String} - indicator type     * @param {HTMLDOMElement} - container where elements should be added     * @param {String} - dafault value of input i.e period value is 14,     * extracted from defaultOptions (ADD mode) or series options (EDIT mode)     *     */    addInput: function (option, type, parentDiv, value) {        var optionParamList = option.split('.'),            optionName = optionParamList[optionParamList.length - 1],            lang = this.lang,            inputName = PREFIX + type + '-' + optionName;        if (!inputName.match(indexFilter)) {            // add label            createElement(                LABEL, {                    innerHTML: lang[optionName] || optionName,                    htmlFor: inputName                },                null,                parentDiv            );        }        // add input        createElement(            INPUT,            {                name: inputName,                value: value[0],                type: value[1],                className: PREFIX + 'popup-field'            },            null,            parentDiv        ).setAttribute(PREFIX + 'data-name', option);    },    /*     * Create button.     *     * @param {HTMLDOMElement} - container where elements should be added     * @param {String} - text placed as button label     * @param {String} - add | edit | remove     * @param {Function} - on click callback     * @param {HTMLDOMElement} - container where inputs are generated     *     * @return {HTMLDOMElement} - html button     */    addButton: function (parentDiv, label, type, callback, fieldsDiv) {        var _self = this,            closePopup = this.closePopup,            getFields = this.getFields,            button;        button = createElement(BUTTON, {            innerHTML: label        }, null, parentDiv);        ['click', 'touchstart'].forEach(function (eventName) {            addEvent(button, eventName, function () {                closePopup.call(_self);                return callback(                    getFields(fieldsDiv, type)                );            });        });        return button;    },    /*     * Get values from all inputs and create JSON.     *     * @param {HTMLDOMElement} - container where inputs are created     * @param {String} - add | edit | remove     *     * @return {Object} - fields     */    getFields: function (parentDiv, type) {        var inputList = parentDiv.querySelectorAll('input'),            optionSeries = '#' + PREFIX + 'select-series > option:checked',            optionVolume = '#' + PREFIX + 'select-volume > option:checked',            linkedTo = parentDiv.querySelectorAll(optionSeries)[0],            volumeTo = parentDiv.querySelectorAll(optionVolume)[0],            seriesId,            param,            fieldsOutput;        fieldsOutput = {            actionType: type,            linkedTo: linkedTo && linkedTo.getAttribute('value'),            fields: { }        };        inputList.forEach(function (input) {            param = input.getAttribute(PREFIX + 'data-name');            seriesId = input.getAttribute(PREFIX + 'data-series-id');            // params            if (seriesId) {                fieldsOutput.seriesId = input.value;            } else if (param) {                fieldsOutput.fields[param] = input.value;            } else {                // type like sma / ema                fieldsOutput.type = input.value;            }        });        if (volumeTo) {            fieldsOutput.fields['params.volumeSeriesID'] = volumeTo                .getAttribute('value');        }        return fieldsOutput;    },    /*     * Reset content of the current popup and show.     *     * @param {Chart} - chart     * @param {Function} - on click callback     *     * @return {Object} - fields     */    showPopup: function () {        var popupDiv = this.container,            toolbarClass = PREFIX + 'annotation-toolbar',            popupCloseBtn = popupDiv                .querySelectorAll('.' + PREFIX + 'popup-close')[0];        // reset content        popupDiv.innerHTML = '';        // reset toolbar styles if exists        if (popupDiv.className.indexOf(toolbarClass) >= 0) {            popupDiv.classList.remove(toolbarClass);            // reset toolbar inline styles            popupDiv.removeAttribute('style');        }        // add close button        popupDiv.appendChild(popupCloseBtn);        popupDiv.style.display = 'block';    },    /*     * Hide popup.     *     */    closePopup: function () {        this.popup.container.style.display = 'none';    },    /*     * Create content and show popup.     *     * @param {String} - type of popup i.e indicators     * @param {Chart} - chart     * @param {Object} - options     * @param {Function} - on click callback     *     */    showForm: function (type, chart, options, callback) {        this.popup = chart.navigationBindings.popup;        // show blank popup        this.showPopup();        // indicator form        if (type === 'indicators') {            this.indicators.addForm.call(this, chart, options, callback);        }        // annotation small toolbar        if (type === 'annotation-toolbar') {            this.annotations.addToolbar.call(this, chart, options, callback);        }        // annotation edit form        if (type === 'annotation-edit') {            this.annotations.addForm.call(this, chart, options, callback);        }        // flags form - add / edit        if (type === 'flag') {            this.annotations.addForm.call(this, chart, options, callback, true);        }    },    /*     * Return lang definitions for popup.     *     * @return {Object} - elements translations.     */    getLangpack: function () {        return H.getOptions().lang.navigation.popup;    },    annotations: {        /*         * Create annotation simple form. It contains two buttons         * (edit / remove) and text label.         *         * @param {Chart} - chart         * @param {Object} - options         * @param {Function} - on click callback         *         */        addToolbar: function (chart, options, callback) {            var _self = this,                lang = this.lang,                popupDiv = this.popup.container,                showForm = this.showForm,                toolbarClass = PREFIX + 'annotation-toolbar',                button;            // set small size            if (popupDiv.className.indexOf(toolbarClass) === -1) {                popupDiv.className += ' ' + toolbarClass;            }            // set position            popupDiv.style.top = chart.plotTop + 10 + 'px';            // create label            createElement(SPAN, {                innerHTML: pick(                    // Advanced annotations:                    lang[options.langKey] || options.langKey,                    // Basic shapes:                    options.shapes && options.shapes[0].type                )            }, null, popupDiv);            // add buttons            button = this.addButton(                popupDiv,                lang.removeButton || 'remove',                'remove',                callback,                popupDiv            );            button.className += ' ' + PREFIX + 'annotation-remove-button';            button = this.addButton(                popupDiv,                lang.editButton || 'edit',                'edit',                function () {                    showForm.call(                        _self,                        'annotation-edit',                        chart,                        options,                        callback                    );                },                popupDiv            );            button.className += ' ' + PREFIX + 'annotation-edit-button';        },        /*         * Create annotation simple form.         * It contains fields with param names.         *         * @param {Chart} - chart         * @param {Object} - options         * @param {Function} - on click callback         * @param {Boolean} - if it is a form declared for init annotation         *         */        addForm: function (chart, options, callback, isInit) {            var popupDiv = this.popup.container,                lang = this.lang,                bottomRow,                lhsCol;            // create title of annotations            lhsCol = createElement('h2', {                innerHTML: lang[options.langKey] || options.langKey,                className: PREFIX + 'popup-main-title'            }, null, popupDiv);            // left column            lhsCol = createElement(DIV, {                className: PREFIX + 'popup-lhs-col ' + PREFIX + 'popup-lhs-full'            }, null, popupDiv);            bottomRow = createElement(DIV, {                className: PREFIX + 'popup-bottom-row'            }, null, popupDiv);            this.annotations.addFormFields.call(                this,                lhsCol,                chart,                '',                options,                [],                true            );            this.addButton(                bottomRow,                isInit ?                    (lang.addButton || 'add') :                    (lang.saveButton || 'save'),                isInit ? 'add' : 'save',                callback,                popupDiv            );        },        /*         * Create annotation's form fields.         *         * @param {HTMLDOMElement} - div where inputs are placed         * @param {Chart} - chart         * @param {String} - name of parent to create chain of names         * @param {Object} - options         * @param {Array} - storage - array where all items are stored         * @param {Boolean} - isRoot - recursive flag for root         *         */        addFormFields: function (            parentDiv,            chart,            parentNode,            options,            storage,            isRoot        ) {            var _self = this,                addFormFields = this.annotations.addFormFields,                addInput = this.addInput,                lang = this.lang,                parentFullName,                titleName;            objectEach(options, function (value, option) {                // create name like params.styles.fontSize                parentFullName = parentNode !== '' ?                    parentNode + '.' + option : option;                if (isObject(value)) {                    if (                        // value is object of options                        !isArray(value) ||                        // array of objects with params. i.e labels in Fibonacci                        (isArray(value) && isObject(value[0]))                    ) {                        titleName = lang[option] || option;                        if (!titleName.match(indexFilter)) {                            storage.push([                                true,                                titleName,                                parentDiv                            ]);                        }                        addFormFields.call(                            _self,                            parentDiv,                            chart,                            parentFullName,                            value,                            storage,                            false                        );                    } else {                        storage.push([                            _self,                            parentFullName,                            'annotation',                            parentDiv,                            value                        ]);                    }                }            });            if (isRoot) {                storage = storage.sort(function (a) {                    return a[1].match(/format/g) ? -1 : 1;                });                storage.forEach(function (genInput) {                    if (genInput[0] === true) {                        createElement(SPAN, {                            className: PREFIX + 'annotation-title',                            innerHTML: genInput[1]                        }, null, genInput[2]);                    } else {                        addInput.apply(genInput[0], genInput.splice(1));                    }                });            }        }    },    indicators: {        /*         * Create indicator's form. It contains two tabs (ADD and EDIT) with         * content.         *         * @param {Chart} - chart         * @param {Object} - options         * @param {Function} - on click callback         *         */        addForm: function (chart, options, callback) {            var tabsContainers,                indicators = this.indicators,                lang = this.lang,                buttonParentDiv;            // add tabs            this.tabs.init.call(this, chart);            // get all tabs content divs            tabsContainers = this.popup.container                .querySelectorAll('.' + PREFIX + 'tab-item-content');            // ADD tab            this.addColsContainer(tabsContainers[0]);            indicators.addIndicatorList.call(                this,                chart,                tabsContainers[0],                'add'            );            buttonParentDiv = tabsContainers[0]                .querySelectorAll('.' + PREFIX + 'popup-rhs-col')[0];            this.addButton(                buttonParentDiv,                lang.addButton || 'add',                'add',                callback,                buttonParentDiv            );            // EDIT tab            this.addColsContainer(tabsContainers[1]);            indicators.addIndicatorList.call(                this,                chart,                tabsContainers[1],                'edit'            );            buttonParentDiv = tabsContainers[1]                .querySelectorAll('.' + PREFIX + 'popup-rhs-col')[0];            this.addButton(                buttonParentDiv,                lang.saveButton || 'save',                'edit',                callback,                buttonParentDiv            );            this.addButton(                buttonParentDiv,                lang.removeButton || 'remove',                'remove',                callback,                buttonParentDiv            );        },        /*         * Create HTML list of all indicators (ADD mode) or added indicators         * (EDIT mode).         *         * @param {Chart} - chart         * @param {HTMLDOMElement} - container where list is added         * @param {String} - 'edit' or 'add' mode         *         */        addIndicatorList: function (chart, parentDiv, listType) {            var _self = this,                lhsCol = parentDiv                    .querySelectorAll('.' + PREFIX + 'popup-lhs-col')[0],                rhsCol = parentDiv                    .querySelectorAll('.' + PREFIX + 'popup-rhs-col')[0],                defaultOptions = H.getOptions(),                isEdit = listType === 'edit',                series = isEdit ? chart.series : // EDIT mode                    defaultOptions.plotOptions, // ADD mode                addFormFields = this.indicators.addFormFields,                rhsColWrapper,                indicatorList,                item;            // create wrapper for list            indicatorList = createElement(UL, {                className: PREFIX + 'indicator-list'            }, null, lhsCol);            rhsColWrapper = rhsCol                .querySelectorAll('.' + PREFIX + 'popup-rhs-col-wrapper')[0];            objectEach(series, function (serie, value) {                var seriesOptions = serie.options;                if (                    serie.params ||                    seriesOptions && seriesOptions.params                ) {                    var indicatorNameType = _self.indicators                            .getNameType(serie, value),                        indicatorType = indicatorNameType.type;                    item = createElement(LI, {                        className: PREFIX + 'indicator-list',                        innerHTML: indicatorNameType.name                    }, null, indicatorList);                    ['click', 'touchstart'].forEach(function (eventName) {                        addEvent(item, eventName, function () {                            addFormFields.call(                                _self,                                chart,                                isEdit ? serie : series[indicatorType],                                indicatorNameType.type,                                rhsColWrapper                            );                            // add hidden input with series.id                            if (isEdit && serie.options) {                                createElement(INPUT, {                                    type: 'hidden',                                    name: PREFIX + 'id-' + indicatorType,                                    value: serie.options.id                                }, null, rhsColWrapper)                                    .setAttribute(                                        PREFIX + 'data-series-id',                                        serie.options.id                                    );                            }                        });                    });                }            });            // select first item from the list            if (indicatorList.childNodes.length > 0) {                indicatorList.childNodes[0].click();            }        },        /*         * Extract full name and type of requested indicator.         *         * @param {Series} - series which name is needed.         * (EDIT mode - defaultOptions.series, ADD mode - indicator series).         * @param {String} - indicator type like: sma, ema, etc.         *         * @return {Object} - series name and type like: sma, ema, etc.         *         */        getNameType: function (series, type) {            var options = series.options,                seriesTypes = H.seriesTypes,                // add mode                seriesName = seriesTypes[type] &&                    seriesTypes[type].prototype.nameBase || type.toUpperCase(),                seriesType = type;            // edit            if (options && options.type) {                seriesType = series.options.type;                seriesName = series.name;            }            return {                name: seriesName,                type: seriesType            };        },        /*         * List all series with unique ID. Its mandatory for indicators to set         * correct linking.         *         * @param {String} - indicator type like: sma, ema, etc.         * @param {String} - type of select i.e series or volume.         * @param {Chart} - chart         * @param {HTMLDOMElement} - element where created HTML list is added         *         */        listAllSeries: function (type, optionName, chart, parentDiv) {            var selectName = PREFIX + optionName + '-type-' + type,                lang = this.lang,                selectBox,                seriesOptions;            createElement(                LABEL, {                    innerHTML: lang[optionName] || optionName,                    htmlFor: selectName                },                null,                parentDiv            );            // select type            selectBox = createElement(                SELECT,                {                    name: selectName,                    className: PREFIX + 'popup-field'                },                null,                parentDiv            );            selectBox.setAttribute('id', PREFIX + 'select-' + optionName);            // list all series which have id - mandatory for creating indicator            chart.series.forEach(function (serie) {                seriesOptions = serie.options;                if (                    !seriesOptions.params &&                    seriesOptions.id &&                    seriesOptions.id !== PREFIX + 'navigator-series'                ) {                    createElement(                        OPTION,                        {                            innerHTML: seriesOptions.name || seriesOptions.id,                            value: seriesOptions.id                        },                        null,                        selectBox                    );                }            });        },        /*         * Create typical inputs for chosen indicator. Fields are extracted from         * defaultOptions (ADD mode) or current indicator (ADD mode). Two extra         * fields are added:         * - hidden input - contains indicator type (required for callback)         * - select - list of series which can be linked with indicator         *         * @param {Chart} - chart         * @param {Series} - indicator         * @param {String} - indicator type like: sma, ema, etc.         * @param {HTMLDOMElement} - element where created HTML list is added         *         */        addFormFields: function (chart, series, seriesType, rhsColWrapper) {            var fields = series.params || series.options.params,                getNameType = this.indicators.getNameType;            // reset current content            rhsColWrapper.innerHTML = '';            // create title (indicator name in the right column)            createElement(                H3,                {                    className: PREFIX + 'indicator-title',                    innerHTML: getNameType(series, seriesType).name                },                null,                rhsColWrapper            );            // input type            createElement(                INPUT,                {                    type: 'hidden',                    name: PREFIX + 'type-' + seriesType,                    value: seriesType                },                null,                rhsColWrapper            );            // list all series with id            this.indicators.listAllSeries.call(                this,                seriesType,                'series',                chart,                rhsColWrapper            );            if (fields.volumeSeriesID) {                this.indicators.listAllSeries.call(                    this,                    seriesType,                    'volume',                    chart,                    rhsColWrapper                );            }            // add param fields            this.indicators.addParamInputs.call(                this,                chart,                'params',                fields,                seriesType,                rhsColWrapper            );        },        /*         * Recurent function which lists all fields, from params object and         * create them as inputs. Each input has unique `data-name` attribute,         * which keeps chain of fields i.e params.styles.fontSize.         *         * @param {Chart} - chart         * @param {String} - name of parent to create chain of names         * @param {Series} - fields - params which are based for input create         * @param {String} - indicator type like: sma, ema, etc.         * @param {HTMLDOMElement} - element where created HTML list is added         *         */        addParamInputs: function (chart, parentNode, fields, type, parentDiv) {            var _self = this,                addParamInputs = this.indicators.addParamInputs,                addInput = this.addInput,                parentFullName;            objectEach(fields, function (value, fieldName) {                // create name like params.styles.fontSize                parentFullName = parentNode + '.' + fieldName;                if (isObject(value)) {                    addParamInputs.call(                        _self,                        chart,                        parentFullName,                        value,                        type,                        parentDiv                    );                } else if (                // skip volume field which is created by addFormFields                    parentFullName !== 'params.volumeSeriesID'                ) {                    addInput.call(                        _self,                        parentFullName,                        type,                        parentDiv,                        [value, 'text'] // all inputs are text type                    );                }            });        },        /*         * Get amount of indicators added to chart.         *         * @return {Number} - Amount of indicators         */        getAmount: function () {            var series = this.series,                counter = 0;            objectEach(series, function (serie) {                var seriesOptions = serie.options;                if (                    serie.params ||                    seriesOptions && seriesOptions.params                ) {                    counter++;                }            });            return counter;        }    },    tabs: {        /*         * Init tabs. Create tab menu items, tabs containers         *         * @param {Chart} - reference to current chart         *         */        init: function (chart) {            var tabs = this.tabs,                indicatorsCount = this.indicators.getAmount.call(chart),                firstTab; // run by default            // create menu items            firstTab = tabs.addMenuItem.call(this, 'add');            tabs.addMenuItem.call(this, 'edit', indicatorsCount);            // create tabs containers            tabs.addContentItem.call(this, 'add');            tabs.addContentItem.call(this, 'edit');            tabs.switchTabs.call(this, indicatorsCount);            // activate first tab            tabs.selectTab.call(this, firstTab, 0);        },        /*         * Create tab menu item         *         * @param {String} - `add` or `edit`         * @param {Number} - Disable tab when 0         *         * @return {HTMLDOMElement} - created HTML tab-menu element         */        addMenuItem: function (tabName, disableTab) {            var popupDiv = this.popup.container,                className = PREFIX + 'tab-item',                lang = this.lang,                menuItem;            if (disableTab === 0) {                className += ' ' + PREFIX + 'tab-disabled';            }            // tab 1            menuItem = createElement(                SPAN,                {                    innerHTML: lang[tabName + 'Button'] || tabName,                    className: className                },                null,                popupDiv            );            menuItem.setAttribute(PREFIX + 'data-tab-type', tabName);            return menuItem;        },        /*         * Create tab content         *         * @return {HTMLDOMElement} - created HTML tab-content element         *         */        addContentItem: function () {            var popupDiv = this.popup.container;            return createElement(                DIV,                {                    className: PREFIX + 'tab-item-content'                },                null,                popupDiv            );        },        /*         * Add click event to each tab         *         * @param {Number} - Disable tab when 0         *         */        switchTabs: function (disableTab) {            var _self = this,                popupDiv = this.popup.container,                tabs = popupDiv.querySelectorAll('.' + PREFIX + 'tab-item'),                dataParam;            tabs.forEach(function (tab, i) {                dataParam = tab.getAttribute(PREFIX + 'data-tab-type');                if (dataParam === 'edit' && disableTab === 0) {                    return;                }                ['click', 'touchstart'].forEach(function (eventName) {                    addEvent(tab, eventName, function () {                        // reset class on other elements                        _self.tabs.deselectAll.call(_self);                        _self.tabs.selectTab.call(_self, this, i);                    });                });            });        },        /*         * Set tab as visible         *         * @param {HTMLDOMElement} - current tab         * @param {Number} - Index of tab in menu         *         */        selectTab: function (tab, index) {            var allTabs = this.popup.container                .querySelectorAll('.' + PREFIX + 'tab-item-content');            tab.className += ' ' + PREFIX + 'tab-item-active';            allTabs[index].className += ' ' + PREFIX + 'tab-item-show';        },        /*         * Set all tabs as invisible.         *         */        deselectAll: function () {            var popupDiv = this.popup.container,                tabs = popupDiv                    .querySelectorAll('.' + PREFIX + 'tab-item'),                tabsContent = popupDiv                    .querySelectorAll('.' + PREFIX + 'tab-item-content'),                i;            for (i = 0; i < tabs.length; i++) {                tabs[i].classList.remove(PREFIX + 'tab-item-active');                tabsContent[i].classList.remove(PREFIX + 'tab-item-show');            }        }    }};addEvent(H.NavigationBindings, 'showPopup', function (config) {    if (!this.popup) {        // Add popup to main container        this.popup = new H.Popup(this.chart.container);    }    this.popup.showForm(        config.formType,        this.chart,        config.options,        config.onSubmit    );});addEvent(H.NavigationBindings, 'closePopup', function () {    if (this.popup) {        this.popup.closePopup();    }});
 |