<commit_before>CATS.Model.Chart = Classify(CATS.Model.Entity, {
    init: function (table) {
        this.$$parent();
        this.type = "chart";
        this.table = table;
        this.series = [];
        this.series_params = [];
        this.chart_type = "line";
        this.contests_url_param = null;
        this.colors = [
            'green',
            'black',
            'blue',
            'red',
            'orange',
            'yellow',
            'cyan',
            'magenta',
            'gray',
            'violet',
        ];
        this.series_id_generator = 0;
        var self = this;
        this.group_by = {
            time: function (r) { return Math.ceil(r.minutes_since_start() / _.last(self.series_params).period); },
            status: function (r) { return r.status; },
        };

        this.group_by_xaxes_value = {
            time: function (idx) { return idx * _.last(self.series_params).period; },
            status: function (idx) { return self.statuses_arr.indexOf(idx); },
        };
    },

    statuses: {
        OK: 'accepted',
        WA: 'wrong_answer',
        PE: 'presentation_error',
        TL: 'time_limit_exceeded',
        RE: 'runtime_error',
        CE: 'compilation_error',
        SV: 'security_violation',
        ML: 'memory_limit_exceeded',
        IS: 'ignore_submit',
        IL: 'idleness_limit_exceeded',
    },

    statuses_arr: [
        'accepted',
        'wrong_answer',
        'presentation_error',
        'time_limit_exceeded',
        'runtime_error',
        'compilation_error',
        'security_violation',
        'memory_limit_exceeded',
        'ignore_submit',
        'idleness_limit_exceeded',
    ],

    statuses_color: {
        accepted: 'green',
        wrong_answer: 'black',
        presentation_error: 'blue',
        time_limit_exceeded: 'red',
        runtime_error: 'orange',
        compilation_error: 'yellow',
        security_violation: 'cyan',
        memory_limit_exceeded: 'magenta',
        ignore_submit: 'gray',
        idleness_limit_exceeded: 'violet',
    },

    get_contest: function () {
        var t = CATS.App.result_tables[this.table];
        return CATS.App.contests[t.contest];
    },

    params_pack: function (p) {
        var result = _.clone(p);
        if (result.statuses.length === _.keys(this.statuses).length)
            delete result.statuses;
        if (result.problems.length === this.get_contest().problems.length)
            delete result.problems;
        return result;
    },

    params_unpack: function (p) {
        var result = _.clone(p);
        result.statuses = p.statuses ? p.statuses : _.keys(this.statuses);
        result.problems = p.problems ? p.problems : this.get_contest().problems;
        return result;
    },

    settings: function(settings) {
        var self = this;
        if (settings != undefined) {
            this.series = [];
            this.series_params = [];
            this.series = [];
            this.chart_type = settings.chart_type;
            _.each(settings.params, function (v) { self.add_new_series(self.params_unpack(v)); });
        }
        return {chart_type: this.chart_type, params: _.map(this.series_params, function (v) { return self.params_pack(v); })};
    },

    parameter_yaxes: {
        run_cnt: 1,
        points: 2,
        place: 3,
    },

    parameter_xaxes: {
        time: 1,
        status: 2,
    },

    add_new_series: function(params) {
        this.series_params.push(params);
        this.series.push({
            label: params.parameter,
            data: this.aggregate(
                params.parameter === 'place' ? this.place_data(params) : this.simple_data(params), params),
            color: params.color != undefined ? params.color : this.colors[this.series.length % this.colors.length],
            xaxis: this.parameter_xaxes[params.group_by],
            yaxis: this.parameter_yaxes[params.parameter],
            id: ++this.series_id_generator,
        });
    },

    series_pie_format: function() {
        var pies = [];
        var self = this;
        var series_idx = 0;
        _.each(this.series, function (s) {
            var pie = [];
            _.each(s.data, function (data) {
                pie.push({label: self.series_params[series_idx].group_by == "status" ? self.statuses_arr[data[0]] : data[0], data: data[1]})
            });
            pies.push(pie);
            series_idx++;
        });

        return pies;
    },

    element_to_values: {
        run_cnt: function (runs) { return _.map(runs, function () { return 1; }); },
        points: function (runs) { return _.map(runs, function (r) { return r.points; }); },
        place: function (sb_rows) { return _.map(sb_rows, function (r) { return r.place; }); },
    },

    aggregate: function(chain, params) {
        var self = this;
        return chain.map(function(element, period_idx) {
            return [ self.group_by_xaxes_value[params.group_by](period_idx) ,
                self.aggregation[params.aggregation](self.element_to_values[params.parameter](element)) ];
        }).value();
    },

    simple_data: function(params) {
        var self = this;
        var statuses = _.countBy(params.statuses, function (s) { return self.statuses[s]; });
        var problems = _.countBy(params.problems, _.identity);
        var affiliation_regexp = new RegExp(params.affiliation);
        var user_regexp = new RegExp(params.user);
        var contest = this.get_contest();
        return _.chain(contest.runs).
            map(function (rid) { return CATS.App.runs[rid]; }).
            filter(function (r) {
                var user = CATS.App.users[r.user];
                return (
                    r.start_processing_time <= contest.finish_time &&
                    statuses[r.status] && problems[r.problem] &&
                    affiliation_regexp.test(user.some_affiliation()) && user_regexp.test(user.name));
            }).
            groupBy(self.group_by[params.group_by]);
    },

    place_data: function(params) {
        var tbl = CATS.App.result_tables[this.table];
        var contest = CATS.App.contests[tbl.contest];
        var duration = contest.compute_duration_minutes() + 1;

        var affiliation_regexp = new RegExp(params.affiliation);
        var user_regexp = new RegExp(params.user);

        var result = [];
        for(var period_idx = 1; period_idx * params.period <= duration; ++period_idx) {
            tbl.clean_score_board();
            tbl.filters.duration = { minutes: period_idx * params.period, type: 'history' };
            CATS.App.rules[contest.scoring].process(contest, tbl);
            result[period_idx] = _.filter(tbl.score_board, function (row) {
                var user = CATS.App.users[row.user];
                return affiliation_regexp.test(user.some_affiliation()) && user_regexp.test(user.name);
            });
        }
        return _.chain(result);
    },

    delete_series: function(seriesId) {
        var idx = _.findIndex(this.series, function (s) { return s.id === seriesId; });
        this.series.splice(idx, 1);
        this.series_params.splice(idx, 1);
    },

    aggregation: {
        sum: function(arr) { return arr.reduce(function(pv, cv) { return pv + cv; }, 0); },
        avg: function(arr) { return arr.reduce(function(pv, cv) { return pv + cv; }, 0) / arr.length; },
        min: _.min,
        max: _.max,
    },

    gen_submittions_per_problem_params: function () {
        var params = [];
        var contest = this.get_contest();
        _.each(contest.problems, function (id) {
            params.push({"period":10,"parameter":"run_cnt","aggregation":"sum","group_by":"status","problems":[id],"user":".*?","affiliation":".*?"});
        });
        return {"chart" : { params : params, chart_type: 'pie'}};
    }
});
<commit_msg>charts: fix line chart group by status<commit_after>CATS.Model.Chart = Classify(CATS.Model.Entity, {
    init: function (table) {
        this.$$parent();
        this.type = "chart";
        this.table = table;
        this.series = [];
        this.series_params = [];
        this.chart_type = "line";
        this.contests_url_param = null;
        this.colors = [
            'green',
            'black',
            'blue',
            'red',
            'orange',
            'yellow',
            'cyan',
            'magenta',
            'gray',
            'violet',
        ];
        this.series_id_generator = 0;
        var self = this;
        this.group_by = {
            time: function (r) { return Math.ceil(r.minutes_since_start() / _.last(self.series_params).period); },
            status: function (r) { return self.statuses_arr.indexOf(r.status); },
        };

        this.group_by_xaxes_value = {
            time: function (idx) { return idx * _.last(self.series_params).period; },
            status: function (idx) { return idx; },
        };
    },

    statuses: {
        OK: 'accepted',
        WA: 'wrong_answer',
        PE: 'presentation_error',
        TL: 'time_limit_exceeded',
        RE: 'runtime_error',
        CE: 'compilation_error',
        SV: 'security_violation',
        ML: 'memory_limit_exceeded',
        IS: 'ignore_submit',
        IL: 'idleness_limit_exceeded',
    },

    statuses_arr: [
        'accepted',
        'wrong_answer',
        'presentation_error',
        'time_limit_exceeded',
        'runtime_error',
        'compilation_error',
        'security_violation',
        'memory_limit_exceeded',
        'ignore_submit',
        'idleness_limit_exceeded',
    ],

    statuses_color: {
        accepted: 'green',
        wrong_answer: 'black',
        presentation_error: 'blue',
        time_limit_exceeded: 'red',
        runtime_error: 'orange',
        compilation_error: 'yellow',
        security_violation: 'cyan',
        memory_limit_exceeded: 'magenta',
        ignore_submit: 'gray',
        idleness_limit_exceeded: 'violet',
    },

    get_contest: function () {
        var t = CATS.App.result_tables[this.table];
        return CATS.App.contests[t.contest];
    },

    params_pack: function (p) {
        var result = _.clone(p);
        if (result.statuses.length === _.keys(this.statuses).length)
            delete result.statuses;
        if (result.problems.length === this.get_contest().problems.length)
            delete result.problems;
        return result;
    },

    params_unpack: function (p) {
        var result = _.clone(p);
        result.statuses = p.statuses ? p.statuses : _.keys(this.statuses);
        result.problems = p.problems ? p.problems : this.get_contest().problems;
        return result;
    },

    settings: function(settings) {
        var self = this;
        if (settings != undefined) {
            this.series = [];
            this.series_params = [];
            this.series = [];
            this.chart_type = settings.chart_type;
            _.each(settings.params, function (v) { self.add_new_series(self.params_unpack(v)); });
        }
        return {chart_type: this.chart_type, params: _.map(this.series_params, function (v) { return self.params_pack(v); })};
    },

    parameter_yaxes: {
        run_cnt: 1,
        points: 2,
        place: 3,
    },

    parameter_xaxes: {
        time: 1,
        status: 2,
    },

    add_new_series: function(params) {
        this.series_params.push(params);
        this.series.push({
            label: params.parameter,
            data: this.aggregate(
                params.parameter === 'place' ? this.place_data(params) : this.simple_data(params), params),
            color: params.color != undefined ? params.color : this.colors[this.series.length % this.colors.length],
            xaxis: this.parameter_xaxes[params.group_by],
            yaxis: this.parameter_yaxes[params.parameter],
            id: ++this.series_id_generator,
        });
    },

    series_pie_format: function() {
        var pies = [];
        var self = this;
        var series_idx = 0;
        _.each(this.series, function (s) {
            var pie = [];
            _.each(s.data, function (data) {
                pie.push({label: self.series_params[series_idx].group_by == "status" ? self.statuses_arr[data[0]] : data[0], data: data[1]})
            });
            pies.push(pie);
            series_idx++;
        });

        return pies;
    },

    element_to_values: {
        run_cnt: function (runs) { return _.map(runs, function () { return 1; }); },
        points: function (runs) { return _.map(runs, function (r) { return r.points; }); },
        place: function (sb_rows) { return _.map(sb_rows, function (r) { return r.place; }); },
    },

    aggregate: function(chain, params) {
        var self = this;
        return chain.map(function(element, period_idx) {
            return [ self.group_by_xaxes_value[params.group_by](period_idx) ,
                self.aggregation[params.aggregation](self.element_to_values[params.parameter](element)) ];
        }).value();
    },

    simple_data: function(params) {
        var self = this;
        var statuses = _.countBy(params.statuses, function (s) { return self.statuses[s]; });
        var problems = _.countBy(params.problems, _.identity);
        var affiliation_regexp = new RegExp(params.affiliation);
        var user_regexp = new RegExp(params.user);
        var contest = this.get_contest();
        return _.chain(contest.runs).
            map(function (rid) { return CATS.App.runs[rid]; }).
            filter(function (r) {
                var user = CATS.App.users[r.user];
                return (
                    r.start_processing_time <= contest.finish_time &&
                    statuses[r.status] && problems[r.problem] &&
                    affiliation_regexp.test(user.some_affiliation()) && user_regexp.test(user.name));
            }).
            groupBy(self.group_by[params.group_by]);
    },

    place_data: function(params) {
        var tbl = CATS.App.result_tables[this.table];
        var contest = CATS.App.contests[tbl.contest];
        var duration = contest.compute_duration_minutes() + 1;

        var affiliation_regexp = new RegExp(params.affiliation);
        var user_regexp = new RegExp(params.user);

        var result = [];
        for(var period_idx = 1; period_idx * params.period <= duration; ++period_idx) {
            tbl.clean_score_board();
            tbl.filters.duration = { minutes: period_idx * params.period, type: 'history' };
            CATS.App.rules[contest.scoring].process(contest, tbl);
            result[period_idx] = _.filter(tbl.score_board, function (row) {
                var user = CATS.App.users[row.user];
                return affiliation_regexp.test(user.some_affiliation()) && user_regexp.test(user.name);
            });
        }
        return _.chain(result);
    },

    delete_series: function(seriesId) {
        var idx = _.findIndex(this.series, function (s) { return s.id === seriesId; });
        this.series.splice(idx, 1);
        this.series_params.splice(idx, 1);
    },

    aggregation: {
        sum: function(arr) { return arr.reduce(function(pv, cv) { return pv + cv; }, 0); },
        avg: function(arr) { return arr.reduce(function(pv, cv) { return pv + cv; }, 0) / arr.length; },
        min: _.min,
        max: _.max,
    },

    gen_submittions_per_problem_params: function () {
        var params = [];
        var contest = this.get_contest();
        _.each(contest.problems, function (id) {
            params.push({"period":10,"parameter":"run_cnt","aggregation":"sum","group_by":"status","problems":[id],"user":".*?","affiliation":".*?"});
        });
        return {"chart" : { params : params, chart_type: 'pie'}};
    }
});
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';

/*
 * A controlled input that calls `valueChanged` on changes.
 */
export default class InputControl extends Component {
    constructor() {
        super()
        this.state = {
            value: ''
        }
    }

    handleChange(value) {
        this.setState({value});
        this.props.valueChanged({value});
    }

    render() {
        return (
            <input
                value={this.state.value}
                onChange={e => this.handleChange(e.target.value)}
                {...this.props}
            />
        )
    }
}

InputControl.propTypes = {

    /**
     * Function that updates the state tree.
     */
    valueChanged: PropTypes.func
};

InputControl.defaultProps = {
    valueChanged: () => {}
};
<commit_msg>InputControl is only managed via props, not state<commit_after>import React, {Component, PropTypes} from 'react';

/*
 * An input that calls `valueChanged` on changes.
 */
export default class InputControl extends Component {
    handleChange(value) {
        this.props.valueChanged({value});
    }

    render() {
        return (
            <input
                value={this.props.value}
                onChange={e => this.handleChange(e.target.value)}
                {...this.props}
            />
        )
    }
}

InputControl.propTypes = {

    /**
     * Function that updates the state tree.
     */
    valueChanged: PropTypes.func,

    /**
     * Initial input value
     */
    value: PropTypes.string
};

InputControl.defaultProps = {
    valueChanged: () => {},
    value: ''
};
<|endoftext|>


<commit_before>import React from 'react';
import { reduxForm } from 'redux-form';
import { createPetition } from 'actions/PetitionActions';
import petitionValidator from 'form/petitionValidator';
import Fieldset from 'components/Fieldset';
import TextField from 'components/TextField';
import Button from 'components/Button';
import settings from 'settings';

export const FIELDS = [
  {
    name: 'title',
    element: 'input',
    label: settings.petitionFields.title.label,
    hint: settings.petitionFields.title.hint,
    html: {
      type: 'text',
      placeholder: settings.petitionFields.title.placeholder,
      required: true,
      minLength: 15,
      maxLength: 80
    }
  },
  {
    name: 'description',
    element: 'textarea',
    label: settings.petitionFields.description.label,
    hint: settings.petitionFields.description.hint,
    html: {
      placeholder: settings.petitionFields.description.placeholder,
      required: true,
      minLength: 5,
      maxLength: 500
    }
  },
  {
    name: 'suggested_solution',
    element: 'textarea',
    label: settings.petitionFields.suggested_solution.label,
    hint: settings.petitionFields.suggested_solution.hint,
    html: {
      placeholder: settings.petitionFields.suggested_solution.placeholder,
      maxLength: 500
    }
  }
];

const PetitionForm = React.createClass({
  render () {
    const { fields, handleSubmit, submitting } = this.props;

    const formIsValid = fields._meta.allValid;

    return (
      <form onSubmit={handleSubmit(createPetition)}>
        <Fieldset>
          {FIELDS.map(field => (
            <TextField
              key={field.name}
              config={field}
              helper={fields[field.name]}
            />
          ))}
        </Fieldset>
        <Fieldset modifier={'actions'}>
          <Button
            disabled={submitting || !formIsValid}
            modifier={'accent'}
            text={settings.petitionForm.saveButton}
          />
        </Fieldset>
      </form>
    );
  }
});

PetitionForm.propTypes = {
  fields: React.PropTypes.object.isRequired,
  handleSubmit: React.PropTypes.func.isRequired,
  resetForm: React.PropTypes.func.isRequired,
  submitting: React.PropTypes.bool.isRequired
};

export default reduxForm({
  form: 'simple',
  fields: FIELDS.map(field => field.name),
  validate: petitionValidator
})(PetitionForm);
<commit_msg>Make PetitionForm a pure stateless component<commit_after>import React from 'react';
import { reduxForm } from 'redux-form';
import { createPetition } from 'actions/PetitionActions';
import petitionValidator from 'form/petitionValidator';
import Fieldset from 'components/Fieldset';
import TextField from 'components/TextField';
import Button from 'components/Button';
import settings from 'settings';

export const FIELDS = [
  {
    name: 'title',
    element: 'input',
    label: settings.petitionFields.title.label,
    hint: settings.petitionFields.title.hint,
    html: {
      type: 'text',
      placeholder: settings.petitionFields.title.placeholder,
      required: true,
      minLength: 15,
      maxLength: 80
    }
  },
  {
    name: 'description',
    element: 'textarea',
    label: settings.petitionFields.description.label,
    hint: settings.petitionFields.description.hint,
    html: {
      placeholder: settings.petitionFields.description.placeholder,
      required: true,
      minLength: 5,
      maxLength: 500
    }
  },
  {
    name: 'suggested_solution',
    element: 'textarea',
    label: settings.petitionFields.suggested_solution.label,
    hint: settings.petitionFields.suggested_solution.hint,
    html: {
      placeholder: settings.petitionFields.suggested_solution.placeholder,
      maxLength: 500
    }
  }
];

const PetitionForm = ({ fields, handleSubmit, submitting }) => (
  <form onSubmit={handleSubmit(createPetition)}>
    <Fieldset>
      {FIELDS.map(field => (
        <TextField
          key={field.name}
          config={field}
          helper={fields[field.name]}
        />
      ))}
    </Fieldset>
    <Fieldset modifier={'actions'}>
      <Button
        disabled={submitting || !fields._meta.allValid}
        modifier={'accent'}
        text={settings.petitionForm.saveButton}
      />
    </Fieldset>
  </form>
);

PetitionForm.propTypes = {
  fields: React.PropTypes.object.isRequired,
  handleSubmit: React.PropTypes.func.isRequired,
  resetForm: React.PropTypes.func.isRequired,
  submitting: React.PropTypes.bool.isRequired
};

export default reduxForm({
  form: 'simple',
  fields: FIELDS.map(field => field.name),
  validate: petitionValidator
})(PetitionForm);
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Polyline, Tooltip, Marker } from 'react-leaflet';

import RichMarker from 'fm3/components/RichMarker';
import ElevationChartActivePoint from 'fm3/components/ElevationChartActivePoint';
import { routePlannerSetStart, routePlannerSetFinish, routePlannerAddMidpoint, routePlannerSetMidpoint, routePlannerRemoveMidpoint, routePlannerSetActiveAlternativeIndex }
  from 'fm3/actions/routePlannerActions';
import { toastsAdd } from 'fm3/actions/toastsActions';

import * as FmPropTypes from 'fm3/propTypes';

const nf = Intl.NumberFormat('sk', { minimumFractionDigits: 1, maximumFractionDigits: 1 });

class RoutePlannerResult extends React.Component {
  static propTypes = {
    start: FmPropTypes.point,
    finish: FmPropTypes.point,
    midpoints: FmPropTypes.points,
    alternatives: PropTypes.arrayOf(PropTypes.shape({
      duration: PropTypes.number,
      distance: PropTypes.number,
      itinerary: PropTypes.arrayOf(PropTypes.shape({
        lat: PropTypes.number.isRequired,
        lon: PropTypes.number.isRequired,
        desc: PropTypes.string.isRequired,
        km: PropTypes.number.isRequired,
        mode: PropTypes.string.isRequired,
        shapePoints: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number.isRequired).isRequired).isRequired,
      }).isRequired).isRequired,
    }).isRequired),
    onStartSet: PropTypes.func.isRequired,
    onFinishSet: PropTypes.func.isRequired,
    onMidpointSet: PropTypes.func.isRequired,
    onAddMidpoint: PropTypes.func.isRequired,
    onRemoveMidpoint: PropTypes.func.isRequired,
    activeAlternativeIndex: PropTypes.number.isRequired,
    onAlternativeChange: PropTypes.func.isRequired,
    transportType: PropTypes.string,
    timestamp: PropTypes.number,
  }

  state = {
    lat: null,
    lon: null,
  }

  componentWillUnmount() {
    if (this.t) {
      clearTimeout(this.t);
    }
  }

  bringToFront = (ele) => {
    if (ele) {
      ele.leafletElement.bringToFront();
    }
  }

  handleRouteMarkerDragEnd(movedPointType, position, event) {
    this.dragging = false;

    const { lat, lng: lon } = event.target.getLatLng();

    switch (movedPointType) {
      case 'start':
        this.props.onStartSet({ lat, lon });
        break;
      case 'finish':
        this.props.onFinishSet({ lat, lon });
        break;
      case 'midpoint':
        this.props.onMidpointSet(position, { lat, lon });
        break;
      default:
        throw new Error('unknown pointType');
    }
  }

  handleMidpointClick(position) {
    this.props.onRemoveMidpoint(position);
  }

  handleEndPointClick = () => {
    // just to prevent click propagation to map
  }

  handlePolyMouseMove = (e, segment, alt) => {
    if (this.dragging) {
      return;
    }
    if (this.t) {
      clearTimeout(this.t);
      this.t = null;
    }
    this.setState({
      lat: e.latlng.lat,
      lon: e.latlng.lng,
      segment,
      alt,
    });
  }

  handlePolyMouseOut = () => {
    if (this.dragging) {
      return;
    }
    this.resetOnTimeout();
  }

  handleFutureMouseOver = () => {
    if (this.dragging) {
      return;
    }
    if (this.t) {
      clearTimeout(this.t);
      this.t = null;
    }
  }

  handleFutureMouseOut = () => {
    if (this.dragging) {
      return;
    }
    this.resetOnTimeout();
  }

  resetOnTimeout() {
    if (this.t) {
      clearTimeout(this.t);
    }
    this.t = setTimeout(() => {
      this.setState({
        lat: null,
        lon: null,
      });
    }, 200);
  }

  handleDragStart = () => {
    if (this.t) {
      clearTimeout(this.t);
    }
    this.dragging = true;
  }

  handleFutureDragEnd = (e) => {
    this.dragging = false;
    this.setState({
      lat: null,
      lon: null,
    });

    this.props.onAddMidpoint(this.state.segment, {
      lat: e.target.getLatLng().lat,
      lon: e.target.getLatLng().lng,
    });
  }

  handleFutureClick = () => {
    this.props.onAlternativeChange(this.state.alt);
  }

  render() {
    const { start, midpoints, finish, activeAlternativeIndex, onAlternativeChange, transportType, timestamp, alternatives } = this.props;
    const Icon = L.divIcon;
    const circularIcon = new Icon({ // CircleMarker is not draggable
      iconSize: [14, 14],
      iconAnchor: [7, 7],
      html: '

',
    });

    const { distance, duration, summary0 } = alternatives.find((_, alt) => alt === activeAlternativeIndex) || {};

    return (
      <React.Fragment>
        {start &&
          <RichMarker
            faIcon="play"
            zIndexOffset={10}
            faIconLeftPadding="2px"
            color="#409a40"
            draggable
            onDragStart={this.handleDragStart}
            onDragEnd={e => this.handleRouteMarkerDragEnd('start', null, e)}
            position={L.latLng(start.lat, start.lon)}
            onClick={this.handleEndPointClick}
          />
        }
        {this.state.lat !== null && this.state.lon !== null &&
          <Marker
            draggable
            icon={circularIcon}
            onDragStart={this.handleDragStart}
            onDragEnd={this.handleFutureDragEnd}
            onMouseOver={this.handleFutureMouseOver}
            onMouseOut={this.handleFutureMouseOut}
            position={L.latLng(this.state.lat, this.state.lon)}
            onClick={this.handleFutureClick}
          />
        }
        {
          midpoints.map(({ lat, lon }, i) => (
            <RichMarker
              draggable
              onDragStart={this.handleDragStart}
              onDragEnd={e => this.handleRouteMarkerDragEnd('midpoint', i, e)}
              onClick={() => this.handleMidpointClick(i)}
              key={`c4ReUQrKT7-${i}`}
              zIndexOffset={9}
              label={i + 1}
              position={L.latLng(lat, lon)}
            />
          ))
        }
        {finish &&
          <RichMarker
            faIcon="stop"
            color="#d9534f"
            zIndexOffset={10}
            draggable
            onDragStart={this.handleDragStart}
            onDragEnd={e => this.handleRouteMarkerDragEnd('finish', null, e)}
            position={L.latLng(finish.lat, finish.lon)}
            onClick={this.handleEndPointClick}
          >
            {
              transportType === 'imhd' && summary0 ?
                <Tooltip direction="top" offset={[0, -36]} permanent>

                </Tooltip>
              : distance ?
                <Tooltip direction="top" offset={[0, -36]} permanent>

Vzdialenosť: {nf.format(distance)} km

Čas: {Math.floor(duration / 60)} h {Math.round(duration % 60)} m

                </Tooltip>
              : null
            }
          </RichMarker>
        }
        {
          (transportType !== 'imhd' ? alternatives : alternatives.map(addMissingSegments))
          .map((x, index) => ({ ...x, alt: index, index: index === activeAlternativeIndex ? 1000 : index }))
          .sort((a, b) => a.index - b.index).map(({ itinerary, alt }) => (
            <React.Fragment key={`alt-${timestamp}-${alt}`}>
              {
                alt === activeAlternativeIndex && transportType === 'imhd' && itinerary.map((routeSlice, i) => (
                  <Marker
                    key={`mark-${i}`}
                    icon={circularIcon}
                    position={routeSlice.shapePoints[0]}
                  >
                    <Tooltip direction="right" permanent>

{routeSlice.desc}

                    </Tooltip>
                  </Marker>
                ))
              }
              {
                itinerary.map((routeSlice, i) => (
                  <Polyline
                    key={`slice-${i}`}
                    ref={ele => this.bringToFront(ele)}
                    positions={routeSlice.shapePoints}
                    weight={10}
                    color="#fff"
                    onClick={() => onAlternativeChange(alt)}
                    onMouseMove={transportType === 'imhd' ? undefined : e => this.handlePolyMouseMove(e, i, alt)}
                    onMouseOut={this.handlePolyMouseOut}
                  />
                ))
              }
              {
                itinerary.map((routeSlice, i) => (
                  <Polyline
                    key={`slice-${timestamp}-${alt}-${i}`}
                    ref={ele => this.bringToFront(ele)}
                    positions={routeSlice.shapePoints}
                    weight={6}
                    color={
                      alt !== activeAlternativeIndex ? '#868e96'
                        : transportType !== 'imhd' && routeSlice.legIndex % 2 ? 'hsl(211, 100%, 66%)'
                        : 'hsl(211, 100%, 50%)'
                    }
                    opacity={/* alt === activeAlternativeIndex ? 1 : 0.5 */ 1}
                    dashArray={['foot', 'pushing bike', 'ferry'].includes(routeSlice.mode) ? '0, 10' : undefined}
                    interactive={false}
                  />
                ))
              }
            </React.Fragment>
          ))
        }
        <ElevationChartActivePoint />
      </React.Fragment>
    );
  }
}

export default connect(
  state => ({
    start: state.routePlanner.start,
    finish: state.routePlanner.finish,
    midpoints: state.routePlanner.midpoints,
    alternatives: state.routePlanner.alternatives,
    activeAlternativeIndex: state.routePlanner.activeAlternativeIndex,
    transportType: state.routePlanner.effectiveTransportType,
    timestamp: state.routePlanner.timestamp,
  }),
  dispatch => ({
    onStartSet(start) {
      dispatch(routePlannerSetStart(start));
    },
    onFinishSet(finish) {
      dispatch(routePlannerSetFinish(finish));
    },
    onAddMidpoint(position, midpoint) {
      dispatch(routePlannerAddMidpoint(midpoint, position));
    },
    onMidpointSet(position, midpoint) {
      dispatch(routePlannerSetMidpoint(position, midpoint));
    },
    onRemoveMidpoint(position) {
      dispatch(toastsAdd({
        collapseKey: 'routePlanner.removeMidpoint',
        message: 'Odstrániť zastávku?',
        style: 'warning',
        cancelType: 'SET_TOOL',
        actions: [
          { name: 'Áno', action: routePlannerRemoveMidpoint(position), style: 'danger' },
          { name: 'Nie' },
        ],
      }));
    },
    onAlternativeChange(index) {
      dispatch(routePlannerSetActiveAlternativeIndex(index));
    },
  }),
)(RoutePlannerResult);

// TODO do it in logic so that GPX export is the same
// adds missing foot segments (between bus-stop and footway)
function addMissingSegments(alt) {
  const routeSlices = [];
  for (let i = 0; i < alt.itinerary.length; i += 1) {
    const slice = alt.itinerary[i];
    const prevSlice = alt.itinerary[i - 1];
    const nextSlice = alt.itinerary[i + 1];

    const prevSliceLastShapePoint = prevSlice ? prevSlice.shapePoints[prevSlice.shapePoints.length - 1] : null;
    const firstShapePoint = slice.shapePoints[0];

    const lastShapePoint = slice.shapePoints[slice.shapePoints.length - 1];
    const nextSliceFirstShapePoint = nextSlice ? nextSlice.shapePoints[0] : null;

    const shapePoints = [...slice.shapePoints];

    if (slice.mode === 'foot') {
      if (prevSliceLastShapePoint
        && (Math.abs(prevSliceLastShapePoint[0] - firstShapePoint[0]) > 0.0000001 || Math.abs(prevSliceLastShapePoint[1] - firstShapePoint[1]) > 0.0000001)
      ) {
        shapePoints.unshift(prevSliceLastShapePoint);
      }

      if (nextSliceFirstShapePoint
        && (Math.abs(nextSliceFirstShapePoint[0] - lastShapePoint[0]) > 0.0000001 || Math.abs(nextSliceFirstShapePoint[1] - lastShapePoint[1]) > 0.0000001)
      ) {
        shapePoints.push(nextSliceFirstShapePoint);
      }
    }

    routeSlices.push({
      ...slice,
      shapePoints,
    });
  }

  return { ...alt, itinerary: routeSlices };
}
<commit_msg>fixed incorrectly adding midpoints in route planner<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Polyline, Tooltip, Marker } from 'react-leaflet';

import RichMarker from 'fm3/components/RichMarker';
import ElevationChartActivePoint from 'fm3/components/ElevationChartActivePoint';
import { routePlannerSetStart, routePlannerSetFinish, routePlannerAddMidpoint, routePlannerSetMidpoint, routePlannerRemoveMidpoint, routePlannerSetActiveAlternativeIndex }
  from 'fm3/actions/routePlannerActions';
import { toastsAdd } from 'fm3/actions/toastsActions';

import * as FmPropTypes from 'fm3/propTypes';

const nf = Intl.NumberFormat('sk', { minimumFractionDigits: 1, maximumFractionDigits: 1 });

class RoutePlannerResult extends React.Component {
  static propTypes = {
    start: FmPropTypes.point,
    finish: FmPropTypes.point,
    midpoints: FmPropTypes.points,
    alternatives: PropTypes.arrayOf(PropTypes.shape({
      duration: PropTypes.number,
      distance: PropTypes.number,
      itinerary: PropTypes.arrayOf(PropTypes.shape({
        lat: PropTypes.number.isRequired,
        lon: PropTypes.number.isRequired,
        desc: PropTypes.string.isRequired,
        km: PropTypes.number.isRequired,
        mode: PropTypes.string.isRequired,
        shapePoints: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number.isRequired).isRequired).isRequired,
      }).isRequired).isRequired,
    }).isRequired),
    onStartSet: PropTypes.func.isRequired,
    onFinishSet: PropTypes.func.isRequired,
    onMidpointSet: PropTypes.func.isRequired,
    onAddMidpoint: PropTypes.func.isRequired,
    onRemoveMidpoint: PropTypes.func.isRequired,
    activeAlternativeIndex: PropTypes.number.isRequired,
    onAlternativeChange: PropTypes.func.isRequired,
    transportType: PropTypes.string,
    timestamp: PropTypes.number,
  }

  state = {
    lat: null,
    lon: null,
  }

  componentWillUnmount() {
    if (this.t) {
      clearTimeout(this.t);
    }
  }

  bringToFront = (ele) => {
    if (ele) {
      ele.leafletElement.bringToFront();
    }
  }

  handleRouteMarkerDragEnd(movedPointType, position, event) {
    this.dragging = false;

    const { lat, lng: lon } = event.target.getLatLng();

    switch (movedPointType) {
      case 'start':
        this.props.onStartSet({ lat, lon });
        break;
      case 'finish':
        this.props.onFinishSet({ lat, lon });
        break;
      case 'midpoint':
        this.props.onMidpointSet(position, { lat, lon });
        break;
      default:
        throw new Error('unknown pointType');
    }
  }

  handleMidpointClick(position) {
    this.props.onRemoveMidpoint(position);
  }

  handleEndPointClick = () => {
    // just to prevent click propagation to map
  }

  handlePolyMouseMove = (e, segment, alt) => {
    if (this.dragging) {
      return;
    }
    if (this.t) {
      clearTimeout(this.t);
      this.t = null;
    }
    this.setState({
      lat: e.latlng.lat,
      lon: e.latlng.lng,
      segment,
      alt,
    });
  }

  handlePolyMouseOut = () => {
    if (this.dragging) {
      return;
    }
    this.resetOnTimeout();
  }

  handleFutureMouseOver = () => {
    if (this.dragging) {
      return;
    }
    if (this.t) {
      clearTimeout(this.t);
      this.t = null;
    }
  }

  handleFutureMouseOut = () => {
    if (this.dragging) {
      return;
    }
    this.resetOnTimeout();
  }

  resetOnTimeout() {
    if (this.t) {
      clearTimeout(this.t);
    }
    this.t = setTimeout(() => {
      this.setState({
        lat: null,
        lon: null,
      });
    }, 200);
  }

  handleDragStart = () => {
    if (this.t) {
      clearTimeout(this.t);
    }
    this.dragging = true;
  }

  handleFutureDragEnd = (e) => {
    this.dragging = false;
    this.setState({
      lat: null,
      lon: null,
    });

    this.props.onAddMidpoint(this.state.segment, {
      lat: e.target.getLatLng().lat,
      lon: e.target.getLatLng().lng,
    });
  }

  handleFutureClick = () => {
    this.props.onAlternativeChange(this.state.alt);
  }

  render() {
    const { start, midpoints, finish, activeAlternativeIndex, onAlternativeChange, transportType, timestamp, alternatives } = this.props;
    const Icon = L.divIcon;
    const circularIcon = new Icon({ // CircleMarker is not draggable
      iconSize: [14, 14],
      iconAnchor: [7, 7],
      html: '

',
    });

    const { distance, duration, summary0 } = alternatives.find((_, alt) => alt === activeAlternativeIndex) || {};

    return (
      <React.Fragment>
        {start &&
          <RichMarker
            faIcon="play"
            zIndexOffset={10}
            faIconLeftPadding="2px"
            color="#409a40"
            draggable
            onDragStart={this.handleDragStart}
            onDragEnd={e => this.handleRouteMarkerDragEnd('start', null, e)}
            position={L.latLng(start.lat, start.lon)}
            onClick={this.handleEndPointClick}
          />
        }
        {this.state.lat !== null && this.state.lon !== null &&
          <Marker
            draggable
            icon={circularIcon}
            onDragStart={this.handleDragStart}
            onDragEnd={this.handleFutureDragEnd}
            onMouseOver={this.handleFutureMouseOver}
            onMouseOut={this.handleFutureMouseOut}
            position={L.latLng(this.state.lat, this.state.lon)}
            onClick={this.handleFutureClick}
          />
        }
        {
          midpoints.map(({ lat, lon }, i) => (
            <RichMarker
              draggable
              onDragStart={this.handleDragStart}
              onDragEnd={e => this.handleRouteMarkerDragEnd('midpoint', i, e)}
              onClick={() => this.handleMidpointClick(i)}
              key={`c4ReUQrKT7-${i}`}
              zIndexOffset={9}
              label={i + 1}
              position={L.latLng(lat, lon)}
            />
          ))
        }
        {finish &&
          <RichMarker
            faIcon="stop"
            color="#d9534f"
            zIndexOffset={10}
            draggable
            onDragStart={this.handleDragStart}
            onDragEnd={e => this.handleRouteMarkerDragEnd('finish', null, e)}
            position={L.latLng(finish.lat, finish.lon)}
            onClick={this.handleEndPointClick}
          >
            {
              transportType === 'imhd' && summary0 ?
                <Tooltip direction="top" offset={[0, -36]} permanent>

                </Tooltip>
              : distance ?
                <Tooltip direction="top" offset={[0, -36]} permanent>

Vzdialenosť: {nf.format(distance)} km

Čas: {Math.floor(duration / 60)} h {Math.round(duration % 60)} m

                </Tooltip>
              : null
            }
          </RichMarker>
        }
        {
          (transportType !== 'imhd' ? alternatives : alternatives.map(addMissingSegments))
          .map((x, index) => ({ ...x, alt: index, index: index === activeAlternativeIndex ? 1000 : index }))
          .sort((a, b) => a.index - b.index).map(({ itinerary, alt }) => (
            <React.Fragment key={`alt-${timestamp}-${alt}`}>
              {
                alt === activeAlternativeIndex && transportType === 'imhd' && itinerary.map((routeSlice, i) => (
                  <Marker
                    key={`mark-${i}`}
                    icon={circularIcon}
                    position={routeSlice.shapePoints[0]}
                  >
                    <Tooltip direction="right" permanent>

{routeSlice.desc}

                    </Tooltip>
                  </Marker>
                ))
              }
              {
                itinerary.map((routeSlice, i) => (
                  <Polyline
                    key={`slice-${i}`}
                    ref={ele => this.bringToFront(ele)}
                    positions={routeSlice.shapePoints}
                    weight={10}
                    color="#fff"
                    onClick={() => onAlternativeChange(alt)}
                    onMouseMove={transportType === 'imhd' ? undefined : e => this.handlePolyMouseMove(e, routeSlice.legIndex, alt)}
                    onMouseOut={this.handlePolyMouseOut}
                  />
                ))
              }
              {
                itinerary.map((routeSlice, i) => (
                  <Polyline
                    key={`slice-${timestamp}-${alt}-${i}`}
                    ref={ele => this.bringToFront(ele)}
                    positions={routeSlice.shapePoints}
                    weight={6}
                    color={
                      alt !== activeAlternativeIndex ? '#868e96'
                        : transportType !== 'imhd' && routeSlice.legIndex % 2 ? 'hsl(211, 100%, 66%)'
                        : 'hsl(211, 100%, 50%)'
                    }
                    opacity={/* alt === activeAlternativeIndex ? 1 : 0.5 */ 1}
                    dashArray={['foot', 'pushing bike', 'ferry'].includes(routeSlice.mode) ? '0, 10' : undefined}
                    interactive={false}
                  />
                ))
              }
            </React.Fragment>
          ))
        }
        <ElevationChartActivePoint />
      </React.Fragment>
    );
  }
}

export default connect(
  state => ({
    start: state.routePlanner.start,
    finish: state.routePlanner.finish,
    midpoints: state.routePlanner.midpoints,
    alternatives: state.routePlanner.alternatives,
    activeAlternativeIndex: state.routePlanner.activeAlternativeIndex,
    transportType: state.routePlanner.effectiveTransportType,
    timestamp: state.routePlanner.timestamp,
  }),
  dispatch => ({
    onStartSet(start) {
      dispatch(routePlannerSetStart(start));
    },
    onFinishSet(finish) {
      dispatch(routePlannerSetFinish(finish));
    },
    onAddMidpoint(position, midpoint) {
      dispatch(routePlannerAddMidpoint(midpoint, position));
    },
    onMidpointSet(position, midpoint) {
      dispatch(routePlannerSetMidpoint(position, midpoint));
    },
    onRemoveMidpoint(position) {
      dispatch(toastsAdd({
        collapseKey: 'routePlanner.removeMidpoint',
        message: 'Odstrániť zastávku?',
        style: 'warning',
        cancelType: 'SET_TOOL',
        actions: [
          { name: 'Áno', action: routePlannerRemoveMidpoint(position), style: 'danger' },
          { name: 'Nie' },
        ],
      }));
    },
    onAlternativeChange(index) {
      dispatch(routePlannerSetActiveAlternativeIndex(index));
    },
  }),
)(RoutePlannerResult);

// TODO do it in logic so that GPX export is the same
// adds missing foot segments (between bus-stop and footway)
function addMissingSegments(alt) {
  const routeSlices = [];
  for (let i = 0; i < alt.itinerary.length; i += 1) {
    const slice = alt.itinerary[i];
    const prevSlice = alt.itinerary[i - 1];
    const nextSlice = alt.itinerary[i + 1];

    const prevSliceLastShapePoint = prevSlice ? prevSlice.shapePoints[prevSlice.shapePoints.length - 1] : null;
    const firstShapePoint = slice.shapePoints[0];

    const lastShapePoint = slice.shapePoints[slice.shapePoints.length - 1];
    const nextSliceFirstShapePoint = nextSlice ? nextSlice.shapePoints[0] : null;

    const shapePoints = [...slice.shapePoints];

    if (slice.mode === 'foot') {
      if (prevSliceLastShapePoint
        && (Math.abs(prevSliceLastShapePoint[0] - firstShapePoint[0]) > 0.0000001 || Math.abs(prevSliceLastShapePoint[1] - firstShapePoint[1]) > 0.0000001)
      ) {
        shapePoints.unshift(prevSliceLastShapePoint);
      }

      if (nextSliceFirstShapePoint
        && (Math.abs(nextSliceFirstShapePoint[0] - lastShapePoint[0]) > 0.0000001 || Math.abs(nextSliceFirstShapePoint[1] - lastShapePoint[1]) > 0.0000001)
      ) {
        shapePoints.push(nextSliceFirstShapePoint);
      }
    }

    routeSlices.push({
      ...slice,
      shapePoints,
    });
  }

  return { ...alt, itinerary: routeSlices };
}
<|endoftext|>


<commit_before>import React from 'react';
import { settings } from 'carbon-components';

const { prefix } = settings;

export default class TabsSkeleton extends React.Component {
  render() {
    const tab = (
      <li className={`${prefix}--tabs__nav-item`}>
        <div className={`${prefix}--tabs__nav-link`}>

    );
    return (
      <nav className={`${prefix}--tabs ${prefix}--skeleton`}>
        <div className={`${prefix}--tabs-trigger`}>
          <div className={`${prefix}--tabs-trigger-text`}>

          <svg width="10" height="5" viewBox="0 0 10 5" fill-rule="evenodd">
            <path d="M10 0L5 5 0 0z" />
          </svg>

        <ul className={`${prefix}--tabs__nav ${prefix}--tabs__nav--hidden`}>
          <li
            className={`${prefix}--tabs__nav-item ${prefix}--tabs__nav-item--selected`}>
            <div className={`${prefix}--tabs__nav-link`}>

          {tab}
          {tab}
          {tab}

    );
  }
}
<commit_msg>fix(TabsSkeleton): remove a console warning error of fill-rule in TabsSkeleton (#1594)<commit_after>import React from 'react';
import { settings } from 'carbon-components';

const { prefix } = settings;

export default class TabsSkeleton extends React.Component {
  render() {
    const tab = (
      <li className={`${prefix}--tabs__nav-item`}>
        <div className={`${prefix}--tabs__nav-link`}>

    );
    return (
      <nav className={`${prefix}--tabs ${prefix}--skeleton`}>
        <div className={`${prefix}--tabs-trigger`}>
          <div className={`${prefix}--tabs-trigger-text`}>

          <svg width="10" height="5" viewBox="0 0 10 5" fillRule="evenodd">
            <path d="M10 0L5 5 0 0z" />
          </svg>

        <ul className={`${prefix}--tabs__nav ${prefix}--tabs__nav--hidden`}>
          <li
            className={`${prefix}--tabs__nav-item ${prefix}--tabs__nav-item--selected`}>
            <div className={`${prefix}--tabs__nav-link`}>

          {tab}
          {tab}
          {tab}

    );
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
    Text,
    View,
    Image,
    ActivityIndicator,
    AsyncStorage,
    LayoutAnimation,
    UIManager,
    ScrollView
} from 'react-native';
import { connect } from 'react-redux';
import moment from 'moment';
import Orientation from 'react-native-orientation';
import Swiper from 'react-native-swiper'
import { IconButton } from './common';
import { fetchWeek, selectDay } from '../actions';
import i18n from 'react-native-i18n';
import bundledTranslations from '../translations';
import DayView from './DayViewComponent';
import DaySwitcher from './DaySwitcherComponent';
import WeekView from './WeekViewComponent';

class Timetable extends Component {
    componentWillMount() {
        setTimeout(() => { this.props.fetchWeek(this.props.user, this.props.program, '17', this.props.semester); }, 1000);  // Using the 17th week of the year to get results from the API
        // TODO: Try to refactor the line above so one does not need the 'setTimeout()'

        let initialOrientation = Orientation.getInitialOrientation();
        if (initialOrientation === 'PORTRAIT') {
            this.setState({ orientation: initialOrientation });
        } else if (initialOrientation === 'LANDSCAPE') {
            this.setState({ orientation: initialOrientation });
        }

        Orientation.addOrientationListener(this._orientationDidChange);

        let today = moment().format('ddd');
        let hour = moment().format('H');
        if (today === "Sat" || today === "Sun") {  // If it's on a weekend, jump to the next Monday
            this.props.selectDay('Mon');
        } else if (hour >= 19 && today === "Fri") {  // If it's after 19:00 and a Friday, jump to the next Monday
            this.props.selectDay('Mon');
        } else if (hour >= 19 && today !== "Fri") {  // If it's after 19:00, jump to the following day of the week
            today = moment().add(1, 'day').format('ddd');
            this.props.selectDay(today);
        } else {
            this.props.selectDay(today);
        }
    }

    componentWillUpdate() {
        // Had to disable the animation in virtue of it causing the 'DayView' to freeze during the rendering
        // UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
        // LayoutAnimation.easeInEaseOut();
    }

    _orientationDidChange = (changedOrientation) => {
        if (changedOrientation === 'PORTRAIT') {
            this.setState({ orientation: changedOrientation });
        } else if (changedOrientation === 'LANDSCAPE') {
            this.setState({ orientation: changedOrientation });
        }
    };

    _onMomentumScrollEnd(e, state, context) {
        switch (state.index) {
            case 0:
                this.props.selectDay('Mon');
                break;
            case 1:
                this.props.selectDay('Tue');
                break;
            case 2:
                this.props.selectDay('Wed');
                break;
            case 3:
                this.props.selectDay('Thu');
                break;
            case 4:
                this.props.selectDay('Fri');
                break;
        }
    }

    renderTimetable() {
        if (this.props.loading) {
            return (
                <View style={styles.spinner}>
                    <ActivityIndicator size={'large'} />
                </View>
            );
        } else {
            if (this.props.week && this.props.slots) {
                if (this.state.orientation === 'PORTRAIT') {  // Render: DayView
                    let eventsList = JSON.parse(this.props.week)["events"];
                    let eventsMon = [];
                    let eventIndexMon = 0;
                    let eventsTue = [];
                    let eventIndexTue = 0;
                    let eventsWed = [];
                    let eventIndexWed = 0;
                    let eventsThu = [];
                    let eventIndexThu = 0;
                    let eventsFri = [];
                    let eventIndexFri = 0;

                    for (let event in eventsList) {
                        switch (eventsList[event]["day"]) {
                            case 'mon':
                                eventsMon.push(eventsList[event]);
                                eventsMon[eventIndexMon]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexMon += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'tue':
                                eventsTue.push(eventsList[event]);
                                eventsTue[eventIndexTue]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexTue += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'wed':
                                eventsWed.push(eventsList[event]);
                                eventsWed[eventIndexWed]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexWed += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'thu':
                                eventsThu.push(eventsList[event]);
                                eventsThu[eventIndexThu]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexThu += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'fri':
                                eventsFri.push(eventsList[event]);
                                eventsFri[eventIndexFri]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexFri += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                        }
                    }

                    let swiperIndex = -1;

                    switch (this.props.selectedDay) {
                        case 'mon':
                            swiperIndex = 0;
                            break;
                        case 'tue':
                            swiperIndex = 1;
                            break;
                        case 'wed':
                            swiperIndex = 2;
                            break;
                        case 'thu':
                            swiperIndex = 3;
                            break;
                        case 'fri':
                            swiperIndex = 4;
                            break;
                    }

                    let day = moment().day(this.props.selectedDay).week(this.props.currentWeek).format('ddd');
                    let date = moment().day(this.props.selectedDay).week(this.props.currentWeek).format('DD.MM.YYYY');

                    return (
                        <View
                            onLayout={(event) => { this.setState({swiperHeight: event.nativeEvent.layout.height}); }}  // Set the height of the 'View' for the 'Swiper'
                            style={styles.dayView}
                        >
                            <ScrollView>
                                <Swiper
                                    loop={false}
                                    showsPagination={false}
                                    index={swiperIndex}
                                    height={this.state.swiperHeight}
                                    onMomentumScrollEnd={this._onMomentumScrollEnd.bind(this)}
                                >
                                    <DayView events={eventsMon} />
                                    <DayView events={eventsTue} />
                                    <DayView events={eventsWed} />
                                    <DayView events={eventsThu} />
                                    <DayView events={eventsFri} />
                                </Swiper>
                            </ScrollView>
                            <DaySwitcher day={i18n.t(day) + ', ' + date} />
                        </View>
                    );
                } else if (this.state.orientation === 'LANDSCAPE') {  // Render: WeekView
                    return (
                        <ScrollView style={styles.weekView}>
                            <WeekView />
                        </ScrollView>
                    );
                }
            }
        }
    }

    findAnnotation() {
        let weeks = JSON.parse(this.props.masterdata)["timetable"]["weeks"]["kw" + this.props.currentWeek];
        if (weeks !== undefined) {
            return weeks["annotation"];
        }
    }

    render() {
        return (
            <View style={{ flex: 1 }}>
                <View style={styles.header}>
                    <IconButton onPress={() => {this.props.fetchWeek(this.props.user, this.props.program, (this.props.currentWeek - 1), this.props.semester); this.props.selectDay('Mon');}}>
                        <Image source={require('../assets/images/backwards.png')} style={styles.imageIconButton} />
                    </IconButton>
                    <View style={styles.headerView}>
                        <Text style={styles.headerText}>
                            {i18n.t('week_of_the_year')} {this.props.currentWeek}
                        </Text>
                        <Text>
                            {this.findAnnotation()}
                        </Text>
                    </View>
                    <IconButton onPress={() => {this.props.fetchWeek(this.props.user, this.props.program, (this.props.currentWeek + 1), this.props.semester); this.props.selectDay('Mon');}}>
                        <Image source={require('../assets/images/forward.png')} style={styles.imageIconButton} />
                    </IconButton>
                </View>
                {this.renderTimetable()}
            </View>
        );
    }
}

i18n.locale = 'de';
i18n.fallbacks = true;
i18n.translations = bundledTranslations;

const styles = {
    dayView: {
        flex: 1,
        flexDirection: "column"
    },
    weekView: {
        flex: 1
    },
    header: {
        flexDirection: "row",
        justifyContent: "space-between",
        alignItems: "center",
        backgroundColor: "#F4F4F4",
        elevation: 5,
        borderBottomWidth: 1,
        borderBottomColor: "#CCCCCC",
        height: 60,
        padding: 10
    },
    headerText: {
        fontSize: 20,
        fontWeight: "bold"
    },
    headerView: {
        flexDirection: "column",
        alignItems: "center"
    },
    spinner: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    },
    imageIconButton: {
        height: 20,
        width: 20,
        alignSelf: "flex-end"
    }
};

const mapStateToProps = state => {
    return {
        user: state.login.user,
        program: state.login.program,
        masterdata: state.timetable.masterdata,
        slots: state.timetable.timeslots,
        week: state.timetable.fetchedWeek,
        currentWeek: state.timetable.currentWeek,
        selectedDay: state.timetable.selectedDay,
        loading: state.timetable.loadingFetch,
        semester: state.settings.semester
    }
};

export default connect(mapStateToProps, { fetchWeek, selectDay })(Timetable);
<commit_msg>Passed the events for the 'currentWeek' to the 'WeekView'<commit_after>import React, { Component } from 'react';
import {
    Text,
    View,
    Image,
    ActivityIndicator,
    AsyncStorage,
    LayoutAnimation,
    UIManager,
    ScrollView
} from 'react-native';
import { connect } from 'react-redux';
import moment from 'moment';
import Orientation from 'react-native-orientation';
import Swiper from 'react-native-swiper'
import { IconButton } from './common';
import { fetchWeek, selectDay } from '../actions';
import i18n from 'react-native-i18n';
import bundledTranslations from '../translations';
import DayView from './DayViewComponent';
import DaySwitcher from './DaySwitcherComponent';
import WeekView from './WeekViewComponent';

class Timetable extends Component {
    componentWillMount() {
        setTimeout(() => { this.props.fetchWeek(this.props.user, this.props.program, '17', this.props.semester); }, 1000);  // Using the 17th week of the year to get results from the API
        // TODO: Try to refactor the line above so one does not need the 'setTimeout()'

        let initialOrientation = Orientation.getInitialOrientation();
        if (initialOrientation === 'PORTRAIT') {
            this.setState({ orientation: initialOrientation });
        } else if (initialOrientation === 'LANDSCAPE') {
            this.setState({ orientation: initialOrientation });
        }

        Orientation.addOrientationListener(this._orientationDidChange);

        let today = moment().format('ddd');
        let hour = moment().format('H');
        if (today === "Sat" || today === "Sun") {  // If it's on a weekend, jump to the next Monday
            this.props.selectDay('Mon');
        } else if (hour >= 19 && today === "Fri") {  // If it's after 19:00 and a Friday, jump to the next Monday
            this.props.selectDay('Mon');
        } else if (hour >= 19 && today !== "Fri") {  // If it's after 19:00, jump to the following day of the week
            today = moment().add(1, 'day').format('ddd');
            this.props.selectDay(today);
        } else {
            this.props.selectDay(today);
        }
    }

    componentWillUpdate() {
        // Had to disable the animation in virtue of it causing the 'DayView' to freeze during the rendering
        // UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
        // LayoutAnimation.easeInEaseOut();
    }

    _orientationDidChange = (changedOrientation) => {
        if (changedOrientation === 'PORTRAIT') {
            this.setState({ orientation: changedOrientation });
        } else if (changedOrientation === 'LANDSCAPE') {
            this.setState({ orientation: changedOrientation });
        }
    };

    _onMomentumScrollEnd(e, state, context) {
        switch (state.index) {
            case 0:
                this.props.selectDay('Mon');
                break;
            case 1:
                this.props.selectDay('Tue');
                break;
            case 2:
                this.props.selectDay('Wed');
                break;
            case 3:
                this.props.selectDay('Thu');
                break;
            case 4:
                this.props.selectDay('Fri');
                break;
        }
    }

    renderTimetable() {
        if (this.props.loading) {
            return (
                <View style={styles.spinner}>
                    <ActivityIndicator size={'large'} />
                </View>
            );
        } else {
            if (this.props.week && this.props.slots) {
                if (this.state.orientation === 'PORTRAIT') {  // Render: DayView
                    let eventsList = JSON.parse(this.props.week)["events"];
                    let eventsMon = [];
                    let eventIndexMon = 0;
                    let eventsTue = [];
                    let eventIndexTue = 0;
                    let eventsWed = [];
                    let eventIndexWed = 0;
                    let eventsThu = [];
                    let eventIndexThu = 0;
                    let eventsFri = [];
                    let eventIndexFri = 0;

                    for (let event in eventsList) {
                        switch (eventsList[event]["day"]) {
                            case 'mon':
                                eventsMon.push(eventsList[event]);
                                eventsMon[eventIndexMon]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexMon += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'tue':
                                eventsTue.push(eventsList[event]);
                                eventsTue[eventIndexTue]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexTue += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'wed':
                                eventsWed.push(eventsList[event]);
                                eventsWed[eventIndexWed]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexWed += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'thu':
                                eventsThu.push(eventsList[event]);
                                eventsThu[eventIndexThu]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexThu += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                            case 'fri':
                                eventsFri.push(eventsList[event]);
                                eventsFri[eventIndexFri]["slot"] = `${this.props.slots[eventsList[event]["start"]].start} - ${this.props.slots[eventsList[event]["end"]].end}`;
                                eventIndexFri += 1;  // Increase the actual index by one every iteration the if-statement passes
                                break;
                        }
                    }

                    let swiperIndex = -1;

                    switch (this.props.selectedDay) {
                        case 'mon':
                            swiperIndex = 0;
                            break;
                        case 'tue':
                            swiperIndex = 1;
                            break;
                        case 'wed':
                            swiperIndex = 2;
                            break;
                        case 'thu':
                            swiperIndex = 3;
                            break;
                        case 'fri':
                            swiperIndex = 4;
                            break;
                    }

                    let day = moment().day(this.props.selectedDay).week(this.props.currentWeek).format('ddd');
                    let date = moment().day(this.props.selectedDay).week(this.props.currentWeek).format('DD.MM.YYYY');

                    return (
                        <View
                            onLayout={(event) => { this.setState({swiperHeight: event.nativeEvent.layout.height}); }}  // Set the height of the 'View' for the 'Swiper'
                            style={styles.dayView}
                        >
                            <ScrollView>
                                <Swiper
                                    loop={false}
                                    showsPagination={false}
                                    index={swiperIndex}
                                    height={this.state.swiperHeight}
                                    onMomentumScrollEnd={this._onMomentumScrollEnd.bind(this)}
                                >
                                    <DayView events={eventsMon} />
                                    <DayView events={eventsTue} />
                                    <DayView events={eventsWed} />
                                    <DayView events={eventsThu} />
                                    <DayView events={eventsFri} />
                                </Swiper>
                            </ScrollView>
                            <DaySwitcher day={i18n.t(day) + ', ' + date} />
                        </View>
                    );
                } else if (this.state.orientation === 'LANDSCAPE') {  // Render: WeekView
                    let eventsWeek = JSON.parse(this.props.week)["events"];

                    return (
                        <ScrollView style={styles.weekView}>
                            <WeekView events={eventsWeek} />
                        </ScrollView>
                    );
                }
            }
        }
    }

    findAnnotation() {
        let weeks = JSON.parse(this.props.masterdata)["timetable"]["weeks"]["kw" + this.props.currentWeek];
        if (weeks !== undefined) {
            return weeks["annotation"];
        }
    }

    render() {
        return (
            <View style={{ flex: 1 }}>
                <View style={styles.header}>
                    <IconButton onPress={() => {this.props.fetchWeek(this.props.user, this.props.program, (this.props.currentWeek - 1), this.props.semester); this.props.selectDay('Mon');}}>
                        <Image source={require('../assets/images/backwards.png')} style={styles.imageIconButton} />
                    </IconButton>
                    <View style={styles.headerView}>
                        <Text style={styles.headerText}>
                            {i18n.t('week_of_the_year')} {this.props.currentWeek}
                        </Text>
                        <Text>
                            {this.findAnnotation()}
                        </Text>
                    </View>
                    <IconButton onPress={() => {this.props.fetchWeek(this.props.user, this.props.program, (this.props.currentWeek + 1), this.props.semester); this.props.selectDay('Mon');}}>
                        <Image source={require('../assets/images/forward.png')} style={styles.imageIconButton} />
                    </IconButton>
                </View>
                {this.renderTimetable()}
            </View>
        );
    }
}

i18n.locale = 'de';
i18n.fallbacks = true;
i18n.translations = bundledTranslations;

const styles = {
    dayView: {
        flex: 1,
        flexDirection: "column"
    },
    weekView: {
        flex: 1
    },
    header: {
        flexDirection: "row",
        justifyContent: "space-between",
        alignItems: "center",
        backgroundColor: "#F4F4F4",
        elevation: 5,
        borderBottomWidth: 1,
        borderBottomColor: "#CCCCCC",
        height: 60,
        padding: 10
    },
    headerText: {
        fontSize: 20,
        fontWeight: "bold"
    },
    headerView: {
        flexDirection: "column",
        alignItems: "center"
    },
    spinner: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    },
    imageIconButton: {
        height: 20,
        width: 20,
        alignSelf: "flex-end"
    }
};

const mapStateToProps = state => {
    return {
        user: state.login.user,
        program: state.login.program,
        masterdata: state.timetable.masterdata,
        slots: state.timetable.timeslots,
        week: state.timetable.fetchedWeek,
        currentWeek: state.timetable.currentWeek,
        selectedDay: state.timetable.selectedDay,
        loading: state.timetable.loadingFetch,
        semester: state.settings.semester
    }
};

export default connect(mapStateToProps, { fetchWeek, selectDay })(Timetable);
<|endoftext|>


<commit_before>import React, { cloneElement, PureComponent } from 'react';
import PropTypes from 'prop-types';
import Box from '../box';
import cx from 'classnames';
import theme from './theme.css';
import uiUtilities from '@teamleader/ui-utilities';

class AvatarStack extends PureComponent {
  render() {
    const { children, className, direction, displayMax, inverse, onOverflowClick, size, ...others } = this.props;

    const classNames = cx(
      theme['stack'],
      theme[direction],
      theme[`is-${size}`],
      inverse ? [theme['light']] : [theme['dark']],
      className,
    );

    const childrenToDisplay = displayMax > 0 ? children.slice(0, displayMax) : children;
    const overflowAmount = children.length - displayMax;
    return (
      <Box data-teamleader-ui="avatar-stack" className={classNames} {...others}>
        {overflowAmount > 0 && (

{`+${overflowAmount}`}

        )}
        {childrenToDisplay.map((child, index) => cloneElement(child, { key: index, ...child.props, size }))}
      </Box>
    );
  }
}

AvatarStack.propTypes = {
  /** The avatars to display in a stack. */
  children: PropTypes.node,
  /** A class name for the wrapper to give custom styles. */
  className: PropTypes.string,
  /** The direction in which the avatars will be rendered. */
  direction: PropTypes.oneOf(['horizontal', 'vertical']),
  /** The maximum amount of avatars to render. */
  displayMax: PropTypes.number,
  /** If true, component will be rendered in inverse mode. */
  inverse: PropTypes.bool,
  /** Callback function that is fired when the overflow circle is clicked. */
  onOverflowClick: PropTypes.func,
  /** The size of the avatar stack. */
  size: PropTypes.oneOf(['tiny', 'small', 'medium']),
};

AvatarStack.defaultProps = {
  direction: 'horizontal',
  displayMax: 0,
  inverse: false,
  size: 'medium',
};

export default AvatarStack;
<commit_msg>AvatarStack: add 'large' size to the prop types<commit_after>import React, { cloneElement, PureComponent } from 'react';
import PropTypes from 'prop-types';
import Box from '../box';
import cx from 'classnames';
import theme from './theme.css';
import uiUtilities from '@teamleader/ui-utilities';

class AvatarStack extends PureComponent {
  render() {
    const { children, className, direction, displayMax, inverse, onOverflowClick, size, ...others } = this.props;

    const classNames = cx(
      theme['stack'],
      theme[direction],
      theme[`is-${size}`],
      inverse ? [theme['light']] : [theme['dark']],
      className,
    );

    const childrenToDisplay = displayMax > 0 ? children.slice(0, displayMax) : children;
    const overflowAmount = children.length - displayMax;
    return (
      <Box data-teamleader-ui="avatar-stack" className={classNames} {...others}>
        {overflowAmount > 0 && (

{`+${overflowAmount}`}

        )}
        {childrenToDisplay.map((child, index) => cloneElement(child, { key: index, ...child.props, size }))}
      </Box>
    );
  }
}

AvatarStack.propTypes = {
  /** The avatars to display in a stack. */
  children: PropTypes.node,
  /** A class name for the wrapper to give custom styles. */
  className: PropTypes.string,
  /** The direction in which the avatars will be rendered. */
  direction: PropTypes.oneOf(['horizontal', 'vertical']),
  /** The maximum amount of avatars to render. */
  displayMax: PropTypes.number,
  /** If true, component will be rendered in inverse mode. */
  inverse: PropTypes.bool,
  /** Callback function that is fired when the overflow circle is clicked. */
  onOverflowClick: PropTypes.func,
  /** The size of the avatar stack. */
  size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large']),
};

AvatarStack.defaultProps = {
  direction: 'horizontal',
  displayMax: 0,
  inverse: false,
  size: 'medium',
};

export default AvatarStack;
<|endoftext|>


<commit_before>import React, { Component, Fragment } from 'react'
import {
  Box,
  Flex,
  Text,
  Heading,
  Link,
  Button,
  Icon
} from '@hackclub/design-system'
import Auth from 'components/Auth'
import LoadingBar from 'components/LoadingBar'
import ErrorPage from 'components/admin/ErrorPage'
import Comment from 'components/challenge/Comment'
import NewComment from 'components/challenge/NewComment'
import PropTypes from 'prop-types'
import api from 'api'
import { remove } from 'lodash'

const Header = Flex.extend`
  align-items: baseline;
  border-bottom: 1px solid ${props => props.theme.colors.smoke};
`

class Comments extends Component {
  state = { data: [] }

  static getDerivedStateFromProps = (nextProps, prevState) => ({
    data: nextProps.data
  })

  onSubmit = data => {
    this.setState(state => {
      data: state.data.push(data)
    })
  }

  onDelete = id => {
    const { data } = this.state
    api
      .delete(`v1/post_comments/${id}`)
      .then(() => {
        this.setState(state => ({
          data: remove(state.data, comment => comment.id === id)
        }))
      })
      .catch(e => {})
  }

  render() {
    const { status, name, url, id, email } = this.props
    const { data } = this.state
    console.log(this.props, this.state)
    return (
      <Fragment>
        <Header pb={1}>
          <Heading.h2 f={5} children={name} />
          <Link href={url} target="_blank" ml={2}>
            <Icon name="open_in_new" color="info" size={24} />
          </Link>
        </Header>
        <Auth
          headline="Sign in to comment"
          type="public"
          cardProps={{
            p: 3,
            my: 3,
            bg: 'info',
            boxShadowSize: 'md'
          }}
          textProps={{ my: 3, color: 'muted' }}
        />
        {status === 'loading' && <LoadingBar />}
        {status === 'error' && <ErrorPage />}
        {data.map((c, i) => (
          <Comment
            key={c.id}
            createdAt={c.created_at}
            mine={c.user.email === email}
            following={i > 0 ? data[i - 1].user.email === c.user.email : false}
            user={c.user}
            body={c.body}
            onDelete={this.onDelete}
          />
        ))}
        {status === 'success' && (
          <Fragment>
            <NewComment id={id} email={email} />
          </Fragment>
        )}
      </Fragment>
    )
  }
}

export default Comments
<commit_msg>Center Auth text<commit_after>import React, { Component, Fragment } from 'react'
import {
  Box,
  Flex,
  Text,
  Heading,
  Link,
  Button,
  Icon
} from '@hackclub/design-system'
import Auth from 'components/Auth'
import LoadingBar from 'components/LoadingBar'
import ErrorPage from 'components/admin/ErrorPage'
import Comment from 'components/challenge/Comment'
import NewComment from 'components/challenge/NewComment'
import PropTypes from 'prop-types'
import api from 'api'
import { remove } from 'lodash'

const Header = Flex.extend`
  align-items: baseline;
  border-bottom: 1px solid ${props => props.theme.colors.smoke};
`

class Comments extends Component {
  state = { data: [] }

  static getDerivedStateFromProps = (nextProps, prevState) => ({
    data: nextProps.data
  })

  onSubmit = data => {
    this.setState(state => {
      data: state.data.push(data)
    })
  }

  onDelete = id => {
    const { data } = this.state
    api
      .delete(`v1/post_comments/${id}`)
      .then(() => {
        this.setState(state => ({
          data: remove(state.data, comment => comment.id === id)
        }))
      })
      .catch(e => {})
  }

  render() {
    const { status, name, url, id, email } = this.props
    const { data } = this.state
    console.log(this.props, this.state)
    return (
      <Fragment>
        <Header pb={1}>
          <Heading.h2 f={5} children={name} />
          <Link href={url} target="_blank" ml={2}>
            <Icon name="open_in_new" color="info" size={24} />
          </Link>
        </Header>
        <Auth
          headline="Sign in to comment"
          type="public"
          cardProps={{
            p: 3,
            my: 3,
            bg: 'info',
            boxShadowSize: 'md'
          }}
          textProps={{ justify: 'center', my: 3, color: 'muted' }}
        />
        {status === 'loading' && <LoadingBar />}
        {status === 'error' && <ErrorPage />}
        {data.map((c, i) => (
          <Comment
            key={c.id}
            createdAt={c.created_at}
            mine={c.user.email === email}
            following={i > 0 ? data[i - 1].user.email === c.user.email : false}
            user={c.user}
            body={c.body}
            onDelete={this.onDelete}
          />
        ))}
        {status === 'success' && (
          <Fragment>
            <NewComment id={id} email={email} />
          </Fragment>
        )}
      </Fragment>
    )
  }
}

export default Comments
<|endoftext|>


<commit_before>var path = require('path');
var findup = require('findup');
var _ = require('lodash');

var CONFIG_FILENAME = 'styleguide.config.js';
var DEFAULT_CONFIG = {
	title: 'Style guide',
	serverHost: 'localhost',
	serverPort: 3000,
	getExampleFilename: function(componentpath) {
		return path.join(path.dirname(componentpath),  'examples.md');
	}
};

function readConfig() {
	try {
		var configDir = findup.sync(__dirname, CONFIG_FILENAME);
	}
	catch (e) {
		throw Error('Styleguidist config not found: ' + CONFIG_FILENAME + '.');
	}

	var options = require(path.join(configDir, CONFIG_FILENAME));

	validateConfig(options);

	options = _.merge({}, DEFAULT_CONFIG, options, {
		rootDir: path.join(configDir, options.rootDir)
	});

	return options;
}

function validateConfig(options) {
	if (!options.rootDir) {
		throw Error('Styleguidist: "rootDir" options is required.');
	}
	if (!options.components) {
		throw Error('Styleguidist: "components" options is required.');
	}
	if (options.getExampleFilename && typeof options.getExampleFilename !== 'function') {
		throw Error('Styleguidist: "getExampleFilename" options must be a function.');
	}
	if (options.updateWebpackConfig && typeof options.updateWebpackConfig !== 'function') {
		throw Error('Styleguidist: "updateWebpackConfig" options must be a function.');
	}
}

module.exports = readConfig();
<commit_msg>Default examples file: examples.md → Readme.md.<commit_after>var path = require('path');
var findup = require('findup');
var _ = require('lodash');

var CONFIG_FILENAME = 'styleguide.config.js';
var DEFAULT_CONFIG = {
	title: 'Style guide',
	serverHost: 'localhost',
	serverPort: 3000,
	getExampleFilename: function(componentpath) {
		return path.join(path.dirname(componentpath),  'Readme.md');
	}
};

function readConfig() {
	try {
		var configDir = findup.sync(__dirname, CONFIG_FILENAME);
	}
	catch (e) {
		throw Error('Styleguidist config not found: ' + CONFIG_FILENAME + '.');
	}

	var options = require(path.join(configDir, CONFIG_FILENAME));

	validateConfig(options);

	options = _.merge({}, DEFAULT_CONFIG, options, {
		rootDir: path.join(configDir, options.rootDir)
	});

	return options;
}

function validateConfig(options) {
	if (!options.rootDir) {
		throw Error('Styleguidist: "rootDir" options is required.');
	}
	if (!options.components) {
		throw Error('Styleguidist: "components" options is required.');
	}
	if (options.getExampleFilename && typeof options.getExampleFilename !== 'function') {
		throw Error('Styleguidist: "getExampleFilename" options must be a function.');
	}
	if (options.updateWebpackConfig && typeof options.updateWebpackConfig !== 'function') {
		throw Error('Styleguidist: "updateWebpackConfig" options must be a function.');
	}
}

module.exports = readConfig();
<|endoftext|>


<commit_before>var error = require('debug')('device:error');

var vrDisplay;

// It catches vrdisplayactivate early to ensure we can enter VR mode after the scene loads.
window.addEventListener('vrdisplayactivate', function (evt) {
  var canvasEl;
  // WebXR takes priority if available.
  if (navigator.xr) { return; }
  canvasEl = document.createElement('canvas');
  vrDisplay = evt.display;
  // Request present immediately. a-scene will be allowed to enter VR without user gesture.
  vrDisplay.requestPresent([{source: canvasEl}]).then(function () {}, function () {});
});

// Support both WebVR and WebXR APIs.
if (navigator.xr) {
  navigator.xr.requestDevice().then(function (device) {
    if (!device) { return; }
    device.supportsSession({immersive: true, exclusive: true}).then(function () {
      var sceneEl = document.querySelector('a-scene');
      vrDisplay = device;
      if (sceneEl) { sceneEl.emit('displayconnected', {vrDisplay: vrDisplay}); }
    });
  }).catch(function (err) {
    error('WebXR Request Device: ' + err.message);
  });
} else {
  if (navigator.getVRDisplays) {
    navigator.getVRDisplays().then(function (displays) {
      var sceneEl = document.querySelector('a-scene');
      vrDisplay = displays.length && displays[0];
      if (sceneEl) { sceneEl.emit('displayconnected', {vrDisplay: vrDisplay}); }
    });
  }
}

module.exports.isWebXRAvailable = navigator.xr !== undefined;

function getVRDisplay () { return vrDisplay; }
module.exports.getVRDisplay = getVRDisplay;

/**
 * Determine if a headset is connected by checking if a vrDisplay is available.
 */
function checkHeadsetConnected () { return !!getVRDisplay(); }
module.exports.checkHeadsetConnected = checkHeadsetConnected;

/**
 * Checks if browser is mobile and not stand-alone dedicated vr device.
 * @return {Boolean} True if mobile browser detected.
 */
var isMobile = (function () {
  var _isMobile = false;
  (function (a) {
    // eslint-disable-next-line no-useless-escape
    if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
      _isMobile = true;
    }
    if (isIOS() || isTablet() || isR7()) {
      _isMobile = true;
    }
    if (isMobileVR()) {
      _isMobile = false;
    }
  })(window.navigator.userAgent || window.navigator.vendor || window.opera);

  return function () { return _isMobile; };
})();
module.exports.isMobile = isMobile;

/**
 *  Detect tablet devices.
 *  @param {string} mockUserAgent - Allow passing a mock user agent for testing.
 */
function isTablet (mockUserAgent) {
  var userAgent = mockUserAgent || window.navigator.userAgent;
  return /ipad|Nexus (7|9)|xoom|sch-i800|playbook|tablet|kindle/i.test(userAgent);
}
module.exports.isTablet = isTablet;

function isIOS () {
  return /iPad|iPhone|iPod/.test(window.navigator.platform);
}
module.exports.isIOS = isIOS;

/**
 *  Detect browsers in Stand-Alone headsets
 */
function isMobileVR () {
  return /(OculusBrowser)|(SamsungBrowser)|(Mobile VR)/i.test(window.navigator.userAgent);
}
module.exports.isMobileVR = isMobileVR;

function isR7 () {
  return /R7 Build/.test(window.navigator.userAgent);
}
module.exports.isR7 = isR7;

/**
 * Checks mobile device orientation.
 * @return {Boolean} True if landscape orientation.
 */
module.exports.isLandscape = function () {
  var orientation = window.orientation;
  if (isR7()) { orientation += 90; }
  return orientation === 90 || orientation === -90;
};

/**
 * Check if running in a browser or spoofed browser (bundler).
 * We need to check a node api that isn't mocked on either side.
 * `require` and `module.exports` are mocked in browser by bundlers.
 * `window` is mocked in node.
 * `process` is also mocked by browserify, but has custom properties.
 */
module.exports.isBrowserEnvironment = !!(!process || process.browser);

/**
 * Check if running in node on the server.
 */
module.exports.isNodeEnvironment = !module.exports.isBrowserEnvironment;

/**
 * Update an Object3D pose if a polyfilled vrDisplay is present.
 */
module.exports.PolyfillControls = function PolyfillControls (object) {
  var frameData;
  var vrDisplay = window.webvrpolyfill && window.webvrpolyfill.getPolyfillDisplays()[0];
  if (window.VRFrameData) { frameData = new window.VRFrameData(); }
  this.update = function () {
    var pose;
    if (!vrDisplay) { return; }
    vrDisplay.getFrameData(frameData);
    pose = frameData.pose;
    if (pose.orientation !== null) {
      object.quaternion.fromArray(pose.orientation);
    }
    if (pose.position !== null) {
      object.position.fromArray(pose.position);
    } else {
      object.position.set(0, 0, 0);
    }
  };
};

<commit_msg>Update device.js<commit_after>var error = require('debug')('device:error');

var vrDisplay;

// Catch vrdisplayactivate early to ensure we can enter VR mode after the scene loads.
window.addEventListener('vrdisplayactivate', function (evt) {
  var canvasEl;
  // WebXR takes priority if available.
  if (navigator.xr) { return; }
  canvasEl = document.createElement('canvas');
  vrDisplay = evt.display;
  // Request present immediately. a-scene will be allowed to enter VR without user gesture.
  vrDisplay.requestPresent([{source: canvasEl}]).then(function () {}, function () {});
});

// Support both WebVR and WebXR APIs.
if (navigator.xr) {
  navigator.xr.requestDevice().then(function (device) {
    if (!device) { return; }
    device.supportsSession({immersive: true, exclusive: true}).then(function () {
      var sceneEl = document.querySelector('a-scene');
      vrDisplay = device;
      if (sceneEl) { sceneEl.emit('displayconnected', {vrDisplay: vrDisplay}); }
    });
  }).catch(function (err) {
    error('WebXR Request Device: ' + err.message);
  });
} else {
  if (navigator.getVRDisplays) {
    navigator.getVRDisplays().then(function (displays) {
      var sceneEl = document.querySelector('a-scene');
      vrDisplay = displays.length && displays[0];
      if (sceneEl) { sceneEl.emit('displayconnected', {vrDisplay: vrDisplay}); }
    });
  }
}

module.exports.isWebXRAvailable = navigator.xr !== undefined;

function getVRDisplay () { return vrDisplay; }
module.exports.getVRDisplay = getVRDisplay;

/**
 * Determine if a headset is connected by checking if a vrDisplay is available.
 */
function checkHeadsetConnected () { return !!getVRDisplay(); }
module.exports.checkHeadsetConnected = checkHeadsetConnected;

/**
 * Checks if browser is mobile and not stand-alone dedicated vr device.
 * @return {Boolean} True if mobile browser detected.
 */
var isMobile = (function () {
  var _isMobile = false;
  (function (a) {
    // eslint-disable-next-line no-useless-escape
    if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
      _isMobile = true;
    }
    if (isIOS() || isTablet() || isR7()) {
      _isMobile = true;
    }
    if (isMobileVR()) {
      _isMobile = false;
    }
  })(window.navigator.userAgent || window.navigator.vendor || window.opera);

  return function () { return _isMobile; };
})();
module.exports.isMobile = isMobile;

/**
 *  Detect tablet devices.
 *  @param {string} mockUserAgent - Allow passing a mock user agent for testing.
 */
function isTablet (mockUserAgent) {
  var userAgent = mockUserAgent || window.navigator.userAgent;
  return /ipad|Nexus (7|9)|xoom|sch-i800|playbook|tablet|kindle/i.test(userAgent);
}
module.exports.isTablet = isTablet;

function isIOS () {
  return /iPad|iPhone|iPod/.test(window.navigator.platform);
}
module.exports.isIOS = isIOS;

/**
 *  Detect browsers in Stand-Alone headsets
 */
function isMobileVR () {
  return /(OculusBrowser)|(SamsungBrowser)|(Mobile VR)/i.test(window.navigator.userAgent);
}
module.exports.isMobileVR = isMobileVR;

function isR7 () {
  return /R7 Build/.test(window.navigator.userAgent);
}
module.exports.isR7 = isR7;

/**
 * Checks mobile device orientation.
 * @return {Boolean} True if landscape orientation.
 */
module.exports.isLandscape = function () {
  var orientation = window.orientation;
  if (isR7()) { orientation += 90; }
  return orientation === 90 || orientation === -90;
};

/**
 * Check if running in a browser or spoofed browser (bundler).
 * We need to check a node api that isn't mocked on either side.
 * `require` and `module.exports` are mocked in browser by bundlers.
 * `window` is mocked in node.
 * `process` is also mocked by browserify, but has custom properties.
 */
module.exports.isBrowserEnvironment = !!(!process || process.browser);

/**
 * Check if running in node on the server.
 */
module.exports.isNodeEnvironment = !module.exports.isBrowserEnvironment;

/**
 * Update an Object3D pose if a polyfilled vrDisplay is present.
 */
module.exports.PolyfillControls = function PolyfillControls (object) {
  var frameData;
  var vrDisplay = window.webvrpolyfill && window.webvrpolyfill.getPolyfillDisplays()[0];
  if (window.VRFrameData) { frameData = new window.VRFrameData(); }
  this.update = function () {
    var pose;
    if (!vrDisplay) { return; }
    vrDisplay.getFrameData(frameData);
    pose = frameData.pose;
    if (pose.orientation !== null) {
      object.quaternion.fromArray(pose.orientation);
    }
    if (pose.position !== null) {
      object.position.fromArray(pose.position);
    } else {
      object.position.set(0, 0, 0);
    }
  };
};

<|endoftext|>


<commit_before>

export class MethodNotImplemented extends Error {
    constructor(methodName) {
        this.name    = 'MethodNotImplemented';
        this.message = 'Method ' + methodName + ' must be implemented in subclass';
    }
}
<commit_msg>subclasses need to call super()<commit_after>

export class MethodNotImplemented extends Error {
    constructor(methodName) {
        super();
        this.name    = 'MethodNotImplemented';
        this.message = 'Method ' + methodName + ' must be implemented in subclass';
    }
}
<|endoftext|>


<commit_before>'use strict';

// Extending @obj by another arguments (if they are objects too).
// For create not deep copy pass 'true' to last argument.
SoundJSDirector.extend = function extend (target) {

  var args = SoundJSDirector.toArray(arguments, 1),
      len = args.length,
      lastArg = args[len - 1],
      isNotDeep = lastArg !== true;

  if ('object' !== typeof target) {
    target = {};
  }

  if (!len) {
    args[0] = target;
    len = 1;
    target = {};
  }

  for (var i = 0, o, val; i < len; i++) {

    o = args[i];

    if ('object' !== typeof o) {
      continue;
    }

    for (var p in o) {

      val = o[p];

      if (!o.hasOwnProperty(p) || val === null || 'undefined' === typeof val) {
        continue;
      }

      // Deep copy of object.
      if ('object' === typeof val && isNotDeep && val !== o) {
        target[p] = extend(val);
      }
      else {
        target[p] = val;
      }
    }

  }

  return target;

};
<commit_msg>Fixed SoundJSDirector.extend utility.<commit_after>'use strict';

// Extending @obj by another arguments (if they are objects too).
// For create not deep copy pass 'true' to last argument.
SoundJSDirector.extend = function extend (target) {

  var args = SoundJSDirector.toArray(arguments, 1),
      len = args.length,
      lastArg = args[len - 1],
      isNotDeep = lastArg !== true;

  if ('object' !== typeof target) {
    target = {};
  }

  if (!len) {
    args[0] = target;
    len = 1;
    target = {};
  }

  for (var i = 0, o, val, tVal; i < len; i++) {

    o = args[i];

    if ('object' !== typeof o) {
      continue;
    }

    for (var p in o) {

      val = o[p];
      tVal = target[p];

      if ((tVal || 'undefined' !== typeof tVal) || !o.hasOwnProperty(p) || val === null || 'undefined' === typeof val) {
        continue;
      }

      // Deep copy of object.
      if ('object' === typeof val && isNotDeep && val !== o) {
        target[p] = extend(val);
      }
      else {
        target[p] = val;
      }
    }

  }

  return target;

};
<|endoftext|>