<commit_before>'use strict';

import './styles.postcss';
import Modal from '../Modal';
import FiltersView from '../Filters';
import React from 'react';
import moment from 'moment';

class ModalFilters extends Modal {

  constructor(props) {
    super(props);
  }

  shouldComponentUpdate(nextProps) {
    /* Just for optimization: don't render if nothing changed */
    if(nextProps.visible !==  this.props.visible) {
      if(nextProps.visible) {
        /* Google Analytics */
        ga && ga('send', 'event', 'Settings', 'Open Menu');

        this.filters.updateFilters();
      }
      return true;
    }

    if(nextProps.domain !== this.props.domain) {
      const domain = nextProps.domain.map(date => moment.utc(date, 'YYYY-MM-DD').toDate());
      this.filters.updateAvailableRange(domain);
    }

    return false;
  }

  /* TODO: the params should be in app's state at the beginning */
  getInitialFilters() {
    const routerParams = this.props.routerParams;
    if(!routerParams) return {};

    const res = {};

    if(routerParams.startDate) {
      const date = moment.utc(routerParams.startDate, 'YYYY-MM-DD');
      if(date.isValid()) {
        res['from-day']   = date.format('D');
        res['from-month'] = date.format('M');
        res['from-year']  = date.format('YYYY');
        res.from          = date.toDate();
      }
    }

    if(routerParams.endDate) {
      const date = moment.utc(routerParams.endDate, 'YYYY-MM-DD');
      if(date.isValid()) {
        res['to-day']   = date.format('D');
        res['to-month'] = date.format('M');
        res['to-year']  = date.format('YYYY');
        res.to          = date.toDate();
      }
    }

    if(routerParams.region) {
      res.region = routerParams.region;
    }

    if(routerParams.sectors && routerParams.sectors.length) {
      res.sectors = routerParams.sectors;
    }

    return res;
  }

  componentDidMount() {
    this.filters = new FiltersView({
      el: this.refs.Filters,
      closeCallback: this.props.onClose.bind(this),
      saveCallback: this.props.onSave.bind(this),
      wholeDomain: this.props.wholeDomain,
      domain: this.props.domain,
      initialFilters: this.getInitialFilters()
    });
  }

  getContent() {
    return

        <fieldset className="date date-from">
          <legend className="text -dark text-form-labels">From</legend>

              <select className="js-from-month" name="from-month">
                <option value="" disabled="disabled">Month</option>
              </select>

              <select className="js-from-day" name="from-day">
                <option value="" disabled="disabled">Day</option>
              </select>

              <select className="js-from-year" name="from-year">
                <option value="" disabled="disabled">Year</option>
              </select>

        </fieldset>

        <fieldset className="date date-to">
          <legend className="text -dark text-form-labels">To</legend>

              <select className="js-to-month" name="to-month">
                <option value="" disabled="disabled">Month</option>
              </select>

              <select className="js-to-day" name="to-day">
                <option value="" disabled="disabled">Day</option>
              </select>

              <select className="js-to-year" name="to-year">
                <option value="" disabled="disabled">Year</option>
              </select>

        </fieldset>

        <fieldset className="regions">
          <legend className="text -dark text-form-labels">Countries of interest</legend>

            <select className="js-regions" name="region">
              <option value="" disabled="disabeld">All regions</option>
            </select>

        </fieldset>

        <fieldset>
          <legend className="text -dark text-form-labels">Sectors</legend>

        </fieldset>

        <button type="button" className="btn btn-primary js-apply">Apply filters</button>
        <button type="button" className="btn btn-third js-clear">Clear filters</button>

;
  }

}

ModalFilters.propTypes = {
  visible: React.PropTypes.bool.isRequired,
  onClose: React.PropTypes.func.isRequired,
  onSave: React.PropTypes.func.isRequired
};

export default ModalFilters;
<commit_msg>Filters select text set in mobile<commit_after>'use strict';

import './styles.postcss';
import Modal from '../Modal';
import FiltersView from '../Filters';
import React from 'react';
import moment from 'moment';
import utils from './../../scripts/helpers/utils';

class ModalFilters extends Modal {

  constructor(props) {
    super(props);
    this.state = {
    }
  }

  componentWillMount() {
    this.setState(utils.checkDevice());
  }

  shouldComponentUpdate(nextProps) {
    /* Just for optimization: don't render if nothing changed */
    if(nextProps.visible !==  this.props.visible) {
      if(nextProps.visible) {
        /* Google Analytics */
        ga && ga('send', 'event', 'Settings', 'Open Menu');

        this.filters.updateFilters();
      }
      return true;
    }

    if(nextProps.domain !== this.props.domain) {
      const domain = nextProps.domain.map(date => moment.utc(date, 'YYYY-MM-DD').toDate());
      this.filters.updateAvailableRange(domain);
    }

    return false;
  }

  /* TODO: the params should be in app's state at the beginning */
  getInitialFilters() {
    const routerParams = this.props.routerParams;
    if(!routerParams) return {};

    const res = {};

    if(routerParams.startDate) {
      const date = moment.utc(routerParams.startDate, 'YYYY-MM-DD');
      if(date.isValid()) {
        res['from-day']   = date.format('D');
        res['from-month'] = date.format('M');
        res['from-year']  = date.format('YYYY');
        res.from          = date.toDate();
      }
    }

    if(routerParams.endDate) {
      const date = moment.utc(routerParams.endDate, 'YYYY-MM-DD');
      if(date.isValid()) {
        res['to-day']   = date.format('D');
        res['to-month'] = date.format('M');
        res['to-year']  = date.format('YYYY');
        res.to          = date.toDate();
      }
    }

    if(routerParams.region) {
      res.region = routerParams.region;
    }

    if(routerParams.sectors && routerParams.sectors.length) {
      res.sectors = routerParams.sectors;
    }

    return res;
  }

  componentDidMount() {
    this.filters = new FiltersView({
      el: this.refs.Filters,
      closeCallback: this.props.onClose.bind(this),
      saveCallback: this.props.onSave.bind(this),
      wholeDomain: this.props.wholeDomain,
      domain: this.props.domain,
      initialFilters: this.getInitialFilters()
    });
  }

  getContent() {
    const day = this.state.mobile ? 'DD' : 'Day';
    const month = this.state.mobile ? 'MM' : 'Month';
    const year = this.state.mobile ? 'YYYY' : 'Year';

    return

        <fieldset className="date date-from">
          <legend className="text -dark text-form-labels">From</legend>

              <select className="js-from-month" name="from-month">
                <option value="" disabled="disabled">{ month }</option>
              </select>

              <select className="js-from-day" name="from-day">
                <option value="" disabled="disabled">{ day }</option>
              </select>

              <select className="js-from-year" name="from-year">
                <option value="" disabled="disabled">{ year }</option>
              </select>

        </fieldset>

        <fieldset className="date date-to">
          <legend className="text -dark text-form-labels">To</legend>

              <select className="js-to-month" name="to-month">
                <option value="" disabled="disabled">{ month }</option>
              </select>

              <select className="js-to-day" name="to-day">
                <option value="" disabled="disabled">{ day }</option>
              </select>

              <select className="js-to-year" name="to-year">
                <option value="" disabled="disabled">{ year }</option>
              </select>

        </fieldset>

        <fieldset className="regions">
          <legend className="text -dark text-form-labels">Countries of interest</legend>

            <select className="js-regions" name="region">
              <option value="" disabled="disabeld">All regions</option>
            </select>

        </fieldset>

        <fieldset>
          <legend className="text -dark text-form-labels">Sectors</legend>

        </fieldset>

        <button type="button" className="btn btn-primary js-apply">Apply filters</button>
        <button type="button" className="btn btn-third js-clear">Clear filters</button>

;
  }

}

ModalFilters.propTypes = {
  visible: React.PropTypes.bool.isRequired,
  onClose: React.PropTypes.func.isRequired,
  onSave: React.PropTypes.func.isRequired
};

export default ModalFilters;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
    Text,
    View,
    ActivityIndicator,
    AsyncStorage
} from 'react-native';
import { connect } from 'react-redux';
import { Actions } from 'react-native-router-flux';
//import moment from 'moment';
import { fetchWeek } from '../actions';
import DayView from './DayViewComponent';

class Timetable extends Component {
    componentWillMount(){
        this.props.fetchWeek(this.props.user, this.props.program, '16');  // Using the 16th week of the year to get results from the API
    }

    renderTimetable(){
        if (this.props.loading) {
            return (
                <View style={styles.spinner}>
                    <ActivityIndicator size={'large'} />
                </View>
            );
        } else {
            if (this.props.week && this.props.masterdata) {
                return (
                    <DayView week={this.props.week} masterdata={this.props.masterdata} />  // For now I built the 'DayView' first
                );
            }
        }
    }

    render() {
        return (
            <View style={{ flex: 1 }}>
                {this.renderTimetable()}
            </View>
        );
    }
}

const styles = {
    headerDay: {
        flexDirection: "row",
        justifyContent: "space-between",
        backgroundColor: "#CCCCCC",
        padding: 10
    },
    spinner: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    }
};

const mapStateToProps = state => {
    return {
        user: state.login.user,
        program: state.login.program,
        masterdata: state.timetable.masterdata,
        week: state.timetable.fetchedWeek,
        loading: state.timetable.loadingFetch
    }
};

export default connect(mapStateToProps, { fetchWeek })(Timetable);
<commit_msg>Removed the check for the 'week' and 'masterdata' props because that caused the 'View' component to fail at some start-ups.<commit_after>import React, { Component } from 'react';
import {
    Text,
    View,
    ActivityIndicator,
    AsyncStorage
} from 'react-native';
import { connect } from 'react-redux';
import { Actions } from 'react-native-router-flux';
//import moment from 'moment';
import { fetchWeek } from '../actions';
import DayView from './DayViewComponent';

class Timetable extends Component {
    componentWillMount(){
        this.props.fetchWeek(this.props.user, this.props.program, '16');  // Using the 16th week of the year to get results from the API
    }

    renderTimetable(){
        if (this.props.loading) {
            return (
                <View style={styles.spinner}>
                    <ActivityIndicator size={'large'} />
                </View>
            );
        } else {
            return (
                <DayView week={this.props.week} masterdata={this.props.masterdata} />  // For now I built the 'DayView' first
            );
        }
    }

    render() {
        return (
            <View style={{ flex: 1 }}>
                {this.renderTimetable()}
            </View>
        );
    }
}

const styles = {
    headerDay: {
        flexDirection: "row",
        justifyContent: "space-between",
        backgroundColor: "#CCCCCC",
        padding: 10
    },
    spinner: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    }
};

const mapStateToProps = state => {
    return {
        user: state.login.user,
        program: state.login.program,
        masterdata: state.timetable.masterdata,
        week: state.timetable.fetchedWeek,
        loading: state.timetable.loadingFetch
    }
};

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


<commit_before>(function() {
	console.log("hej");
})();<commit_msg>First working version v0.1.0<commit_after>/**
 * Main binary file for web-resource-complier
 * @param	{string} path to configuration file.
 * @return	{string} results.
 */
(function() {
	if (process.argv.length < 3) {
		console.log("Missing argument: Path to configuration file.");
		return;
	}

	var exec 		= require('child_process').exec,
		fs 			= require('fs'),
		minify		= require('minify'),
		deferred	= require('deferred'),
		readFile 	= deferred.promisify(fs.readFile),
		args		= process.argv.slice(2),
		config 		= JSON.parse(fs.readFileSync(args[0], 'utf8'));

	var main = function () {
		console.log('Compiling using configuration: "' + args[0] + '"');

		for (var group_name in config) {
			console.log("Compiling group: " + group_name);
			var group = config[group_name];

			//Try to remove previous compiled resource
			try { fs.unlinkSync(group_name); } catch (ex) {
				if (ex.errno != 34) {
					console.log("Couldn't delete compilation target: " + ex.message);
				}
			}
			var writer = fs.createWriteStream(group_name);

			deferred.map(group, function (file) {
				var name = "";
				var concatonly = false;

				if (typeof file == "string") {
					name = file;
				} else if (typeof file == "object") {
					name = file.name;
					concatonly = file.options.indexOf('concatonly') >= 0;
				}

				if (concatonly) {
					return concatFile(name, writer);
				} else {
					return minifyFile(name, writer);
				}
			}).done(
				function(data) {
					writer.end();
				},
				function (error) {
					console.log("An error occurred: ");
					console.log(error);
				}
			);
		}
	};

	var concatFile = function (file, writer) {
		var def = deferred();

		readFile(file, 'utf-8').done(
			function(data) {
				writer.write(data);
				def.resolve();
			},
			function (error) {
				def.reject(error);
			}
		);

		return def.promise;
	};

	var minifyFile = function (file, writer) {
		var def = deferred();

		minify(file, function(error, data) {
			if (!error) {
				writer.write(data);
				def.resolve();
			} else {
				def.reject(error);
			}
		});

		return def.promise;
	};

	main();
})();<|endoftext|>


<commit_before>//
// Provides interface logic for the crowdfunding payment form
// Depends on Stripe Checkout
//

var form = '.crowdfund-form';
var amount = 'input[name=amount]';
var button = '.crowdfund-form #crowdfund-button';
var prettyInput = 'input[name=pretty-input]';

function submitForm(form) {

    var c = $(form).parents('.crowdfund');
    var pendingOverlay = $(c).children('.pending.overlay');
    var completeOverlay = $(c).children('.complete.overlay');
    var errorOverlay = $(c).children('.error.overlay');

    var formFields = $(form).serializeArray();
    var data = {};
    for (var i = 0; i < formFields.length; i++) {
        var field = formFields[i];
        data[field.name] = field.value;
    }

    $(document).ajaxStart(function(){
        $(pendingOverlay).addClass('visible');
    }).ajaxError(function(){
        $(pendingOverlay).removeClass('visible');
        $(errorOverlay).addClass('visible');
    }).ajaxComplete(function(e){
        $(pendingOverlay).removeClass('visible');
        $(completeOverlay).addClass('visible');
        $(document).off('ajaxStart').off('ajaxError').off('ajaxComplete');
    });

    $.ajax({
        url: $(form).attr('action'),
        type: 'post',
        data: data,
        success: null,
        dataType: 'json'
    });
}

// Stripe (should figure out how to divorce this from the file)
function checkoutCrowdfund(crowdfund) {
    // Passes in the button that was clicked
    var b = $(crowdfund).find(button);
    var a = $(crowdfund).find(amount).val();
    if (!a) {
        return false;
    }
    var f = $(crowdfund).find(form);
    var key = b.data('key');
    var icon = b.data('icon');
    var email = b.data('email');
    var label = 'Contribute';
    var description = 'Contribute (' + $(prettyInput).val() + ')';
    var token = function(token) {
        f.append('<input type="hidden" name="token" value="' + token.id + '" />');
        f.append('<input type="hidden" name="email" value="' + token.email + '" />');
        submitForm(f);
    }
    StripeCheckout.open({
        key: key,
        image: icon,
        name: 'MuckRock',
        description: description,
        amount: a,
        bitcoin: true,
        email: email,
        panelLabel: label,
        token: token
    });
    return true;
}

function prettifyAmountInput(input, pretty) {
    // pretty_amount_input is used as a functional wrapper for the amount input field
    // progressive enhancement ftw!
    $(input).attr('hidden', true).hide();
    var initialAmount = $(input).attr('value');
    var prettyInputElement = '<input name="pretty-input" class="success" >';
    $(input).before(prettyInputElement);
    $(pretty).autoNumeric('init', {aSign:'$', pSign:'p'});
    $(pretty).autoNumeric('set', initialAmount/100.00);
    $(pretty).keyup(function(e){
        var value = $(this).autoNumeric('get') * 100;
        $(input).attr('value', value);
    });
}

prettifyAmountInput(amount, prettyInput);
$(button).click(function(e){
    // get the crowdfund associated with this button
    e.preventDefault();
    var crowdfund = $(this).closest('.crowdfund.widget');
    checkoutCrowdfund(crowdfund);
});
<commit_msg>crowdfund bugfix<commit_after>//
// Provides interface logic for the crowdfunding payment form
// Depends on Stripe Checkout
//

var form = '.crowdfund-form';
var amount = 'input[name=amount]';
var button = '.crowdfund-form #crowdfund-button';
var prettyInput = 'input[name=pretty-input]';

function submitForm(form) {

    var c = $(form).parents('.crowdfund');
    var pendingOverlay = $(c).children('.pending.overlay');
    var completeOverlay = $(c).children('.complete.overlay');
    var errorOverlay = $(c).children('.error.overlay');

    var formFields = $(form).serializeArray();
    var data = {};
    for (var i = 0; i < formFields.length; i++) {
        var field = formFields[i];
        data[field.name] = field.value;
    }

    $(document).ajaxStart(function(){
        $(pendingOverlay).addClass('visible');
    }).ajaxError(function(){
        $(pendingOverlay).removeClass('visible');
        $(errorOverlay).addClass('visible');
        $(document).off('ajaxStart').off('ajaxError').off('ajaxComplete');
    }).ajaxComplete(function(e){
        $(pendingOverlay).removeClass('visible');
        $(completeOverlay).addClass('visible');
        $(document).off('ajaxStart').off('ajaxError').off('ajaxComplete');
    });

    $.ajax({
        url: $(form).attr('action'),
        type: 'post',
        data: data,
        success: null,
        dataType: 'json'
    });
}

// Stripe (should figure out how to divorce this from the file)
function checkoutCrowdfund(crowdfund) {
    // Passes in the button that was clicked
    var b = $(crowdfund).find(button);
    var a = $(crowdfund).find(amount).val();
    if (!a) {
        return false;
    }
    var f = $(crowdfund).find(form);
    var key = b.data('key');
    var icon = b.data('icon');
    var email = b.data('email');
    var label = 'Contribute';
    var description = 'Contribute (' + $(prettyInput).val() + ')';
    var token = function(token) {
        f.append('<input type="hidden" name="token" value="' + token.id + '" />');
        f.append('<input type="hidden" name="email" value="' + token.email + '" />');
        submitForm(f);
    }
    StripeCheckout.open({
        key: key,
        image: icon,
        name: 'MuckRock',
        description: description,
        amount: a,
        bitcoin: true,
        email: email,
        panelLabel: label,
        token: token
    });
    return true;
}

function prettifyAmountInput(input, pretty) {
    // pretty_amount_input is used as a functional wrapper for the amount input field
    // progressive enhancement ftw!
    $(input).attr('hidden', true).hide();
    var initialAmount = $(input).attr('value');
    var prettyInputElement = '<input name="pretty-input" class="success" >';
    $(input).before(prettyInputElement);
    $(pretty).autoNumeric('init', {aSign:'$', pSign:'p'});
    $(pretty).autoNumeric('set', initialAmount/100.00);
    $(pretty).keyup(function(e){
        var value = $(this).autoNumeric('get') * 100;
        $(input).attr('value', value);
    });
}

prettifyAmountInput(amount, prettyInput);
$(button).click(function(e){
    // get the crowdfund associated with this button
    e.preventDefault();
    var crowdfund = $(this).closest('.crowdfund.widget');
    checkoutCrowdfund(crowdfund);
});
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import { connect } from 'react-redux';

import * as courseActions from '../../actions/courseActions';

class CoursesPage extends React.Component {
    constructor(props, context) {
        super(props, context);

        this.state = {
            course: { title: '' }
        };

        this.onTitleChange = this.onTitleChange.bind(this);
        this.onClickSave = this.onClickSave.bind(this);
    }

    onTitleChange(event) {
        const course = this.state.course;
        course.title = event.target.value;

        this.setState({ course });
    }

    onClickSave() {
        this.props.dispatch(courseActions.createCourse(this.state.course));
    }

    courseRow(course, index) {
        return
{course.title}
;
    }

    render() {
        return (

Courses

                {this.props.courses.map(this.courseRow)}

Add Course

                <input
                    onChange={this.onTitleChange}
                    type="text"
                    value={this.state.course.title} />

                <input
                    onClick={this.onClickSave}
                    type="submit"
                    value="Save" />

        );
    }
}

CoursesPage.propTypes = {
    courses: PropTypes.array.isRequired,
    dispatch: PropTypes.func.isRequired
};

function mapStateToProps(state, ownProps) {
    return {
        courses: state.courses
    };
}

export default connect(mapStateToProps)(CoursesPage);
<commit_msg>Refactor dispatch in CoursesPage<commit_after>import React, { PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import * as courseActions from '../../actions/courseActions';

class CoursesPage extends React.Component {
    constructor(props, context) {
        super(props, context);

        this.state = {
            course: { title: '' }
        };

        this.onTitleChange = this.onTitleChange.bind(this);
        this.onClickSave = this.onClickSave.bind(this);
    }

    onTitleChange(event) {
        const course = this.state.course;
        course.title = event.target.value;

        this.setState({ course });
    }

    onClickSave() {
        this.props.createCourse(this.state.course);
    }

    courseRow(course, index) {
        return
{course.title}
;
    }

    render() {
        return (

Courses

                {this.props.courses.map(this.courseRow)}

Add Course

                <input
                    onChange={this.onTitleChange}
                    type="text"
                    value={this.state.course.title} />

                <input
                    onClick={this.onClickSave}
                    type="submit"
                    value="Save" />

        );
    }
}

CoursesPage.propTypes = {
    courses: PropTypes.array.isRequired,
    createCourse: PropTypes.func.isRequired
};

function mapStateToProps(state, ownProps) {
    return {
        courses: state.courses
    };
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators(courseActions, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(CoursesPage);
<|endoftext|>


<commit_before>import React from 'react';
import * as Objects from './objects/_index';

export default class extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      rowResult: {
        checks: [
          {
            object: 'Lamp',
            value: 'aan',
            valid: true
          },
          {
            object: 'Lamp',
            value: 'uit',
            valid: false
          }
        ],
        assignments: [
          {
            object: 'Deur',
            value: 'aan',
            valid: false
          },
          {
            object: 'Deur',
            value: 'open',
            valid: true
          }
        ]
      }
    }
  }

  render() {
    const checks = this.state.rowResult.checks.map((check, i) => {
      const ObjectRef = Objects[check.object];
      const classNames = 'check' + (check.valid ? ' valid' : '');
      return
<ObjectRef/> = {check.value}

    });

    const assignments = this.state.rowResult.assignments.map((assignment, i) => {
      const ObjectRef = Objects[assignment.object];
      const classNames = 'assignment' + (assignment.valid ? ' valid' : '');
      return
<ObjectRef/> = {assignment.value}

    });

    let output = [];

    if(checks.length > 0) {
      output.push(

          { checks }

      );

      if(checks.length > 1) {
        output.push(

            EN

        );
      }

      if(assignments.length > 0) {
        output.push(

            // Hier staat een computer //

        );
      }
    }

    if(assignments.length > 0) {
      output.push(

          { assignments }

      );
    }

    return(

        { output }

    );
  }
};
<commit_msg>Don't fail on non-existent objects.<commit_after>import React from 'react';
import * as Objects from './objects/_index';

export default class extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      rowResult: {
        checks: [
          {
            object: 'Lamp',
            value: 'aan',
            valid: true
          },
          {
            object: 'Lamp',
            value: 'uit',
            valid: false
          }
        ],
        assignments: [
          {
            object: 'Deur',
            value: 'aan',
            valid: false
          },
          {
            object: 'Deur',
            value: 'open',
            valid: true
          }
        ]
      }
    }
  }

  render() {
    const checks = this.state.rowResult.checks.map((check, i) => {
      const ObjectRef = Objects[check.object];
      if(!ObjectRef) return false;

      const classNames = 'check' + (check.valid ? ' valid' : '');
      return
<ObjectRef/> = {check.value}

    });

    const assignments = this.state.rowResult.assignments.map((assignment, i) => {
      const ObjectRef = Objects[assignment.object];
      if(!ObjectRef) return false;

      const classNames = 'assignment' + (assignment.valid ? ' valid' : '');
      return
<ObjectRef/> = {assignment.value}

    });

    let output = [];

    if(checks.length > 0) {
      output.push(

          { checks }

      );

      if(checks.length > 1) {
        output.push(

            EN

        );
      }

      if(assignments.length > 0) {
        output.push(

            // Hier staat een computer //

        );
      }
    }

    if(assignments.length > 0) {
      output.push(

          { assignments }

      );
    }

    return(

        { output }

    );
  }
};
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import * as MAPPING_TYPES from '../../constants/mapping_types'
import { getLinkObject } from '../base/json_helper'
import { body, repostedBody, setModels } from './RegionParser'
import Avatar from '../assets/Avatar'
import UserAvatars from '../../components/users/UserAvatars'
import ContentWarningButton from '../posts/ContentWarningButton'
import PostTools from '../posts/PostTools'
import CommentStream from '../streams/CommentStream'
import { HeartIcon, RepostIcon } from '../posts/PostIcons'
import RelationsGroup from '../relationships/RelationsGroup'
import Editor from '../../components/editor/Editor'
import { postLovers, postReposters } from '../../networking/api'

function getPostDetailPath(author, post) {
  return `/${author.username}/post/${post.token}`
}

function postLoversDrawer(post) {
  return (
    <UserAvatars
      endpoint={ postLovers(post.id) }
      icon={ <HeartIcon /> }
      key={ `userAvatarsLovers_${post.id}` }
      post={ post }
      resultType="love"
    />
  )
}

function postRepostersDrawer(post) {
  return (
    <UserAvatars
      endpoint={ postReposters(post.id) }
      icon={ <RepostIcon /> }
      key={ `userAvatarsReposters_${post.id}` }
      post={ post }
      resultType="repost"
    />
  )
}

function commentStream(post, author) {
  return (
    <CommentStream
      key={ `Comments_${post.id}_${post.commentsCount}` }
      post={ post }
      author={ author }
    />
  )
}

const PostHeaderTimeAgoLink = ({ to, createdAt }) =>
  <Link className="PostHeaderTimeAgoLink" to={ to }>
    {new Date(createdAt).timeAgoInWords()}
  </Link>

PostHeaderTimeAgoLink.propTypes = {
  createdAt: PropTypes.string,
  to: PropTypes.string,
}

function header(post, author) {
  if (!post || !author) { return null }
  const postDetailPath = getPostDetailPath(author, post)
  return (
    <header className="PostHeader" key={ `PostHeader_${post.id}` }>

        <Link className="PostHeaderLink" to={ `/${author.username}` }>
          <Avatar sources={ author.avatar } />
          { `@${author.username}` }
        </Link>

      <RelationsGroup user={ author } classList="inHeader" />
      <PostHeaderTimeAgoLink to={ postDetailPath } createdAt={ post.createdAt } />

  )
}

function repostHeader(post, repostAuthor, repostSource, repostedBy) {
  if (!post || !repostedBy) { return null }
  const postDetailPath = getPostDetailPath(repostAuthor, post)
  return (
    <header className="RepostHeader" key={ `RepostHeader_${post.id}` }>

        <Link className="PostHeaderLink" to={ `/${repostAuthor.username}` }>
          <Avatar sources={ repostAuthor.avatar } />
          { `@${repostAuthor.username}` }
        </Link>

      <RelationsGroup user={ repostAuthor } classList="inHeader" />

        <Link className="PostHeaderLink" to={ `/${repostedBy.username}` }>
          <RepostIcon />
          { ` by @${repostedBy.username}` }
        </Link>

      <PostHeaderTimeAgoLink to={ postDetailPath } createdAt={ post.createdAt } />

  )
}

function footer(post, author, currentUser, isGridLayout) {
  if (!author) { return null }
  return (
    <PostTools
      author={ author }
      post={ post }
      currentUser={ currentUser }
      isGridLayout={ isGridLayout }
      key={ `PostTools_${post.id}` }
    />
  )
}

export function parsePost(post, author, currentUser, isGridLayout = true) {
  if (!post) { return null }
  const cells = []
  const postDetailPath = getPostDetailPath(author, post)

  if (post.contentWarning) {
    cells.push(<ContentWarningButton post={ post } />)
  }

  if (post.repostContent && post.repostContent.length) {
    // this is weird, but the post summary is
    // actually the repost summary on reposts
    if (isGridLayout) {
      cells.push(body(post.summary, post.id, isGridLayout, postDetailPath))
    } else {
      cells.push(body(post.repostContent, `repost_${post.id}`, isGridLayout, postDetailPath))
      if (post.content && post.content.length) {
        cells.push(repostedBody(author, post.content, post.id, isGridLayout, postDetailPath))
      }
    }
  } else {
    const content = isGridLayout ? post.summary : post.content
    cells.push(body(content, post.id, isGridLayout, postDetailPath))
  }
  cells.push(footer(post, author, currentUser, isGridLayout))
  setModels({})
  return cells
}

function isRepost(post) {
  return post.repostContent && post.repostContent.length
}

/* eslint-disable react/prefer-stateless-function */
class PostParser extends Component {
  static propTypes = {
    assets: PropTypes.any,
    author: PropTypes.object,
    authorLinkObject: PropTypes.object,
    currentUser: PropTypes.object,
    isEditing: PropTypes.bool,
    isGridLayout: PropTypes.bool,
    isPostDetail: PropTypes.bool,
    isReposting: PropTypes.bool,
    post: PropTypes.object,
    postBody: PropTypes.array,
    showComments: PropTypes.bool,
    showLovers: PropTypes.bool,
    showReposters: PropTypes.bool,
    sourceLinkObject: PropTypes.object,
  }

  render() {
    const {
      assets,
      author,
      currentUser,
      isGridLayout,
      isPostDetail,
      post,
      postBody,
    } = this.props
    if (!post) { return null }
    setModels({ assets })

    const showLovers = !isGridLayout && this.props.showLovers
    const showReposters = !isGridLayout && this.props.showReposters
    const showComments = !isPostDetail && this.props.showComments

    let postHeader
    if (isRepost(post)) {
      const { authorLinkObject, sourceLinkObject } = this.props
      postHeader = repostHeader(post, authorLinkObject, sourceLinkObject, author)
    } else {
      postHeader = header(post, author)
    }

    const showEditor = (post.isEditing || post.isReposting) && postBody
    return (

        { postHeader }
        { showEditor ?
          <Editor post={ post } /> :
          parsePost(post, author, currentUser, isGridLayout)}
        { showLovers ? postLoversDrawer(post) : null }
        { showReposters ? postRepostersDrawer(post) : null }
        { showComments ? <Editor post={ post } isComment /> : null }
        { showComments && post.commentsCount > 0 ? commentStream(post, author, currentUser) : null }

)
  }
}

const mapStateToProps = ({ json, profile: currentUser }, ownProps) => {
  const post = json[MAPPING_TYPES.POSTS][ownProps.post.id]
  const author = json[MAPPING_TYPES.USERS][post.authorId]
  const assets = json.assets

  let newProps = {
    assets,
    author,
    currentUser,
    isEditing: post.isEditing || false,
    isReposting: post.isReposting || false,
    showComments: post.showComments || false,
    showLovers: post.showLovers || false,
    showReposters: post.showReposters || false,
    postBody: post.body,
    post,
  }

  if (isRepost(post)) {
    newProps = {
      ...newProps,
      authorLinkObject: post.repostAuthor || getLinkObject(post, 'repostAuthor', json),
      sourceLinkObject: getLinkObject(post, 'repostedSource', json),
    }
  }

  return newProps
}

export default connect(mapStateToProps)(PostParser)

<commit_msg>these are props, so treat them as props<commit_after>import React, { Component, PropTypes } from 'react'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import * as MAPPING_TYPES from '../../constants/mapping_types'
import { getLinkObject } from '../base/json_helper'
import { body, repostedBody, setModels } from './RegionParser'
import Avatar from '../assets/Avatar'
import UserAvatars from '../../components/users/UserAvatars'
import ContentWarningButton from '../posts/ContentWarningButton'
import PostTools from '../posts/PostTools'
import CommentStream from '../streams/CommentStream'
import { HeartIcon, RepostIcon } from '../posts/PostIcons'
import RelationsGroup from '../relationships/RelationsGroup'
import Editor from '../../components/editor/Editor'
import { postLovers, postReposters } from '../../networking/api'

function getPostDetailPath(author, post) {
  return `/${author.username}/post/${post.token}`
}

function postLoversDrawer(post) {
  return (
    <UserAvatars
      endpoint={ postLovers(post.id) }
      icon={ <HeartIcon /> }
      key={ `userAvatarsLovers_${post.id}` }
      post={ post }
      resultType="love"
    />
  )
}

function postRepostersDrawer(post) {
  return (
    <UserAvatars
      endpoint={ postReposters(post.id) }
      icon={ <RepostIcon /> }
      key={ `userAvatarsReposters_${post.id}` }
      post={ post }
      resultType="repost"
    />
  )
}

function commentStream(post, author) {
  return (
    <CommentStream
      key={ `Comments_${post.id}_${post.commentsCount}` }
      post={ post }
      author={ author }
    />
  )
}

const PostHeaderTimeAgoLink = ({ to, createdAt }) =>
  <Link className="PostHeaderTimeAgoLink" to={ to }>
    {new Date(createdAt).timeAgoInWords()}
  </Link>

PostHeaderTimeAgoLink.propTypes = {
  createdAt: PropTypes.string,
  to: PropTypes.string,
}

function header(post, author) {
  if (!post || !author) { return null }
  const postDetailPath = getPostDetailPath(author, post)
  return (
    <header className="PostHeader" key={ `PostHeader_${post.id}` }>

        <Link className="PostHeaderLink" to={ `/${author.username}` }>
          <Avatar sources={ author.avatar } />
          { `@${author.username}` }
        </Link>

      <RelationsGroup user={ author } classList="inHeader" />
      <PostHeaderTimeAgoLink to={ postDetailPath } createdAt={ post.createdAt } />

  )
}

function repostHeader(post, repostAuthor, repostSource, repostedBy) {
  if (!post || !repostedBy) { return null }
  const postDetailPath = getPostDetailPath(repostAuthor, post)
  return (
    <header className="RepostHeader" key={ `RepostHeader_${post.id}` }>

        <Link className="PostHeaderLink" to={ `/${repostAuthor.username}` }>
          <Avatar sources={ repostAuthor.avatar } />
          { `@${repostAuthor.username}` }
        </Link>

      <RelationsGroup user={ repostAuthor } classList="inHeader" />

        <Link className="PostHeaderLink" to={ `/${repostedBy.username}` }>
          <RepostIcon />
          { ` by @${repostedBy.username}` }
        </Link>

      <PostHeaderTimeAgoLink to={ postDetailPath } createdAt={ post.createdAt } />

  )
}

function footer(post, author, currentUser, isGridLayout) {
  if (!author) { return null }
  return (
    <PostTools
      author={ author }
      post={ post }
      currentUser={ currentUser }
      isGridLayout={ isGridLayout }
      key={ `PostTools_${post.id}` }
    />
  )
}

export function parsePost(post, author, currentUser, isGridLayout = true) {
  if (!post) { return null }
  const cells = []
  const postDetailPath = getPostDetailPath(author, post)

  if (post.contentWarning) {
    cells.push(<ContentWarningButton post={ post } />)
  }

  if (post.repostContent && post.repostContent.length) {
    // this is weird, but the post summary is
    // actually the repost summary on reposts
    if (isGridLayout) {
      cells.push(body(post.summary, post.id, isGridLayout, postDetailPath))
    } else {
      cells.push(body(post.repostContent, `repost_${post.id}`, isGridLayout, postDetailPath))
      if (post.content && post.content.length) {
        cells.push(repostedBody(author, post.content, post.id, isGridLayout, postDetailPath))
      }
    }
  } else {
    const content = isGridLayout ? post.summary : post.content
    cells.push(body(content, post.id, isGridLayout, postDetailPath))
  }
  cells.push(footer(post, author, currentUser, isGridLayout))
  setModels({})
  return cells
}

function isRepost(post) {
  return post.repostContent && post.repostContent.length
}

/* eslint-disable react/prefer-stateless-function */
class PostParser extends Component {
  static propTypes = {
    assets: PropTypes.any,
    author: PropTypes.object,
    authorLinkObject: PropTypes.object,
    currentUser: PropTypes.object,
    isEditing: PropTypes.bool,
    isGridLayout: PropTypes.bool,
    isPostDetail: PropTypes.bool,
    isReposting: PropTypes.bool,
    post: PropTypes.object,
    postBody: PropTypes.array,
    showComments: PropTypes.bool,
    showLovers: PropTypes.bool,
    showReposters: PropTypes.bool,
    sourceLinkObject: PropTypes.object,
  }

  render() {
    const {
      assets,
      author,
      currentUser,
      isEditing,
      isGridLayout,
      isPostDetail,
      isReposting,
      post,
      postBody,
    } = this.props
    if (!post) { return null }
    setModels({ assets })

    const showLovers = !isGridLayout && this.props.showLovers
    const showReposters = !isGridLayout && this.props.showReposters
    const showComments = !isPostDetail && this.props.showComments

    let postHeader
    if (isRepost(post)) {
      const { authorLinkObject, sourceLinkObject } = this.props
      postHeader = repostHeader(post, authorLinkObject, sourceLinkObject, author)
    } else {
      postHeader = header(post, author)
    }

    const showEditor = (isEditing || isReposting) && postBody
    return (

        { postHeader }
        { showEditor ?
          <Editor post={ post } /> :
          parsePost(post, author, currentUser, isGridLayout)}
        { showLovers ? postLoversDrawer(post) : null }
        { showReposters ? postRepostersDrawer(post) : null }
        { showComments ? <Editor post={ post } isComment /> : null }
        { showComments && post.commentsCount > 0 ? commentStream(post, author, currentUser) : null }

)
  }
}

const mapStateToProps = ({ json, profile: currentUser }, ownProps) => {
  const post = json[MAPPING_TYPES.POSTS][ownProps.post.id]
  const author = json[MAPPING_TYPES.USERS][post.authorId]
  const assets = json.assets

  let newProps = {
    assets,
    author,
    currentUser,
    isEditing: post.isEditing || false,
    isReposting: post.isReposting || false,
    showComments: post.showComments || false,
    showLovers: post.showLovers || false,
    showReposters: post.showReposters || false,
    postBody: post.body,
    post,
  }

  if (isRepost(post)) {
    newProps = {
      ...newProps,
      authorLinkObject: post.repostAuthor || getLinkObject(post, 'repostAuthor', json),
      sourceLinkObject: getLinkObject(post, 'repostedSource', json),
    }
  }

  return newProps
}

export default connect(mapStateToProps)(PostParser)

<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import range from 'array-range';

class VirtualListRow extends PureComponent {
  render() {
    const { renderItem, item, index, columnIndex } = this.props;
    return renderItem(item, index, columnIndex);
  }
}

VirtualListRow.propTypes = {
  renderItem: PropTypes.func.isRequired,
  item: PropTypes.any.isRequired,
  index: PropTypes.number.isRequired,
  columnIndex: PropTypes.number.isRequired,
  isSpecial: PropTypes.bool,
};

class VirtualListInnerChunk extends PureComponent {
  render() {
    const {
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
    } = this.props;

    return (

        {range(
          visibleRangeStart,
          Math.max(visibleRangeStart, visibleRangeEnd)
        ).map(i => {
          const item = items[i];
          return (
            <VirtualListRow
              key={i}
              index={i}
              columnIndex={columnIndex}
              renderItem={renderItem}
              item={item}
              items={items}
              isSpecial={specialItems.includes(item)}
            />
          );
        })}

    );
  }
}

VirtualListInnerChunk.propTypes = {
  className: PropTypes.string,
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.array.isRequired,
  specialItems: PropTypes.array.isRequired,
  visibleRangeStart: PropTypes.number.isRequired,
  visibleRangeEnd: PropTypes.number.isRequired,
  columnIndex: PropTypes.number.isRequired,
};

class VirtualListInner extends PureComponent {
  constructor(props) {
    super(props);
    this._containerCreated = e => {
      this._container = e;
    };
  }

  getBoundingClientRect() {
    if (this._container) {
      return this._container.getBoundingClientRect();
    }
    return new window.DOMRect();
  }

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
    } = this.props;

    const chunkSize = 16;
    const startChunkIndex = Math.floor(visibleRangeStart / chunkSize);
    const endChunkIndex = Math.ceil(visibleRangeEnd / chunkSize);
    const chunks = range(
      startChunkIndex,
      Math.max(startChunkIndex, endChunkIndex)
    ).map(c => c * chunkSize);

    return (
      <div
        className={className}
        ref={this._containerCreated}
        style={{
          height: `${items.length * itemHeight}px`,
          width: columnIndex === 1 ? '3000px' : undefined,
        }}
      >
        <div
          className={`${className}TopSpacer`}
          key={-1}
          style={{ height: Math.max(0, visibleRangeStart) * itemHeight + 'px' }}
        />
        {chunks.map(chunkStart => {
          return (
            <VirtualListInnerChunk
              className={`${className}InnerChunk`}
              key={chunkStart}
              visibleRangeStart={Math.max(chunkStart, visibleRangeStart)}
              visibleRangeEnd={Math.min(
                chunkStart + chunkSize,
                visibleRangeEnd
              )}
              columnIndex={columnIndex}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
            />
          );
        })}

    );
  }
}

VirtualListInner.propTypes = {
  itemHeight: PropTypes.number.isRequired,
  className: PropTypes.string,
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.array.isRequired,
  specialItems: PropTypes.array.isRequired,
  visibleRangeStart: PropTypes.number.isRequired,
  visibleRangeEnd: PropTypes.number.isRequired,
  columnIndex: PropTypes.number.isRequired,
};

class VirtualList extends PureComponent {
  constructor(props) {
    super(props);
    this._onScroll = this._onScroll.bind(this);
    this._onCopy = this._onCopy.bind(this);
    this._geometry = undefined;
    this._containerCreated = elem => {
      this._container = elem;
    };
    this._innerCreated = elem => {
      this._inner = elem;
    };
  }

  componentDidMount() {
    document.addEventListener('copy', this._onCopy, false);
    this._container.addEventListener('scroll', this._onScroll);
    this._onScroll(); // for initial size
  }

  componentWillUnmount() {
    document.removeEventListener('copy', this._onCopy, false);
    this._container.removeEventListener('scroll', this._onScroll);
  }

  _onScroll() {
    this._geometry = this._queryGeometry();
    this.forceUpdate();
  }

  _onCopy(event) {
    if (document.activeElement === this._container) {
      this.props.onCopy(event);
    }
  }

  _queryGeometry() {
    if (!this._container) {
      return undefined;
    }
    const outerRect = this._container.getBoundingClientRect();
    const innerRectY = this._inner.getBoundingClientRect().top;
    return { outerRect, innerRectY };
  }

  computeVisibleRange() {
    const { itemHeight, disableOverscan } = this.props;
    if (!this._geometry) {
      return { visibleRangeStart: 0, visibleRangeEnd: 100 };
    }
    const { outerRect, innerRectY } = this._geometry;
    const overscan = disableOverscan ? 0 : 25;
    const chunkSize = 16;
    let visibleRangeStart =
      Math.floor((outerRect.top - innerRectY) / itemHeight) - overscan;
    let visibleRangeEnd =
      Math.ceil((outerRect.bottom - innerRectY) / itemHeight) + overscan;
    if (!disableOverscan) {
      visibleRangeStart = Math.floor(visibleRangeStart / chunkSize) * chunkSize;
      visibleRangeEnd = Math.ceil(visibleRangeEnd / chunkSize) * chunkSize;
    }
    return { visibleRangeStart, visibleRangeEnd };
  }

  scrollItemIntoView(itemIndex, offsetX) {
    if (!this._container) {
      return;
    }
    const itemTop = itemIndex * this.props.itemHeight;
    const itemBottom = itemTop + this.props.itemHeight;
    const container = this._container;

    if (container.scrollTop > itemTop) {
      container.scrollTop = itemTop;
    } else if (container.scrollTop + container.clientHeight < itemBottom) {
      container.scrollTop = Math.min(
        itemTop,
        itemBottom - container.clientHeight
      );
    }

    const interestingWidth = 400;
    const itemLeft = offsetX;
    const itemRight = itemLeft + interestingWidth;

    if (container.scrollLeft > itemLeft) {
      container.scrollLeft = itemLeft;
    } else if (container.scrollLeft + container.clientWidth < itemRight) {
      container.scrollLeft = Math.min(
        itemLeft,
        itemRight - container.clientWidth
      );
    }
  }

  focus() {
    this._container.focus();
  }

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      focusable,
      specialItems,
      onKeyDown,
    } = this.props;
    const columnCount = this.props.columnCount || 1;
    const { visibleRangeStart, visibleRangeEnd } = this.computeVisibleRange();
    return (

        <div className={`${className}InnerWrapper`}>
          {range(columnCount).map(columnIndex =>
            <VirtualListInner
              className={classNames(
                `${className}Inner`,
                `${className}Inner${columnIndex}`
              )}
              visibleRangeStart={Math.max(0, visibleRangeStart)}
              visibleRangeEnd={Math.min(items.length, visibleRangeEnd)}
              itemHeight={itemHeight}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
              columnIndex={columnIndex}
              key={columnIndex}
              ref={columnIndex === 0 ? this._innerCreated : undefined}
            />
          )}

    );
  }
}

VirtualList.propTypes = {
  itemHeight: PropTypes.number.isRequired,
  className: PropTypes.string,
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.array.isRequired,
  focusable: PropTypes.bool.isRequired,
  specialItems: PropTypes.array.isRequired,
  onKeyDown: PropTypes.func.isRequired,
  onCopy: PropTypes.func.isRequired,
  disableOverscan: PropTypes.bool,
  columnCount: PropTypes.number,
};

export default VirtualList;
<commit_msg>VirtualList: disable rule no-unused-prop-type for the isSpecial prop<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import range from 'array-range';

class VirtualListRow extends PureComponent {
  render() {
    const { renderItem, item, index, columnIndex } = this.props;
    return renderItem(item, index, columnIndex);
  }
}

VirtualListRow.propTypes = {
  renderItem: PropTypes.func.isRequired,
  item: PropTypes.any.isRequired,
  index: PropTypes.number.isRequired,
  columnIndex: PropTypes.number.isRequired,
  // This prop is not used directly, it's used merely to force its rerendering,
  // especially when it's selected / unselected.
  isSpecial: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
};

class VirtualListInnerChunk extends PureComponent {
  render() {
    const {
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
    } = this.props;

    return (

        {range(
          visibleRangeStart,
          Math.max(visibleRangeStart, visibleRangeEnd)
        ).map(i => {
          const item = items[i];
          return (
            <VirtualListRow
              key={i}
              index={i}
              columnIndex={columnIndex}
              renderItem={renderItem}
              item={item}
              items={items}
              isSpecial={specialItems.includes(item)}
            />
          );
        })}

    );
  }
}

VirtualListInnerChunk.propTypes = {
  className: PropTypes.string,
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.array.isRequired,
  specialItems: PropTypes.array.isRequired,
  visibleRangeStart: PropTypes.number.isRequired,
  visibleRangeEnd: PropTypes.number.isRequired,
  columnIndex: PropTypes.number.isRequired,
};

class VirtualListInner extends PureComponent {
  constructor(props) {
    super(props);
    this._containerCreated = e => {
      this._container = e;
    };
  }

  getBoundingClientRect() {
    if (this._container) {
      return this._container.getBoundingClientRect();
    }
    return new window.DOMRect();
  }

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
    } = this.props;

    const chunkSize = 16;
    const startChunkIndex = Math.floor(visibleRangeStart / chunkSize);
    const endChunkIndex = Math.ceil(visibleRangeEnd / chunkSize);
    const chunks = range(
      startChunkIndex,
      Math.max(startChunkIndex, endChunkIndex)
    ).map(c => c * chunkSize);

    return (
      <div
        className={className}
        ref={this._containerCreated}
        style={{
          height: `${items.length * itemHeight}px`,
          width: columnIndex === 1 ? '3000px' : undefined,
        }}
      >
        <div
          className={`${className}TopSpacer`}
          key={-1}
          style={{ height: Math.max(0, visibleRangeStart) * itemHeight + 'px' }}
        />
        {chunks.map(chunkStart => {
          return (
            <VirtualListInnerChunk
              className={`${className}InnerChunk`}
              key={chunkStart}
              visibleRangeStart={Math.max(chunkStart, visibleRangeStart)}
              visibleRangeEnd={Math.min(
                chunkStart + chunkSize,
                visibleRangeEnd
              )}
              columnIndex={columnIndex}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
            />
          );
        })}

    );
  }
}

VirtualListInner.propTypes = {
  itemHeight: PropTypes.number.isRequired,
  className: PropTypes.string,
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.array.isRequired,
  specialItems: PropTypes.array.isRequired,
  visibleRangeStart: PropTypes.number.isRequired,
  visibleRangeEnd: PropTypes.number.isRequired,
  columnIndex: PropTypes.number.isRequired,
};

class VirtualList extends PureComponent {
  constructor(props) {
    super(props);
    this._onScroll = this._onScroll.bind(this);
    this._onCopy = this._onCopy.bind(this);
    this._geometry = undefined;
    this._containerCreated = elem => {
      this._container = elem;
    };
    this._innerCreated = elem => {
      this._inner = elem;
    };
  }

  componentDidMount() {
    document.addEventListener('copy', this._onCopy, false);
    this._container.addEventListener('scroll', this._onScroll);
    this._onScroll(); // for initial size
  }

  componentWillUnmount() {
    document.removeEventListener('copy', this._onCopy, false);
    this._container.removeEventListener('scroll', this._onScroll);
  }

  _onScroll() {
    this._geometry = this._queryGeometry();
    this.forceUpdate();
  }

  _onCopy(event) {
    if (document.activeElement === this._container) {
      this.props.onCopy(event);
    }
  }

  _queryGeometry() {
    if (!this._container) {
      return undefined;
    }
    const outerRect = this._container.getBoundingClientRect();
    const innerRectY = this._inner.getBoundingClientRect().top;
    return { outerRect, innerRectY };
  }

  computeVisibleRange() {
    const { itemHeight, disableOverscan } = this.props;
    if (!this._geometry) {
      return { visibleRangeStart: 0, visibleRangeEnd: 100 };
    }
    const { outerRect, innerRectY } = this._geometry;
    const overscan = disableOverscan ? 0 : 25;
    const chunkSize = 16;
    let visibleRangeStart =
      Math.floor((outerRect.top - innerRectY) / itemHeight) - overscan;
    let visibleRangeEnd =
      Math.ceil((outerRect.bottom - innerRectY) / itemHeight) + overscan;
    if (!disableOverscan) {
      visibleRangeStart = Math.floor(visibleRangeStart / chunkSize) * chunkSize;
      visibleRangeEnd = Math.ceil(visibleRangeEnd / chunkSize) * chunkSize;
    }
    return { visibleRangeStart, visibleRangeEnd };
  }

  scrollItemIntoView(itemIndex, offsetX) {
    if (!this._container) {
      return;
    }
    const itemTop = itemIndex * this.props.itemHeight;
    const itemBottom = itemTop + this.props.itemHeight;
    const container = this._container;

    if (container.scrollTop > itemTop) {
      container.scrollTop = itemTop;
    } else if (container.scrollTop + container.clientHeight < itemBottom) {
      container.scrollTop = Math.min(
        itemTop,
        itemBottom - container.clientHeight
      );
    }

    const interestingWidth = 400;
    const itemLeft = offsetX;
    const itemRight = itemLeft + interestingWidth;

    if (container.scrollLeft > itemLeft) {
      container.scrollLeft = itemLeft;
    } else if (container.scrollLeft + container.clientWidth < itemRight) {
      container.scrollLeft = Math.min(
        itemLeft,
        itemRight - container.clientWidth
      );
    }
  }

  focus() {
    this._container.focus();
  }

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      focusable,
      specialItems,
      onKeyDown,
    } = this.props;
    const columnCount = this.props.columnCount || 1;
    const { visibleRangeStart, visibleRangeEnd } = this.computeVisibleRange();
    return (

        <div className={`${className}InnerWrapper`}>
          {range(columnCount).map(columnIndex =>
            <VirtualListInner
              className={classNames(
                `${className}Inner`,
                `${className}Inner${columnIndex}`
              )}
              visibleRangeStart={Math.max(0, visibleRangeStart)}
              visibleRangeEnd={Math.min(items.length, visibleRangeEnd)}
              itemHeight={itemHeight}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
              columnIndex={columnIndex}
              key={columnIndex}
              ref={columnIndex === 0 ? this._innerCreated : undefined}
            />
          )}

    );
  }
}

VirtualList.propTypes = {
  itemHeight: PropTypes.number.isRequired,
  className: PropTypes.string,
  renderItem: PropTypes.func.isRequired,
  items: PropTypes.array.isRequired,
  focusable: PropTypes.bool.isRequired,
  specialItems: PropTypes.array.isRequired,
  onKeyDown: PropTypes.func.isRequired,
  onCopy: PropTypes.func.isRequired,
  disableOverscan: PropTypes.bool,
  columnCount: PropTypes.number,
};

export default VirtualList;
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import Triangle from './TriangleComponent';
import Circle from './CircleComponent';

require('styles/svg/Radar.scss');
var UUID = require('uuid-js');

class RadarComponent extends React.Component {
  constructor(props) {
    super();
    this.radius = props.radius;
    this.didChangedPoints = props.didChangedPoints;
    this.state = {points: props.points}
  }

  onCreateANewPoint(event) {
    let radius = this.radius;
    let pointRadius = 0.05;
    let points = this.state.points;
    var point = {type: 'new', x: event.nativeEvent.offsetX, y: event.nativeEvent.offsetY, id: UUID.create().toString()};
    point = this.screen2Cartesian(point)
    let deletePoints = points.filter(function(item){
      return (point.x >= item.x - pointRadius) && (point.x <= item.x + pointRadius) && (point.y >= item.y - pointRadius) && (point.y <= item.y + pointRadius)
    });
    if (deletePoints.length > 0) {
      let index = points.indexOf(deletePoints[0]);
      points.splice(index, 1);
    } else {
      let enterName = prompt("Enter a value");
      if (enterName === null) {
        return;
      }
      point.name = enterName;
      points.push(point);
    }
    this.setState({points: points});
    this.didChangedPoints(points);
  }

  getColorByPoint(point) {
    if (point.x > 0 && point.y > 0) {
      return "#BA68C8"
    } else if (point.x > 0 && point.y < 0) {
      return "#2196F3"
    } else if (point.x < 0 && point.y > 0) {
      return "#FF8A80"
    } else if (point.x < 0 && point.y < 0) {
      return "#009688"
    }
  }

  cartesian2Screen(point) {
    return {
      x: (1 + point.x) * this.radius,
      y: (1 - point.y) * this.radius,
      type: point.type,
      id: point.id,
      name: point.name,
      index: point.index
    }
  }

  screen2Cartesian(point) {
    return {
      x: point.x / this.radius - 1,
      y: 1 - point.y / this.radius,
      type: point.type,
      id: point.id,
      name: point.name,
      index: point.index
    }
  }

  render() {
    let radius = this.radius;
    let length = radius * 2;
    let accessR = radius * 0.8;
    let trialR = radius * 0.6;
    let adoptR = radius * 0.3;
    let serviceTrackWidth = radius * 0.05;
    let serviceTrackOrigin = radius - serviceTrackWidth * 0.5;
    let pointRadius = radius * 0.05;
    let holdTextX = radius * 0.1;
    let assessTextX = radius * 0.3;
    let trialTextX = radius * 0.55;
    let adoptTextX = radius * 0.85;
    let labelFontSize = serviceTrackWidth * 0.8;

    return (
      <svg width={length} height={length} version="1.1"
xmlns="http://www.w3.org/2000/svg" onClick={this.onCreateANewPoint.bind(this)}>
        <circle cx={radius} cy={radius} r={radius} fill="#F5F5F5"/>
        <circle cx={radius} cy={radius} r={accessR} fill="#EEEEEE" stroke="white"
        stroke-width="2"/>
        <circle cx={radius} cy={radius} r={trialR} fill="#E0E0E0" stroke="white"
        stroke-width="2"/>
        <circle cx={radius} cy={radius} r={adoptR} fill="#BDBDBD" stroke="white"
        stroke-width="2"/>
        <rect x={serviceTrackOrigin} y="0" width={serviceTrackWidth} height={length} fill="rgba(255, 255, 255, 0.5)" class="service-track"/>
        <rect x="0" y={serviceTrackOrigin} width={length} height={serviceTrackWidth} fill="rgba(255, 255, 255, 0.5)" class="service-track">
        </rect>
        <text x={holdTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">HOLD</text>
        <text x={assessTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">ASSESS</text>
        <text x={trialTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">TRIAL</text>
        <text x={adoptTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">ADOPT</text>
        {
          this.state.points
            .map(item => {
              let point = this.cartesian2Screen(item);
              let color = this.getColorByPoint(item);
              if (item.type == 'old') {
                return (<Triangle point={point} radius={pointRadius} key={`${item.id}`} fillColor={color} />)
              } else {

                return (<Circle point={point} radius={pointRadius} key={`${item.id}`} fillColor={color} />)
              }
            })
        }
        {
          this.state.points.map((item) => {
            let item1 = item;
            let index = this.state.points.indexOf(item) + 1;
            item1.index = index;
            return item1;
          }).map((point) => {return this.cartesian2Screen(point)})
            .map(item => {
              return (<text x={item.x} y={item.y} fill="#FFFFFF" fontSize={pointRadius} textAnchor="middle" dominantBaseline="central">{item.index}</text>)
            })
        }
      </svg>
    );
  }
}

RadarComponent.displayName = 'SvgRadarComponent';

// Uncomment properties you need
// RadarComponent.propTypes = {};
// RadarComponent.defaultProps = {};

export default RadarComponent;
<commit_msg>support new or old for type<commit_after>'use strict';

import React from 'react';
import Triangle from './TriangleComponent';
import Circle from './CircleComponent';

require('styles/svg/Radar.scss');
var UUID = require('uuid-js');

class RadarComponent extends React.Component {
  constructor(props) {
    super();
    this.radius = props.radius;
    this.didChangedPoints = props.didChangedPoints;
    this.state = {points: props.points}
  }

  onCreateANewPoint(event) {
    let radius = this.radius;
    let pointRadius = 0.05;
    let points = this.state.points;
    var point = {type: 'new', x: event.nativeEvent.offsetX, y: event.nativeEvent.offsetY, id: UUID.create().toString()};
    point = this.screen2Cartesian(point)
    let deletePoints = points.filter(function(item){
      return (point.x >= item.x - pointRadius) && (point.x <= item.x + pointRadius) && (point.y >= item.y - pointRadius) && (point.y <= item.y + pointRadius)
    });
    if (deletePoints.length > 0) {
      let index = points.indexOf(deletePoints[0]);
      points.splice(index, 1);
    } else {
      let enterName = prompt("Enter a value");
      let type = confirm('Is a new one?');
      if (enterName === null) {
        return;
      }
      point.name = enterName;
      point.type = type ? 'new' : 'old';
      points.push(point);
    }
    this.setState({points: points});
    this.didChangedPoints(points);
  }

  getColorByPoint(point) {
    if (point.x > 0 && point.y > 0) {
      return "#BA68C8"
    } else if (point.x > 0 && point.y < 0) {
      return "#2196F3"
    } else if (point.x < 0 && point.y > 0) {
      return "#FF8A80"
    } else if (point.x < 0 && point.y < 0) {
      return "#009688"
    }
  }

  cartesian2Screen(point) {
    return {
      x: (1 + point.x) * this.radius,
      y: (1 - point.y) * this.radius,
      type: point.type,
      id: point.id,
      name: point.name,
      index: point.index
    }
  }

  screen2Cartesian(point) {
    return {
      x: point.x / this.radius - 1,
      y: 1 - point.y / this.radius,
      type: point.type,
      id: point.id,
      name: point.name,
      index: point.index
    }
  }

  render() {
    let radius = this.radius;
    let length = radius * 2;
    let accessR = radius * 0.8;
    let trialR = radius * 0.6;
    let adoptR = radius * 0.3;
    let serviceTrackWidth = radius * 0.05;
    let serviceTrackOrigin = radius - serviceTrackWidth * 0.5;
    let pointRadius = radius * 0.05;
    let holdTextX = radius * 0.1;
    let assessTextX = radius * 0.3;
    let trialTextX = radius * 0.55;
    let adoptTextX = radius * 0.85;
    let labelFontSize = serviceTrackWidth * 0.8;

    return (
      <svg width={length} height={length} version="1.1"
xmlns="http://www.w3.org/2000/svg" onClick={this.onCreateANewPoint.bind(this)}>
        <circle cx={radius} cy={radius} r={radius} fill="#F5F5F5"/>
        <circle cx={radius} cy={radius} r={accessR} fill="#EEEEEE" stroke="white"
        stroke-width="2"/>
        <circle cx={radius} cy={radius} r={trialR} fill="#E0E0E0" stroke="white"
        stroke-width="2"/>
        <circle cx={radius} cy={radius} r={adoptR} fill="#BDBDBD" stroke="white"
        stroke-width="2"/>
        <rect x={serviceTrackOrigin} y="0" width={serviceTrackWidth} height={length} fill="rgba(255, 255, 255, 0.5)" class="service-track"/>
        <rect x="0" y={serviceTrackOrigin} width={length} height={serviceTrackWidth} fill="rgba(255, 255, 255, 0.5)" class="service-track">
        </rect>
        <text x={holdTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">HOLD</text>
        <text x={assessTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">ASSESS</text>
        <text x={trialTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">TRIAL</text>
        <text x={adoptTextX} y={radius} fill="#37474F" fontSize={labelFontSize} textAnchor="middle" dominantBaseline="central">ADOPT</text>
        {
          this.state.points
            .map(item => {
              let point = this.cartesian2Screen(item);
              let color = this.getColorByPoint(item);
              if (item.type == 'old') {
                return (<Triangle point={point} radius={pointRadius} key={`${item.id}`} fillColor={color} />)
              } else {

                return (<Circle point={point} radius={pointRadius} key={`${item.id}`} fillColor={color} />)
              }
            })
        }
        {
          this.state.points.map((item) => {
            let item1 = item;
            let index = this.state.points.indexOf(item) + 1;
            item1.index = index;
            return item1;
          }).map((point) => {return this.cartesian2Screen(point)})
            .map(item => {
              return (<text x={item.x} y={item.y} fill="#FFFFFF" fontSize={pointRadius} textAnchor="middle" dominantBaseline="central">{item.index}</text>)
            })
        }
      </svg>
    );
  }
}

RadarComponent.displayName = 'SvgRadarComponent';

// Uncomment properties you need
// RadarComponent.propTypes = {};
// RadarComponent.defaultProps = {};

export default RadarComponent;
<|endoftext|>


<commit_before>import React, { Component, Fragment } from 'react'
import Slider from 'react-slick'
import { remove } from 'lodash'

import api from 'api'
import styled from 'styled-components'
import {
  Box,
  Flex,
  Text,
  Heading,
  LargeButton,
  Loading,
  Icon,
  theme
} from '@hackclub/design-system'

import CarouselProject from 'components/workshops/CarouselProject'
import CarouselSubmissionForm from 'components/workshops/CarouselSubmissionForm'

import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'

const RehackSlider = styled(Slider)`
  align-self: stretch;
  .slick-center {
    padding: 0 ${theme.space[3]}px;
    ${theme.mediaQueries.md} {
      padding: 0;
    }
  }
`

const CarouselOuter = styled(Flex).attrs({
  bg: 'smoke',
  color: 'black',
  pt: [2, 3, 4],
  pb: [3, null, 4],
  flexDirection: 'column',
  align: 'center'
})``

const StaticWrapper = styled(Flex).attrs({ mb: [3, 3, 4], justify: 'center' })`
  align-self: stretch;
`

const SliderWrapper = styled(Box).attrs({ mb: [0, 3, 4] })`
  align-self: stretch;
`

const ShowAllProjects = styled(Flex).attrs({
  align: 'center',
  fontSize: [2, 3],
  color: 'muted'
})`
  cursor: pointer;
  svg {
    transform: rotate(-90deg);
    transition: ${theme.transition} transform;
  }
  &[aria-expanded='true'] svg {
    transform: rotate(0deg);
  }
`

const ShowAllGrid = styled(Flex).attrs({
  justify: 'center',
  flexDirection: 'row',
  wrap: true,
  px: 3,
  mb: 1
})``

const LoadingWrapper = styled(Box)`
  position: relative;
  width: 140px;
  height: 140px;
`

const LoadingCarousel = () => (
  <Fragment>
    <Heading.h3>Loading Projects…</Heading.h3>
    <LoadingWrapper>
      <Loading />
    </LoadingWrapper>
  </Fragment>
)

const AllProjects = projects => (
  <ShowAllGrid>
    {projects.map(project => (
      <CarouselProject
        project={project}
        key={project.screenshot.id}
        m={[1, null, 2]}
      />
    ))}
  </ShowAllGrid>
)

const LiveProject = submissionProject => (
  <StaticWrapper>
    <CarouselProject liveFrame project={submissionProject} />
  </StaticWrapper>
)

const EmptyProject = emptyProject => (
  <StaticWrapper>
    <CarouselProject project={emptyProject} />
  </StaticWrapper>
)

const ShortList = projects => (
  <StaticWrapper>
    {projects.map(project => (
      <CarouselProject project={project} key={project.screenshot.id} />
    ))}
  </StaticWrapper>
)

const sliderSettings = {
  arrows: false,
  speed: 500,
  autoplay: true,
  initialSlide: 0,
  autoplaySpeed: 5000,
  slidesToShow: 1,
  slidesToScroll: 1,
  centerMode: true,
  pauseOnHover: true,
  variableWidth: true,
  focusOnSelect: true
}

const SliderList = projects => (
  <SliderWrapper>
    <RehackSlider {...sliderSettings}>
      {projects.map(project => (
        <CarouselProject project={project} key={project.screenshot.id} />
      ))}
    </RehackSlider>
  </SliderWrapper>
)

const LoadedCarousel = (
  projects,
  projectCount,
  showAll,
  onClickShowAll,
  liveFrameStatus,
  submissionProject,
  emptyProject
) => (
  <Fragment>
    <Flex
      flexDirection={['column', null, 'row']}
      align="center"
      mb={[0, null, 3]}
    >
      <Heading.h3 fontSize={4} mr={[null, null, 3]}>
        {projectCount} Rehack
        {projectCount != 1 && 's'}
      </Heading.h3>
      <ShowAllProjects onClick={onClickShowAll} aria-expanded={showAll}>
        <Icon glyph="down-caret" size={32} />
        <Text.span
          ml={1}
          children={showAll ? 'ok stack them back up' : 'Show All'}
        />
      </ShowAllProjects>
    </Flex>
    {showAll
      ? AllProjects(projects)
      : liveFrameStatus != 'empty'
        ? LiveProject(submissionProject)
        : projects.length == 0
          ? EmptyProject(emptyProject)
          : projects.length < 3
            ? ShortList(projects)
            : SliderList(projects)}
  </Fragment>
)

class Carousel extends Component {
  state = {
    loading: true,
    submitting: false,
    submissionData: {
      liveUrl: '',
      codeUrl: ''
    },
    authed: false,
    authData: {},
    liveFrameStatus: 'empty',
    liveFrameImage: null,
    showAll: false,
    original: this.emptyProject,
    projects: []
  }
  emptyProject = {
    user: { username: '' },
    empty: true,
    live_url: '',
    code_url: '',
    screenshot: {
      id: -1,
      created_at: '',
      updated_at: '',
      type: '',
      file_path: ''
    }
  }

  componentDidMount() {
    const { slug } = this.props

    api.get(`v1/workshops/${slug}/projects`).then(projects => {
      let original = projects.find(
        project => project.user && project.user.username == 'prophetorpheus'
      )
      if (original) {
        remove(projects, original)
        projects.unshift(original)
      } else original = this.emptyProject

      this.setState({
        loading: false,
        original,
        projects
      })
    })

    api
      .get(`v1/users/current`)
      .then(authData => {
        console.log(
          `User is authorized! Auth data: ${JSON.stringify(authData)}`
        )
        this.setState({ authed: true, authData })
      })
      .catch(error => {
        console.log(`User is not authorized! Error: ${JSON.stringify(error)}`)
        this.setState({ authed: false, authData: {} })
      })
  }

  setLiveFrameStatus(liveFrameStatus) {
    this.setState({ liveFrameStatus })
  }

  setSubmissionData = submissionData => {
    // Disable "Live Frame" feature for now
    // this.setLiveFrameStatus(submissionData.liveUrl == '' ? 'empty' : 'loading')
    this.setState({ submissionData })
  }

  onClickSubmitButton = () => {
    this.setState({ submitting: true })
  }

  onClickShowAll = () => {
    this.setState({ showAll: !this.state.showAll })
  }

  onSignOut = () => {
    console.log('Signing out')
    this.setState({ authed: false, authData: {} })
  }

  render() {
    const { slug } = this.props
    const {
      loading,
      original,
      projects,
      submitting,
      submissionData,
      authed,
      authData,
      liveFrameStatus,
      // liveFrameImage,
      showAll
    } = this.state

    const {
      setSubmissionData,
      onClickSubmitButton,
      onClickShowAll,
      onSignOut,
      emptyProject
    } = this

    const submissionProject = {
      user: authData,
      live_url: submissionData.liveUrl,
      code_url: submissionData.codeUrl,
      screenshot: {}
    }

    const projectCount = projects.length - (original ? 0 : 1)

    return (
      <CarouselOuter>
        {loading
          ? LoadingCarousel()
          : LoadedCarousel(
              projects,
              projectCount,
              showAll,
              onClickShowAll,
              liveFrameStatus,
              submissionProject,
              emptyProject
            )}

        {submitting ? (
          <CarouselSubmissionForm
            authed={authed}
            authData={authData}
            onSignOut={onSignOut}
            workshopSlug={slug}
            submissionData={submissionData}
            setSubmissionData={setSubmissionData}
          />
        ) : (
          <LargeButton scale fontSize={4} onClick={onClickSubmitButton}>
            I made something
          </LargeButton>
        )}
      </CarouselOuter>
    )
  }
}

export default Carousel
<commit_msg>Fix copy<commit_after>import React, { Component, Fragment } from 'react'
import Slider from 'react-slick'
import { remove } from 'lodash'

import api from 'api'
import styled from 'styled-components'
import {
  Box,
  Flex,
  Text,
  Heading,
  LargeButton,
  Loading,
  Icon,
  theme
} from '@hackclub/design-system'

import CarouselProject from 'components/workshops/CarouselProject'
import CarouselSubmissionForm from 'components/workshops/CarouselSubmissionForm'

import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'

const RehackSlider = styled(Slider)`
  align-self: stretch;
  .slick-center {
    padding: 0 ${theme.space[3]}px;
    ${theme.mediaQueries.md} {
      padding: 0;
    }
  }
`

const CarouselOuter = styled(Flex).attrs({
  bg: 'smoke',
  color: 'black',
  pt: [2, 3, 4],
  pb: [3, null, 4],
  flexDirection: 'column',
  align: 'center'
})``

const StaticWrapper = styled(Flex).attrs({ mb: [3, 3, 4], justify: 'center' })`
  align-self: stretch;
`

const SliderWrapper = styled(Box).attrs({ mb: [0, 3, 4] })`
  align-self: stretch;
`

const ShowAllProjects = styled(Flex).attrs({
  align: 'center',
  fontSize: [2, 3],
  color: 'muted'
})`
  cursor: pointer;
  svg {
    transform: rotate(-90deg);
    transition: ${theme.transition} transform;
  }
  &[aria-expanded='true'] svg {
    transform: rotate(0deg);
  }
`

const ShowAllGrid = styled(Flex).attrs({
  justify: 'center',
  flexDirection: 'row',
  wrap: true,
  px: 3,
  mb: 1
})``

const LoadingWrapper = styled(Box)`
  position: relative;
  width: 140px;
  height: 140px;
`

const LoadingCarousel = () => (
  <Fragment>
    <Heading.h3>Loading Projects…</Heading.h3>
    <LoadingWrapper>
      <Loading />
    </LoadingWrapper>
  </Fragment>
)

const AllProjects = projects => (
  <ShowAllGrid>
    {projects.map(project => (
      <CarouselProject
        project={project}
        key={project.screenshot.id}
        m={[1, null, 2]}
      />
    ))}
  </ShowAllGrid>
)

const LiveProject = submissionProject => (
  <StaticWrapper>
    <CarouselProject liveFrame project={submissionProject} />
  </StaticWrapper>
)

const EmptyProject = emptyProject => (
  <StaticWrapper>
    <CarouselProject project={emptyProject} />
  </StaticWrapper>
)

const ShortList = projects => (
  <StaticWrapper>
    {projects.map(project => (
      <CarouselProject project={project} key={project.screenshot.id} />
    ))}
  </StaticWrapper>
)

const sliderSettings = {
  arrows: false,
  speed: 500,
  autoplay: true,
  initialSlide: 0,
  autoplaySpeed: 5000,
  slidesToShow: 1,
  slidesToScroll: 1,
  centerMode: true,
  pauseOnHover: true,
  variableWidth: true,
  focusOnSelect: true
}

const SliderList = projects => (
  <SliderWrapper>
    <RehackSlider {...sliderSettings}>
      {projects.map(project => (
        <CarouselProject project={project} key={project.screenshot.id} />
      ))}
    </RehackSlider>
  </SliderWrapper>
)

const LoadedCarousel = (
  projects,
  projectCount,
  showAll,
  onClickShowAll,
  liveFrameStatus,
  submissionProject,
  emptyProject
) => (
  <Fragment>
    <Flex
      flexDirection={['column', null, 'row']}
      align="center"
      mb={[0, null, 3]}
    >
      <Heading.h3 fontSize={4} mr={[null, null, 3]}>
        {projectCount} Rehack
        {projectCount != 1 && 's'}
      </Heading.h3>
      <ShowAllProjects onClick={onClickShowAll} aria-expanded={showAll}>
        <Icon glyph="down-caret" size={32} />
        <Text.span
          ml={1}
          children={showAll ? 'Show Less' : 'Show All'}
        />
      </ShowAllProjects>
    </Flex>
    {showAll
      ? AllProjects(projects)
      : liveFrameStatus != 'empty'
        ? LiveProject(submissionProject)
        : projects.length == 0
          ? EmptyProject(emptyProject)
          : projects.length < 3
            ? ShortList(projects)
            : SliderList(projects)}
  </Fragment>
)

class Carousel extends Component {
  state = {
    loading: true,
    submitting: false,
    submissionData: {
      liveUrl: '',
      codeUrl: ''
    },
    authed: false,
    authData: {},
    liveFrameStatus: 'empty',
    liveFrameImage: null,
    showAll: false,
    original: this.emptyProject,
    projects: []
  }
  emptyProject = {
    user: { username: '' },
    empty: true,
    live_url: '',
    code_url: '',
    screenshot: {
      id: -1,
      created_at: '',
      updated_at: '',
      type: '',
      file_path: ''
    }
  }

  componentDidMount() {
    const { slug } = this.props

    api.get(`v1/workshops/${slug}/projects`).then(projects => {
      let original = projects.find(
        project => project.user && project.user.username == 'prophetorpheus'
      )
      if (original) {
        remove(projects, original)
        projects.unshift(original)
      } else original = this.emptyProject

      this.setState({
        loading: false,
        original,
        projects
      })
    })

    api
      .get(`v1/users/current`)
      .then(authData => {
        console.log(
          `User is authorized! Auth data: ${JSON.stringify(authData)}`
        )
        this.setState({ authed: true, authData })
      })
      .catch(error => {
        console.log(`User is not authorized! Error: ${JSON.stringify(error)}`)
        this.setState({ authed: false, authData: {} })
      })
  }

  setLiveFrameStatus(liveFrameStatus) {
    this.setState({ liveFrameStatus })
  }

  setSubmissionData = submissionData => {
    // Disable "Live Frame" feature for now
    // this.setLiveFrameStatus(submissionData.liveUrl == '' ? 'empty' : 'loading')
    this.setState({ submissionData })
  }

  onClickSubmitButton = () => {
    this.setState({ submitting: true })
  }

  onClickShowAll = () => {
    this.setState({ showAll: !this.state.showAll })
  }

  onSignOut = () => {
    console.log('Signing out')
    this.setState({ authed: false, authData: {} })
  }

  render() {
    const { slug } = this.props
    const {
      loading,
      original,
      projects,
      submitting,
      submissionData,
      authed,
      authData,
      liveFrameStatus,
      // liveFrameImage,
      showAll
    } = this.state

    const {
      setSubmissionData,
      onClickSubmitButton,
      onClickShowAll,
      onSignOut,
      emptyProject
    } = this

    const submissionProject = {
      user: authData,
      live_url: submissionData.liveUrl,
      code_url: submissionData.codeUrl,
      screenshot: {}
    }

    const projectCount = projects.length - (original ? 0 : 1)

    return (
      <CarouselOuter>
        {loading
          ? LoadingCarousel()
          : LoadedCarousel(
              projects,
              projectCount,
              showAll,
              onClickShowAll,
              liveFrameStatus,
              submissionProject,
              emptyProject
            )}

        {submitting ? (
          <CarouselSubmissionForm
            authed={authed}
            authData={authData}
            onSignOut={onSignOut}
            workshopSlug={slug}
            submissionData={submissionData}
            setSubmissionData={setSubmissionData}
          />
        ) : (
          <LargeButton scale fontSize={4} onClick={onClickSubmitButton}>
            I made something
          </LargeButton>
        )}
      </CarouselOuter>
    )
  }
}

export default Carousel
<|endoftext|>


<commit_before>define("js!EventBus", function(){

   var EventBusClass = function() {
      this.listeners = {};
   };

   EventBusClass.prototype = {
      on :function(type, callback, scope) {
         var args = [];
         var numOfArgs = arguments.length;
         for(var i=0; i<numOfArgs; i++){
            args.push(arguments[i]);
         }
         args = args.length > 3 ? args.splice(3, args.length-1) : [];
         if(typeof this.listeners[type] != "undefined") {
            this.listeners[type].push({scope:scope, callback:callback, args:args});
         } else {
            this.listeners[type] = [{scope:scope, callback:callback, args:args}];
         }
      },
      off :function(type, callback, scope) {
         if(typeof this.listeners[type] != "undefined") {
            var numOfCallbacks = this.listeners[type].length;
            var newArray = [];
            for(var i=0; i<numOfCallbacks; i++) {
               var listener = this.listeners[type][i];
               if(listener.scope == scope && listener.callback == callback) {

               } else {
                  newArray.push(listener);
               }
            }
            this.listeners[type] = newArray;
         }
      },
      has :function(type, callback, scope) {
         if(typeof this.listeners[type] != "undefined") {
            var numOfCallbacks = this.listeners[type].length;
            if(callback === undefined && scope === undefined){
               return numOfCallbacks > 0;
            }
            for(var i=0; i<numOfCallbacks; i++) {
               var listener = this.listeners[type][i];
               if(listener.scope == scope && listener.callback == callback) {
                  return true;
               }
            }
         }
         return false;
      },
      trigger :function(type, target) {
         var numOfListeners = 0;
         var event = {
            type:type,
            target:target
         };
         var args = [];
         var numOfArgs = arguments.length;
         for(var i=0; i<numOfArgs; i++){
            args.push(arguments[i]);
         }
         args = args.length > 2 ? args.splice(2, args.length-1) : [];
         args = [event].concat(args);
         if(typeof this.listeners[type] != "undefined") {
            var numOfCallbacks = this.listeners[type].length;
            for(var j=0; j<numOfCallbacks; j++) {
               var listener = this.listeners[type][j];
               if(listener && listener.callback) {
                  var concatArgs = args.concat(listener.args);
                  listener.callback.apply(listener.scope, concatArgs);
                  numOfListeners += 1;
               }
            }
         }
      },
      getEvents:function() {
         var str = "";
         for(var type in this.listeners) {
            if (this.listeners.hasOwnProperty(type)){
               var numOfCallbacks = this.listeners[type].length;
               for(var i=0; i<numOfCallbacks; i++) {
                  var listener = this.listeners[type][i];
                  str += listener.scope && listener.scope.className ? listener.scope.className : "anonymous";
                  str += " listen for '" + type + "'\n";
               }
            }
         }
         return str;
      }
   };

   var AdvancedEventBus = function(){
      this._channels = {};
   };

   AdvancedEventBus.prototype.channel = function(name){
      this._channels[name] = this._channels[name] || new EventBusClass();
      return this._channels[name];
   };

   AdvancedEventBus.prototype.removeChannel = function(name){
      delete this._channels[name];
   };
   return new AdvancedEventBus();
});
<commit_msg>minor refactoring - renamed arguments<commit_after>define("js!EventBus", function(){

   var EventBusClass = function() {
      this.listeners = {};
   };

   EventBusClass.prototype = {
      on :function(eventName, callback, scope) {
         var args = [];
         var numOfArgs = arguments.length;
         for(var i=0; i<numOfArgs; i++){
            args.push(arguments[i]);
         }
         args = args.length > 3 ? args.splice(3, args.length-1) : [];
         if(typeof this.listeners[eventName] != "undefined") {
            this.listeners[eventName].push({scope:scope, callback:callback, args:args});
         } else {
            this.listeners[eventName] = [{scope:scope, callback:callback, args:args}];
         }
      },
      off :function(eventName, callback, scope) {
         if(typeof this.listeners[eventName] != "undefined") {
            var numOfCallbacks = this.listeners[eventName].length;
            var newArray = [];
            for(var i=0; i<numOfCallbacks; i++) {
               var listener = this.listeners[eventName][i];
               if(listener.scope == scope && listener.callback == callback) {

               } else {
                  newArray.push(listener);
               }
            }
            this.listeners[eventName] = newArray;
         }
      },
      has :function(eventName, callback, scope) {
         if(typeof this.listeners[eventName] != "undefined") {
            var numOfCallbacks = this.listeners[eventName].length;
            if(callback === undefined && scope === undefined){
               return numOfCallbacks > 0;
            }
            for(var i=0; i<numOfCallbacks; i++) {
               var listener = this.listeners[eventName][i];
               if(listener.scope == scope && listener.callback == callback) {
                  return true;
               }
            }
         }
         return false;
      },
      trigger :function(eventName, data) {
         var numOfListeners = 0;
         var event = {
            type:eventName,
            target:data
         };
         var args = [];
         var numOfArgs = arguments.length;
         for(var i=0; i<numOfArgs; i++){
            args.push(arguments[i]);
         }
         args = args.length > 2 ? args.splice(2, args.length-1) : [];
         args = [event].concat(args);
         if(typeof this.listeners[eventName] != "undefined") {
            var numOfCallbacks = this.listeners[eventName].length;
            for(var j=0; j<numOfCallbacks; j++) {
               var listener = this.listeners[eventName][j];
               if(listener && listener.callback) {
                  var concatArgs = args.concat(listener.args);
                  listener.callback.apply(listener.scope, concatArgs);
                  numOfListeners += 1;
               }
            }
         }
      },
      getEvents:function() {
         var str = "";
         for(var type in this.listeners) {
            if (this.listeners.hasOwnProperty(type)){
               var numOfCallbacks = this.listeners[type].length;
               for(var i=0; i<numOfCallbacks; i++) {
                  var listener = this.listeners[type][i];
                  str += listener.scope && listener.scope.className ? listener.scope.className : "anonymous";
                  str += " listen for '" + type + "'\n";
               }
            }
         }
         return str;
      }
   };

   var AdvancedEventBus = function(){
      this._channels = {};
   };

   AdvancedEventBus.prototype.channel = function(name){
      this._channels[name] = this._channels[name] || new EventBusClass();
      return this._channels[name];
   };

   AdvancedEventBus.prototype.removeChannel = function(name){
      delete this._channels[name];
   };
   return new AdvancedEventBus();
});
<|endoftext|>