<commit_before>/**
 * @class Oskari.statistics.bundle.statsgrid.StatsGridBundleInstance
 *
 * Sample extension bundle definition which inherits most functionalty
 * from DefaultExtension class.
 *
 */
Oskari.clazz.define('Oskari.statistics.bundle.statsgrid.StatsGridBundleInstance',
/**
 * @static constructor function
 */
function() {
    this.conf =  {
        "name": "StatsGrid",
        "sandbox": "sandbox",
        "stateful" : true,

        // stats mode can be accessed from stats layers tools
        // to enable a mode triggering tile, you can uncomment the tileClazz on next line
        //"tileClazz": "Oskari.userinterface.extension.DefaultTile",
        "viewClazz": "Oskari.statistics.bundle.statsgrid.StatsView"
    };
    this.state = {};
}, {
    "init" : function() {
    	var me = this;
        var conf = me.conf ;
        var locale = me.getLocalization();
		var sandboxName = ( conf ? conf.sandbox : null ) || 'sandbox' ;
		var sandbox = Oskari.getSandbox(sandboxName);
        var mapModule = sandbox.findRegisteredModuleInstance('MainMapModule');

        // create the StatisticsService for handling ajax calls
        // and common functionality.
        var statsService = Oskari.clazz.create('Oskari.statistics.bundle.statsgrid.StatisticsService', me);
        sandbox.registerService(statsService);
        this.statsService = statsService;

        // register plugin for map
        var classifyPlugin = Oskari.clazz.create('Oskari.statistics.bundle.statsgrid.plugin.ManageClassificationPlugin', conf ,locale);
        mapModule.registerPlugin(classifyPlugin);
        mapModule.startPlugin(classifyPlugin);
        this.classifyPlugin = classifyPlugin;
        return null;
    },
	"eventHandlers" : {
		/**
		 * @method userinterface.ExtensionUpdatedEvent
		 */
		'userinterface.ExtensionUpdatedEvent' : function(event) {

			var me = this, view = this.plugins['Oskari.userinterface.View'];

			if(event.getExtension().getName() != me.getName()) {
				// not me -> do nothing
				return;
			}

			var isShown = event.getViewState() != "close";

            view.showMode(isShown, true);
			view.showContent(isShown);
		},
        'MapStats.StatsVisualizationChangeEvent' : function(event) {
            this._afterStatsVisualizationChangeEvent(event);
        }
	},
    /**
     * @method setState
     * Sets the map state to one specified in the parameter. State is bundle specific, check the
     * bundle documentation for details.
     * @param {Object} state bundle state as JSON
     * @param {Boolean} ignoreLocation true to NOT set map location based on state
     */
    setState : function(state, ignoreLocation) {
        var me = this, view = this.plugins['Oskari.userinterface.View'];
        var layer = this.sandbox.findMapLayerFromAllAvailable(state.layerId);
        var contentLoadedCallback = function() {
            if(state.indicators.length > 0){

                //send ajax calls and build the grid
                view.getSotkaIndicatorsMeta(state.indicators, function(){

                    //send ajax calls and build the grid
                    view.getSotkaIndicatorsData(state.indicators, function(){

                        if(state.currentColumn != null) {

                            if(state.methodId != null && state.methodId > 0) {
                                var select = me.classifyPlugin.element.find('.classificationMethod').find('.method');
                                select.val(state.methodId);
                                // The manual breaks method:
                                if(state.methodId == 4 && state.manualBreaksInput) {
                                    var manualInput = me.classifyPlugin.element.find('.manualBreaks').find('input[name=breaksInput]');
                                    manualInput.val(state.manualBreaksInput);
                                    me.classifyPlugin.element.find('.classCount').hide();
                                    me.classifyPlugin.element.find('.manualBreaks').show();
                                }
                            }
                            if(state.numberOfClasses != null && state.numberOfClasses > 0) {
                                var slider = me.classifyPlugin.rangeSlider;
                                if(slider != null) {
                                    slider.slider("value", state.numberOfClasses);
                                    slider.parent().find('input#amount_class').val(state.numberOfClasses);
                                }
                            }
                            // current column is needed for rendering map
                            var columns = view.grid.getColumns();
                            for (var i = 0; i < columns.length; i++) {
                                var column = columns[i];
                                if (column.id == state.currentColumn) {
                                    view.classifyData(column);
                                }
                            };
                        }
                    });
                });
            }
        };

        // Load the mode and show content if not loaded already.
        if (!view.isVisible) {
            view.showMode(true);
            view.showContent(true, layer, contentLoadedCallback);
        }
        // Otherwise just set the state.
        else {
            contentLoadedCallback();
        }

        if(!view.grid) {
            return;
        }

        view.clearDataFromGrid();

        if(this.state != null && this.state.indicators != null && this.state.indicators.length > 0) {
            this.state.indicators = [];
        }
    },
    getState : function() {
        if(this.sandbox.getUser().isLoggedIn()) {
            return this.state;
        }
    },

	    /**
     * @method showMessage
     * Shows user a message with ok button
     * @param {String} title popup title
     * @param {String} message popup message
     */
    showMessage : function(title, message) {
        var loc = this.getLocalization();
    	var dialog = Oskari.clazz.create('Oskari.userinterface.component.Popup');
    	var okBtn = Oskari.clazz.create('Oskari.userinterface.component.Button');
    	okBtn.setTitle(loc.buttons.ok);
    	okBtn.addClass('primary');
    	okBtn.setHandler(function() {
            dialog.close(true);
    	});
    	dialog.show(title, message, [okBtn]);
    },
    _afterStatsVisualizationChangeEvent: function(event) {
        var params = event.getParams();
        this.state.methodId = params.methodId;
        this.state.numberOfClasses = params.numberOfClasses;
        this.state.manualBreaksInput = params.manualBreaksInput;
    }
}, {
	"extend" : ["Oskari.userinterface.extension.DefaultExtension"]
});

<commit_msg>Fixed a function renaming issue.<commit_after>/**
 * @class Oskari.statistics.bundle.statsgrid.StatsGridBundleInstance
 *
 * Sample extension bundle definition which inherits most functionalty
 * from DefaultExtension class.
 *
 */
Oskari.clazz.define('Oskari.statistics.bundle.statsgrid.StatsGridBundleInstance',
/**
 * @static constructor function
 */
function() {
    this.conf =  {
        "name": "StatsGrid",
        "sandbox": "sandbox",
        "stateful" : true,

        // stats mode can be accessed from stats layers tools
        // to enable a mode triggering tile, you can uncomment the tileClazz on next line
        //"tileClazz": "Oskari.userinterface.extension.DefaultTile",
        "viewClazz": "Oskari.statistics.bundle.statsgrid.StatsView"
    };
    this.state = {};
}, {
    "init" : function() {
    	var me = this;
        var conf = me.conf ;
        var locale = me.getLocalization();
		var sandboxName = ( conf ? conf.sandbox : null ) || 'sandbox' ;
		var sandbox = Oskari.getSandbox(sandboxName);
        var mapModule = sandbox.findRegisteredModuleInstance('MainMapModule');

        // create the StatisticsService for handling ajax calls
        // and common functionality.
        var statsService = Oskari.clazz.create('Oskari.statistics.bundle.statsgrid.StatisticsService', me);
        sandbox.registerService(statsService);
        this.statsService = statsService;

        // register plugin for map
        var classifyPlugin = Oskari.clazz.create('Oskari.statistics.bundle.statsgrid.plugin.ManageClassificationPlugin', conf ,locale);
        mapModule.registerPlugin(classifyPlugin);
        mapModule.startPlugin(classifyPlugin);
        this.classifyPlugin = classifyPlugin;
        return null;
    },
	"eventHandlers" : {
		/**
		 * @method userinterface.ExtensionUpdatedEvent
		 */
		'userinterface.ExtensionUpdatedEvent' : function(event) {

			var me = this, view = this.plugins['Oskari.userinterface.View'];

			if(event.getExtension().getName() != me.getName()) {
				// not me -> do nothing
				return;
			}

			var isShown = event.getViewState() != "close";

            view.showMode(isShown, true);
			view.showContent(isShown);
		},
        'MapStats.StatsVisualizationChangeEvent' : function(event) {
            this._afterStatsVisualizationChangeEvent(event);
        }
	},
    /**
     * @method setState
     * Sets the map state to one specified in the parameter. State is bundle specific, check the
     * bundle documentation for details.
     * @param {Object} state bundle state as JSON
     * @param {Boolean} ignoreLocation true to NOT set map location based on state
     */
    setState : function(state, ignoreLocation) {
        var me = this, view = this.plugins['Oskari.userinterface.View'];
        var layer = this.sandbox.findMapLayerFromAllAvailable(state.layerId);
        var contentLoadedCallback = function() {
            if(state.indicators.length > 0){

                //send ajax calls and build the grid
                view.getSotkaIndicatorsMeta(state.indicators, function(){

                    //send ajax calls and build the grid
                    view.getSotkaIndicatorsData(state.indicators, function(){

                        if(state.currentColumn != null) {

                            if(state.methodId != null && state.methodId > 0) {
                                var select = me.classifyPlugin.element.find('.classificationMethod').find('.method');
                                select.val(state.methodId);
                                // The manual breaks method:
                                if(state.methodId == 4 && state.manualBreaksInput) {
                                    var manualInput = me.classifyPlugin.element.find('.manualBreaks').find('input[name=breaksInput]');
                                    manualInput.val(state.manualBreaksInput);
                                    me.classifyPlugin.element.find('.classCount').hide();
                                    me.classifyPlugin.element.find('.manualBreaks').show();
                                }
                            }
                            if(state.numberOfClasses != null && state.numberOfClasses > 0) {
                                var slider = me.classifyPlugin.rangeSlider;
                                if(slider != null) {
                                    slider.slider("value", state.numberOfClasses);
                                    slider.parent().find('input#amount_class').val(state.numberOfClasses);
                                }
                            }
                            // current column is needed for rendering map
                            var columns = view.grid.getColumns();
                            for (var i = 0; i < columns.length; i++) {
                                var column = columns[i];
                                if (column.id == state.currentColumn) {
                                    view.sendStatsData(column);
                                }
                            };
                        }
                    });
                });
            }
        };

        // Load the mode and show content if not loaded already.
        if (!view.isVisible) {
            view.showMode(true);
            view.showContent(true, layer, contentLoadedCallback);
        }
        // Otherwise just set the state.
        else {
            contentLoadedCallback();
        }

        if(!view.grid) {
            return;
        }

        view.clearDataFromGrid();

        if(this.state != null && this.state.indicators != null && this.state.indicators.length > 0) {
            this.state.indicators = [];
        }
    },
    getState : function() {
        if(this.sandbox.getUser().isLoggedIn()) {
            return this.state;
        }
    },

	    /**
     * @method showMessage
     * Shows user a message with ok button
     * @param {String} title popup title
     * @param {String} message popup message
     */
    showMessage : function(title, message) {
        var loc = this.getLocalization();
    	var dialog = Oskari.clazz.create('Oskari.userinterface.component.Popup');
    	var okBtn = Oskari.clazz.create('Oskari.userinterface.component.Button');
    	okBtn.setTitle(loc.buttons.ok);
    	okBtn.addClass('primary');
    	okBtn.setHandler(function() {
            dialog.close(true);
    	});
    	dialog.show(title, message, [okBtn]);
    },
    _afterStatsVisualizationChangeEvent: function(event) {
        var params = event.getParams();
        this.state.methodId = params.methodId;
        this.state.numberOfClasses = params.numberOfClasses;
        this.state.manualBreaksInput = params.manualBreaksInput;
    }
}, {
	"extend" : ["Oskari.userinterface.extension.DefaultExtension"]
});

<|endoftext|>


<commit_before>/**
 * @file DatePicker component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep';
import classNames from 'classnames';
import Position from '../_statics/Position';

import TextField from '../TextField';
import Month from '../_MonthPicker';
import Year from '../_YearPicker';
import Popup from '../Popup';

class MonthPicker extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.validValue = true;

        this.state = {
            value: props.value,
            popupVisible: false,
            triggerEl: null,
            year: moment(props.value).format('YYYY'),
            month: moment(props.value).format('MM'),
            datePickerLevel: 'month'
        };

        this.textFieldChangeHandle = ::this.textFieldChangeHandle;
        this.togglePopup = ::this.togglePopup;
        this.closePopup = ::this.closePopup;
        this.datePickerChangeHandle = ::this.datePickerChangeHandle;
        this.yearPickerChangeHandle = ::this.yearPickerChangeHandle;
        this.monthPickerChangeHandle = ::this.monthPickerChangeHandle;

    }

    datePickerChangeHandle(selectLevel) {
        this.setState({
            datePickerLevel: selectLevel
        });
    }

    textFieldChangeHandle(text) {
        const {minValue, maxValue, dateFormat} = this.props;
        if (text && text.length) {
            const flag = moment(text, this.props.dateFormat, true).isValid();
            if (flag) {
                if (minValue && moment(text).isBefore(minValue) || maxValue && moment(text).isAfter(maxValue)) {

                } else {
                    const year = moment(text).format('YYYY'),
                        month = moment(text).format('MM');
                    this.setState({
                        value: moment(text, dateFormat),
                        year: year,
                        month: month
                    });
                }
            } else {

            }
        } else {
            this.setState({
                value: ''
            });
        }
    }

    monthPickerChangeHandle(date) {
        const {dateFormat, autoClose} = this.props;
        let state = cloneDeep(this.state);
        state.popupVisible = !autoClose;
        state.value = moment(`${date.year}-${date.month}`, dateFormat);
        state.year = date.year;
        state.month = date.month;
        if (state.popupVisible) {
            this.setState(state);
        } else {
            !this.props.disabled && this.setState(state, () => {
                this.props.onChange && this.props.onChange(state.value && moment(state.value).format(dateFormat));
            });
        }
    }

    yearPickerChangeHandle(year) {
        this.setState({
            datePickerLevel: 'month',
            year: year
        });
    }

    togglePopup(e) {
        if (this.validValue) {
            this.setState({
                popupVisible: !this.state.popupVisible,
                triggerEl: e.target
            });
        }
    }

    closePopup() {
        const {value} = this.state;
        !this.props.disabled && this.setState({
            popupVisible: false
        }, () => {
            this.props.onChange && this.props.onChange(value && moment(value).format(this.props.dateFormat));
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value || nextProps.dateFormat !== this.props.dateFormat) {
            this.setState({
                value: moment(nextProps.value, nextProps.dateFormat),
                dateFormat: nextProps.dateFormat,
                year: moment(nextProps.value).format('YYYY'),
                month: moment(nextProps.value).format('MM')
            });
        }
    }

    componentDidMount() {
        // debugger
        const {value, dateFormat} = this.props;
        let state = cloneDeep(this.state);
        if (value) {
            if (moment(value, dateFormat).isValid()) {
                const year = moment(value).format('YYYY'),
                    month = moment(value).format('MM');
                state.value = moment(value, dateFormat);
                state.year = year;
                state.month = month;
                this.setState(state);
            } else {
                console.error('Invalid date');
                this.validValue = false;
            }
        }
    }

    render() {

        const {className, name, placeholder, dateFormat, maxValue, minValue, position} = this.props,
            {value, popupVisible, datePickerLevel, year, month, triggerEl} = this.state,

            pickerClassName = classNames('month-picker', {
                [className]: className
            }),

            textValue = value && moment(value).format(dateFormat);

        return (

                <TextField className="month-picker-field"
                           name={name}
                           placeholder={placeholder}
                           value={textValue}
                           readOnly={true}
                           clearButtonVisible={false}
                           isFocusedSelectAll={false}
                           onTouchTap={e => {
                               this.togglePopup(e);
                           }}/>

                <Popup className="month-picker-popup"
                       visible={popupVisible}
                       triggerEl={triggerEl}
                       position={position}
                       hasTriangle={false}
                       onRequestClose={() => {
                           this.closePopup();
                       }}>

                    <TextField className='calendar-input'
                               placeholder={placeholder}
                               clearButtonVisible={false}
                               value={textValue}
                               onChange={this.textFieldChangeHandle}/>
                    {
                        datePickerLevel == 'month' ?
                            <Month value={value}
                                   year={year}
                                   month={month}
                                   maxValue={maxValue}
                                   minValue={minValue}
                                   onChange={this.monthPickerChangeHandle}
                                   previousClick={this.datePickerChangeHandle}/>
                            :
                            <Year value={value}
                                  year={year}
                                  month={month}
                                  maxValue={maxValue}
                                  minValue={minValue}
                                  onChange={this.yearPickerChangeHandle}/>
                    }

                </Popup>

        );
    }
};

MonthPicker.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Date picker input name.
     */
    name: PropTypes.string,

    /**
     * This is the initial date value of the component.
     */
    value: PropTypes.any,

    /**
     * The ending of a range of valid dates. The range includes the endDate.
     */
    maxValue: PropTypes.any,

    /**
     * The beginning of a range of valid dates. The range includes the startDate.
     */
    minValue: PropTypes.any,

    /**
     * DatePicker textField element placeholder.
     */
    placeholder: PropTypes.string,

    /**
     * Date format.
     */
    dateFormat: PropTypes.string,

    /**
     * If true,hide date display.
     */
    autoClose: PropTypes.bool,

    /**
     * Callback function that is fired when the date value changes.
     */
    onChange: PropTypes.func

};

MonthPicker.defaultProps = {
    className: '',
    style: null,
    name: '',
    maxValue: '',
    minValue: '',
    placeholder: 'Date',
    dateFormat: 'YYYY-MM',
    autoClose: true,
    isFooter: true,
    position: Position.BOTTOM_LEFT
};

export default MonthPicker;<commit_msg>update DatePicker<commit_after>/**
 * @file DatePicker component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep';
import classNames from 'classnames';

import TextField from '../TextField';
import Month from '../_MonthPicker';
import Year from '../_YearPicker';
import Popup from '../Popup';

import Position from '../_statics/Position';

import PureRender from '../_vendors/PureRender';

@PureRender
class MonthPicker extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.validValue = true;

        this.state = {
            value: props.value,
            popupVisible: false,
            triggerEl: null,
            year: moment(props.value).format('YYYY'),
            month: moment(props.value).format('MM'),
            datePickerLevel: 'month'
        };

        this.textFieldChangeHandle = ::this.textFieldChangeHandle;
        this.togglePopup = ::this.togglePopup;
        this.closePopup = ::this.closePopup;
        this.datePickerChangeHandle = ::this.datePickerChangeHandle;
        this.yearPickerChangeHandle = ::this.yearPickerChangeHandle;
        this.monthPickerChangeHandle = ::this.monthPickerChangeHandle;

    }

    datePickerChangeHandle(selectLevel) {
        this.setState({
            datePickerLevel: selectLevel
        });
    }

    textFieldChangeHandle(text) {
        const {minValue, maxValue, dateFormat} = this.props;
        if (text && text.length) {
            const flag = moment(text, this.props.dateFormat, true).isValid();
            if (flag) {
                if (minValue && moment(text).isBefore(minValue) || maxValue && moment(text).isAfter(maxValue)) {

                } else {
                    const year = moment(text).format('YYYY'),
                        month = moment(text).format('MM');
                    this.setState({
                        value: moment(text, dateFormat),
                        year: year,
                        month: month
                    });
                }
            } else {

            }
        } else {
            this.setState({
                value: ''
            });
        }
    }

    monthPickerChangeHandle(date) {
        const {dateFormat, autoClose} = this.props;
        let state = cloneDeep(this.state);
        state.popupVisible = !autoClose;
        state.value = moment(`${date.year}-${date.month}`, dateFormat);
        state.year = date.year;
        state.month = date.month;
        if (state.popupVisible) {
            this.setState(state);
        } else {
            !this.props.disabled && this.setState(state, () => {
                this.props.onChange && this.props.onChange(state.value && moment(state.value).format(dateFormat));
            });
        }
    }

    yearPickerChangeHandle(year) {
        this.setState({
            datePickerLevel: 'month',
            year: year
        });
    }

    togglePopup(e) {
        if (this.validValue) {
            this.setState({
                popupVisible: !this.state.popupVisible,
                triggerEl: e.target
            });
        }
    }

    closePopup() {
        const {value} = this.state;
        !this.props.disabled && this.setState({
            popupVisible: false
        }, () => {
            this.props.onChange && this.props.onChange(value && moment(value).format(this.props.dateFormat));
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value || nextProps.dateFormat !== this.props.dateFormat) {
            this.setState({
                value: moment(nextProps.value, nextProps.dateFormat),
                dateFormat: nextProps.dateFormat,
                year: moment(nextProps.value).format('YYYY'),
                month: moment(nextProps.value).format('MM')
            });
        }
    }

    componentDidMount() {
        // debugger
        const {value, dateFormat} = this.props;
        let state = cloneDeep(this.state);
        if (value) {
            if (moment(value, dateFormat).isValid()) {
                const year = moment(value).format('YYYY'),
                    month = moment(value).format('MM');
                state.value = moment(value, dateFormat);
                state.year = year;
                state.month = month;
                this.setState(state);
            } else {
                console.error('Invalid date');
                this.validValue = false;
            }
        }
    }

    render() {

        const {className, name, placeholder, dateFormat, maxValue, minValue, position} = this.props,
            {value, popupVisible, datePickerLevel, year, month, triggerEl} = this.state,

            pickerClassName = classNames('month-picker', {
                [className]: className
            }),

            textValue = value && moment(value).format(dateFormat);

        return (

                <TextField className="month-picker-field"
                           name={name}
                           placeholder={placeholder}
                           value={textValue}
                           readOnly={true}
                           clearButtonVisible={false}
                           isFocusedSelectAll={false}
                           onTouchTap={e => {
                               this.togglePopup(e);
                           }}/>

                <Popup className="month-picker-popup"
                       visible={popupVisible}
                       triggerEl={triggerEl}
                       position={position}
                       hasTriangle={false}
                       onRequestClose={() => {
                           this.closePopup();
                       }}>

                    <TextField className='calendar-input'
                               placeholder={placeholder}
                               clearButtonVisible={false}
                               value={textValue}
                               onChange={this.textFieldChangeHandle}/>
                    {
                        datePickerLevel == 'month' ?
                            <Month value={value}
                                   year={year}
                                   month={month}
                                   maxValue={maxValue}
                                   minValue={minValue}
                                   onChange={this.monthPickerChangeHandle}
                                   previousClick={this.datePickerChangeHandle}/>
                            :
                            <Year value={value}
                                  year={year}
                                  month={month}
                                  maxValue={maxValue}
                                  minValue={minValue}
                                  onChange={this.yearPickerChangeHandle}/>
                    }

                </Popup>

        );
    }
};

if (process.env.NODE_ENV === 'development') {

    const PropTypes = require('prop-types');

    MonthPicker.propTypes = {

        /**
         * The CSS class name of the root element.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the root element.
         */
        style: PropTypes.object,

        /**
         * Date picker input name.
         */
        name: PropTypes.string,

        /**
         * This is the initial date value of the component.
         */
        value: PropTypes.any,

        /**
         * The ending of a range of valid dates. The range includes the endDate.
         */
        maxValue: PropTypes.any,

        /**
         * The beginning of a range of valid dates. The range includes the startDate.
         */
        minValue: PropTypes.any,

        /**
         * DatePicker textField element placeholder.
         */
        placeholder: PropTypes.string,

        /**
         * Date format.
         */
        dateFormat: PropTypes.string,

        /**
         * If true,hide date display.
         */
        autoClose: PropTypes.bool,

        /**
         * Callback function that is fired when the date value changes.
         */
        onChange: PropTypes.func

    };

}

MonthPicker.defaultProps = {
    name: '',
    maxValue: '',
    minValue: '',
    placeholder: 'Date',
    dateFormat: 'YYYY-MM',
    autoClose: true,
    isFooter: true,
    position: Position.BOTTOM_LEFT
};

export default MonthPicker;<|endoftext|>


<commit_before>let $carousels = [];
let loadedCarousels = {};
let slidesPerScreen = 1;
let currentElement = 1; // <--- or something ...
let css = {
    'containers': '.carousel__js',
    'handles': '.carousel__link--next, .carousel__link--prev'
};

export function init() {
    $carousels = $(css.containers);

    /* $(".article-video__link").on("click", function (e) {
     e.preventDefault();
     }); */

    // prevent flicker effect on page load
    $carousels.on('init', function () {
        $(css.containers).css("visibility", "visible");
    });

    // img carousels
    $.each($carousels, function (i, carousel) {
        var $carousel = $(carousel),
            id = $carousel.attr("id");
        loadedCarousels[id] = false;

        $carousel.slick({
            speed: 300,
            slidesToShow: 1,
            infinite: false,
            appendArrows: "#" + id + " .slick-list",
            prevArrow: '<button class="carousel__link--prev">Vorhergehendes Bild</button>',
            nextArrow: '<button class="carousel__link--next carousel__link--waggle">Nächstes Bild</button>',
            slide: ".carousel__item"
        });
        registerListener($carousel);
    });

    // video carousels
    $(".video_carousel__js").on("init", function (slick) {
        $(this).css("visibility", "visible");
        // triggering a "recalculation of dots" (via "setPosition" below)
        $(window).trigger('resize');
    });

    // video carousel:
    $.each($('.video_carousel__js'), function (i, carousel) {
        let $carousel = $(carousel),
            id = $carousel.attr("id");

        loadedCarousels[id] = false;

        $carousel.slick({
            infinite: false,
            slide: ".carousel__item",
            slidesToShow: 1, // we need all dots - initially
            slidesToScroll: 1,
            accessibility: true, // default?
            // focusOnSelect: false, // <-- not adjustable "on the fly" :/
            appendArrows: "#" + id, //  + " .slick-list",
            dots: true,
            centerPadding: 0,
            variableWidth: true,
            prevArrow: '<button class="carousel__link--prev">Vorhergehender Slide</button>',
            nextArrow: '<button class="carousel__link--next">Nächster Slide</button>'
        });
    });

    // "position change" (resize page or "activate" slider in any way)
    $(".video_carousel__js").on("setPosition", function (slick) {
        slidesPerScreen = getNumberOfSlidesPerScreen(1);
        let slidesToShow = slidesPerScreen, // mobile : 1
            $carousel = $(this),
            currentSlide = $carousel.slick("slickCurrentSlide");

        // button / dot text info for screenreaders, initial & on slide change
        if ($carousel.find(".slick-dots button").first().text() == "1" || currentElement != currentSlide) {
            addTextToDots($carousel);
        }
        currentElement = currentSlide;

        // if previous num. of slides shown != the num. we'll see now (window resize)
        if ($carousel.slick("slickGetOption", "slidesToShow") != slidesToShow) {
            let screensToShow = Math.ceil($carousel.find(".carousel__item").length / slidesToShow);

            // desktop / more than one slide / arrows displayed
            if (slidesToShow > 1) {
                // slider not at initial pos.
                if (currentSlide > 0) {
                    // move it to 0 - so the "arrows" don't get "confused"
                    currentSlide = 0;
                    currentElement = currentSlide;
                    $carousel.slick("slickGoTo", currentSlide, true);
                }

                // disabling dots if arrows present (accessibility)
                disableDots($carousel);

                // no right arrow when at the rightmost position within the carousel
                handleRightArrow($carousel, currentSlide, screensToShow);

                // adjust num. of dots (after resize)
                rePaintDots($carousel, screensToShow);

                $carousel.find(".slick-dots").removeClass("h-element--hide");  // show dots (container) if more than one
            } else {
                $carousel.find(".slick-dots").addClass("h-element--hide");    // else hide the one :)
            }

            // adjust num. of slides per page
            $carousel.slick("slickSetOption", "slidesToShow", slidesToShow, false);
            $carousel.slick("slickSetOption", "slidesToScroll", slidesToShow, false);
        }

        // accessibility:
        if (slidesToShow > 1) { // desktop: only for more than one slide
            // unhide the following slidesToShow - 1 from screenreaders as well:
            let maxSlideVisible = currentSlide + slidesToShow - 1;
            $carousel.find(".carousel__item").each(function (i) {
                // remove not currently visible slides from tabindex
                (i >= currentSlide && i <= maxSlideVisible)
                    ? $(this).attr("aria-hidden", false).find(".article-video__link").attr("tabindex", 0)
                    : $(this).attr("aria-hidden", true).find(".article-video__link").attr("tabindex", -1);
            });
        }

    }).on("click", ".article-video__link", function (e) {
        // unfortunately $carousel.slick("slickSetOption", "focusOnSelect", ...); cannot be set "on the fly" :/

        if (e.type == "click" && slidesPerScreen === 1) {
            /// vo has a crazy problem: it fires click for the video left to the "selected" one - from the 3rd on
            /* let $clickedLink = $(this),
             $chosenItem = $clickedLink.closest(".video_carousel__js").find(".slick-current");

             if ($clickedLink.closest(".carousel__item") != $chosenItem) {
             e.preventDefault();
             // gotTo($chosenItem.find(".article-video__link")); // select the proper link
             // the following interferes with "design wish" without any positive effect
             // $chosenItem.find(".article-video__link").trigger("click"); // click that one too
             } else { // regularly :/
             gotTo($clickedLink);
             } */

            gotTo($(this)); // enable selecting "barely visible next video"
        }
    }).on("keyup", ".article-video__link", function (e) {
        // someone is tabbing => clicked <enter> (desktop)
        if (e.keyCode === 13) {
            $(this).trigger("click");
        }
    }).on("keyup", ".carousel__link--prev, .carousel__link--next", function (e) {
        // someone is tabbing => clicked <enter> on the arrow going to the next page
        if (e.keyCode === 13) {
            // we select the first video-link available on the page
            $(this).closest(".video_carousel__js").find(".slick-current .article-video__link").css({"border": "2px dotted pink"}).focus();
        }
    });

    /* $(".video_carousel__js").on("click", ".slick-dots button", function (e) {
     let $carousel = $(this).closest(".video_carousel__js");

     $carousel.find(".carousel__item").each(function (i) {
     let $elm = $(this);
     if ($elm.hasClass("slick-current")) { // selected element
     $elm.find(".article-video__link")
     .attr("tabindex", 0).attr("aria-hidden", false).attr("aria-disabled", false)
     .removeAttr("disabled")
     .css({"pointer-events":"auto", "border": "2px dotted pink", "background":"yellow"})
     } else {
     $elm.find(".article-video__link") // I'd do ANYTHING to make vo / safari understand!!
     .attr("tabindex", -1).attr("aria-hidden", true).attr("aria-disabled", true)
     .attr("disabled", "disabled")
     // .css({"pointer-events":"none"})
     }
     }); // this was meant to help along the hitting of the "correct" link with vo
     }); */
}

function gotTo($selectedLink) {
    $selectedLink.closest(".video_carousel__js")
        .slick("slickGoTo", $selectedLink.closest(".carousel__item").data("slick-index"));
}

function registerListener($carousel) {
    // load all images after user starts interacting with carousel
    $carousel.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
        if (loadedCarousels[$carousel.attr("id")] === false) {
            loadedCarousels[$carousel.attr("id")] = true;
            loadLazyImages(slick.$slides.find(".image-figure__js"));
        }
    });

    $carousel.on('swipe', function (event, slick, direction) {
        // on "interacting" with the carousel => no more animation
        $(this).find(".carousel__link--next").removeClass("carousel__link--waggle");
    });

    $carousel.find(css.handles).on("touchstart mousedown mouseenter", function () {
        $(this).removeClass("untouched");
        // if the handles are clicked / touched: stop the animation
        $(this).removeClass("carousel__link--waggle");
    }).on("touchend touchcancel", function () {
        $(this).addClass("untouched");
    });
}

function loadLazyImages(images) {
    images.each(function (i, image) {
        var $image = $(image);
        if ($image.data("src")) {
            $image.attr("srcset", $image.data("srcset"));
            $image.attr("src", $image.data("src"));
        }
    });
}

function rePaintDots($carousel, screensToShow) {
    let x = screensToShow + 1;
    $carousel.find(".slick-dots li").removeClass("h-element--hide");
    // adding text to dots
    addTextToDots($carousel);
    $carousel.find(".slick-dots li:nth-child(1n + " + x + ")").addClass("h-element--hide");
}

function addTextToDots($carousel) {
    // adding text to dots
    $carousel.find(".slick-dots li").each(function (i) {
        let $elm = $(this);
        // reenabling buttons (after slick) for mobile :/
        $elm.find("button").text($elm.hasClass("slick-active") // and provide textual info
            ? $carousel.data("dot-current")
            : (i + 1) + $carousel.data("dot-info")
        ); // button (dot)

        // reenabling dots for mobile
        if (slidesPerScreen == 1) {
            enableDots($elm);
        }
    }); // this is silly and not informative :/
}

function enableDots($list) {
    $list.attr("aria-hidden", false).find("button")
        .attr("tabindex", "0").attr("aria-hidden", false).attr("role", "button"); // li
}

function disableDots($carousel) {
    $carousel.find(".slick-dots button").attr("tabindex", "-1")
        .attr("aria-hidden", true).attr("role", "presentation");
}

function handleRightArrow($carousel, currentSlide, screensToShow) {
    if ((currentSlide + 1) == screensToShow) {
        $carousel.find(".carousel__link--next").addClass("h-element--hide").attr("aria-disabled", true);
    } else {
        $carousel.find(".carousel__link--next").removeClass("h-element--hide").attr("aria-disabled", false);
    }
}

function getNumberOfSlidesPerScreen(slidesToShow = 1) {
    // let slidesToShow = 1; // mobile

    if (matchMedia('screen and (min-width: 720px) and (max-width: 1023px)').matches) {
        slidesToShow = 2; // tablet
    } else if (matchMedia('screen and (min-width: 1024px)').matches) {
        slidesToShow = 3; // larger
    }
    return slidesToShow
}<commit_msg>remove debugging info<commit_after>let $carousels = [];
let loadedCarousels = {};
let slidesPerScreen = 1;
let currentElement = 1; // <--- or something ...
let css = {
    'containers': '.carousel__js',
    'handles': '.carousel__link--next, .carousel__link--prev'
};

export function init() {
    $carousels = $(css.containers);

    /* $(".article-video__link").on("click", function (e) {
     e.preventDefault();
     }); */

    // prevent flicker effect on page load
    $carousels.on('init', function () {
        $(css.containers).css("visibility", "visible");
    });

    // img carousels
    $.each($carousels, function (i, carousel) {
        var $carousel = $(carousel),
            id = $carousel.attr("id");
        loadedCarousels[id] = false;

        $carousel.slick({
            speed: 300,
            slidesToShow: 1,
            infinite: false,
            appendArrows: "#" + id + " .slick-list",
            prevArrow: '<button class="carousel__link--prev">Vorhergehendes Bild</button>',
            nextArrow: '<button class="carousel__link--next carousel__link--waggle">Nächstes Bild</button>',
            slide: ".carousel__item"
        });
        registerListener($carousel);
    });

    // video carousels
    $(".video_carousel__js").on("init", function (slick) {
        $(this).css("visibility", "visible");
        // triggering a "recalculation of dots" (via "setPosition" below)
        $(window).trigger('resize');
    });

    // video carousel:
    $.each($('.video_carousel__js'), function (i, carousel) {
        let $carousel = $(carousel),
            id = $carousel.attr("id");

        loadedCarousels[id] = false;

        $carousel.slick({
            infinite: false,
            slide: ".carousel__item",
            slidesToShow: 1, // we need all dots - initially
            slidesToScroll: 1,
            accessibility: true, // default?
            // focusOnSelect: false, // <-- not adjustable "on the fly" :/
            appendArrows: "#" + id, //  + " .slick-list",
            dots: true,
            centerPadding: 0,
            variableWidth: true,
            prevArrow: '<button class="carousel__link--prev">Vorhergehender Slide</button>',
            nextArrow: '<button class="carousel__link--next">Nächster Slide</button>'
        });
    });

    // "position change" (resize page or "activate" slider in any way)
    $(".video_carousel__js").on("setPosition", function (slick) {
        slidesPerScreen = getNumberOfSlidesPerScreen(1);
        let slidesToShow = slidesPerScreen, // mobile : 1
            $carousel = $(this),
            currentSlide = $carousel.slick("slickCurrentSlide");

        // button / dot text info for screenreaders, initial & on slide change
        if ($carousel.find(".slick-dots button").first().text() == "1" || currentElement != currentSlide) {
            addTextToDots($carousel);
        }
        currentElement = currentSlide;

        // if previous num. of slides shown != the num. we'll see now (window resize)
        if ($carousel.slick("slickGetOption", "slidesToShow") != slidesToShow) {
            let screensToShow = Math.ceil($carousel.find(".carousel__item").length / slidesToShow);

            // desktop / more than one slide / arrows displayed
            if (slidesToShow > 1) {
                // slider not at initial pos.
                if (currentSlide > 0) {
                    // move it to 0 - so the "arrows" don't get "confused"
                    currentSlide = 0;
                    currentElement = currentSlide;
                    $carousel.slick("slickGoTo", currentSlide, true);
                }

                // disabling dots if arrows present (accessibility)
                disableDots($carousel);

                // no right arrow when at the rightmost position within the carousel
                handleRightArrow($carousel, currentSlide, screensToShow);

                // adjust num. of dots (after resize)
                rePaintDots($carousel, screensToShow);

                $carousel.find(".slick-dots").removeClass("h-element--hide");  // show dots (container) if more than one
            } else {
                $carousel.find(".slick-dots").addClass("h-element--hide");    // else hide the one :)
            }

            // adjust num. of slides per page
            $carousel.slick("slickSetOption", "slidesToShow", slidesToShow, false);
            $carousel.slick("slickSetOption", "slidesToScroll", slidesToShow, false);
        }

        // accessibility:
        if (slidesToShow > 1) { // desktop: only for more than one slide
            // unhide the following slidesToShow - 1 from screenreaders as well:
            let maxSlideVisible = currentSlide + slidesToShow - 1;
            $carousel.find(".carousel__item").each(function (i) {
                // remove not currently visible slides from tabindex
                (i >= currentSlide && i <= maxSlideVisible)
                    ? $(this).attr("aria-hidden", false).find(".article-video__link").attr("tabindex", 0)
                    : $(this).attr("aria-hidden", true).find(".article-video__link").attr("tabindex", -1);
            });
        }

    }).on("click", ".article-video__link", function (e) {
        // unfortunately $carousel.slick("slickSetOption", "focusOnSelect", ...); cannot be set "on the fly" :/

        if (e.type == "click" && slidesPerScreen === 1) {
            /// vo has a crazy problem: it fires click for the video left to the "selected" one - from the 3rd on
            /* let $clickedLink = $(this),
             $chosenItem = $clickedLink.closest(".video_carousel__js").find(".slick-current");

             if ($clickedLink.closest(".carousel__item") != $chosenItem) {
             e.preventDefault();
             // gotTo($chosenItem.find(".article-video__link")); // select the proper link
             // the following interferes with "design wish" without any positive effect
             // $chosenItem.find(".article-video__link").trigger("click"); // click that one too
             } else { // regularly :/
             gotTo($clickedLink);
             } */

            gotTo($(this)); // enable selecting "barely visible next video"
        }
    }).on("keyup", ".article-video__link", function (e) {
        // someone is tabbing => clicked <enter> (desktop)
        if (e.keyCode === 13) {
            $(this).trigger("click");
        }
    }).on("keyup", ".carousel__link--prev, .carousel__link--next", function (e) {
        // someone is tabbing => clicked <enter> on the arrow going to the next page
        if (e.keyCode === 13) {
            // we select the first video-link available on the page
            $(this).closest(".video_carousel__js").find(".slick-current .article-video__link").focus();
        }
    });

    /* $(".video_carousel__js").on("click", ".slick-dots button", function (e) {
     let $carousel = $(this).closest(".video_carousel__js");

     $carousel.find(".carousel__item").each(function (i) {
     let $elm = $(this);
     if ($elm.hasClass("slick-current")) { // selected element
     $elm.find(".article-video__link")
     .attr("tabindex", 0).attr("aria-hidden", false).attr("aria-disabled", false)
     .removeAttr("disabled")
     .css({"pointer-events":"auto", "border": "2px dotted pink", "background":"yellow"})
     } else {
     $elm.find(".article-video__link") // I'd do ANYTHING to make vo / safari understand!!
     .attr("tabindex", -1).attr("aria-hidden", true).attr("aria-disabled", true)
     .attr("disabled", "disabled")
     // .css({"pointer-events":"none"})
     }
     }); // this was meant to help along the hitting of the "correct" link with vo
     }); */
}

function gotTo($selectedLink) {
    $selectedLink.closest(".video_carousel__js")
        .slick("slickGoTo", $selectedLink.closest(".carousel__item").data("slick-index"));
}

function registerListener($carousel) {
    // load all images after user starts interacting with carousel
    $carousel.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
        if (loadedCarousels[$carousel.attr("id")] === false) {
            loadedCarousels[$carousel.attr("id")] = true;
            loadLazyImages(slick.$slides.find(".image-figure__js"));
        }
    });

    $carousel.on('swipe', function (event, slick, direction) {
        // on "interacting" with the carousel => no more animation
        $(this).find(".carousel__link--next").removeClass("carousel__link--waggle");
    });

    $carousel.find(css.handles).on("touchstart mousedown mouseenter", function () {
        $(this).removeClass("untouched");
        // if the handles are clicked / touched: stop the animation
        $(this).removeClass("carousel__link--waggle");
    }).on("touchend touchcancel", function () {
        $(this).addClass("untouched");
    });
}

function loadLazyImages(images) {
    images.each(function (i, image) {
        var $image = $(image);
        if ($image.data("src")) {
            $image.attr("srcset", $image.data("srcset"));
            $image.attr("src", $image.data("src"));
        }
    });
}

function rePaintDots($carousel, screensToShow) {
    let x = screensToShow + 1;
    $carousel.find(".slick-dots li").removeClass("h-element--hide");
    // adding text to dots
    addTextToDots($carousel);
    $carousel.find(".slick-dots li:nth-child(1n + " + x + ")").addClass("h-element--hide");
}

function addTextToDots($carousel) {
    // adding text to dots
    $carousel.find(".slick-dots li").each(function (i) {
        let $elm = $(this);
        // reenabling buttons (after slick) for mobile :/
        $elm.find("button").text($elm.hasClass("slick-active") // and provide textual info
            ? $carousel.data("dot-current")
            : (i + 1) + $carousel.data("dot-info")
        ); // button (dot)

        // reenabling dots for mobile
        if (slidesPerScreen == 1) {
            enableDots($elm);
        }
    }); // this is silly and not informative :/
}

function enableDots($list) {
    $list.attr("aria-hidden", false).find("button")
        .attr("tabindex", "0").attr("aria-hidden", false).attr("role", "button"); // li
}

function disableDots($carousel) {
    $carousel.find(".slick-dots button").attr("tabindex", "-1")
        .attr("aria-hidden", true).attr("role", "presentation");
}

function handleRightArrow($carousel, currentSlide, screensToShow) {
    if ((currentSlide + 1) == screensToShow) {
        $carousel.find(".carousel__link--next").addClass("h-element--hide").attr("aria-disabled", true);
    } else {
        $carousel.find(".carousel__link--next").removeClass("h-element--hide").attr("aria-disabled", false);
    }
}

function getNumberOfSlidesPerScreen(slidesToShow = 1) {
    // let slidesToShow = 1; // mobile

    if (matchMedia('screen and (min-width: 720px) and (max-width: 1023px)').matches) {
        slidesToShow = 2; // tablet
    } else if (matchMedia('screen and (min-width: 1024px)').matches) {
        slidesToShow = 3; // larger
    }
    return slidesToShow
}<|endoftext|>


<commit_before>/*!
|* @name createEvent
|*
|* @description
|* Creates an event of the type specified. The returned object should be first
|* initialized and can then be passed to `element.dispatchEvent`.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|* Possible event types include "UIEvents", "MouseEvents", "MutationEvents",
|* and "HTMLEvents".
|*
|* @api public
`*/
export function createEvent(type, options) {
	let event = { type };
	const opts = Object.assign({ bubbles: false, cancelable: false }, options);
	if (document.createEvent) {
		event = document.createEvent('HTMLEvents');
		event.initEvent(type, opts.bubbles, opts.cancelable);
		event.detail = opts.detail;
		return event;
	} else if (document.createEventObject) {
		event = document.createEventObject();
		event.eventType = type;
		event.type = type;
	}
	return Object.assign(event, opts);
}

/*!
|* @name addEventListener
|*
|* @description
|* The EventTarget.addEventListener() method adds the specified EventListener-compatible
|* object to the list of event listeners for the specified event type on the EventTarget
|* on which it's called. The event target may be an Element in a document, the Document
|* itself, a Window, or any other object that supports events (such as XMLHttpRequest).
|*
|* @param {HTMLElement} domEl The target object for events dispatched to the EventTarget object.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|*
|* @param {Function} listener The listener function that processes the event.
|* This function must accept an Event object as its only parameter and must return nothing.
|*
|* @param {Object} options An options object that specifies characteristics about the event
|* listener.
|*
|* @param {Boolean} useCapture Determines whether the listener works in the capture phase or
|* the target and bubbling phases. If useCapture is set to true, the listener processes the
|* event only during the capture phase and not in the target or bubbling phase.
|* If useCapture is false, the listener processes the event only during the target or bubbling
|* phase.
|* To listen for the event in all three phases, call addEventListener twice, once
|* with useCapture set to true, then again with useCapture set to false.
|*
|* @api public
`*/
export function addEventListener(domEl, type, listener, ...options) {
	if (domEl.addEventListener) {
		domEl.addEventListener(type, listener, ...options);
	} else if (domEl.attachEvent && window.htmlEvents[`on${type}`]) {
		domEl.attachEvent(`on${type}`, listener);
	} else {
		domEl[`on${type}`] = listener;
	}
}

/*!
|* @name removeEventListener
|*
|* @description
|* The EventTarget.removeEventListener() method removes from the EventTarget an event
|* listener previously registered with EventTarget.addEventListener(). The event
|* listener to be removed is identified using a combination of the event type, the
|* event listener function itself, and various optional options that may affect the
|* matching process; see Matching event listeners for removal.
|*
|* @param {HTMLElement} domEl The target object for events dispatched to the EventTarget object.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|*
|* @param {Function} listener The listener function that processes the event.
|* This function must accept an Event object as its only parameter and must return nothing.
|*
|* @param {Object} options An options object that specifies characteristics about the event
|* listener.
|*
|* @param {Boolean} useCapture Determines whether the listener works in the capture phase or
|* the target and bubbling phases. If useCapture is set to true, the listener processes the
|* event only during the capture phase and not in the target or bubbling phase.
|* If useCapture is false, the listener processes the event only during the target or
|* bubbling phase.
|* To listen for the event in all three phases, call addEventListener twice, once with useCapture
|* set to true, then again with useCapture set to false.
|*
|* @api public
`*/
export function removeEventListener(domEl, type, listener, ...options) {
	if (domEl.removeEventListener) {
		domEl.removeEventListener(type, listener, ...options);
	} else if (domEl.detachEvent && window.htmlEvents[`on${type}`]) {
		domEl.detachEvent(`on${type}`, listener);
	} else {
		domEl[`on${type}`] = null;
	}
}

/*!
|* @name dispatchEvent
|*
|* @description
|* Dispatches an Event at the specified EventTarget, invoking the affected EventListeners
|* in the appropriate order. The normal event processing rules (including the capturing
|* and optional bubbling phase) also apply to events dispatched manually with
|* dispatchEvent().
|*
|* @param {HTMLElement} domEl The target object for events dispatched to the
|* EventTarget object.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|*
|* @param {Mixed} options
|*
|* @returns {Boolean} cancelled Indicating whether the event was canceled
|* by an event handler.
|*
|* @api public
`*/
export function dispatchEvent(domEl, type, options) {
	let cancelled;
	const event = createEvent(type, options);
	if (domEl.dispatchEvent) {
		cancelled = domEl.dispatchEvent(event);
	} else if (domEl.fireEvent && window.htmlEvents[`on${event.type}`]) {
		cancelled = domEl.fireEvent(`on${event.type}`, event);
	} else if (domEl[event.type]) {
		cancelled = domEl[event.type](event);
	} else if (domEl[`on${event.type}`]) {
		cancelled = domEl[`on${event.type}`](event);
	}
	return typeof cancelled === 'undefined' || cancelled;
}
<commit_msg>style: comments<commit_after>/*!
|* @name createEvent
|*
|* @description
|* Creates an event of the type specified. The returned object should be first
|* initialized and can then be passed to `element.dispatchEvent`.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|* Possible event types include "UIEvents", "MouseEvents", "MutationEvents",
|* and "HTMLEvents".
|*
|* @api public
`*/
export function createEvent(type, options) {
	let event = { type };
	const opts = Object.assign({ bubbles: false, cancelable: false }, options);
	if (document.createEvent) {
		event = document.createEvent('HTMLEvents');
		event.initEvent(type, opts.bubbles, opts.cancelable);
		event.detail = opts.detail;
		return event;
	} else if (document.createEventObject) {
		event = document.createEventObject();
		event.eventType = type;
		event.type = type;
	}
	return Object.assign(event, opts);
}

/*!
|* @name addEventListener
|*
|* @description
|* The EventTarget.addEventListener() method adds the specified EventListener-compatible
|* object to the list of event listeners for the specified event type on the EventTarget
|* on which it's called. The event target may be an Element in a document, the Document
|* itself, a Window, or any other object that supports events (such as XMLHttpRequest).
|*
|* @param {HTMLElement} domEl The target object for events dispatched to the EventTarget object.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|*
|* @param {Function} listener The listener function that processes the event.
|* This function must accept an Event object as its only parameter and must return nothing.
|*
|* @param {Object} options An options object that specifies characteristics about the event
|* listener.
|*
|* @param {Boolean} useCapture Determines whether the listener works in the capture phase or
|* the target and bubbling phases. If useCapture is set to true, the listener processes the
|* event only during the capture phase and not in the target or bubbling phase.
|* If useCapture is false, the listener processes the event only during the target or bubbling
|* phase.
|* To listen for the event in all three phases, call addEventListener twice, once
|* with useCapture set to true, then again with useCapture set to false.
|*
|* @api public
`*/
export function addEventListener(domEl, type, listener, ...options) {
	if (domEl.addEventListener) {
		domEl.addEventListener(type, listener, ...options);
	} else if (domEl.attachEvent && window.htmlEvents[`on${type}`]) {
		domEl.attachEvent(`on${type}`, listener);
	} else {
		domEl[`on${type}`] = listener;
	}
}

/*!
|* @name removeEventListener
|*
|* @description
|* The EventTarget.removeEventListener() method removes from the EventTarget an event
|* listener previously registered with EventTarget.addEventListener(). The event
|* listener to be removed is identified using a combination of the event type, the
|* event listener function itself, and various optional options that may affect the
|* matching process; see Matching event listeners for removal.
|*
|* @param {HTMLElement} domEl The target object for events dispatched to the EventTarget object.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|*
|* @param {Function} listener The listener function that processes the event.
|* This function must accept an Event object as its only parameter and must return nothing.
|*
|* @param {Object} options An options object that specifies characteristics about the event
|* listener.
|*
|* @param {Boolean} useCapture Determines whether the listener works in the capture phase or
|* the target and bubbling phases. If useCapture is set to true, the listener processes the
|* event only during the capture phase and not in the target or bubbling phase.
|* If useCapture is false, the listener processes the event only during the target or
|* bubbling phase.
|* To listen for the event in all three phases, call addEventListener twice, once with useCapture
|* set to true, then again with useCapture set to false.
|*
|* @api public
`*/
export function removeEventListener(domEl, type, listener, ...options) {
	if (domEl.removeEventListener) {
		domEl.removeEventListener(type, listener, ...options);
	} else if (domEl.detachEvent && window.htmlEvents[`on${type}`]) {
		domEl.detachEvent(`on${type}`, listener);
	} else {
		domEl[`on${type}`] = null;
	}
}

/*!
|* @name dispatchEvent
|*
|* @description
|* Dispatches an Event at the specified EventTarget, invoking the affected EventListeners
|* in the appropriate order. The normal event processing rules (including the capturing
|* and optional bubbling phase) also apply to events dispatched manually with
|* dispatchEvent().
|*
|* @param {HTMLElement} domEl The target object for events dispatched to the EventTarget object.
|*
|* @param {String} type The type is the name of Event object to be dispatched.
|*
|* @param {Mixed} options
|*
|* @returns {Boolean} cancelled Indicating whether the event was canceled
|* by an event handler.
|*
|* @api public
`*/
export function dispatchEvent(domEl, type, options) {
	let cancelled;
	const event = createEvent(type, options);
	if (domEl.dispatchEvent) {
		cancelled = domEl.dispatchEvent(event);
	} else if (domEl.fireEvent && window.htmlEvents[`on${event.type}`]) {
		cancelled = domEl.fireEvent(`on${event.type}`, event);
	} else if (domEl[event.type]) {
		cancelled = domEl[event.type](event);
	} else if (domEl[`on${event.type}`]) {
		cancelled = domEl[`on${event.type}`](event);
	}
	return typeof cancelled === 'undefined' || cancelled;
}
<|endoftext|>


<commit_before>/*
==================================================================================================
  Core - JavaScript Foundation
  Copyright 2012-2013 Sebastian Werner
==================================================================================================
*/

core.Class("core.mvc.view.Dom",
{
  include : [core.mvc.view.Abstract],

  construct: function(presenter) {
    core.mvc.view.Abstract.call(this, presenter);
  },

  properties :
  {
    /** The root element to render into */
    root :
    {
      type: "Node",
      nullable : true,
      apply : function(value, old) {
        this.render();
      }
    }
  },

  members :
  {
    // Definition of abstract method
    render : function()
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      var template = this.getTemplate();
      if (!template) {
        return;
      }

      var presenter = this.getPresenter() || {};
      if (this.getPresenter() == null) {
        this.warn("Missing presenter!");
      }

      elem.innerHTML = template.render(presenter);
      return this;
    }
  }

});
<commit_msg>Added hide/show API<commit_after>/*
==================================================================================================
  Core - JavaScript Foundation
  Copyright 2012-2013 Sebastian Werner
==================================================================================================
*/

core.Class("core.mvc.view.Dom",
{
  include : [core.mvc.view.Abstract],

  construct: function(presenter) {
    core.mvc.view.Abstract.call(this, presenter);
  },

  properties :
  {
    /** The root element to render into */
    root :
    {
      type: "Node",
      nullable : true,
      apply : function(value, old) {
        this.render();
      }
    }
  },

  members :
  {
    // Definition of abstract method
    render : function()
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      var template = this.getTemplate();
      if (!template) {
        return;
      }

      var presenter = this.getPresenter() || {};
      if (this.getPresenter() == null) {
        this.warn("Missing presenter!");
      }

      this.debug("Render...");
      elem.innerHTML = template.render(presenter);
      return this;
    },

    show : function()
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      elem.style.display = "";
      this.fireEvent("show");
    },

    hide : function()
    {
      var elem = this.getRoot();
      if (!elem) {
        return;
      }

      elem.style.display = "none";
      this.fireEvent("hide");
    }
  }

});
<|endoftext|>


<commit_before>import scroll from "scroll";
import scrollDoc from "scroll-doc";

const page = scrollDoc();

function scrollTo({ to = 0, options = { duration: 700 } }) {
  if (page.scrollTop) {
    const cancel = scroll.top(page, to, options, () => {
      page.removeEventListener("wheel", cancel);
    });

    page.addEventListener("wheel", cancel);
  }
}

export default scrollTo;
<commit_msg>Update scrollTo.js<commit_after>import scroll from "scroll";
import scrollDoc from "scroll-doc";

const page = scrollDoc();

function scrollTo({ to = 0, options = { duration: 700 } }) {
  if (page.scrollTop >= 0) {
    const cancel = scroll.top(page, to, options, () => {
      page.removeEventListener("wheel", cancel);
    });

    page.addEventListener("wheel", cancel);
  }
}

export default scrollTo;
<|endoftext|>


<commit_before>/**
 * @flow
 * All About Olaf
 * iOS Contact page
 */

import React from 'react'
import {
  StyleSheet,
  ListView,
} from 'react-native'
import ContactCard from './card'
import {data as numbers} from '../../../docs/contact-info.json'

export default class ContactView extends React.Component {
  state = {
    dataSource: new ListView.DataSource({
      rowHasChanged: (r1, r2) => r1 !== r2,
    }).cloneWithRows(numbers),
  }

  render() {
    return (
      <ListView
        contentContainerStyle={styles.container}
        initialListSize={3}
        removeClippedSubviews={false}
        dataSource={this.state.dataSource}
        renderRow={data =>
          <ContactCard
            title={data.title}
            text={data.text}
            phoneNumber={data.phoneNumber}
            buttonText={data.buttonText}
          />}
      />
    )
  }
}

let styles = StyleSheet.create({
  container: {
    flex: 1,
  },
})
<commit_msg>allow Contacts' ListView to scroll again (#754)<commit_after>/**
 * @flow
 * All About Olaf
 * iOS Contact page
 */

import React from 'react'
import {ListView} from 'react-native'
import ContactCard from './card'
import {data as numbers} from '../../../docs/contact-info.json'

export default class ContactView extends React.Component {
  state = {
    dataSource: new ListView.DataSource({
      rowHasChanged: (r1, r2) => r1 !== r2,
    }).cloneWithRows(numbers),
  }

  render() {
    return (
      <ListView
        initialListSize={3}
        removeClippedSubviews={false}
        dataSource={this.state.dataSource}
        renderRow={data =>
          <ContactCard
            title={data.title}
            text={data.text}
            phoneNumber={data.phoneNumber}
            buttonText={data.buttonText}
          />}
      />
    )
  }
}
<|endoftext|>


<commit_before>// @flow

import * as React from 'react'
import {View, StyleSheet, Dimensions, Platform} from 'react-native'
import * as c from '../components/colors'
import {GrabberBar} from './grabber'
import Interactable from 'react-native-interactable'

type ViewState = 'min' | 'mid' | 'max'

type Props = {
	children: React.Node,
	style?: any,
	size: ViewState,
	onSizeChange: ViewState => any,
}

// See https://mydevice.io/devices/ for device dimensions
const X_WIDTH = 375
const X_HEIGHT = 812

const {height: D_HEIGHT, width: D_WIDTH} = Dimensions.get('window')

const isIPhoneX = (() => {
	return (
		Platform.OS === 'ios' &&
		((D_HEIGHT === X_HEIGHT && D_WIDTH === X_WIDTH) ||
			(D_HEIGHT === X_WIDTH && D_WIDTH === X_HEIGHT))
	)
})()

const screenHeight = Dimensions.get('window').height - 75

export class Overlay extends React.Component<Props> {
	componentDidUpdate(prevProps: Props) {
		if (prevProps.size !== this.props.size) {
			if (!this._view) {
				return
			}

			this._view.snapTo({index: this.positionsOrder.indexOf(this.props.size)})
		}
	}

	positionsOrder = ['max', 'mid', 'min']
	positions = {
		max: 40,
		mid: isIPhoneX ? screenHeight - 370 : screenHeight - 300,
		min: isIPhoneX ? screenHeight - 120 : screenHeight - 58,
	}

	lookupPosition = (size: ViewState) => this.positions[size]
	resizeMin = () => this.props.onSizeChange('min')
	resizeMid = () => this.props.onSizeChange('mid')
	resizeMax = () => this.props.onSizeChange('max')

	_view: any = null
	// _deltaY = new Animated.Value(this.lookupPosition(this.props.size))

	onSnap = (ev: {nativeEvent: {index: number, id: ViewState}}) => {
		this.props.onSizeChange(ev.nativeEvent.id)
	}

	render() {
		const {style: outerStyle, size: viewState} = this.props

		let style = [styles.overlay, outerStyle]

		return (
			<View pointerEvents="box-none" style={styles.panelContainer}>
				{/* this would be a way to darken the background as you move the panel up, but
				    it currently starts out dark – we'd need it to start out transparent. */}
				{/* <Animated.View
					pointerEvents="box-none"
					style={[
						styles.overlayBackground,
						{
							opacity: this._deltaY.interpolate({
								inputRange: [0, screenHeight - 100],
								outputRange: [0.5, 0],
								extrapolateRight: 'clamp',
							}),
						},
					]}
				/> */}

				<Interactable.View
					ref={ref => (this._view = ref)}
					// animatedValueY={this._deltaY}
					boundaries={{top: -300}}
					initialPosition={{y: this.positions[viewState]}}
					onSnap={this.onSnap}
					snapPoints={[
						{id: 'max', y: this.positions.max},
						{id: 'mid', y: this.positions.mid},
						{id: 'min', y: this.positions.min},
					]}
					verticalOnly={true}
				>
					<View style={style}>
						<GrabberBar />
						{this.props.children}
					</View>
				</Interactable.View>
			</View>
		)
	}
}

const styles = StyleSheet.create({
	panelContainer: StyleSheet.absoluteFillObject,
	// overlayBackground: {
	// 	...StyleSheet.absoluteFillObject,
	// 	backgroundColor: c.black,
	// },
	overlay: {
		backgroundColor: c.white,
		height: screenHeight + 300,
		borderTopLeftRadius: 10,
		borderTopRightRadius: 10,
		shadowColor: c.black,
		shadowOffset: {height: -4},
		shadowOpacity: 0.15,
		shadowRadius: 4,
	},
})
<commit_msg>remove the spacing from above the map overlay<commit_after>// @flow

import * as React from 'react'
import {View, StyleSheet, Dimensions, Platform} from 'react-native'
import * as c from '../components/colors'
import {GrabberBar} from './grabber'
import Interactable from 'react-native-interactable'

type ViewState = 'min' | 'mid' | 'max'

type Props = {
	children: React.Node,
	style?: any,
	size: ViewState,
	onSizeChange: ViewState => any,
}

// See https://mydevice.io/devices/ for device dimensions
const X_WIDTH = 375
const X_HEIGHT = 812

const {height: D_HEIGHT, width: D_WIDTH} = Dimensions.get('window')

const isIPhoneX = (() => {
	return (
		Platform.OS === 'ios' &&
		((D_HEIGHT === X_HEIGHT && D_WIDTH === X_WIDTH) ||
			(D_HEIGHT === X_WIDTH && D_WIDTH === X_HEIGHT))
	)
})()

const screenHeight = Dimensions.get('window').height - 66

export class Overlay extends React.Component<Props> {
	componentDidUpdate(prevProps: Props) {
		if (prevProps.size !== this.props.size) {
			if (!this._view) {
				return
			}

			this._view.snapTo({index: this.positionsOrder.indexOf(this.props.size)})
		}
	}

	positionsOrder = ['max', 'mid', 'min']
	positions = {
		max: 0,
		mid: isIPhoneX ? screenHeight - 370 : screenHeight - 300,
		min: isIPhoneX ? screenHeight - 127 : screenHeight - 65,
	}

	lookupPosition = (size: ViewState) => this.positions[size]
	resizeMin = () => this.props.onSizeChange('min')
	resizeMid = () => this.props.onSizeChange('mid')
	resizeMax = () => this.props.onSizeChange('max')

	_view: any = null
	// _deltaY = new Animated.Value(this.lookupPosition(this.props.size))

	onSnap = (ev: {nativeEvent: {index: number, id: ViewState}}) => {
		this.props.onSizeChange(ev.nativeEvent.id)
	}

	render() {
		const {style: outerStyle, size: viewState} = this.props

		let style = [styles.overlay, outerStyle]

		return (
			<View pointerEvents="box-none" style={styles.panelContainer}>
				{/* this would be a way to darken the background as you move the panel up, but
				    it currently starts out dark – we'd need it to start out transparent. */}
				{/* <Animated.View
					pointerEvents="box-none"
					style={[
						styles.overlayBackground,
						{
							opacity: this._deltaY.interpolate({
								inputRange: [0, screenHeight - 100],
								outputRange: [0.5, 0],
								extrapolateRight: 'clamp',
							}),
						},
					]}
				/> */}

				<Interactable.View
					ref={ref => (this._view = ref)}
					// animatedValueY={this._deltaY}
					boundaries={{top: 0}}
					initialPosition={{y: this.positions[viewState]}}
					onSnap={this.onSnap}
					snapPoints={[
						{id: 'max', y: this.positions.max},
						{id: 'mid', y: this.positions.mid},
						{id: 'min', y: this.positions.min},
					]}
					verticalOnly={true}
				>
					<View style={style}>
						<GrabberBar />
						{this.props.children}
					</View>
				</Interactable.View>
			</View>
		)
	}
}

const styles = StyleSheet.create({
	panelContainer: StyleSheet.absoluteFillObject,
	// overlayBackground: {
	// 	...StyleSheet.absoluteFillObject,
	// 	backgroundColor: c.black,
	// },
	overlay: {
		backgroundColor: c.white,
		height: screenHeight,
		borderTopLeftRadius: 10,
		borderTopRightRadius: 10,
		shadowColor: c.black,
		shadowOffset: {height: -4},
		shadowOpacity: 0.15,
		shadowRadius: 4,
	},
})
<|endoftext|>


<commit_before>(function () {

    var accache = {};

    if (!OP.search) { OP.search = {};}

    OP.search.VSfacetMatches = function(callback) {
        callback([
            'MP', 'Party', 'Committee', 'Province', 'Type'
        ]);
    };

    OP.search.VSvalueMatches = function(facet, searchTerm, callback) {
        switch (facet) {
            case 'Year':
                callback([
                    '1994','1995','1996','1997','1998','1999','2000','2001',
                    '2002','2003','2004','2005','2006','2007','2008','2009',
                    '2010','2011','2012'
                ], {preserveOrder: true});
                break;
            case 'MP':
//            case 'Person':
                $.getJSON('/politicians/autocomplete/?' + $.param({q: searchTerm}), function (data) {
                    callback(data.content);
                });
                break;
            case 'Party':
                callback([
                    {value: 'Conservative', label: 'Conservative'},
                    {value: 'Liberal', label: 'Liberal'},
                    {value: 'NDP', label: 'NDP'},
                    {value: 'Bloc', label: 'Bloc Québécois'},
                    {value: 'Green', label: 'Green'},
                    {value: 'Progressive Conservative', label: 'Progressive Conservative'},
                    {value: 'Reform', label: 'Reform'},
                    {value: 'Canadian Alliance', label: 'Canadian Alliance'},
                    {value: 'Independent', label: 'Independent'}
                ], {preserveOrder: true});
                break;
            case 'Province':
                callback([
                    'AB',
                    'BC',
                    'MB',
                    'NB',
                    'NL',
                    'NS',
                    'NT',
                    'NU',
                    'ON',
                    'PE',
                    'QC',
                    'SK',
                    'YK'
                ], {preserveOrder: true});
                break;
            case 'Committee':
                if (accache.committees) {
                    return callback(accache.committees, {preserveOrder: true});
                }
                $.getJSON('/committees/?format=json', function(data) {
                    accache.committees = _.map(data.objects, function(i) {
                        return {
                            value: i.slug,
                            label: i.name.en
                        };
                    });
                    callback(accache.committees, {preserveOrder: true});
                });
                break;
            case 'Type':
                callback([
                    {label: 'Speech (House debate)', value: 'debate'},
                    {label: 'Speech (Committee meeting)', value: 'committee'},
                    {label: 'Bill', value: 'bill'}
                ]);
                break;
        }
    };

    OP.PROVINCES = {
        BC: 'B.C.',
        AB: 'Alberta',
        MB: 'Manitoba',
        SK: 'Saskatchewan',
        ON: 'Ontario',
        QC: 'Qu\u00e9bec',
        NB: 'New Brunswick',
        PE: 'P.E.I.',
        NS: 'Nova Scotia',
        NL: 'Newfoundland',
        NT: 'N.W.T.',
        YT: 'Yukon',
        NU: 'Nunavut'
    };

    OP.search.DOCTYPES = {
        debate: 'House debate',
        committee: 'Committee meeting',
        mp: 'Person',
        bill: 'Bill'
    };

})();<commit_msg>request all committees in search autocomplete<commit_after>(function () {

    var accache = {};

    if (!OP.search) { OP.search = {};}

    OP.search.VSfacetMatches = function(callback) {
        callback([
            'MP', 'Party', 'Committee', 'Province', 'Type'
        ]);
    };

    OP.search.VSvalueMatches = function(facet, searchTerm, callback) {
        switch (facet) {
            case 'Year':
                callback([
                    '1994','1995','1996','1997','1998','1999','2000','2001',
                    '2002','2003','2004','2005','2006','2007','2008','2009',
                    '2010','2011','2012'
                ], {preserveOrder: true});
                break;
            case 'MP':
//            case 'Person':
                $.getJSON('/politicians/autocomplete/?' + $.param({q: searchTerm}), function (data) {
                    callback(data.content);
                });
                break;
            case 'Party':
                callback([
                    {value: 'Conservative', label: 'Conservative'},
                    {value: 'Liberal', label: 'Liberal'},
                    {value: 'NDP', label: 'NDP'},
                    {value: 'Bloc', label: 'Bloc Québécois'},
                    {value: 'Green', label: 'Green'},
                    {value: 'Progressive Conservative', label: 'Progressive Conservative'},
                    {value: 'Reform', label: 'Reform'},
                    {value: 'Canadian Alliance', label: 'Canadian Alliance'},
                    {value: 'Independent', label: 'Independent'}
                ], {preserveOrder: true});
                break;
            case 'Province':
                callback([
                    'AB',
                    'BC',
                    'MB',
                    'NB',
                    'NL',
                    'NS',
                    'NT',
                    'NU',
                    'ON',
                    'PE',
                    'QC',
                    'SK',
                    'YK'
                ], {preserveOrder: true});
                break;
            case 'Committee':
                if (accache.committees) {
                    return callback(accache.committees, {preserveOrder: true});
                }
                $.getJSON('/committees/?format=json&limit=100', function(data) {
                    accache.committees = _.map(data.objects, function(i) {
                        return {
                            value: i.slug,
                            label: i.name.en
                        };
                    });
                    callback(accache.committees, {preserveOrder: true});
                });
                break;
            case 'Type':
                callback([
                    {label: 'Speech (House debate)', value: 'debate'},
                    {label: 'Speech (Committee meeting)', value: 'committee'},
                    {label: 'Bill', value: 'bill'}
                ]);
                break;
        }
    };

    OP.PROVINCES = {
        BC: 'B.C.',
        AB: 'Alberta',
        MB: 'Manitoba',
        SK: 'Saskatchewan',
        ON: 'Ontario',
        QC: 'Qu\u00e9bec',
        NB: 'New Brunswick',
        PE: 'P.E.I.',
        NS: 'Nova Scotia',
        NL: 'Newfoundland',
        NT: 'N.W.T.',
        YT: 'Yukon',
        NU: 'Nunavut'
    };

    OP.search.DOCTYPES = {
        debate: 'House debate',
        committee: 'Committee meeting',
        mp: 'Person',
        bill: 'Bill'
    };

})();<|endoftext|>


<commit_before>import m from 'mithril';
import { noop, gameIcon } from '../../utils';
import layout from '../layout';
import i18n from '../../i18n';
import helper from '../helper';
import newGameForm from '../newGameForm';
import gameApi from '../../lichess/game';
import { header as headerWidget, userStatus } from '../shared/common';
import ViewOnlyBoard from '../shared/ViewOnlyBoard';

export default function homeView(ctrl) {

  function body() {
    const nbPlayers = i18n('nbConnectedPlayers', ctrl.nbConnectedPlayers() || '?');
    const nbGames = i18n('nbGamesInPlay', ctrl.nbGamesInPlay() || '?');

    return (

{m.trust(nbPlayers.replace(/(\d+)/, '$1'))}

{m.trust(nbGames.replace(/(\d+)/, '$1'))}

            <button className="fatButton" config={helper.ontouchY(newGameForm.openRealTime)}>{i18n('createAGame')}</button>

          {renderFeatured(ctrl)}
          {renderDailyPuzzle(ctrl)}
          {renderWeekLeaders(ctrl)}

    );
  }

  return layout.free(headerWidget.bind(undefined, 'lichess.org'), body, noop);
}

function renderFeatured(ctrl) {
  const feat = ctrl.featured();

  if (!feat) return null;

  const { fen, lastMove } = ctrl.featured().game;
  const orientation = ctrl.featured().orientation;

  return (

Featured game

          {m.component(ViewOnlyBoard, {fen, lastMove, orientation })}

              {feat.player.user.username}

              {feat.opponent.user.username}

              {feat.player.rating}

              {gameApi.time(feat)}

              {feat.opponent.rating}

  );
}

function renderDailyPuzzle(ctrl) {
  const puzzle = ctrl.dailyPuzzle();

  return (

{i18n('puzzleOfTheDay')}

 m.route('/training/' + puzzle.id))}>

          {m.component(ViewOnlyBoard, {
            fen: puzzle.fen,
            orientation: puzzle.color
          })}

  );
}

function renderWeekLeaders(ctrl) {
  const players = ctrl.weekTopPlayers();
  console.log(players);

  return (

Week's leaders

        { players.map(renderPlayer) }

  );
}

function renderPlayer(p) {
  const perfKey = Object.keys(p.perfs)[0];
  const perf = p.perfs[perfKey];
  return (

 m.route('/@/' + p.id))}>
      {userStatus(p)}

          {perf.rating}

        {helper.progress(perf.progress)}

  );
}
<commit_msg>Only show supported variants<commit_after>import m from 'mithril';
import { noop, gameIcon } from '../../utils';
import layout from '../layout';
import i18n from '../../i18n';
import helper from '../helper';
import newGameForm from '../newGameForm';
import gameApi from '../../lichess/game';
import settings from '../../settings';
import { header as headerWidget, userStatus } from '../shared/common';
import ViewOnlyBoard from '../shared/ViewOnlyBoard';

export default function homeView(ctrl) {

  function body() {
    const nbPlayers = i18n('nbConnectedPlayers', ctrl.nbConnectedPlayers() || '?');
    const nbGames = i18n('nbGamesInPlay', ctrl.nbGamesInPlay() || '?');

    return (

{m.trust(nbPlayers.replace(/(\d+)/, '$1'))}

{m.trust(nbGames.replace(/(\d+)/, '$1'))}

            <button className="fatButton" config={helper.ontouchY(newGameForm.openRealTime)}>{i18n('createAGame')}</button>

          {renderFeatured(ctrl)}
          {renderDailyPuzzle(ctrl)}
          {renderWeekLeaders(ctrl)}

    );
  }

  return layout.free(headerWidget.bind(undefined, 'lichess.org'), body, noop);
}

function renderFeatured(ctrl) {
  const feat = ctrl.featured();

  if (!feat) return null;

  const { fen, lastMove } = ctrl.featured().game;
  const orientation = ctrl.featured().orientation;

  return (

Featured game

          {m.component(ViewOnlyBoard, {fen, lastMove, orientation })}

              {feat.player.user.username}

              {feat.opponent.user.username}

              {feat.player.rating}

              {gameApi.time(feat)}

              {feat.opponent.rating}

  );
}

function renderDailyPuzzle(ctrl) {
  const puzzle = ctrl.dailyPuzzle();

  return (

{i18n('puzzleOfTheDay')}

 m.route('/training/' + puzzle.id))}>

          {m.component(ViewOnlyBoard, {
            fen: puzzle.fen,
            orientation: puzzle.color
          })}

  );
}

function renderWeekLeaders(ctrl) {
  const players = ctrl.weekTopPlayers();
  console.log(players);

  return (

Week's leaders

        { players.map(renderPlayer) }

  );
}

function renderPlayer(p) {
  const perfKey = Object.keys(p.perfs)[0];
  const perf = p.perfs[perfKey];

  const supportedPerfs = settings.game.supportedVariants.concat([
    'blitz', 'bullet', 'classical'
  ]);

  if (supportedPerfs.indexOf(perfKey) === -1) return null;

  return (

 m.route('/@/' + p.id))}>
      {userStatus(p)}

          {perf.rating}

        {helper.progress(perf.progress)}

  );
}
<|endoftext|>


<commit_before>import i18n from '../../i18n';
import loginModal from '../loginModal';
import popupWidget from '../shared/popup';
import backbutton from '../../backbutton';
import helper from '../helper';
import m from 'mithril';
import Chart from 'chart.js';
import formWidgets from '../shared/form';

Chart.defaults.global.animation = false;

export default {

  oninit: function(root) {
    let isOpen = false;

    function open() {
      backbutton.stack.push(close);
      isOpen = true;
    }

    function close(fromBB) {
      if (fromBB !== 'backbutton' && isOpen) backbutton.stack.pop();
      isOpen = false;
    }

    return {
      open: open,
      close: close,
      isOpen: function() {
        return isOpen;
      },
      root
    };
  },

  view: function(ctrl) {
    return popupWidget(
      'trainingMenu',
      null,
      renderTrainingMenu.bind(undefined, ctrl.root),
      ctrl.isOpen(),
      ctrl.close
    );
  }
};

function renderDifficulty(ctrl) {
  const opts = ctrl.data.difficulty.choices.map(c => ['difficulty' + c[1], c[0]]);
  return m('div.select_input.puzzleDifficulty',
    formWidgets.renderSelect('level', 'difficulty', opts,
      () => ctrl.data.difficulty.current, false, ctrl.setDifficulty)
  );
}

export function renderUserInfos(ctrl) {
  return [
    m('p', m.trust(i18n('yourPuzzleRatingX', `<strong>${ctrl.data.user.rating}</strong>`))),
    helper.isPortrait() ? m('br') : null,
    ctrl.data.user.history ? m('canvas#puzzleChart', {
      width: '240px',
      height: '150px',
      oncreate: function(vnode) {
        const hash = ctrl.data.user.history.join('') + (helper.isPortrait() ? 'portrait' : 'landscape');
        if (hash === vnode.state.hash) return;
        vnode.state.hash = hash;
        const ctx = document.getElementById('puzzleChart').getContext('2d');
        if (helper.isPortrait()) {
          ctx.canvas.width = ctx.canvas.parentElement.offsetWidth - 20;
        } else {
          ctx.canvas.width = ctx.canvas.parentElement.offsetWidth;
          ctx.canvas.height = ctx.canvas.parentElement.offsetHeight - 20;
        }
        new Chart(ctx).Line({
          labels: ctrl.data.user.history.map(() => ''),
          datasets: [
            {
              data: ctrl.data.user.history,
              fillColor: 'rgba(196, 168, 111, 0.4)',
              strokeColor: 'rgba(196, 168, 111, 0.8)'
            }
          ]
        }, {
          pointDot: false,
          scaleShowGridLines: false
        });
      }
    }) : null
  ];
}

export function renderSigninBox() {
  return m('div', [
    m('p', i18n('toTrackYourProgress')),
    m('p',
      m('button', {
        oncreate: helper.ontouch(loginModal.open)
      }, [m('span.fa.fa-user'), i18n('signIn')])
    ),
    m('p', i18n('trainingSignupExplanation'))
  ]);
}

function renderTrainingMenu(ctrl) {
  if (ctrl.data.user) {
    if (helper.isPortrait())
      return renderUserInfos(ctrl).concat(renderDifficulty(ctrl));
    else
      return renderDifficulty(ctrl);
  } else {
    return renderSigninBox();
  }
}
<commit_msg>Fix training chart<commit_after>import i18n from '../../i18n';
import loginModal from '../loginModal';
import popupWidget from '../shared/popup';
import backbutton from '../../backbutton';
import helper from '../helper';
import m from 'mithril';
import Chart from 'chart.js';
import formWidgets from '../shared/form';

Chart.defaults.global.animation = false;

export default {

  oninit: function(root) {
    let isOpen = false;

    function open() {
      backbutton.stack.push(close);
      isOpen = true;
    }

    function close(fromBB) {
      if (fromBB !== 'backbutton' && isOpen) backbutton.stack.pop();
      isOpen = false;
    }

    return {
      open: open,
      close: close,
      isOpen: function() {
        return isOpen;
      },
      root
    };
  },

  view: function(ctrl) {
    return popupWidget(
      'trainingMenu',
      null,
      renderTrainingMenu.bind(undefined, ctrl.root),
      ctrl.isOpen(),
      ctrl.close
    );
  }
};

export function renderUserInfos(ctrl) {
  return [
    m('p', m.trust(i18n('yourPuzzleRatingX', `<strong>${ctrl.data.user.rating}</strong>`))),
    helper.isPortrait() ? m('br') : null,
    ctrl.data.user.history ? m('canvas', {
      oncreate(vnode) {
        const ctx = vnode.dom.getContext('2d');
        console.log(vnode.dom);
        drawChart(ctrl, ctx);
        vnode.state.hash = chartHash(ctrl);
      },
      onupdate(vnode) {
        const hash = chartHash(ctrl);
        if (hash === vnode.state.hash) return;
        vnode.state.hash = hash;
        const ctx = vnode.dom.getContext('2d');
        drawChart(ctrl, ctx);
      }
    }) : null
  ];
}

export function renderSigninBox() {
  return m('div', [
    m('p', i18n('toTrackYourProgress')),
    m('p',
      m('button', {
        oncreate: helper.ontouch(loginModal.open)
      }, [m('span.fa.fa-user'), i18n('signIn')])
    ),
    m('p', i18n('trainingSignupExplanation'))
  ]);
}

function chartHash(ctrl) {
  return ctrl.data.user.history.join('') + (helper.isPortrait() ? 'portrait' : 'landscape');
}

function drawChart(ctrl, ctx) {
  const canvas = ctx.canvas;
  if (helper.isPortrait()) {
    canvas.width = canvas.style.width = canvas.parentElement.offsetWidth - 20;
    canvas.height = canvas.style.height = 150;
  } else {
    canvas.width = canvas.style.width = canvas.parentElement.offsetWidth;
    canvas.height = canvas.style.height = canvas.parentElement.offsetHeight - 20;
  }
  new Chart(ctx).Line({
    labels: ctrl.data.user.history.map(() => ''),
    datasets: [
      {
        data: ctrl.data.user.history,
        fillColor: 'rgba(196, 168, 111, 0.4)',
        strokeColor: 'rgba(196, 168, 111, 0.8)'
      }
    ]
  }, {
    pointDot: false,
    scaleShowGridLines: false
  });
}

function renderDifficulty(ctrl) {
  const opts = ctrl.data.difficulty.choices.map(c => ['difficulty' + c[1], c[0]]);
  return m('div.select_input.puzzleDifficulty',
    formWidgets.renderSelect('level', 'difficulty', opts,
      () => ctrl.data.difficulty.current, false, ctrl.setDifficulty)
  );
}

function renderTrainingMenu(ctrl) {
  if (ctrl.data.user) {
    if (helper.isPortrait())
      return renderUserInfos(ctrl).concat(renderDifficulty(ctrl));
    else
      return renderDifficulty(ctrl);
  } else {
    return renderSigninBox();
  }
}
<|endoftext|>


<commit_before>'use strict';

module.exports = function(gulp, config) {
  return function(command, cb) {
    var _ = require('lodash');
    var spawn = require('child_process').spawn;
    var env = _.extend({}, process.env, config.server.environmentVariables);
    var argv = process.argv.slice(2);
    var proc = spawn('node', [command].concat(argv), { env: env, stdio: 'inherit' });

    proc.on('close', function (code) {
      if (code === 0) {
        cb();
      } else {
        cb(new Error(code));
      }
    });
  };
};
<commit_msg>feat(run-command): allow environment to override variables defined in tasks config<commit_after>'use strict';

module.exports = function(gulp, config) {
  return function(command, cb) {
    var _ = require('lodash');
    var spawn = require('child_process').spawn;
    var env = _.extend({}, config.server.environmentVariables, process.env);
    var argv = process.argv.slice(2);
    var proc = spawn('node', [command].concat(argv), { env: env, stdio: 'inherit' });

    proc.on('close', function (code) {
      if (code === 0) {
        cb();
      } else {
        cb(new Error(code));
      }
    });
  };
};
<|endoftext|>