403Webshell
Server IP : 182.53.201.61  /  Your IP : 216.73.217.175
Web Server : Apache/2.2.15 (Fedora)
System : Linux km10.dyndns.org 2.6.31.5-127.fc12.i686.PAE #1 SMP Sat Nov 7 21:25:57 EST 2009 i686
User : apache ( 48)
PHP Version : 5.3.3
Disable Function : NONE
MySQL : ON  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/html/temp/code/js/es-modules/parts/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/html/temp/code/js/es-modules/parts/RangeSelector.js
/**
 * (c) 2010-2017 Torstein Honsi
 *
 * License: www.highcharts.com/license
 */
/* eslint max-len: 0 */
'use strict';
import H from './Globals.js';
import './Axis.js';
import './Chart.js';
var addEvent = H.addEvent,
    Axis = H.Axis,
    Chart = H.Chart,
    css = H.css,
    createElement = H.createElement,
    defaultOptions = H.defaultOptions,
    defined = H.defined,
    destroyObjectProperties = H.destroyObjectProperties,
    discardElement = H.discardElement,
    each = H.each,
    extend = H.extend,
    fireEvent = H.fireEvent,
    isNumber = H.isNumber,
    merge = H.merge,
    pick = H.pick,
    pInt = H.pInt,
    splat = H.splat,
    wrap = H.wrap;

/* ****************************************************************************
 * Start Range Selector code                                                  *
 *****************************************************************************/
extend(defaultOptions, {

    /**
     * The range selector is a tool for selecting ranges to display within
     * the chart. It provides buttons to select preconfigured ranges in
     * the chart, like 1 day, 1 week, 1 month etc. It also provides input
     * boxes where min and max dates can be manually input.
     *
     * @product highstock
     * @optionparent rangeSelector
     */
    rangeSelector: {
        // allButtonsEnabled: false,
        // enabled: true,
        // buttons: {Object}
        // buttonSpacing: 0,

        /**
         * The vertical alignment of the rangeselector box. Allowed properties are `top`,
         * `middle`, `bottom`.
         *
         * @since 6.0.0
         *
         * @sample {highstock} stock/rangeselector/vertical-align-middle/ Middle
         *
         * @sample {highstock} stock/rangeselector/vertical-align-bottom/ Bottom
         */
        verticalAlign: 'top',

        /**
         * A collection of attributes for the buttons. The object takes SVG
         * attributes like `fill`, `stroke`, `stroke-width`, as well as `style`,
         * a collection of CSS properties for the text.
         *
         * The object can also be extended with states, so you can set presentational
         * options for `hover`, `select` or `disabled` button states.
         *
         * CSS styles for the text label.
         *
         * In styled mode, the buttons are styled by the
         * `.highcharts-range-selector-buttons .highcharts-button` rule with its
         * different states.
         *
         * @type {Object}
         * @sample {highstock} stock/rangeselector/styling/ Styling the buttons and inputs
         * @product highstock
         */
        buttonTheme: {
            'stroke-width': 0,
            width: 28,
            height: 18,
            padding: 2,
            zIndex: 7 // #484, #852
        },

        /**
         * When the rangeselector is floating, the plot area does not reserve
         * space for it. This opens for positioning anywhere on the chart.
         *
         * @sample {highstock} stock/rangeselector/floating/
         *         Placing the range selector between the plot area and the
         *         navigator
         * @since 6.0.0
         * @product highstock
         */
        floating: false,

        /**
         * The x offset of the range selector relative to its horizontal
         * alignment within `chart.spacingLeft` and `chart.spacingRight`.
         *
         * @since 6.0.0
         * @product highstock
         */
        x: 0,

        /**
         * The y offset of the range selector relative to its horizontal
         * alignment within `chart.spacingLeft` and `chart.spacingRight`.
         *
         * @since 6.0.0
         * @product highstock
         */
        y: 0,

        /**
         * Deprecated. The height of the range selector. Currently it is
         * calculated dynamically.
         *
         * @type {Number}
         * @default undefined
         * @since 2.1.9
         * @product highstock
         * @deprecated true
         */
        height: undefined, // reserved space for buttons and input

        /**
         * Positioning for the input boxes. Allowed properties are `align`,
         *  `x` and `y`.
         *
         * @type {Object}
         * @default { align: "right" }
         * @since 1.2.4
         * @product highstock
         */
        inputPosition: {
            /**
             * The alignment of the input box. Allowed properties are `left`,
             * `center`, `right`.
              * @validvalue ["left", "center", "right"]
              * @sample {highstock} stock/rangeselector/input-button-position/
              *         Alignment
              * @since 6.0.0
             */
            align: 'right',
            x: 0,
            y: 0
        },

        /**
         * Positioning for the button row.
         *
         * @since 1.2.4
         * @product highstock
         */
        buttonPosition: {
            /**
             * The alignment of the input box. Allowed properties are `left`,
             * `center`, `right`.
              *
              * @validvalue ["left", "center", "right"]
              * @sample {highstock} stock/rangeselector/input-button-position/
              *         Alignment
              * @since 6.0.0
             */
            align: 'left',
            /**
             * X offset of the button row.
             */
            x: 0,
            /**
             * Y offset of the button row.
             */
            y: 0
        },
        // inputDateFormat: '%b %e, %Y',
        // inputEditDateFormat: '%Y-%m-%d',
        // inputEnabled: true,
        // selected: undefined
        
    }
});

defaultOptions.lang = merge(
    defaultOptions.lang,
    /**
     * Language object. The language object is global and it can't be set
     * on each chart initiation. Instead, use `Highcharts.setOptions` to
     * set it before any chart is initialized.
     *
     * <pre>Highcharts.setOptions({
     *     lang: {
     *         months: [
     *             'Janvier', 'Février', 'Mars', 'Avril',
     *             'Mai', 'Juin', 'Juillet', 'Août',
     *             'Septembre', 'Octobre', 'Novembre', 'Décembre'
     *         ],
     *         weekdays: [
     *             'Dimanche', 'Lundi', 'Mardi', 'Mercredi',
     *             'Jeudi', 'Vendredi', 'Samedi'
     *         ]
     *     }
     * });</pre>
     *
     * @optionparent lang
     * @product highstock
     */
    {

        /**
         * The text for the label for the range selector buttons.
         *
         * @type {String}
         * @default Zoom
         * @product highstock
         */
        rangeSelectorZoom: 'Zoom',

        /**
         * The text for the label for the "from" input box in the range
         * selector.
         *
         * @type {String}
         * @default From
         * @product highstock
         */
        rangeSelectorFrom: 'From',

        /**
         * The text for the label for the "to" input box in the range selector.
         *
         * @type {String}
         * @default To
         * @product highstock
         */
        rangeSelectorTo: 'To'
    }
);

/**
 * The range selector.
 * @class
 * @param {Object} chart
 */
function RangeSelector(chart) {

    // Run RangeSelector
    this.init(chart);
}

RangeSelector.prototype = {
    /**
     * The method to run when one of the buttons in the range selectors is clicked
     * @param {Number} i The index of the button
     * @param {Object} rangeOptions
     * @param {Boolean} redraw
     */
    clickButton: function (i, redraw) {
        var rangeSelector = this,
            chart = rangeSelector.chart,
            rangeOptions = rangeSelector.buttonOptions[i],
            baseAxis = chart.xAxis[0],
            unionExtremes = (chart.scroller && chart.scroller.getUnionExtremes()) || baseAxis || {},
            dataMin = unionExtremes.dataMin,
            dataMax = unionExtremes.dataMax,
            newMin,
            newMax = baseAxis && Math.round(Math.min(baseAxis.max, pick(dataMax, baseAxis.max))), // #1568
            type = rangeOptions.type,
            baseXAxisOptions,
            range = rangeOptions._range,
            rangeMin,
            minSetting,
            rangeSetting,
            ctx,
            ytdExtremes,
            dataGrouping = rangeOptions.dataGrouping;

        if (dataMin === null || dataMax === null) { // chart has no data, base series is removed
            return;
        }

        // Set the fixed range before range is altered
        chart.fixedRange = range;

        // Apply dataGrouping associated to button
        if (dataGrouping) {
            this.forcedDataGrouping = true;
            Axis.prototype.setDataGrouping.call(baseAxis || { chart: this.chart }, dataGrouping, false);
        }

        // Apply range
        if (type === 'month' || type === 'year') {
            if (!baseAxis) {
                // This is set to the user options and picked up later when the axis is instantiated
                // so that we know the min and max.
                range = rangeOptions;
            } else {
                ctx = {
                    range: rangeOptions,
                    max: newMax,
                    chart: chart,
                    dataMin: dataMin,
                    dataMax: dataMax
                };
                newMin = baseAxis.minFromRange.call(ctx);
                if (isNumber(ctx.newMax)) {
                    newMax = ctx.newMax;
                }
            }

        // Fixed times like minutes, hours, days
        } else if (range) {
            newMin = Math.max(newMax - range, dataMin);
            newMax = Math.min(newMin + range, dataMax);

        } else if (type === 'ytd') {

            // On user clicks on the buttons, or a delayed action running from the beforeRender
            // event (below), the baseAxis is defined.
            if (baseAxis) {
                // When "ytd" is the pre-selected button for the initial view, its calculation
                // is delayed and rerun in the beforeRender event (below). When the series
                // are initialized, but before the chart is rendered, we have access to the xData
                // array (#942).
                if (dataMax === undefined) {
                    dataMin = Number.MAX_VALUE;
                    dataMax = Number.MIN_VALUE;
                    each(chart.series, function (series) {
                        var xData = series.xData; // reassign it to the last item
                        dataMin = Math.min(xData[0], dataMin);
                        dataMax = Math.max(xData[xData.length - 1], dataMax);
                    });
                    redraw = false;
                }
                ytdExtremes = rangeSelector.getYTDExtremes(
                    dataMax,
                    dataMin,
                    chart.time.useUTC
                );
                newMin = rangeMin = ytdExtremes.min;
                newMax = ytdExtremes.max;

            // "ytd" is pre-selected. We don't yet have access to processed point and extremes data
            // (things like pointStart and pointInterval are missing), so we delay the process (#942)
            } else {
                addEvent(chart, 'beforeRender', function () {
                    rangeSelector.clickButton(i);
                });
                return;
            }
        } else if (type === 'all' && baseAxis) {
            newMin = dataMin;
            newMax = dataMax;
        }

        newMin += rangeOptions._offsetMin;
        newMax += rangeOptions._offsetMax;

        rangeSelector.setSelected(i);

        // Update the chart
        if (!baseAxis) {
            // Axis not yet instanciated. Temporarily set min and range
            // options and remove them on chart load (#4317).
            baseXAxisOptions = splat(chart.options.xAxis)[0];
            rangeSetting = baseXAxisOptions.range;
            baseXAxisOptions.range = range;
            minSetting = baseXAxisOptions.min;
            baseXAxisOptions.min = rangeMin;
            addEvent(chart, 'load', function resetMinAndRange() {
                baseXAxisOptions.range = rangeSetting;
                baseXAxisOptions.min = minSetting;
            });
        } else {
            // Existing axis object. Set extremes after render time.
            baseAxis.setExtremes(
                newMin,
                newMax,
                pick(redraw, 1),
                null, // auto animation
                {
                    trigger: 'rangeSelectorButton',
                    rangeSelectorButton: rangeOptions
                }
            );
        }
    },

    /**
     * Set the selected option. This method only sets the internal flag, it
     * doesn't update the buttons or the actual zoomed range.
     */
    setSelected: function (selected) {
        this.selected = this.options.selected = selected;
    },

    /**
     * The default buttons for pre-selecting time frames
     */
    defaultButtons: [{
        type: 'month',
        count: 1,
        text: '1m'
    }, {
        type: 'month',
        count: 3,
        text: '3m'
    }, {
        type: 'month',
        count: 6,
        text: '6m'
    }, {
        type: 'ytd',
        text: 'YTD'
    }, {
        type: 'year',
        count: 1,
        text: '1y'
    }, {
        type: 'all',
        text: 'All'
    }],

    /**
     * Initialize the range selector
     */
    init: function (chart) {
        var rangeSelector = this,
            options = chart.options.rangeSelector,
            buttonOptions = options.buttons ||
                [].concat(rangeSelector.defaultButtons),
            selectedOption = options.selected,
            blurInputs = function () {
                var minInput = rangeSelector.minInput,
                    maxInput = rangeSelector.maxInput;

                // #3274 in some case blur is not defined
                if (minInput && minInput.blur) {
                    fireEvent(minInput, 'blur');
                }
                if (maxInput && maxInput.blur) {
                    fireEvent(maxInput, 'blur');
                }
            };

        rangeSelector.chart = chart;
        rangeSelector.options = options;
        rangeSelector.buttons = [];

        chart.extraTopMargin = options.height;
        rangeSelector.buttonOptions = buttonOptions;

        this.unMouseDown = addEvent(chart.container, 'mousedown', blurInputs);
        this.unResize = addEvent(chart, 'resize', blurInputs);

        // Extend the buttonOptions with actual range
        each(buttonOptions, rangeSelector.computeButtonRange);

        // zoomed range based on a pre-selected button index
        if (selectedOption !== undefined && buttonOptions[selectedOption]) {
            this.clickButton(selectedOption, false);
        }


        addEvent(chart, 'load', function () {
            // If a data grouping is applied to the current button, release it
            // when extremes change
            if (chart.xAxis && chart.xAxis[0]) {
                addEvent(chart.xAxis[0], 'setExtremes', function (e) {
                    if (
                        this.max - this.min !== chart.fixedRange &&
                        e.trigger !== 'rangeSelectorButton' &&
                        e.trigger !== 'updatedData' &&
                        rangeSelector.forcedDataGrouping
                    ) {
                        this.setDataGrouping(false, false);
                    }
                });
            }
        });
    },

    /**
     * Dynamically update the range selector buttons after a new range has been
     * set
     */
    updateButtonStates: function () {
        var rangeSelector = this,
            chart = this.chart,
            baseAxis = chart.xAxis[0],
            actualRange = Math.round(baseAxis.max - baseAxis.min),
            hasNoData = !baseAxis.hasVisibleSeries,
            day = 24 * 36e5, // A single day in milliseconds
            unionExtremes = (
                chart.scroller &&
                chart.scroller.getUnionExtremes()
            ) || baseAxis,
            dataMin = unionExtremes.dataMin,
            dataMax = unionExtremes.dataMax,
            ytdExtremes = rangeSelector.getYTDExtremes(
                dataMax,
                dataMin,
                chart.time.useUTC
            ),
            ytdMin = ytdExtremes.min,
            ytdMax = ytdExtremes.max,
            selected = rangeSelector.selected,
            selectedExists = isNumber(selected),
            allButtonsEnabled = rangeSelector.options.allButtonsEnabled,
            buttons = rangeSelector.buttons;

        each(rangeSelector.buttonOptions, function (rangeOptions, i) {
            var range = rangeOptions._range,
                type = rangeOptions.type,
                count = rangeOptions.count || 1,
                button = buttons[i],
                state = 0,
                disable,
                select,
                offsetRange = rangeOptions._offsetMax - rangeOptions._offsetMin,
                isSelected = i === selected,
                // Disable buttons where the range exceeds what is allowed in
                // the current view
                isTooGreatRange = range > dataMax - dataMin,
                // Disable buttons where the range is smaller than the minimum
                // range
                isTooSmallRange = range < baseAxis.minRange,
                // Do not select the YTD button if not explicitly told so
                isYTDButNotSelected = false,
                // Disable the All button if we're already showing all
                isAllButAlreadyShowingAll = false,
                isSameRange = range === actualRange;
            // Months and years have a variable range so we check the extremes
            if (
                (type === 'month' || type === 'year') &&
                (
                    actualRange + 36e5 >=
                    { month: 28, year: 365 }[type] * day * count - offsetRange
                ) &&
                (
                    actualRange - 36e5 <=
                    { month: 31, year: 366 }[type] * day * count + offsetRange
                )
            ) {
                isSameRange = true;
            } else if (type === 'ytd') {
                isSameRange = (ytdMax - ytdMin + offsetRange) === actualRange;
                isYTDButNotSelected = !isSelected;
            } else if (type === 'all') {
                isSameRange = baseAxis.max - baseAxis.min >= dataMax - dataMin;
                isAllButAlreadyShowingAll = (
                    !isSelected &&
                    selectedExists &&
                    isSameRange
                );
            }

            // The new zoom area happens to match the range for a button - mark
            // it selected. This happens when scrolling across an ordinal gap.
            // It can be seen in the intraday demos when selecting 1h and scroll
            // across the night gap.
            disable = (
                !allButtonsEnabled &&
                (
                    isTooGreatRange ||
                    isTooSmallRange ||
                    isAllButAlreadyShowingAll ||
                    hasNoData
                )
            );
            select = (
                (isSelected && isSameRange) ||
                (isSameRange && !selectedExists && !isYTDButNotSelected)
            );

            if (disable) {
                state = 3;
            } else if (select) {
                selectedExists = true; // Only one button can be selected
                state = 2;
            }

            // If state has changed, update the button
            if (button.state !== state) {
                button.setState(state);
            }
        });
    },

    /**
     * Compute and cache the range for an individual button
     */
    computeButtonRange: function (rangeOptions) {
        var type = rangeOptions.type,
            count = rangeOptions.count || 1,

            // these time intervals have a fixed number of milliseconds, as
            // opposed to month, ytd and year
            fixedTimes = {
                millisecond: 1,
                second: 1000,
                minute: 60 * 1000,
                hour: 3600 * 1000,
                day: 24 * 3600 * 1000,
                week: 7 * 24 * 3600 * 1000
            };

        // Store the range on the button object
        if (fixedTimes[type]) {
            rangeOptions._range = fixedTimes[type] * count;
        } else if (type === 'month' || type === 'year') {
            rangeOptions._range =
                { month: 30, year: 365 }[type] * 24 * 36e5 * count;
        }

        rangeOptions._offsetMin = pick(rangeOptions.offsetMin, 0);
        rangeOptions._offsetMax = pick(rangeOptions.offsetMax, 0);
        rangeOptions._range +=
            rangeOptions._offsetMax - rangeOptions._offsetMin;
    },

    /**
     * Set the internal and displayed value of a HTML input for the dates
     * @param {String} name
     * @param {Number} inputTime
     */
    setInputValue: function (name, inputTime) {
        var options = this.chart.options.rangeSelector,
            time = this.chart.time,
            input = this[name + 'Input'];

        if (defined(inputTime)) {
            input.previousValue = input.HCTime;
            input.HCTime = inputTime;
        }

        input.value = time.dateFormat(
            options.inputEditDateFormat || '%Y-%m-%d',
            input.HCTime
        );
        this[name + 'DateBox'].attr({
            text: time.dateFormat(
                options.inputDateFormat || '%b %e, %Y',
                input.HCTime
            )
        });
    },

    showInput: function (name) {
        var inputGroup = this.inputGroup,
            dateBox = this[name + 'DateBox'];

        css(this[name + 'Input'], {
            left: (inputGroup.translateX + dateBox.x) + 'px',
            top: inputGroup.translateY + 'px',
            width: (dateBox.width - 2) + 'px',
            height: (dateBox.height - 2) + 'px',
            border: '2px solid silver'
        });
    },

    hideInput: function (name) {
        css(this[name + 'Input'], {
            border: 0,
            width: '1px',
            height: '1px'
        });
        this.setInputValue(name);
    },

    /**
     * Draw either the 'from' or the 'to' HTML input box of the range selector
     * @param {Object} name
     */
    drawInput: function (name) {
        var rangeSelector = this,
            chart = rangeSelector.chart,
            chartStyle = chart.renderer.style || {},
            renderer = chart.renderer,
            options = chart.options.rangeSelector,
            lang = defaultOptions.lang,
            div = rangeSelector.div,
            isMin = name === 'min',
            input,
            label,
            dateBox,
            inputGroup = this.inputGroup;

        function updateExtremes() {
            var inputValue = input.value,
                value = (options.inputDateParser || Date.parse)(inputValue),
                chartAxis = chart.xAxis[0],
                dataAxis = chart.scroller && chart.scroller.xAxis ? chart.scroller.xAxis : chartAxis,
                dataMin = dataAxis.dataMin,
                dataMax = dataAxis.dataMax;
            if (value !== input.previousValue) {
                input.previousValue = value;
                // If the value isn't parsed directly to a value by the browser's Date.parse method,
                // like YYYY-MM-DD in IE, try parsing it a different way
                if (!isNumber(value)) {
                    value = inputValue.split('-');
                    value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
                }

                if (isNumber(value)) {

                    // Correct for timezone offset (#433)
                    if (!chart.time.useUTC) {
                        value = value + new Date().getTimezoneOffset() * 60 * 1000;
                    }

                    // Validate the extremes. If it goes beyound the data min or max, use the
                    // actual data extreme (#2438).
                    if (isMin) {
                        if (value > rangeSelector.maxInput.HCTime) {
                            value = undefined;
                        } else if (value < dataMin) {
                            value = dataMin;
                        }
                    } else {
                        if (value < rangeSelector.minInput.HCTime) {
                            value = undefined;
                        } else if (value > dataMax) {
                            value = dataMax;
                        }
                    }

                    // Set the extremes
                    if (value !== undefined) {
                        chartAxis.setExtremes(
                            isMin ? value : chartAxis.min,
                            isMin ? chartAxis.max : value,
                            undefined,
                            undefined,
                            { trigger: 'rangeSelectorInput' }
                        );
                    }
                }
            }
        }

        // Create the text label
        this[name + 'Label'] = label = renderer.label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset)
            .addClass('highcharts-range-label')
            .attr({
                padding: 2
            })
            .add(inputGroup);
        inputGroup.offset += label.width + 5;

        // Create an SVG label that shows updated date ranges and and records click events that
        // bring in the HTML input.
        this[name + 'DateBox'] = dateBox = renderer.label('', inputGroup.offset)
            .addClass('highcharts-range-input')
            .attr({
                padding: 2,
                width: options.inputBoxWidth || 90,
                height: options.inputBoxHeight || 17,
                stroke: options.inputBoxBorderColor || '#cccccc',
                'stroke-width': 1,
                'text-align': 'center'
            })
            .on('click', function () {
                rangeSelector.showInput(name); // If it is already focused, the onfocus event doesn't fire (#3713)
                rangeSelector[name + 'Input'].focus();
            })
            .add(inputGroup);
        inputGroup.offset += dateBox.width + (isMin ? 10 : 0);


        // Create the HTML input element. This is rendered as 1x1 pixel then set to the right size
        // when focused.
        this[name + 'Input'] = input = createElement('input', {
            name: name,
            className: 'highcharts-range-selector',
            type: 'text'
        }, {
            top: chart.plotTop + 'px' // prevent jump on focus in Firefox
        }, div);

        

        // Blow up the input box
        input.onfocus = function () {
            rangeSelector.showInput(name);
        };
        // Hide away the input box
        input.onblur = function () {
            rangeSelector.hideInput(name);
        };

        // handle changes in the input boxes
        input.onchange = updateExtremes;

        input.onkeypress = function (event) {
            // IE does not fire onchange on enter
            if (event.keyCode === 13) {
                updateExtremes();
            }
        };
    },

    /**
     * Get the position of the range selector buttons and inputs. This can be overridden from outside for custom positioning.
     */
    getPosition: function () {
        var chart = this.chart,
            options = chart.options.rangeSelector,
            top = (options.verticalAlign) === 'top' ? chart.plotTop - chart.axisOffset[0] : 0; // set offset only for varticalAlign top

        return {
            buttonTop: top + options.buttonPosition.y,
            inputTop: top + options.inputPosition.y - 10
        };
    },
    /**
     * Get the extremes of YTD.
     * Will choose dataMax if its value is lower than the current timestamp.
     * Will choose dataMin if its value is higher than the timestamp for
     *     the start of current year.
     * @param  {number} dataMax
     * @param  {number} dataMin
     * @return {object} Returns min and max for the YTD
     */
    getYTDExtremes: function (dataMax, dataMin, useUTC) {
        var time = this.chart.time,
            min,
            now = new time.Date(dataMax),
            year = time.get('FullYear', now),
            startOfYear = useUTC ? time.Date.UTC(year, 0, 1) : +new time.Date(year, 0, 1); // eslint-disable-line new-cap
        min = Math.max(dataMin || 0, startOfYear);
        now = now.getTime();
        return {
            max: Math.min(dataMax || now, now),
            min: min
        };
    },

    /**
     * Render the range selector including the buttons and the inputs. The first time render
     * is called, the elements are created and positioned. On subsequent calls, they are
     * moved and updated.
     * @param {Number} min X axis minimum
     * @param {Number} max X axis maximum
     */
    render: function (min, max) {

        var rangeSelector = this,
            chart = rangeSelector.chart,
            renderer = chart.renderer,
            container = chart.container,
            chartOptions = chart.options,
            navButtonOptions = chartOptions.exporting && chartOptions.exporting.enabled !== false &&
                chartOptions.navigation && chartOptions.navigation.buttonOptions,
            lang = defaultOptions.lang,
            div = rangeSelector.div,
            options = chartOptions.rangeSelector,
            floating = options.floating,
            buttons = rangeSelector.buttons,
            inputGroup = rangeSelector.inputGroup,
            buttonTheme = options.buttonTheme,
            buttonPosition = options.buttonPosition,
            inputPosition = options.inputPosition,
            inputEnabled = options.inputEnabled,
            states = buttonTheme && buttonTheme.states,
            plotLeft = chart.plotLeft,
            buttonLeft,
            buttonGroup = rangeSelector.buttonGroup,
            group,
            groupHeight,
            rendered = rangeSelector.rendered,
            verticalAlign = rangeSelector.options.verticalAlign,
            legend = chart.legend,
            legendOptions = legend && legend.options,
            buttonPositionY = buttonPosition.y,
            inputPositionY = inputPosition.y,
            animate = rendered || false,
            exportingX = 0,
            alignTranslateY,
            legendHeight,
            minPosition,
            translateY = 0,
            translateX;

        if (options.enabled === false) {
            return;
        }

        // create the elements
        if (!rendered) {

            rangeSelector.group = group = renderer.g('range-selector-group')
                .attr({
                    zIndex: 7
                })
                .add();

            rangeSelector.buttonGroup = buttonGroup = renderer.g('range-selector-buttons').add(group);

            rangeSelector.zoomText = renderer.text(lang.rangeSelectorZoom, pick(plotLeft + buttonPosition.x, plotLeft), 15)
                .css(options.labelStyle)
                .add(buttonGroup);

            // button start position
            buttonLeft = pick(plotLeft + buttonPosition.x, plotLeft) + rangeSelector.zoomText.getBBox().width + 5;

            each(rangeSelector.buttonOptions, function (rangeOptions, i) {

                buttons[i] = renderer.button(
                        rangeOptions.text,
                        buttonLeft,
                        0,
                        function () {

                            // extract events from button object and call
                            var buttonEvents = rangeOptions.events && rangeOptions.events.click,
                                callDefaultEvent;

                            if (buttonEvents) {
                                callDefaultEvent = buttonEvents.call(rangeOptions);
                            }

                            if (callDefaultEvent !== false) {
                                rangeSelector.clickButton(i);
                            }

                            rangeSelector.isActive = true;
                        },
                        buttonTheme,
                        states && states.hover,
                        states && states.select,
                        states && states.disabled
                    )
                    .attr({
                        'text-align': 'center'
                    })
                    .add(buttonGroup);

                // increase button position for the next button
                buttonLeft += buttons[i].width + pick(options.buttonSpacing, 5);
            });

            // first create a wrapper outside the container in order to make
            // the inputs work and make export correct
            if (inputEnabled !== false) {
                rangeSelector.div = div = createElement('div', null, {
                    position: 'relative',
                    height: 0,
                    zIndex: 1 // above container
                });

                container.parentNode.insertBefore(div, container);

                // Create the group to keep the inputs
                rangeSelector.inputGroup = inputGroup = renderer.g('input-group')
                    .add(group);
                inputGroup.offset = 0;

                rangeSelector.drawInput('min');
                rangeSelector.drawInput('max');
            }
        }

        plotLeft = chart.plotLeft - chart.spacing[3];
        rangeSelector.updateButtonStates();

        // detect collisiton with exporting
        if
            (
                navButtonOptions &&
                this.titleCollision(chart) &&
                verticalAlign === 'top' &&
                buttonPosition.align === 'right' &&
                (
                    (buttonPosition.y + buttonGroup.getBBox().height - 12) <
                    ((navButtonOptions.y || 0) + navButtonOptions.height)
                )
            ) {
            exportingX = -40;
        }

        if (buttonPosition.align === 'left') {
            translateX = buttonPosition.x - chart.spacing[3];
        } else if (buttonPosition.align === 'right') {
            translateX = buttonPosition.x + exportingX - chart.spacing[1];
        }

        // align button group
        buttonGroup.align({
            y: buttonPosition.y,
            width: buttonGroup.getBBox().width,
            align: buttonPosition.align,
            x: translateX
        }, true, chart.spacingBox);

        // skip animation
        rangeSelector.group.placed = animate;
        rangeSelector.buttonGroup.placed = animate;

        if (inputEnabled !== false) {

            var inputGroupX,
                inputGroupWidth,
                buttonGroupX,
                buttonGroupWidth;

            // detect collision with exporting
            if
                (
                    navButtonOptions &&
                    this.titleCollision(chart) &&
                    verticalAlign === 'top' &&
                    inputPosition.align === 'right' &&
                    (
                        (inputPosition.y - inputGroup.getBBox().height - 12) <
                        ((navButtonOptions.y || 0) + navButtonOptions.height + chart.spacing[0])
                    )
                ) {
                exportingX = -40;
            } else {
                exportingX = 0;
            }

            if (inputPosition.align === 'left') {
                translateX = plotLeft;
            } else if (inputPosition.align === 'right') {
                translateX = -Math.max(chart.axisOffset[1], -exportingX); // yAxis offset
            }

            // Update the alignment to the updated spacing box
            inputGroup.align({
                y: inputPosition.y,
                width: inputGroup.getBBox().width,
                align: inputPosition.align,
                x: inputPosition.x + translateX - 2 // fix wrong getBBox() value on right align
            }, true, chart.spacingBox);

            // detect collision
            inputGroupX = inputGroup.alignAttr.translateX + inputGroup.alignOptions.x -
                            exportingX + inputGroup.getBBox().x + 2; // getBBox for detecing left margin, 2px padding to not overlap input and label

            inputGroupWidth = inputGroup.alignOptions.width;

            buttonGroupX = buttonGroup.alignAttr.translateX + buttonGroup.getBBox().x;
            buttonGroupWidth = buttonGroup.getBBox().width + 20; // 20 is minimal spacing between elements

            if (
                    (inputPosition.align === buttonPosition.align) ||
                    (
                        (buttonGroupX + buttonGroupWidth > inputGroupX) &&
                        (inputGroupX + inputGroupWidth > buttonGroupX) &&
                        (buttonPositionY < (inputPositionY + inputGroup.getBBox().height))
                    )
                )  {

                inputGroup.attr({
                    translateX: inputGroup.alignAttr.translateX + (chart.axisOffset[1] >= -exportingX ? 0 : -exportingX),
                    translateY: inputGroup.alignAttr.translateY + buttonGroup.getBBox().height + 10
                });

            }

            // Set or reset the input values
            rangeSelector.setInputValue('min', min);
            rangeSelector.setInputValue('max', max);

            // skip animation
            rangeSelector.inputGroup.placed = animate;
        }

        // vertical align
        rangeSelector.group.align({
            verticalAlign: verticalAlign
        }, true, chart.spacingBox);

        // set position
        groupHeight = rangeSelector.group.getBBox().height + 20; // # 20 padding
        alignTranslateY = rangeSelector.group.alignAttr.translateY;

        // calculate bottom position
        if (verticalAlign === 'bottom') {
            legendHeight = legendOptions && legendOptions.verticalAlign === 'bottom' && legendOptions.enabled &&
                            !legendOptions.floating ? legend.legendHeight + pick(legendOptions.margin, 10) : 0;

            groupHeight = groupHeight + legendHeight - 20;
            translateY = alignTranslateY - groupHeight - (floating ? 0 : options.y) - 10; // 10 spacing

        }

        if (verticalAlign === 'top') {
            if (floating) {
                translateY = 0;
            }

            if (chart.titleOffset) {
                translateY = chart.titleOffset + chart.options.title.margin;
            }

            translateY += ((chart.margin[0] - chart.spacing[0]) || 0);

        } else if (verticalAlign === 'middle') {
            if (inputPositionY === buttonPositionY) {
                if (inputPositionY < 0) {
                    translateY = alignTranslateY + minPosition;
                } else {
                    translateY = alignTranslateY;
                }
            } else if (inputPositionY || buttonPositionY) {
                if (inputPositionY < 0 || buttonPositionY < 0) {
                    translateY -= Math.min(inputPositionY, buttonPositionY);
                } else {
                    translateY = alignTranslateY - groupHeight + minPosition;
                }
            }
        }

        rangeSelector.group.translate(
            options.x,
            options.y + Math.floor(translateY)
        );

        // translate HTML inputs
        if (inputEnabled !== false) {
            rangeSelector.minInput.style.marginTop = rangeSelector.group.translateY + 'px';
            rangeSelector.maxInput.style.marginTop = rangeSelector.group.translateY + 'px';
        }

        rangeSelector.rendered = true;
    },

    /**
     * Extracts height of range selector
     * @return {Number} Returns rangeSelector height
     */
    getHeight: function () {
        var rangeSelector = this,
            options = rangeSelector.options,
            rangeSelectorGroup = rangeSelector.group,
            inputPosition = options.inputPosition,
            buttonPosition = options.buttonPosition,
            yPosition = options.y,
            buttonPositionY = buttonPosition.y,
            inputPositionY = inputPosition.y,
            rangeSelectorHeight = 0,
            minPosition;

        rangeSelectorHeight = rangeSelectorGroup ? (rangeSelectorGroup.getBBox(true).height) + 13 + yPosition : 0; // 13px to keep back compatibility

        minPosition = Math.min(inputPositionY, buttonPositionY);

        if (
            (inputPositionY < 0 && buttonPositionY < 0) ||
            (inputPositionY > 0 && buttonPositionY > 0)
        ) {
            rangeSelectorHeight += Math.abs(minPosition);
        }

        return rangeSelectorHeight;
    },

    /**
     * Detect collision with title or subtitle
     * @param {object} chart
     * @return {Boolean} Returns collision status
     */
    titleCollision: function (chart) {
        return !(chart.options.title.text || chart.options.subtitle.text);
    },

    /**
     * Update the range selector with new options
     * @param {object} options
     */
    update: function (options) {
        var chart = this.chart;

        merge(true, chart.options.rangeSelector, options);
        this.destroy();
        this.init(chart);
        chart.rangeSelector.render();
    },

    /**
     * Destroys allocated elements.
     */
    destroy: function () {
        var rSelector = this,
            minInput = rSelector.minInput,
            maxInput = rSelector.maxInput;

        rSelector.unMouseDown();
        rSelector.unResize();

        // Destroy elements in collections
        destroyObjectProperties(rSelector.buttons);

        // Clear input element events
        if (minInput) {
            minInput.onfocus = minInput.onblur = minInput.onchange = null;
        }
        if (maxInput) {
            maxInput.onfocus = maxInput.onblur = maxInput.onchange = null;
        }

        // Destroy HTML and SVG elements
        H.objectEach(rSelector, function (val, key) {
            if (val && key !== 'chart') {
                if (val.destroy) { // SVGElement
                    val.destroy();
                } else if (val.nodeType) { // HTML element
                    discardElement(this[key]);
                }
            }
            if (val !== RangeSelector.prototype[key]) {
                rSelector[key] = null;
            }
        }, this);
    }
};

/**
 * Add logic to normalize the zoomed range in order to preserve the pressed state of range selector buttons
 */
Axis.prototype.toFixedRange = function (pxMin, pxMax, fixedMin, fixedMax) {
    var fixedRange = this.chart && this.chart.fixedRange,
        newMin = pick(fixedMin, this.translate(pxMin, true, !this.horiz)),
        newMax = pick(fixedMax, this.translate(pxMax, true, !this.horiz)),
        changeRatio = fixedRange && (newMax - newMin) / fixedRange;

    // If the difference between the fixed range and the actual requested range is
    // too great, the user is dragging across an ordinal gap, and we need to release
    // the range selector button.
    if (changeRatio > 0.7 && changeRatio < 1.3) {
        if (fixedMax) {
            newMin = newMax - fixedRange;
        } else {
            newMax = newMin + fixedRange;
        }
    }
    if (!isNumber(newMin) || !isNumber(newMax)) { // #1195, #7411
        newMin = newMax = undefined;
    }

    return {
        min: newMin,
        max: newMax
    };
};

/**
 * Get the axis min value based on the range option and the current max. For
 * stock charts this is extended via the {@link RangeSelector} so that if the
 * selected range is a multiple of months or years, it is compensated for
 * various month lengths.
 *
 * @return {number} The new minimum value.
 */
Axis.prototype.minFromRange = function () {
    var rangeOptions = this.range,
        type = rangeOptions.type,
        timeName = { month: 'Month', year: 'FullYear' }[type],
        min,
        max = this.max,
        dataMin,
        range,
        // Get the true range from a start date
        getTrueRange = function (base, count) {
            var date = new Date(base),
                basePeriod = date['get' + timeName]();

            date['set' + timeName](basePeriod + count);

            if (basePeriod === date['get' + timeName]()) {
                date.setDate(0); // #6537
            }

            return date.getTime() - base;
        };

    if (isNumber(rangeOptions)) {
        min = max - rangeOptions;
        range = rangeOptions;
    } else {
        min = max + getTrueRange(max, -rangeOptions.count);

        // Let the fixedRange reflect initial settings (#5930)
        if (this.chart) {
            this.chart.fixedRange = max - min;
        }
    }

    dataMin = pick(this.dataMin, Number.MIN_VALUE);
    if (!isNumber(min)) {
        min = dataMin;
    }
    if (min <= dataMin) {
        min = dataMin;
        if (range === undefined) { // #4501
            range = getTrueRange(min, rangeOptions.count);
        }
        this.newMax = Math.min(min + range, this.dataMax);
    }
    if (!isNumber(max)) {
        min = undefined;
    }
    return min;

};

// Initialize rangeselector for stock charts
addEvent(Chart, 'afterGetContainer', function () {
    if (this.options.rangeSelector.enabled) {
        this.rangeSelector = new RangeSelector(this);
    }
});

wrap(Chart.prototype, 'render', function (proceed, options, callback) {

    var chart = this,
        axes = chart.axes,
        rangeSelector = chart.rangeSelector,
        verticalAlign;

    if (rangeSelector) {

        each(axes, function (axis) {
            axis.updateNames();
            axis.setScale();
        });

        chart.getAxisMargins();

        rangeSelector.render();
        verticalAlign = rangeSelector.options.verticalAlign;

        if (!rangeSelector.options.floating) {
            if (verticalAlign === 'bottom') {
                this.extraBottomMargin = true;
            } else if (verticalAlign !== 'middle') {
                this.extraTopMargin = true;
            }
        }
    }

    proceed.call(this, options, callback);

});

addEvent(Chart, 'update', function (e) {

    var chart = this,
        options = e.options,
        rangeSelector = chart.rangeSelector,
        verticalAlign;

    this.extraBottomMargin = false;
    this.extraTopMargin = false;
    this.isDirtyBox = true; //    #7684 - ignored spacingBottom after update

    if (rangeSelector) {

        rangeSelector.render();

        verticalAlign = (options.rangeSelector && options.rangeSelector.verticalAlign) ||
                        (rangeSelector.options && rangeSelector.options.verticalAlign);

        if (!rangeSelector.options.floating) {
            if (verticalAlign === 'bottom') {
                this.extraBottomMargin = true;
            } else if (verticalAlign !== 'middle') {
                this.extraTopMargin = true;
            }
        }

    }

});

wrap(Chart.prototype, 'redraw', function (proceed, options, callback) {
    var chart = this,
        rangeSelector = chart.rangeSelector,
        verticalAlign;

    if (rangeSelector && !rangeSelector.options.floating) {

        rangeSelector.render();
        verticalAlign = rangeSelector.options.verticalAlign;

        if (verticalAlign === 'bottom') {
            this.extraBottomMargin = true;
        } else if (verticalAlign !== 'middle') {
            this.extraTopMargin = true;
        }
    }

    proceed.call(this, options, callback);
});

Chart.prototype.adjustPlotArea = function () {
    var chart = this,
        rangeSelector = chart.rangeSelector,
        rangeSelectorHeight;

    if (this.rangeSelector) {
        rangeSelectorHeight = rangeSelector.getHeight();

        if (this.extraTopMargin) {
            this.plotTop += rangeSelectorHeight;
        }

        if (this.extraBottomMargin) {
            this.marginBottom += rangeSelectorHeight;
        }
    }
};

Chart.prototype.callbacks.push(function (chart) {
    var extremes,
        rangeSelector = chart.rangeSelector,
        unbindRender,
        unbindSetExtremes;

    function renderRangeSelector() {
        extremes = chart.xAxis[0].getExtremes();
        if (isNumber(extremes.min)) {
            rangeSelector.render(extremes.min, extremes.max);
        }
    }

    if (rangeSelector) {
        // redraw the scroller on setExtremes
        unbindSetExtremes = addEvent(
            chart.xAxis[0],
            'afterSetExtremes',
            function (e) {
                rangeSelector.render(e.min, e.max);
            }
        );

        // redraw the scroller chart resize
        unbindRender = addEvent(chart, 'redraw', renderRangeSelector);

        // do it now
        renderRangeSelector();
    }

    // Remove resize/afterSetExtremes at chart destroy
    addEvent(chart, 'destroy', function destroyEvents() {
        if (rangeSelector) {
            unbindRender();
            unbindSetExtremes();
        }
    });
});


H.RangeSelector = RangeSelector;

/* ****************************************************************************
 * End Range Selector code                                                     *
 *****************************************************************************/

Youez - 2016 - github.com/yon3zu
LinuXploit