<commit_before>import React, {Component} from 'react';
import Helmet from 'react-helmet';
import cookie from 'react-cookie';
import superagent from 'superagent';
import { Well, Alert, Button } from 'react-bootstrap/lib';

import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';

import { Link } from 'react-router';

import { getBlogEntries } from '../../redux/actions/getBlogEntriesActions';
import { msgBox } from '../../redux/actions/msgBoxActions';

@connect((store) => {
  return {
    activateNewUserState: store.activateNewUser.userStatus,
    getBlogEntriesState: store.getBlogEntries.articleList,
  };
})

export default class Article extends Component {
  state = {
    formStatus: 0,
    formMsg: '',
    specificArticleData: {},
    specificArticleTechData: '',
    artDeleteStatus: false,
    r01: false, r02: false, r03: false, r04: false, r05: false, r06: false,
    ratedRadio: null
  }

  componentDidMount() {
    let getUrl = this.props.params.id;
    const stylesCommunity = require('../Community/Community.scss');

    superagent
    .post('/getSpecificArticleWithUrl')
    .send({ urlFriendly: getUrl })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if(res.body.status === 1) {
        this.setState({specificArticleData: res.body.specificArticleData});
        let sadData = Object.keys(this.state.specificArticleData.technologies[0]).map(key => this.state.specificArticleData.technologies[0][key].length > 1 ?  : null );
        this.setState({ specificArticleTechData: sadData });
      } else{
        console.log("Error, Article url does not exist in DB");
      }
    });
  }

  rateOrComment = (category, rateOrCommentValue, targetArticleId, targetUuid) => {
    let commentersUuid = cookie.load('ck_uuid');
    let commentersAvatar = cookie.load('ck_avatar');
    let commentersKanton = cookie.load('ck_kanton');
    let commentersNickname = cookie.load('ck_nickname');
    let commentersNicknameUrl = cookie.load('ck_nicknameUrl');

    let checkCode = 1;
    if(category === 'comment'){
      if(rateOrCommentValue.length < 3){
        this.setState({formStatus: 1});
        this.setState({formMsg: 'Fehler: Dein Kommentar ist zu kurz, verfasse mindestens 3 Zeichen!'});
        checkCode = 0;
      }
      if(rateOrCommentValue.length > 1000){
        this.setState({formStatus: 1});
        this.setState({formMsg: 'Fehler: Dein Kommentar ist leider etwas zu lange! Kommentare sollten 1000 Zeichen nicht überschreiten!'});
        checkCode = 0;
      }
    }
    if(category === 'rate'){
      if(rateOrCommentValue.length < 1){
        this.setState({formStatus: 2});
        this.setState({formMsg: 'Fehler: Keine Reaction ausgewählt!'});
        checkCode = 0;
      }
    }

    if(checkCode === 1){
      superagent
      .post('/rateOrComment')
      .send({ category: category, rateOrCommentValue: rateOrCommentValue, targetArticleId: targetArticleId, targetUuid: targetUuid, commentersUuid: commentersUuid, commentersAvatar: commentersAvatar, commentersKanton: commentersKanton, commentersNickname: commentersNickname, commentersNicknameUrl: commentersNicknameUrl })
      .set('Accept', 'application/json')
      .end((error, res) => {
        if(res.body.status === 1) {
          console.log("Success, dispatch Action here to reload Comments/Ratings..");
        } else{
          console.log("Error, rateOrComment");
        }
      });
    }
  }

  checkProfile = (nicknameUrl) => {
    this.props.dispatch(push('/user/'+nicknameUrl));
  }

  editArticle = (id) => {
    cookie.save('ck_tempEditArt', id, { path: '/', expires: new Date(new Date().getTime() + (3600*3600*3600)) });
    this.props.dispatch(push('/community'));
  }

  onChangeRadio = (event, rValue) => {
    if(rValue === 'r01'){ this.setState({ r01: true, r02: false, r03: false, r04: false, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r02'){ this.setState({ r01: false, r02: true, r03: false, r04: false, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r03'){ this.setState({ r01: false, r02: false, r03: true, r04: false, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r04'){ this.setState({ r01: false, r02: false, r03: false, r04: true, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r05'){ this.setState({ r01: false, r02: false, r03: false, r04: false, r05: true, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r06'){ this.setState({ r01: false, r02: false, r03: false, r04: false, r05: false, r06: true }); this.setState({ ratedRadio: rValue }); }
  }

  deleteArticle = () => {
    this.setState({ artDeleteStatus: true });
  }

  deleteArticleRevert = () => {
    this.setState({ artDeleteStatus: false });
  }

  deleteArticleDef = (deleteArticleID) => {
    superagent
    .post('/deleteArticle')
    .send({ deleteArticleID: deleteArticleID })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if(res.body.status === 1) {
        this.props.dispatch(push('/meinprofil'));
        scroll(0,0);
        this.props.dispatch(msgBox(true, 'Der Artikel wurde erfolgreich gelöscht! Schliessen'));
      }
    });
  }

  render() {
    const stylesArticle = require('./Article.scss');
    const styles = require('../Home/Home.scss');
    const stylesMyProfile = require('../MyProfile/MyProfile.scss');

    const {artDeleteStatus, formStatus, formMsg, specificArticleData, specificArticleTechData, ratedRadio} = this.state;
    const { activateNewUserState, getBlogEntriesState} = this.props;

    return (

        <Helmet title={specificArticleData.titel}/>

{specificArticleData.titel + ' '}
                    { specificArticleData.userUuid === cookie.load('ck_uuid') ?

                        <button className={"btn btn-primary " + stylesArticle.mr5} onClick={() => this.editArticle(specificArticleData.articleId)}>Deinen Beitrag bearbeiten</button>
                        { artDeleteStatus === false ?
                          <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.deleteArticle()}> Beitrag löschen</button>
                        :

                            <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.deleteArticleDef(specificArticleData.articleId)}>Wirklich löschen?</button>
                            <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.deleteArticleRevert()}></button>

                        }

                    : null
                    }

 this.checkProfile(specificArticleData.nicknameUrl)}>

Autor: {specificArticleData.userNickname}

{specificArticleTechData}

Beitrag vom: {specificArticleData.timeFormatted} | Kategorie: {specificArticleData.category}

                    Reactionen
                    {formStatus === 2 ?
                      <Alert bsStyle="danger">

</Alert>
                    : null
                    }

Wie findest du diesen Beitrag? Sende dem Autor deine Reaction!

                      <fieldset>

                          <input checked={this.state.r01} type="radio" onChange={(event) => this.onChangeRadio(event, 'r01')} ref="r01" id="r01" value="r01" />

                          <input checked={this.state.r02} type="radio" onChange={(event) => this.onChangeRadio(event, 'r02')} ref="r02" id="r02" value="r02" />

                          <input checked={this.state.r03} type="radio" onChange={(event) => this.onChangeRadio(event, 'r03')} ref="r03" id="r03" value="r03" />

                          <input checked={this.state.r04} type="radio" onChange={(event) => this.onChangeRadio(event, 'r04')} ref="r04" id="r04" value="r04" />

                          <input checked={this.state.r05} type="radio" onChange={(event) => this.onChangeRadio(event, 'r05')} ref="r05" id="r05" value="r05" />

                          <input checked={this.state.r06} type="radio" onChange={(event) => this.onChangeRadio(event, 'r06')} ref="r06" id="r06" value="r06" />

                      </fieldset>
                      <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.rateOrComment('rate',ratedRadio, specificArticleData.articleId, specificArticleData.userUuid)}>Sende Reaction</button>

List all existing Reactions here...

                    Kommentare
                    {formStatus === 1 ?
                      <Alert bsStyle="danger">

</Alert>
                    : null
                    }
                    <textarea name="comment" ref="comment" placeholder="Dein Kommentar..." className={'form-control ' + stylesMyProfile.fixTextarea}></textarea>
                    <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.rateOrComment('comment',this.refs.comment.value, specificArticleData.articleId, specificArticleData.userUuid)}>Kommentar posten</button>

List all existing Comments here...

    );
  }
}
<commit_msg>asdff<commit_after>import React, {Component} from 'react';
import Helmet from 'react-helmet';
import cookie from 'react-cookie';
import superagent from 'superagent';
import { Well, Alert, Button } from 'react-bootstrap/lib';

import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';

import { Link } from 'react-router';

import { getBlogEntries } from '../../redux/actions/getBlogEntriesActions';
import { msgBox } from '../../redux/actions/msgBoxActions';

@connect((store) => {
  return {
    activateNewUserState: store.activateNewUser.userStatus,
    getBlogEntriesState: store.getBlogEntries.articleList,
  };
})

export default class Article extends Component {
  state = {
    formStatus: 0,
    formMsg: '',
    specificArticleData: {},
    specificArticleTechData: '',
    artDeleteStatus: false,
    r01: false, r02: false, r03: false, r04: false, r05: false, r06: false,
    ratedRadio: null
  }

  componentDidMount() {
    let getUrl = this.props.params.id;
    const stylesCommunity = require('../Community/Community.scss');

    superagent
    .post('/getSpecificArticleWithUrl')
    .send({ urlFriendly: getUrl })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if(res.body.status === 1) {
        this.setState({specificArticleData: res.body.specificArticleData});
        let sadData = Object.keys(this.state.specificArticleData.technologies[0]).map(key => this.state.specificArticleData.technologies[0][key].length > 1 ?  : null );
        this.setState({ specificArticleTechData: sadData });
      } else{
        console.log("Error, Article url does not exist in DB");
      }
    });
  }

  rateOrComment = (category, rateOrCommentValue, targetArticleId, targetUuid) => {
    let commentersUuid = cookie.load('ck_uuid');
    let commentersAvatar = cookie.load('ck_avatar');
    let commentersKanton = cookie.load('ck_kanton');
    let commentersNickname = cookie.load('ck_nickname');
    let commentersNicknameUrl = cookie.load('ck_nicknameUrl');

    let checkCode = 1;
    if(category === 'comment'){
      if(rateOrCommentValue.length < 3){
        this.setState({formStatus: 1});
        this.setState({formMsg: 'Fehler: Dein Kommentar ist zu kurz, verfasse mindestens 3 Zeichen!'});
        checkCode = 0;
      }
      if(rateOrCommentValue.length > 1000){
        this.setState({formStatus: 1});
        this.setState({formMsg: 'Fehler: Dein Kommentar ist leider etwas zu lange! Kommentare sollten 1000 Zeichen nicht überschreiten!'});
        checkCode = 0;
      }
    }
    if(category === 'rate'){
      if(rateOrCommentValue.length < 1){
        this.setState({formStatus: 2});
        this.setState({formMsg: 'Fehler: Keine Reaction ausgewählt!'});
        checkCode = 0;
      }
    }

    if(checkCode === 1){
      superagent
      .post('/rateOrComment')
      .send({ category: category, rateOrCommentValue: rateOrCommentValue, targetArticleId: targetArticleId, targetUuid: targetUuid, commentersUuid: commentersUuid, commentersAvatar: commentersAvatar, commentersKanton: commentersKanton, commentersNickname: commentersNickname, commentersNicknameUrl: commentersNicknameUrl })
      .set('Accept', 'application/json')
      .end((error, res) => {
        if(res.body.status === 1) {
          console.log("Success, dispatch Action here to reload Comments/Ratings..");
        } else{
          console.log("Error, rateOrComment");
        }
      });
    }
  }

  checkProfile = (nicknameUrl) => {
    this.props.dispatch(push('/user/'+nicknameUrl));
  }

  editArticle = (id) => {
    cookie.save('ck_tempEditArt', id, { path: '/', expires: new Date(new Date().getTime() + (3600*3600*3600)) });
    this.props.dispatch(push('/community'));
  }

  onChangeRadio = (event, rValue) => {
    if(rValue === 'r01'){ this.setState({ r01: true, r02: false, r03: false, r04: false, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r02'){ this.setState({ r01: false, r02: true, r03: false, r04: false, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r03'){ this.setState({ r01: false, r02: false, r03: true, r04: false, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r04'){ this.setState({ r01: false, r02: false, r03: false, r04: true, r05: false, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r05'){ this.setState({ r01: false, r02: false, r03: false, r04: false, r05: true, r06: false }); this.setState({ ratedRadio: rValue }); }
    if(rValue === 'r06'){ this.setState({ r01: false, r02: false, r03: false, r04: false, r05: false, r06: true }); this.setState({ ratedRadio: rValue }); }
  }

  deleteArticle = () => {
    this.setState({ artDeleteStatus: true });
  }

  deleteArticleRevert = () => {
    this.setState({ artDeleteStatus: false });
  }

  deleteArticleDef = (deleteArticleID) => {
    superagent
    .post('/deleteArticle')
    .send({ deleteArticleID: deleteArticleID })
    .set('Accept', 'application/json')
    .end((error, res) => {
      if(res.body.status === 1) {
        this.props.dispatch(push('/meinprofil'));
        scroll(0,0);
        this.props.dispatch(msgBox(true, 'Der Artikel wurde erfolgreich gelöscht! Schliessen'));
      }
    });
  }

  render() {
    const stylesArticle = require('./Article.scss');
    const styles = require('../Home/Home.scss');
    const stylesMyProfile = require('../MyProfile/MyProfile.scss');

    const {artDeleteStatus, formStatus, formMsg, specificArticleData, specificArticleTechData, ratedRadio} = this.state;
    const { activateNewUserState, getBlogEntriesState} = this.props;

    return (

        <Helmet title={specificArticleData.titel}/>

{specificArticleData.titel + ' '}
                    { specificArticleData.userUuid === cookie.load('ck_uuid') ?

                        <button className={"btn btn-primary " + stylesArticle.mr5} onClick={() => this.editArticle(specificArticleData.articleId)}>Deinen Beitrag bearbeiten</button>
                        { artDeleteStatus === false ?
                          <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.deleteArticle()}> Beitrag löschen</button>
                        :

                            <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.deleteArticleDef(specificArticleData.articleId)}>Wirklich löschen?</button>
                            <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.deleteArticleRevert()}></button>

                        }

                    : null
                    }

 this.checkProfile(specificArticleData.nicknameUrl)}>

Autor: {specificArticleData.userNickname}

{specificArticleTechData}

Beitrag vom: {specificArticleData.timeFormatted} | Kategorie: {specificArticleData.category}

                    Reactionen
                    {formStatus === 2 ?
                      <Alert bsStyle="danger">

</Alert>
                    : null
                    }

Wie findest du diesen Beitrag? Sende dem Autor deine Reaction!

                      <fieldset>

                          <input checked={this.state.r01} type="radio" onChange={(event) => this.onChangeRadio(event, 'r01')} ref="r01" id="r01" value="r01" />

                          <input checked={this.state.r02} type="radio" onChange={(event) => this.onChangeRadio(event, 'r02')} ref="r02" id="r02" value="r02" />

                          <input checked={this.state.r03} type="radio" onChange={(event) => this.onChangeRadio(event, 'r03')} ref="r03" id="r03" value="r03" />

                          <input checked={this.state.r04} type="radio" onChange={(event) => this.onChangeRadio(event, 'r04')} ref="r04" id="r04" value="r04" />

                          <input checked={this.state.r05} type="radio" onChange={(event) => this.onChangeRadio(event, 'r05')} ref="r05" id="r05" value="r05" />

                          <input checked={this.state.r06} type="radio" onChange={(event) => this.onChangeRadio(event, 'r06')} ref="r06" id="r06" value="r06" />

                      </fieldset>
                      <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={(ratedRadio) => this.rateOrComment('rate', ratedRadio, specificArticleData.articleId, specificArticleData.userUuid)}>Sende Reaction</button>

List all existing Reactions here...

                    Kommentare
                    {formStatus === 1 ?
                      <Alert bsStyle="danger">

</Alert>
                    : null
                    }
                    <textarea name="comment" ref="comment" placeholder="Dein Kommentar..." className={'form-control ' + stylesMyProfile.fixTextarea}></textarea>
                    <button className={"btn btn-default " + stylesArticle.btnDelete} onClick={() => this.rateOrComment('comment', this.refs.comment.value, specificArticleData.articleId, specificArticleData.userUuid)}>Kommentar posten</button>

List all existing Comments here...

    );
  }
}
<|endoftext|>


<commit_before>app.api = function() {
	var errorHandler;
	var customHeaders = {};

	/**
	 * Format the URL with access token and API URL
	 * @param String endpoint
	 * @return String
	 */
	var formatURL = function(endpoint) {
		return app.config.API + endpoint;
	};

	/**
	 * Display a nice error message if the request failed
	 * @param XMLHttpRequest request
	 * @param String textStatus
	 * @param String error
	 */
	var handleError = function(request, textStatus, error) {
		if (errorHandler) {
			errorHandler(request.status);
		} else {
			app.log.warning('No API error handler specified.');
			app.log.error('API responded with status ' + request.status);
		}
	};

	/**
	 * Listen for API errors
	 * @param Function callback(httpStatus)
	 */
	this.onError = function(callback) {
		errorHandler = callback;
	};

	/**
	 * Add a custom HTTP header to every API request
	 * @param String key
	 * @param String/Function value
	 */
	this.addHeader = function(key, value) {
		customHeaders[key] = value;
	};

	/**
	 * Merge HTTP headers with custom headers
	 * @param Object headers
	 * @return Object
	 */
	var getHeaders = function(headers) {
		if (!headers) var headers = {};

		if (customHeaders) {
			for (var i in customHeaders) {
				if (typeof customHeaders[i] == 'Function') {
					headers[i] = customHeaders[i]();
				} else {
					headers[i] = customHeaders[i];
				}
			}
		}

		return headers;
	};

	/**
	 * Make a GET request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.get = function(endpoint, callback) {
		app.log.debug('Calling API endpoint [GET ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders(),
			type: 'GET',
			success: callback,
			error: handleError
		});
	};

	/**
	 * Make a POST request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.post = function(endpoint, data, callback) {
		app.log.debug('Calling API endpoint [POST ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders(),
			type: 'POST',
			data: data,
			success: callback,
			error: handleError
		});
	};

	/**
	 * Make a POST request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.put = function(endpoint, data, callback) {
		app.log.debug('Calling API endpoint [POST ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders({
				'x-http-method-override': 'PUT'
			}),
			type: 'POST',
			data: data,
			success: callback,
			error: handleError
		});
	};

	/**
	 * Make a DELETE request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.delete = function(endpoint, data, callback) {
		app.log.debug('Calling API endpoint [DELETE ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders({
				'x-http-method-override': 'DELETE'
			}),
			type: 'POST',
			data: data,
			success: callback,
			error: handleError
		});
	};
};<commit_msg>Added app.api.onSuccess hook<commit_after>app.api = function() {
	var errorHandler;
	var successHandler;
	var customHeaders = {};

	/**
	 * Format the URL with access token and API URL
	 * @param String endpoint
	 * @return String
	 */
	var formatURL = function(endpoint) {
		return app.config.API + endpoint;
	};

	/**
	 * Display a nice error message if the request failed
	 * @param XMLHttpRequest request
	 * @param String textStatus
	 * @param String error
	 */
	var handleError = function(request, textStatus, error) {
		if (errorHandler) {
			return errorHandler(request.status);
		}

		app.log.warning('No API error handler specified.');
		app.log.error('API responded with status ' + request.status);
	};

	/**
	 * Listen for API errors
	 * @param Function callback(httpStatus)
	 */
	this.onError = function(callback) {
		errorHandler = callback;
	};

	/**
	 * Listen for successful API calls
	 * @param Function callback(data, successCallback)
	 */
	this.onSuccess = function(callback) {
		successHandler = callback;
	};

	/**
	 * Add a custom HTTP header to every API request
	 * @param String key
	 * @param String/Function value
	 */
	this.addHeader = function(key, value) {
		customHeaders[key] = value;
	};

	/**
	 * Merge HTTP headers with custom headers
	 * @param Object headers
	 * @return Object
	 */
	var getHeaders = function(headers) {
		if (!headers) var headers = {};

		if (customHeaders) {
			for (var i in customHeaders) {
				if (typeof customHeaders[i] == 'Function') {
					headers[i] = customHeaders[i]();
				} else {
					headers[i] = customHeaders[i];
				}
			}
		}

		return headers;
	};

	/**
	 * Handle successful API requests
	 * @param Mixed data
	 * @param Function callback(data)
	 */
	var handleSuccess = function(data, callback) {
		if (!successHandler) {
			return callback(data);
		}

		successHandler(data, callback);
	};

	/**
	 * Make a GET request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.get = function(endpoint, callback) {
		app.log.debug('Calling API endpoint [GET ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders(),
			type: 'GET',
			success: function(data) {
				handleSuccess(data, callback);
			},
			error: handleError
		});
	};

	/**
	 * Make a POST request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.post = function(endpoint, data, callback) {
		app.log.debug('Calling API endpoint [POST ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders(),
			type: 'POST',
			data: data,
			success: function(data) {
				handleSuccess(data, callback);
			},
			error: handleError
		});
	};

	/**
	 * Make a POST request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.put = function(endpoint, data, callback) {
		app.log.debug('Calling API endpoint [POST ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders({
				'x-http-method-override': 'PUT'
			}),
			type: 'POST',
			data: data,
			success: function(data) {
				handleSuccess(data, callback);
			},
			error: handleError
		});
	};

	/**
	 * Make a DELETE request to the API server
	 * @param String endpoint
	 * @param Function callback
	 */
	this.delete = function(endpoint, data, callback) {
		app.log.debug('Calling API endpoint [DELETE ' + endpoint + ']');

		$.ajax({
			url: formatURL(endpoint),
			headers: getHeaders({
				'x-http-method-override': 'DELETE'
			}),
			type: 'POST',
			data: data,
			success: function(data) {
				handleSuccess(data, callback);
			},
			error: handleError
		});
	};
};<|endoftext|>


<commit_before>define(function() {

  var settings = {
    t1: {min: -1, max: 1},
    d1: {min: -1, max: 1},
    m1: {min: 0, max: 20},
    n11: {min: 0, max: 100},
    n12: {min: 0, max: 100},
    n13: {min: 0, max: 100},
    t2: {min: -1, max: 1},
    d2: {min: -1, max: 1},
    n21: {min: 0, max: 100},
    n22: {min: 0, max: 100},
    n23: {min: 0, max: 100},
    m2: {min: 0, max: 100},
    c1: {min: 0, max: 8},
    c2: {min: 0, max: 8},
    c3: {min: 0, max: 8}
  };

  var controls = new function() {

    this.t1 = 0;
    this.d1 = 0;
    this.m1 = 6;
    this.n11 = 8;
    this.n12 = 15;
    this.n13 = 30;

    this.t2 = 0;
    this.d2 = 0;
    this.m2 = 4;
    this.n21 = 12;
    this.n22 = 10;
    this.n23 = 10;

    this.c1 = 2;
    this.c2 = 4;
    this.c3 = 1;

  };

  var gui = new dat.GUI();

  var f1 = gui.addFolder('First Form');

  for (var i in settings) {
    var s = settings[i];
    f1.add(controls, i, s.min, s.max).listen().onChange(update);
  }

  var listeners = [];

  function update () {
    listeners.forEach(function  (listener) {
      listener(controls);
    })
  }

  return {
    onChange: function  (func) {
      listeners.push(func);
    },
    controls: controls
  }

});
<commit_msg>minor gui changes<commit_after>define(function() {

  var settings = {
    t1: {min: -2, max: 2},
    d1: {min: -2, max: 2},
    m1: {min: 0, max: 20},
    n11: {min: 0, max: 100},
    n12: {min: 0, max: 100},
    n13: {min: 0, max: 100},
    t2: {min: -2, max: 2},
    d2: {min: -2, max: 2},
    n21: {min: 0, max: 100},
    n22: {min: 0, max: 100},
    n23: {min: 0, max: 100},
    m2: {min: 0, max: 100},
    c1: {min: 0, max: 8},
    c2: {min: 0, max: 8},
    c3: {min: 0, max: 8}
  };

  var controls = new function() {

    this.t1 = 0;
    this.d1 = 0;
    this.m1 = 6;
    this.n11 = 8;
    this.n12 = 15;
    this.n13 = 30;

    this.t2 = 0;
    this.d2 = 0;
    this.m2 = 4;
    this.n21 = 12;
    this.n22 = 10;
    this.n23 = 10;

    this.c1 = 2;
    this.c2 = 4;
    this.c3 = 1;

  };

  var gui = new dat.GUI();

//  var f1 = gui.addFolder('First Form');

  for (var i in settings) {
    var s = settings[i];
    gui.add(controls, i, s.min, s.max).listen().onChange(update);
  }

  var listeners = [];

  function update () {
    listeners.forEach(function  (listener) {
      listener(controls);
    })
  }

  return {
    onChange: function  (func) {
      listeners.push(func);
    },
    controls: controls
  }

});
<|endoftext|>


<commit_before>import _ from 'underscore';
import fuzzy from 'fuzzyset.js'
const jsDiff = require('diff');

export default class Question {
  constructor(data) {
    this.prompt = data.prompt;
    this.sentences = data.sentences;
    this.responses = data.responses;
  }

  checkMatch(response) {
    // remove leading and trailing whitespace
    response = response.trim();
    // make sure all words are single spaced
    response = response.replace(/\s{2,}/g, ' ');
    var returnValue = {
      found: true,
      submitted: response
    }
    var exactMatch = this.checkExactMatch(response)
    if (exactMatch !== undefined) {
      returnValue.response = exactMatch
      return returnValue
    }
    var lowerCaseMatch = this.checkCaseInsensitiveMatch(response)
    if (lowerCaseMatch !== undefined) {
      returnValue.caseError = true
      returnValue.author = "CaseInsensitive"
      returnValue.response = lowerCaseMatch
      return returnValue
    }
    var punctuationMatch = this.checkPunctuationInsensitiveMatch(response)
    if (punctuationMatch !== undefined) {
      returnValue.punctuationError = true
      returnValue.author = "PunctuationInsensitive"
      returnValue.response = punctuationMatch
      return returnValue
    }
    var typingErrorMatch = this.checkFuzzyMatch(response)
    if (typingErrorMatch !== undefined) {
      returnValue.typingError = true
      returnValue.author = "Fuzzy"
      returnValue.response = typingErrorMatch
      return returnValue
    }
    returnValue.found = false
    return returnValue
  }

  nonChildResponses(responses) {
    return _.filter(this.responses, function (resp){
      return resp.parentID === undefined && resp.feedback !== undefined
    })
  }

  checkExactMatch(response) {
    return _.find(this.responses, (resp) => {
      return resp.text === response;
    });
  }

  checkCaseInsensitiveMatch(response) {
    return _.find(this.nonChildResponses(this.responses), (resp) => {
      return resp.text.toLowerCase() === response.toLowerCase();
    });
  }

  checkPunctuationInsensitiveMatch(response) {
    return _.find(this.nonChildResponses(this.responses), (resp) => {
      return removePunctuation(resp.text) === removePunctuation(response)
    });
  }

  checkSmallTypoMatch(response) {
    return _.find(this.nonChildResponses(this.responses), (resp) => {
      return getLowAdditionCount(response, resp.text)
    });
  }

  checkFuzzyMatch(response) {
    const set = fuzzy(_.pluck(this.responses, "text"));
    const matches = set.get(response, []);
    var response = undefined;
    var text = undefined;
    if (matches.length > 0) {
      var threshold = (matches[0][1].length - 3) / matches[0][1].length
      // console.log("\nmatch: ", matches[0][0], " threshold: ", threshold)
      text = matches[0][0] > threshold ? matches[0][1] : null;
    }
    if (text) {
      response = _.findWhere(this.responses, {text})
    }
    return response
  }
}

const removePunctuation = (string) => {
  return string.replace(/[^A-Za-z0-9\s]/g,"")
}

// Check number of chars added.

const getLowAdditionCount = (newString, oldString) => {
  var diff = jsDiff.diffChars(newString, oldString)
  var additions = _.where(diff, {added: true})
  if (additions.length > 1) {
    return false
  }
  var count = _.reduce(additions, function(memo, num){ return memo + num.count; }, 0)
  if (count < 3) {
    return true
  }
  return false
}
<commit_msg>Update the questions object to have better spelling checks, use new vocal, and determine if answers are too short/long<commit_after>import _ from 'underscore';
import fuzzy from 'fuzzyset.js'
const jsDiff = require('diff');

export default class Question {
  constructor(data) {
    this.prompt = data.prompt;
    this.sentences = data.sentences;
    this.responses = data.responses;
  }

  getOptimalResponses() {
    return _.where(this.responses, {optimal: true})
  }

  checkMatch(response) {
    // remove leading and trailing whitespace
    response = response.trim();
    // make sure all words are single spaced
    response = response.replace(/\s{2,}/g, ' ');
    var returnValue = {
      found: true,
      submitted: response
    }
    var exactMatch = this.checkExactMatch(response)
    if (exactMatch !== undefined) {
      returnValue.response = exactMatch
      return returnValue
    }
    var lowerCaseMatch = this.checkCaseInsensitiveMatch(response)
    if (lowerCaseMatch !== undefined) {
      returnValue.caseError = true
      returnValue.author = "Capitalization Hint"
      returnValue.response = lowerCaseMatch
      return returnValue
    }
    var punctuationMatch = this.checkPunctuationInsensitiveMatch(response)
    if (punctuationMatch !== undefined) {
      returnValue.punctuationError = true
      returnValue.author = "Punctuation Hint"
      returnValue.response = punctuationMatch
      return returnValue
    }
    var typingErrorMatch = this.checkFuzzyMatch(response)
    if (typingErrorMatch !== undefined) {
      returnValue.typingError = true
      returnValue.author = "Word Error Hint"
      returnValue.response = typingErrorMatch
      return returnValue
    }
    var minLengthMatch = this.checkMinLengthMatch(response)
    if (minLengthMatch !== undefined) {
      returnValue.minLengthError = true
      returnValue.author = "Missing Details Hint"
      returnValue.response = minLengthMatch
      return returnValue
    }
    var maxLengthMatch = this.checkMaxLengthMatch(response)
    if (maxLengthMatch !== undefined) {
      returnValue.maxLengthError = true
      returnValue.author = "Not Concise Hint"
      returnValue.response = maxLengthMatch
      return returnValue
    }
    returnValue.found = false
    return returnValue
  }

  nonChildResponses(responses) {
    return _.filter(this.responses, function (resp){
      return resp.parentID === undefined && resp.feedback !== undefined
    })
  }

  checkExactMatch(response) {
    return _.find(this.responses, (resp) => {
      return resp.text === response;
    });
  }

  checkCaseInsensitiveMatch(response) {
    return _.find(this.nonChildResponses(this.responses), (resp) => {
      return resp.text.toLowerCase() === response.toLowerCase();
    });
  }

  checkPunctuationInsensitiveMatch(response) {
    return _.find(this.nonChildResponses(this.responses), (resp) => {
      return removePunctuation(resp.text) === removePunctuation(response)
    });
  }

  checkSmallTypoMatch(response) {
    return _.find(this.nonChildResponses(this.responses), (resp) => {
      return getLowAdditionCount(response, resp.text)
    });
  }

  checkFuzzyMatch(response) {
    const set = fuzzy(_.pluck(this.responses, "text"));
    const matches = set.get(response, []);
    var foundResponse = undefined;
    var text = undefined;
    if (matches.length > 0) {
      var threshold = (matches[0][1].length - 3) / matches[0][1].length
      // console.log("\nmatch: ", matches[0][0], " threshold: ", threshold)
      text = (matches[0][0] > threshold) && (response.split(" ").length <= matches[0][1].split(" ").length) ? matches[0][1] : null;
    }
    if (text) {
      foundResponse = _.findWhere(this.responses, {text})
    }
    return foundResponse
  }

  checkMinLengthMatch(response) {
    const optimalResponses = this.getOptimalResponses();
    if (optimalResponses.length < 5) {
      return undefined
    }
    const lengthsOfResponses = optimalResponses.map((resp) => {
      return resp.text.split(" ").length;
    })
    const minLength = _.min(lengthsOfResponses)
    if (response.split(" ").length < minLength) {
      return _.sortBy(optimalResponses, (resp) => {
        return resp.text.length
      })[0]
    } else {
      return undefined
    }
  }

  checkMaxLengthMatch(response) {
    const optimalResponses = this.getOptimalResponses();
    if (optimalResponses.length < 5) {
      return undefined
    }
    const lengthsOfResponses = optimalResponses.map((resp) => {
      return resp.text.split(" ").length;
    })
    const maxLength = _.max(lengthsOfResponses)
    if (response.split(" ").length > maxLength) {
      return _.sortBy(optimalResponses, (resp) => {
        return resp.text.length
      }).reverse()[0]
    } else {
      return undefined
    }
  }
}

const removePunctuation = (string) => {
  return string.replace(/[^A-Za-z0-9\s]/g,"")
}

// Check number of chars added.

const getLowAdditionCount = (newString, oldString) => {
  var diff = jsDiff.diffChars(newString, oldString)
  var additions = _.where(diff, {added: true})
  if (additions.length > 1) {
    return false
  }
  var count = _.reduce(additions, function(memo, num){ return memo + num.count; }, 0)
  if (count < 3) {
    return true
  }
  return false
}
<|endoftext|>


<commit_before>import React from 'react'
import { connect } from 'react-redux'
import { prefetch } from 'react-fetcher'
const { array, bool, func, object } = React.PropTypes
import { toPairs, get, some } from 'lodash/fp'
import ModalOnlyPage from '../components/ModalOnlyPage'
import { ModalInput, ModalSelect } from '../components/ModalRow'
import Modal from '../components/Modal'
import Icon from '../components/Icon'
import InviteModal from './InviteModal'
import { uploadImage } from '../actions/uploadImage'
import {
  CREATE_COMMUNITY,
  UPLOAD_IMAGE,
  fetchInvitations,
  navigate
} from '../actions'
import {
  createCommunity,
  resetCommunityValidation,
  updateCommunityEditor,
  validateCommunityAttribute
} from '../actions/communities'
import {
  avatarUploadSettings,
  bannerUploadSettings,
  categories,
  defaultAvatar,
  defaultBanner,
  getCurrentCommunity
} from '../models/community'
import { scrollToBottom } from '../util/scrolling'
import { ADDED_COMMUNITY, trackEvent } from '../util/analytics'
import { saveCurrentCommunityId } from '../actions/util'
import { checklistUrl } from '../routes'

const merkabaUrl = 'https://www.hylo.com/img/hylo-merkaba-300x300.png'

const incrementSuffix = str => {
  const regexp = /\d+$/
  if (!str.match(regexp)) {
    return str + '-1'
  } else {
    return str.replace(/\d+$/, d => +d + 1)
  }
}

const generateSlug = (name, oldSlug) => {
  if (oldSlug) {
    return incrementSuffix(oldSlug)
  } else {
    if (!name) return ''
    return name.toLowerCase().substring(0, 40).trim()
    .replace(/\s/g, '-')
    .replace(/[\!\?'"\/\\\.,]/g, '')
  }
}

export const Topper = ({ community }) => {
  const { name, avatar_url } = community || {}
  const logoUrl = avatar_url || merkabaUrl

  return

    <div className='medium-avatar' style={{backgroundImage: `url(${logoUrl})`}}/>

{name || 'Your New Community'}

}

@connect(({ communityEditor, communityValidation, pending, people }) => {
  const validating = some(id => id, communityValidation.pending)
  let { community, errors } = communityEditor
  const saving = pending[CREATE_COMMUNITY]
  const uploadingImage = !!pending[UPLOAD_IMAGE]
  const currentUser = get('current', people)

  if (!errors) errors = {}
  errors.nameUsed = get('name.unique', communityValidation) === false
  errors.slugUsed = get('slug.unique', communityValidation) === false

  if (!community) community = {}
  if (!community.avatar_url) community.avatar_url = defaultAvatar
  if (!community.banner_url) community.banner_url = defaultBanner

  return {community, errors, validating, saving, uploadingImage, currentUser}
})
export class CreateCommunity extends React.Component {
  static propTypes = {
    saving: bool,
    uploadingImage: bool,
    validating: bool,
    errors: object,
    dispatch: func,
    community: object,
    currentUser: object
  }

  constructor (props) {
    super(props)
    this.state = {expanded: false, editedSlug: false}
  }

  componentWillReceiveProps (nextProps) {
    const { editedSlug } = this.state
    if (!editedSlug && get('errors.slugUsed', nextProps)) {
      const { slug } = this.props.community
      const nextSlug = generateSlug(null, slug)
      this.resetValidation('slug')
      this.modify('slug', nextSlug, true)
    }
  }

  setValue = (key, value) =>
    this.props.dispatch(updateCommunityEditor('community', {[key]: value}))

  setError = obj =>
    this.props.dispatch(updateCommunityEditor('errors', obj))

  resetValidation = key =>
    this.props.dispatch(resetCommunityValidation(key))

  checkUnique = (key, value) =>
    this.props.dispatch(validateCommunityAttribute(key, value, 'unique'))

  // this is a convenience method for use in render() below
  set = (key, isAutoGenerated) => event =>
    this.modify(key, event.target.value)

  modify = (key, value, isAutoGenerated) => {
    this.setValue(key, value)
    this.validate(key, value)
    if (key === 'slug' && !this.state.editedSlug && !isAutoGenerated) {
      this.setState({editedSlug: true})
    }
    if (key === 'name' && !this.state.editedSlug) {
      this.modify('slug', generateSlug(value), true)
    }
  }

  validate (key, value) {
    switch (key) {
      case 'name':
        this.setError({nameBlank: !value})

        if (!value) {
          this.resetValidation('name')
        } else {
          return this.checkUnique('name', value)
        }
        break
      case 'description':
        this.setError({descriptionBlank: !value})
        break
      case 'slug':
        let error = {slugBlank: false, slugInvalid: false}

        if (!value) {
          error.slugBlank = true
        } else if (!value.match(/^[a-z0-9-+]+$/)) {
          error.slugInvalid = true
        }

        this.setError(error)

        if (some(id => id, error)) {
          this.resetValidation('slug')
        } else {
          return this.checkUnique('slug', value)
        }
        break
    }
  }

  attachImage (type) {
    let { dispatch } = this.props
    let community = {id: 'new', slug: 'new'}
    switch (type) {
      case 'avatar':
        dispatch(uploadImage(avatarUploadSettings(community)))
        break
      case 'banner':
        dispatch(uploadImage(bannerUploadSettings(community)))
    }
    return false
  }

  validateAll () {
    let { community } = this.props
    return Promise.all(
      ['name', 'description', 'slug', 'category']
      .map(key => this.validate(key, community[key]))
    )
  }

  submit = () => {
    let { validating, dispatch, community } = this.props
    if (validating) return

    this.validateAll().then(() => {
      if (some(id => id, this.props.errors)) return scrollToBottom()

      dispatch(createCommunity(community))
      .then(({ payload }) => {
        if (some(id => id, this.props.errors)) {
          return scrollToBottom()
        } else {
          trackEvent(ADDED_COMMUNITY, {community})
          saveCurrentCommunityId(dispatch, payload.community_id, true)
          dispatch(navigate(`/invite`))
        }
      })
    })
  }

  render () {
    const { community, errors, uploadingImage, saving } = this.props
    const { expanded } = this.state

    return <ModalOnlyPage className='create-community'>
      <Topper community={community}/>
      <Modal title='Create your community.'
        id='new-community-form'
        subtitle="Let's get started unlocking the creative potential of your community with Hylo."
        standalone>
        <ModalInput label='Community Name' ref='name' onChange={this.set('name')}
          errors={

            {errors.nameBlank &&
Please fill in this field.
}
            {errors.nameUsed &&
This name is already in use.
}

}
          maxLength={80}/>
        <ModalInput label='URL' ref='url' prefix='https://hylo.com/c/'
          onChange={this.set('slug')}
          value={community.slug}
          errors={

              {errors.slugBlank &&
Please fill in this field.
}
              {errors.slugInvalid &&
Use lowercase letters, numbers, and hyphens only.
}
              {errors.slugUsed &&
This URL is already in use.
}

}/>
        <ModalInput label='About your community' ref='description'
          type='textarea'
          onChange={this.set('description')}
          errors={

              {errors.descriptionBlank &&
Please fill in this field.
}

}/>

           this.setState({expanded: !expanded})}>
            More Options
            <Icon name={expanded ? 'Chevron-Up2' : 'Chevron-Down2'} />

        {expanded && <ModalSelect label='Community Type' onChange={this.set('category')}>
          <option value=''>Pick one:</option>
          {toPairs(categories).map(([value, label]) => <option key={value} value={value}>
            {label}
          </option>)}
        </ModalSelect>}
        {expanded && <ModalInput label='Location' onChange={this.set('location')}/>}
        {expanded &&

          Logo
          <div className='small-logo' style={{backgroundImage: `url(${community.avatar_url})`}}>

           this.attachImage('avatar')}>
            {uploadingImage ? 'Saving...' : 'Upload'}

}

            {saving ? 'Creating...' : 'Create'}

      </Modal>
  </ModalOnlyPage>
  }
}

@prefetch(({ params: { id }, dispatch }) =>
  id && dispatch(fetchInvitations(id)))
@connect((state, { params: { id } }) => ({
  community: id ? state.communities[id] : getCurrentCommunity(state),
  invitations: id ? state.invitations[id] : null
}))
export class CreateCommunityInvite extends React.Component {
  static propTypes = {
    community: object,
    dispatch: func,
    invitations: array
  }

  render () {
    const { community, dispatch } = this.props

    const onClose = () => dispatch(navigate(checklistUrl(community)))

    return <ModalOnlyPage className='create-community'>
      <Topper community={community}/>
      <InviteModal onClose={onClose} community={community} standalone/>
    </ModalOnlyPage>
  }
}
<commit_msg>stop changing from uncontrolled to controlled input<commit_after>import React from 'react'
import { connect } from 'react-redux'
import { prefetch } from 'react-fetcher'
const { array, bool, func, object } = React.PropTypes
import { toPairs, get, some } from 'lodash/fp'
import ModalOnlyPage from '../components/ModalOnlyPage'
import { ModalInput, ModalSelect } from '../components/ModalRow'
import Modal from '../components/Modal'
import Icon from '../components/Icon'
import InviteModal from './InviteModal'
import { uploadImage } from '../actions/uploadImage'
import {
  CREATE_COMMUNITY,
  UPLOAD_IMAGE,
  fetchInvitations,
  navigate
} from '../actions'
import {
  createCommunity,
  resetCommunityValidation,
  updateCommunityEditor,
  validateCommunityAttribute
} from '../actions/communities'
import {
  avatarUploadSettings,
  bannerUploadSettings,
  categories,
  defaultAvatar,
  defaultBanner,
  getCurrentCommunity
} from '../models/community'
import { scrollToBottom } from '../util/scrolling'
import { ADDED_COMMUNITY, trackEvent } from '../util/analytics'
import { saveCurrentCommunityId } from '../actions/util'
import { checklistUrl } from '../routes'

const merkabaUrl = 'https://www.hylo.com/img/hylo-merkaba-300x300.png'

const incrementSuffix = str => {
  const regexp = /\d+$/
  if (!str.match(regexp)) {
    return str + '-1'
  } else {
    return str.replace(/\d+$/, d => +d + 1)
  }
}

const generateSlug = (name, oldSlug) => {
  if (oldSlug) {
    return incrementSuffix(oldSlug)
  } else {
    if (!name) return ''
    return name.toLowerCase().substring(0, 40).trim()
    .replace(/\s/g, '-')
    .replace(/[\!\?'"\/\\\.,]/g, '')
  }
}

export const Topper = ({ community }) => {
  const { name, avatar_url } = community || {}
  const logoUrl = avatar_url || merkabaUrl

  return

    <div className='medium-avatar' style={{backgroundImage: `url(${logoUrl})`}}/>

{name || 'Your New Community'}

}

@connect(({ communityEditor, communityValidation, pending, people }) => {
  const validating = some(id => id, communityValidation.pending)
  let { community, errors } = communityEditor
  const saving = pending[CREATE_COMMUNITY]
  const uploadingImage = !!pending[UPLOAD_IMAGE]
  const currentUser = get('current', people)

  if (!errors) errors = {}
  errors.nameUsed = get('name.unique', communityValidation) === false
  errors.slugUsed = get('slug.unique', communityValidation) === false

  if (!community) community = {slug: ''}
  if (!community.avatar_url) community.avatar_url = defaultAvatar
  if (!community.banner_url) community.banner_url = defaultBanner

  return {community, errors, validating, saving, uploadingImage, currentUser}
})
export class CreateCommunity extends React.Component {
  static propTypes = {
    saving: bool,
    uploadingImage: bool,
    validating: bool,
    errors: object,
    dispatch: func,
    community: object,
    currentUser: object
  }

  constructor (props) {
    super(props)
    this.state = {expanded: false, editedSlug: false}
  }

  componentWillReceiveProps (nextProps) {
    const { editedSlug } = this.state
    if (!editedSlug && get('errors.slugUsed', nextProps)) {
      const { slug } = this.props.community
      const nextSlug = generateSlug(null, slug)
      this.resetValidation('slug')
      this.modify('slug', nextSlug, true)
    }
  }

  setValue = (key, value) =>
    this.props.dispatch(updateCommunityEditor('community', {[key]: value}))

  setError = obj =>
    this.props.dispatch(updateCommunityEditor('errors', obj))

  resetValidation = key =>
    this.props.dispatch(resetCommunityValidation(key))

  checkUnique = (key, value) =>
    this.props.dispatch(validateCommunityAttribute(key, value, 'unique'))

  // this is a convenience method for use in render() below
  set = (key, isAutoGenerated) => event =>
    this.modify(key, event.target.value)

  modify = (key, value, isAutoGenerated) => {
    this.setValue(key, value)
    this.validate(key, value)
    if (key === 'slug' && !this.state.editedSlug && !isAutoGenerated) {
      this.setState({editedSlug: true})
    }
    if (key === 'name' && !this.state.editedSlug) {
      this.modify('slug', generateSlug(value), true)
    }
  }

  validate (key, value) {
    switch (key) {
      case 'name':
        this.setError({nameBlank: !value})

        if (!value) {
          this.resetValidation('name')
        } else {
          return this.checkUnique('name', value)
        }
        break
      case 'description':
        this.setError({descriptionBlank: !value})
        break
      case 'slug':
        let error = {slugBlank: false, slugInvalid: false}

        if (!value) {
          error.slugBlank = true
        } else if (!value.match(/^[a-z0-9-+]+$/)) {
          error.slugInvalid = true
        }

        this.setError(error)

        if (some(id => id, error)) {
          this.resetValidation('slug')
        } else {
          return this.checkUnique('slug', value)
        }
        break
    }
  }

  attachImage (type) {
    let { dispatch } = this.props
    let community = {id: 'new', slug: 'new'}
    switch (type) {
      case 'avatar':
        dispatch(uploadImage(avatarUploadSettings(community)))
        break
      case 'banner':
        dispatch(uploadImage(bannerUploadSettings(community)))
    }
    return false
  }

  validateAll () {
    let { community } = this.props
    return Promise.all(
      ['name', 'description', 'slug', 'category']
      .map(key => this.validate(key, community[key]))
    )
  }

  submit = () => {
    let { validating, dispatch, community } = this.props
    if (validating) return

    this.validateAll().then(() => {
      if (some(id => id, this.props.errors)) return scrollToBottom()

      dispatch(createCommunity(community))
      .then(({ payload }) => {
        if (some(id => id, this.props.errors)) {
          return scrollToBottom()
        } else {
          trackEvent(ADDED_COMMUNITY, {community})
          saveCurrentCommunityId(dispatch, payload.community_id, true)
          dispatch(navigate(`/invite`))
        }
      })
    })
  }

  render () {
    const { community, errors, uploadingImage, saving } = this.props
    const { expanded } = this.state

    return <ModalOnlyPage className='create-community'>
      <Topper community={community}/>
      <Modal title='Create your community.'
        id='new-community-form'
        subtitle="Let's get started unlocking the creative potential of your community with Hylo."
        standalone>
        <ModalInput label='Community Name' ref='name' onChange={this.set('name')}
          errors={

            {errors.nameBlank &&
Please fill in this field.
}
            {errors.nameUsed &&
This name is already in use.
}

}
          maxLength={80}/>
        <ModalInput label='URL' ref='url' prefix='https://hylo.com/c/'
          onChange={this.set('slug')}
          value={community.slug}
          errors={

              {errors.slugBlank &&
Please fill in this field.
}
              {errors.slugInvalid &&
Use lowercase letters, numbers, and hyphens only.
}
              {errors.slugUsed &&
This URL is already in use.
}

}/>
        <ModalInput label='About your community' ref='description'
          type='textarea'
          onChange={this.set('description')}
          errors={

              {errors.descriptionBlank &&
Please fill in this field.
}

}/>

           this.setState({expanded: !expanded})}>
            More Options
            <Icon name={expanded ? 'Chevron-Up2' : 'Chevron-Down2'} />

        {expanded && <ModalSelect label='Community Type' onChange={this.set('category')}>
          <option value=''>Pick one:</option>
          {toPairs(categories).map(([value, label]) => <option key={value} value={value}>
            {label}
          </option>)}
        </ModalSelect>}
        {expanded && <ModalInput label='Location' onChange={this.set('location')}/>}
        {expanded &&

          Logo
          <div className='small-logo' style={{backgroundImage: `url(${community.avatar_url})`}}>

           this.attachImage('avatar')}>
            {uploadingImage ? 'Saving...' : 'Upload'}

}

            {saving ? 'Creating...' : 'Create'}

      </Modal>
  </ModalOnlyPage>
  }
}

@prefetch(({ params: { id }, dispatch }) =>
  id && dispatch(fetchInvitations(id)))
@connect((state, { params: { id } }) => ({
  community: id ? state.communities[id] : getCurrentCommunity(state),
  invitations: id ? state.invitations[id] : null
}))
export class CreateCommunityInvite extends React.Component {
  static propTypes = {
    community: object,
    dispatch: func,
    invitations: array
  }

  render () {
    const { community, dispatch } = this.props

    const onClose = () => dispatch(navigate(checklistUrl(community)))

    return <ModalOnlyPage className='create-community'>
      <Topper community={community}/>
      <InviteModal onClose={onClose} community={community} standalone/>
    </ModalOnlyPage>
  }
}
<|endoftext|>


<commit_before>;(function (root, factory) {

  if (typeof define === 'function' && define.amd) {
    define(['ractive'], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory(require('ractive'));
  } else {
    factory(root.Ractive);
  }

}(this, function (Ractive) {

  var Adaptor = Ractive.adaptors.Ractive = {
    filter: filter,
    wrap: wrap
  };

  /*
   * Advanced options:
   * You can adjust these settings via `Ractive.adaptors.Ractive.maxKeyLength`
   * and so on. There's usually no need to do that, but it may be good for
   * optimizing tests.
   */

  Adaptor.fireWrapEvents = true;
  Adaptor.maxKeyLength = 2048;

  /*
   * Ractive instance accessor methods that will be proxied by Ractive-Ractive.
   */

  var ractiveMethods = {
    add: {
      modifiesData: true,
      mapAllowed: false
    },
    animate: {
      modifiesData: true,
      mapAllowed: true
    },
    get: {
      modifiesData: false,
      mapAllowed: false
    },
    merge: {
      modifiesData: true,
      mapAllowed: false
    },
    observe: {
      modifiesData: false,
      mapAllowed: true
    },
    observeOnce: {
      modifiesData: false,
      mapAllowed: false
    },
    pop: {
      modifiesData: true,
      mapAllowed: false
    },
    push: {
      modifiesData: true,
      mapAllowed: false
    },
    set: {
      modifiesData: true,
      mapAllowed: true
    },
    shift: {
      modifiesData: true,
      mapAllowed: false
    },
    splice: {
      modifiesData: true,
      mapAllowed: false
    },
    subtract: {
      modifiesData: true,
      mapAllowed: false
    },
    toggle: {
      modifiesData: true,
      mapAllowed: false
    },
    unshift: {
      modifiesData: true,
      mapAllowed: false
    },
    update: {
      modifiesData: false,
      mapAllowed: false
    },
    updateModel: {
      modifiesData: false,
      mapAllowed: false
    }
  };

  /*
   * Check if the child is an Ractive instance.
   *
   * Also, if this key has been wrapped before, don't rewrap it. (Happens on
   * deeply-nested values, and .reset() for some reason.)
   */

  function filter (child, keypath, parent) {
    if (!(child instanceof Ractive))
      return false;

    if (parent &&
        parent._children &&
        parent._children[keypath])
      return false;

    return true;
  }

  /*
   * Global write lock.
   * This prevents infinite loops from happening where a parent will set a
   * value on the child, and the child will attempt to write back to the
   * parent, and so on.
   */

  var locked = Adaptor.locked = {};

  function lock (key, fn) {
    if (locked[key]) return;
    try {
      locked[key] = true;
      return fn();
    } finally {
      delete locked[key];
    }
  }

  /*
   * Returns a wrapped Adaptor for Ractive.
   * See: http://docs.ractivejs.org/latest/writing-adaptor-plugins
   */

  function wrap (parent, child, keypath, prefixer) {
    setup();

    return {
      get: get,
      set: set,
      reset: reset,
      teardown: teardown
    };

    /*
     * Initializes the adaptor.
     */

    function setup () {
      hookAccessors();
      checkForRecursion();
      updateRoot(parent.root);
      storeReferences();
      child.on('change', onChange);

      if (Adaptor.fireWrapEvents) {
        child.fire('wrap', parent, keypath);
        parent.fire('wrapchild', child, keypath);
      }
    }

    function teardown () {
      delete parent._children[keypath];
      delete child._parents[parent._guid];
      updateChildrenPattern();

      if (parent._childKeys.length === 0) {
        unhookAccessors();
      }

      // Assign the child as its own root
      updateRoot(child);

      child.off('change', onChange);

      if (Adaptor.fireWrapEvents) {
        child.fire('unwrap', parent, keypath);
        parent.fire('unwrapchild', child, keypath);
      }
    }

    /*
     * Propagate changes from child to parent.
     * We well break it apart into key/vals and set those individually because
     * some values may be locked.
     */

    function onChange (updates) {
      each(updates, function (value, key) {
        lock(child._guid + key, function () {
          parent.set(keypath + '.' + key, value);
        });
      });
    }

    /*
     * Returns all attributes of the child, including computed properties.
     * See: https://github.com/ractivejs/ractive/issues/1250
     */

    function get () {
      // Optimization: if there are no computed properties, returning all
      // non-computed data should suffice.
      if (!child.computed) return child.get();

      var re = {};

      each(child.get(), function (val, key) {
        re[key] = val;
      });

      each(child.computed, function (_, key) {
        if (typeof re[key] === 'undefined')
          re[key] = child.get(key);
      });

      return re;
    }

    function set (key, value) {
      lock(child._guid + key, function () {
        child.set(key, value);
      });
    }

    /*
     * Allow setting values by passing a POJO to .set(), for instance,
     * `.set('child', { ... })`. If anything else is passed onto .set()
     * (like another Ractive instance, or another adaptor'able), destroy
     * this wrapper.
     */

    function reset (object) {
      if (object && object.constructor === Object) {
        child.set(object);
      } else {
        return false;
      }
    }

    /*
     * Update the parent's child key RegEx pattern used by accessors
     */

    function updateChildrenPattern () {
      if (!parent._children) {
        delete parent._childrenPattern;
        return;
      }

      // Collect the current `_children` keys
      parent._childKeys = [];
      each(parent._children, function (value, key) {
        parent._childKeys.push(key);
      });

      if (parent._childKeys.length === 0) {
        delete parent._childrenPattern;
        return;
      }

      parent._childrenPattern = new RegExp("^(" + parent._childKeys.join(")\\..+?|(") + ")\\..+?$");
    }

    /*
     * Generates a Ractive accessor proxy for the given method and configuration.
     */

    function generateAccessor(method, config) {
      config = config || {};

      return function (keypath) {
        var child;
        var childKey;
        var result;

        // Exit early if there aren't any children or we're not targeting a keypath
        if (parent._childrenPattern == null || keypath == null) {
          return parent["_" + method].apply(parent, arguments);
        }

        // Handle a `key, value` set
        if (isString(keypath) === true) {
           childKey = parseAccessorKey(keypath);
           child = parent._children[childKey];

          // The key isn't a child of parent
          if (child == null) {
            return parent["_" + method].apply(parent, arguments);
          }

          // Remove the root of the key before marshalling it to the child
          arguments[0] = arguments[0].replace(childKey + ".", "");
          result = child[method].apply(child, arguments);

          if (config.modifiesData === true) {
            parent._update(childKey + "." + arguments[0]);
          }

          return result;
        }

        // Handle when only a value object is passed to the Ractive instance
        if (config.mapAllowed === true) {
          // Filter out and marshall keys which apply to children
          each(keypath, function (value, key) {
            childKey = parseAccessorKey(keypath);
            child = parent._children[childKey];

            if (child == null) {
              return;
            }

            delete keypath[key];

            // Remove the root of the key before marshalling it to the child
            key = key.replace(childKey + ".", "");
            child[method].call(child, key, value);

            if (config.modifiesData === true) {
              parent._update(childKey + "." + key);
            }
          });
        }

        // Default behavior
        return parent["_" + method].apply(parent, arguments);
      };
    }

    /*
     * Check all children to see if there are any matches and return the result.
     */

    function parseAccessorKey(keypath) {
      if (parent._childrenPattern == null) {
        return;
      }

      var childKeys = parent._childrenPattern.exec(keypath);

      if (childKeys == null) {
        return;
      }

      return childKeys[1];
    }

    /*
     * Overwrite the original Ractive methods with Ractive-Ractive accessor proxies.
     */

    function hookAccessors () {
      // Only hook the accessors once
      if (parent._accessorsHooked === true) {
        return;
      }

      parent._accessorsHooked = true;

      each(ractiveMethods, function (config, method) {
        parent["_" + method] = parent[method];
        parent[method] = generateAccessor(method, config);
      });
    }

    /*
     * Replace the Ractive-Ractive accessor proxies with the original implementations.
     */

    function unhookAccessors () {
      // Only hook the accessors once
      if (parent._accessorsHooked !== true) {
        return;
      }

      delete parent._accessorsHooked;

      each(ractiveMethods, function (config, method) {
        parent[method] = parent["_" + method];
        delete parent["_" + method];
      });
    }

    /*
     * Updates the root reference of the child and its descendants.
     */

    function updateRoot (root) {
      if (child._updateRoot == null) {
        child._updateRoot = function (root) {
          var args = arguments;

          this.root = root;

          if (this._ractiveWraps != null) {
            each(this._ractiveWraps, function (child) {
              // Prevent infinite recursion
              if (child.root === root) {
                return;
              }

              child._updateRoot.apply(child, args);
            });
          }
        };
      }

      child._updateRoot(root);
    }

    /*
     * Die on recursion.
     * Keypath will look like 'child.sub.parent.child.sub.parent' ad nauseum.
     */

    function checkForRecursion () {
      if (keypath && keypath.length > Adaptor.maxKeyLength)
        throw new Error("Keypath too long (possible circular dependency)");
    }

    /*
     * Let future wrappers know what we have wrapped Ractive instances.
     * This value is used on `filter()`.
     */

    function storeReferences () {
      if (!parent._children) parent._children = {};
      if (!child._parents) child._parents = {};

      parent._children[keypath] = child;
      child._parents[parent._guid] = parent;

      updateChildrenPattern();
    }
  }

  /*
   * Cross-browser forEach helper
   */

  function each (obj, fn) {
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) fn(obj[key], key);
    }
  }

  /*
   * isString helper
   */

   function isString (string) {
    return typeof string == 'string' || (!!string && typeof string == 'object' && Object.prototype.toString.call(string) == '[object String]');
   }
}));
<commit_msg>fix: Root is now properly reassigned to the new parent or the instance itself when dealing with nested models<commit_after>;(function (root, factory) {

  if (typeof define === 'function' && define.amd) {
    define(['ractive'], factory);
  } else if (typeof exports === 'object') {
    module.exports = factory(require('ractive'));
  } else {
    factory(root.Ractive);
  }

}(this, function (Ractive) {

  var Adaptor = Ractive.adaptors.Ractive = {
    filter: filter,
    wrap: wrap
  };

  /*
   * Advanced options:
   * You can adjust these settings via `Ractive.adaptors.Ractive.maxKeyLength`
   * and so on. There's usually no need to do that, but it may be good for
   * optimizing tests.
   */

  Adaptor.fireWrapEvents = true;
  Adaptor.maxKeyLength = 2048;

  /*
   * Ractive instance accessor methods that will be proxied by Ractive-Ractive.
   */

  var ractiveMethods = {
    add: {
      modifiesData: true,
      mapAllowed: false
    },
    animate: {
      modifiesData: true,
      mapAllowed: true
    },
    get: {
      modifiesData: false,
      mapAllowed: false
    },
    merge: {
      modifiesData: true,
      mapAllowed: false
    },
    observe: {
      modifiesData: false,
      mapAllowed: true
    },
    observeOnce: {
      modifiesData: false,
      mapAllowed: false
    },
    pop: {
      modifiesData: true,
      mapAllowed: false
    },
    push: {
      modifiesData: true,
      mapAllowed: false
    },
    set: {
      modifiesData: true,
      mapAllowed: true
    },
    shift: {
      modifiesData: true,
      mapAllowed: false
    },
    splice: {
      modifiesData: true,
      mapAllowed: false
    },
    subtract: {
      modifiesData: true,
      mapAllowed: false
    },
    toggle: {
      modifiesData: true,
      mapAllowed: false
    },
    unshift: {
      modifiesData: true,
      mapAllowed: false
    },
    update: {
      modifiesData: false,
      mapAllowed: false
    },
    updateModel: {
      modifiesData: false,
      mapAllowed: false
    }
  };

  /*
   * Check if the child is an Ractive instance.
   *
   * Also, if this key has been wrapped before, don't rewrap it. (Happens on
   * deeply-nested values, and .reset() for some reason.)
   */

  function filter (child, keypath, parent) {
    if (!(child instanceof Ractive))
      return false;

    if (parent &&
        parent._children &&
        parent._children[keypath])
      return false;

    return true;
  }

  /*
   * Global write lock.
   * This prevents infinite loops from happening where a parent will set a
   * value on the child, and the child will attempt to write back to the
   * parent, and so on.
   */

  var locked = Adaptor.locked = {};

  function lock (key, fn) {
    if (locked[key]) return;
    try {
      locked[key] = true;
      return fn();
    } finally {
      delete locked[key];
    }
  }

  /*
   * Returns a wrapped Adaptor for Ractive.
   * See: http://docs.ractivejs.org/latest/writing-adaptor-plugins
   */

  function wrap (parent, child, keypath, prefixer) {
    setup();

    return {
      get: get,
      set: set,
      reset: reset,
      teardown: teardown
    };

    /*
     * Initializes the adaptor.
     */

    function setup () {
      hookAccessors();
      checkForRecursion();
      storeReferences();
      updateRoot();
      child.on('change', onChange);

      if (Adaptor.fireWrapEvents) {
        child.fire('wrap', parent, keypath);
        parent.fire('wrapchild', child, keypath);
      }
    }

    function teardown () {
      delete parent._children[keypath];
      delete child._parents[parent._guid];
      updateChildrenPattern();

      if (parent._childKeys.length === 0) {
        unhookAccessors();
      }

      // Calculate how many parents remain for the child
      var numberOfParents = 0;
      each(child._parents, function () {
        numberOfParents++;
      });

      // Assign the child as its own root if it no longer has parents
      if (numberOfParents === 0) {
        updateRoot(child);
      }

      child.off('change', onChange);

      if (Adaptor.fireWrapEvents) {
        child.fire('unwrap', parent, keypath);
        parent.fire('unwrapchild', child, keypath);
      }
    }

    /*
     * Propagate changes from child to parent.
     * We well break it apart into key/vals and set those individually because
     * some values may be locked.
     */

    function onChange (updates) {
      each(updates, function (value, key) {
        lock(child._guid + key, function () {
          parent.set(keypath + '.' + key, value);
        });
      });
    }

    /*
     * Returns all attributes of the child, including computed properties.
     * See: https://github.com/ractivejs/ractive/issues/1250
     */

    function get () {
      // Optimization: if there are no computed properties, returning all
      // non-computed data should suffice.
      if (!child.computed) return child.get();

      var re = {};

      each(child.get(), function (val, key) {
        re[key] = val;
      });

      each(child.computed, function (_, key) {
        if (typeof re[key] === 'undefined')
          re[key] = child.get(key);
      });

      return re;
    }

    function set (key, value) {
      lock(child._guid + key, function () {
        child.set(key, value);
      });
    }

    /*
     * Allow setting values by passing a POJO to .set(), for instance,
     * `.set('child', { ... })`. If anything else is passed onto .set()
     * (like another Ractive instance, or another adaptor'able), destroy
     * this wrapper.
     */

    function reset (object) {
      if (object && object.constructor === Object) {
        child.set(object);
      } else {
        return false;
      }
    }

    /*
     * Update the parent's child key RegEx pattern used by accessors
     */

    function updateChildrenPattern () {
      if (!parent._children) {
        delete parent._childrenPattern;
        return;
      }

      // Collect the current `_children` keys
      parent._childKeys = [];
      each(parent._children, function (value, key) {
        parent._childKeys.push(key);
      });

      if (parent._childKeys.length === 0) {
        delete parent._childrenPattern;
        return;
      }

      parent._childrenPattern = new RegExp("^(" + parent._childKeys.join(")\\..+?|(") + ")\\..+?$");
    }

    /*
     * Generates a Ractive accessor proxy for the given method and configuration.
     */

    function generateAccessor(method, config) {
      config = config || {};

      return function (keypath) {
        var child;
        var childKey;
        var result;

        // Exit early if there aren't any children or we're not targeting a keypath
        if (parent._childrenPattern == null || keypath == null) {
          return parent["_" + method].apply(parent, arguments);
        }

        // Handle a `key, value` set
        if (isString(keypath) === true) {
           childKey = parseAccessorKey(keypath);
           child = parent._children[childKey];

          // The key isn't a child of parent
          if (child == null) {
            return parent["_" + method].apply(parent, arguments);
          }

          // Remove the root of the key before marshalling it to the child
          arguments[0] = arguments[0].replace(childKey + ".", "");
          result = child[method].apply(child, arguments);

          if (config.modifiesData === true) {
            parent._update(childKey + "." + arguments[0]);
          }

          return result;
        }

        // Handle when only a value object is passed to the Ractive instance
        if (config.mapAllowed === true) {
          // Filter out and marshall keys which apply to children
          each(keypath, function (value, key) {
            childKey = parseAccessorKey(keypath);
            child = parent._children[childKey];

            if (child == null) {
              return;
            }

            delete keypath[key];

            // Remove the root of the key before marshalling it to the child
            key = key.replace(childKey + ".", "");
            child[method].call(child, key, value);

            if (config.modifiesData === true) {
              parent._update(childKey + "." + key);
            }
          });
        }

        // Default behavior
        return parent["_" + method].apply(parent, arguments);
      };
    }

    /*
     * Check all children to see if there are any matches and return the result.
     */

    function parseAccessorKey(keypath) {
      if (parent._childrenPattern == null) {
        return;
      }

      var childKeys = parent._childrenPattern.exec(keypath);

      if (childKeys == null) {
        return;
      }

      return childKeys[1];
    }

    /*
     * Overwrite the original Ractive methods with Ractive-Ractive accessor proxies.
     */

    function hookAccessors () {
      // Only hook the accessors once
      if (parent._accessorsHooked === true) {
        return;
      }

      parent._accessorsHooked = true;

      each(ractiveMethods, function (config, method) {
        parent["_" + method] = parent[method];
        parent[method] = generateAccessor(method, config);
      });
    }

    /*
     * Replace the Ractive-Ractive accessor proxies with the original implementations.
     */

    function unhookAccessors () {
      // Only hook the accessors once
      if (parent._accessorsHooked !== true) {
        return;
      }

      delete parent._accessorsHooked;

      each(ractiveMethods, function (config, method) {
        parent[method] = parent["_" + method];
        delete parent["_" + method];
      });
    }

    /*
     * Updates the root reference of the child and its descendants.
     */

    function updateRoot (root) {
      root = root || parent.root;

      child.root = root;

      (function updateChildren(parent) {
        if (parent._children == null) {
          return;
        }

        each(parent._children, function (child) {
          // Prevent infinite recursion
          if (child.root === root) {
            return;
          }

          child.root = root;

          updateChildren(child);
        });
      })(child);
    }

    /*
     * Die on recursion.
     * Keypath will look like 'child.sub.parent.child.sub.parent' ad nauseum.
     */

    function checkForRecursion () {
      if (keypath && keypath.length > Adaptor.maxKeyLength)
        throw new Error("Keypath too long (possible circular dependency)");
    }

    /*
     * Let future wrappers know what we have wrapped Ractive instances.
     * This value is used on `filter()`.
     */

    function storeReferences () {
      if (!parent._children) parent._children = {};
      if (!child._parents) child._parents = {};

      parent._children[keypath] = child;
      child._parents[parent._guid] = parent;

      updateChildrenPattern();
    }
  }

  /*
   * Cross-browser forEach helper
   */

  function each (obj, fn) {
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) fn(obj[key], key);
    }
  }

  /*
   * isString helper
   */

   function isString (string) {
    return typeof string == 'string' || (!!string && typeof string == 'object' && Object.prototype.toString.call(string) == '[object String]');
   }
}));
<|endoftext|>


<commit_before>

var GUI = function()
{
	// Create dat gui
	this.gui = new dat.GUI();
	var gui = this.gui;

	this.createSceneSettings();
	this.createMaterialSettings();
	this.createEmissionSettings();
	this.createLightTracerSettings();
	this.createSurfaceRendererSettings();
}

function updateDisplay(gui) {
    for (var i in gui.__controllers) {
        gui.__controllers[i].updateDisplay();
    }
    for (var f in gui.__folders) {
        updateDisplay(gui.__folders[f]);
    }
}

GUI.prototype.sync = function()
{
	updateDisplay(this.gui);
}

GUI.prototype.createLightTracerSettings = function()
{
	this.lightTracerFolder = this.gui.addFolder('Light Tracer');
	this.lightTracerSettings = {};
	var lightTracer = snelly.getLightTracer();

	this.lightTracerSettings.enable = lightTracer.enabled;
	this.lightTracerSettings.exposure = 1.0;
	this.lightTracerSettings.gamma = 2.2;
	this.lightTracerSettings.maxPathLength = lightTracer.maxPathLength;
	this.lightTracerSettings.maxMarchSteps = lightTracer.maxMarchSteps;
	this.lightTracerSettings.rayBufferSize = lightTracer.raySize;

	this.lightTracerFolder.add(this.lightTracerSettings, 'enable').onChange( function(value) { lightTracer.enabled = value;  } );
	this.lightTracerFolder.add(this.lightTracerSettings, 'exposure', -2.0, 4.0, 0.01);
	this.lightTracerFolder.add(this.lightTracerSettings, 'gamma', 0.0, 4.0, 0.01);
	this.lightTracerFolder.add(this.lightTracerSettings, 'maxPathLength', 4, 1024).onChange( function(value) { lightTracer.maxPathLength = Math.floor(value); lightTracer.reset(); } );
	this.lightTracerFolder.add(this.lightTracerSettings, 'maxMarchSteps', 32, 1024).onChange( function(value) { lightTracer.maxMarchSteps = Math.floor(value); lightTracer.reset(); } );
	this.lightTracerFolder.add(this.lightTracerSettings, 'rayBufferSize', 64, 1024).onChange( function(value) { lightTracer.raySize = Math.floor(value);
																											    lightTracer.initStates();
																												lightTracer.reset(); } );
	this.gui.remember(this.lightTracerSettings);
	this.lightTracerFolder.open();
}

function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}

GUI.prototype.createSurfaceRendererSettings = function()
{
	this.surfaceRendererFolder = this.gui.addFolder('Surface Renderer');
	this.surfaceRendererSettings = {};
	var surfaceRenderer = snelly.getSurfaceRenderer();

	var renderModes = ['normals', 'blinn'];

	this.surfaceRendererFolder.add(surfaceRenderer, 'enable');
	this.surfaceRendererFolder.add(surfaceRenderer, 'depthTest');
	this.surfaceRendererFolder.add(surfaceRenderer, 'showBounds');

	this.surfaceRendererFolder.add(surfaceRenderer, 'renderMode', renderModes).onChange( function(renderMode) { surfaceRenderer.reset(); });
	this.surfaceRendererFolder.add(surfaceRenderer, 'surfaceAlpha', 0.0, 1.0);
	this.surfaceRendererFolder.add(surfaceRenderer, 'maxMarchSteps', 1, 1024).onChange( function(value) { surfaceRenderer.maxMarchSteps = Math.floor(value); surfaceRenderer.reset(); } );

	this.surfaceRendererSettings.diffuseCol1 = [surfaceRenderer.kd1[0]*255.0, surfaceRenderer.kd1[1]*255.0, surfaceRenderer.kd1[2]*255.0];
	this.surfaceRendererSettings.diffuseCol2 = [surfaceRenderer.kd2[0]*255.0, surfaceRenderer.kd2[1]*255.0, surfaceRenderer.kd2[2]*255.0];

	this.surfaceRendererFolder.addColor(this.surfaceRendererSettings, 'diffuseCol1').onChange( function(value)
	{
		if (typeof value==='string' || value instanceof String)
		{
			var color = hexToRgb(value);
			surfaceRenderer.kd1[0] = color.r / 255.0;
			surfaceRenderer.kd1[1] = color.g / 255.0;
			surfaceRenderer.kd1[2] = color.b / 255.0;
		}
		else
		{
			surfaceRenderer.kd1[0] = value[0] / 255.0;
			surfaceRenderer.kd1[1] = value[1] / 255.0;
			surfaceRenderer.kd1[2] = value[2] / 255.0;
		}
		surfaceRenderer.reset();
	});

	this.surfaceRendererFolder.addColor(this.surfaceRendererSettings, 'diffuseCol2').onChange( function(value)
	{
		if (typeof value==='string' || value instanceof String)
		{
			var color = hexToRgb(value);
			surfaceRenderer.kd2[0] = color.r / 255.0;
			surfaceRenderer.kd2[1] = color.g / 255.0;
			surfaceRenderer.kd2[2] = color.b / 255.0;
		}
		else
		{
			surfaceRenderer.kd2[0] = value[0] / 255.0;
			surfaceRenderer.kd2[1] = value[1] / 255.0;
			surfaceRenderer.kd2[2] = value[2] / 255.0;
		}
		surfaceRenderer.reset();
	});

	this.surfaceRendererFolder.add(surfaceRenderer, 'specPower', 1.0, 100.0).onChange( function(renderMode) { surfaceRenderer.reset(); });

	this.gui.remember(this.surfaceRendererSettings);
	this.surfaceRendererFolder.open();
}

GUI.prototype.createEmissionSettings = function()
{
	this.emissionFolder = this.gui.addFolder('Emission');
	var lightTracer = snelly.getLightTracer();
	var laser = snelly.getLaser();
	this.emissionSettings = {};
	this.emissionSettings.showLaserPointer = true;
	this.emissionSettings.spectrum = 'monochromatic';
	this.emissionSettings.emissionRadius = 0.01; // (scene-scale relative)

	this.emissionFolder.add(this.emissionSettings, 'showLaserPointer').onChange( function(value) { laser.toggleVisibility(value); } );
	this.emissionFolder.add(this.emissionSettings, 'emissionRadius', 0.0, 3.0).onChange( function(value)
	{
		var sceneObj = snelly.getLoadedScene();
		var sceneScale = sceneObj.getScale();
		laser.setEmissionRadius(sceneScale*value);
		lightTracer.reset();
	} );
	this.emissionFolder.add(laser, 'emissionSpread', 0.0, 45.0).onChange( function(value) { laser.setEmissionSpreadAngle(value); lightTracer.reset(); } );
	this.gui.remember(laser);

	// Spectrum selection
	var GUI = this;
	var spectrumObj = lightTracer.getLoadedSpectrum();
	var spectrumName = spectrumObj.getName();
	var spectra = lightTracer.getSpectra();
	var spectrumNames = Object.keys(spectra);

	this.emissionSettings["spectrum selection"] = spectrumName;
	this.emissionFolder.add(this.emissionSettings, 'spectrum selection', spectrumNames).onChange( function(spectrumName) {

						// remove gui for current spectrum
						var spectrumObj = lightTracer.getLoadedSpectrum();
						spectrumObj.eraseGui(GUI.emissionFolder);

						// load new scene
				 		lightTracer.loadSpectrum(spectrumName);

				 		// init gui for new scene
				 		spectrumObj = lightTracer.getLoadedSpectrum();
				 		spectrumObj.initGui(GUI.emissionFolder);

				 	} );

	spectrumObj.initGui(this.emissionFolder);

	// Laser transform sliders
	this.emissionTranslationFolder = this.emissionFolder.addFolder('Translation');
	var xT = this.emissionTranslationFolder.add(laser.group.position, 'x');
	var yT = this.emissionTranslationFolder.add(laser.group.position, 'y');
	var zT = this.emissionTranslationFolder.add(laser.group.position, 'z');
	xT.onChange( function(value)  { snelly.reset(); } );
	yT.onChange( function(value)  { snelly.reset(); } );
	zT.onChange( function(value)  { snelly.reset(); } );

	this.emissionRotationFolder = this.emissionFolder.addFolder('Rotation');
	var xR = this.emissionRotationFolder.add(laser.eulerAngles, 'x', -Math.PI, Math.PI);
	xR.onChange( function(value)
	{
		var euler = laser.getEuler();
		euler.x = value;
		laser.setEuler(euler);
		snelly.reset();
	} );
	var yR = this.emissionRotationFolder.add(laser.eulerAngles, 'y', -Math.PI, Math.PI);
	yR.onChange( function(value)
	{
		var euler = laser.getEuler();
		euler.y = value;
		laser.setEuler(euler);
		snelly.reset();
	} );
	var zR = this.emissionRotationFolder.add(laser.eulerAngles, 'z', -Math.PI, Math.PI);
	zR.onChange( function(value)
	{
		var euler = laser.getEuler();
		euler.z = value;
		laser.setEuler(euler);
		snelly.reset();
	} );

	this.gui.remember(this.emissionSettings);
	this.emissionFolder.open();
	return this.emissionFolder;
}

GUI.prototype.createSceneSettings = function()
{
	this.sceneFolder = this.gui.addFolder('Scene');
	var sceneObj = snelly.getLoadedScene();
	var sceneName = sceneObj.getName();
	scenes = snelly.getScenes();
	var sceneNames = Object.keys(scenes);

	// Scene selection menu
	this.sceneSettings = {};
	this.sceneSettings["scene selection"] = sceneName;
	var GUI = this;

	this.sceneFolder.add(this.sceneSettings, 'scene selection', sceneNames).onChange( function(sceneName) {

						// remove gui for current scene
						var sceneObj = snelly.getLoadedScene();
						sceneObj.eraseGui(GUI.sceneFolder);

						// load new scene
				 		snelly.loadScene(sceneName);

				 		// init gui for new scene
				 		sceneObj = snelly.getLoadedScene();
				 		sceneObj.initGui(GUI.sceneFolder);

				 	} );

	sceneObj.initGui(this.sceneFolder);
	this.sceneFolder.open();

	this.gui.remember(this.sceneSettings);
	return this.sceneFolder;
}

GUI.prototype.createMaterialSettings = function()
{
	this.materialFolder = this.gui.addFolder('Material');
	var materialObj = snelly.getLoadedMaterial();
	var materialName = materialObj.getName();
	materials = snelly.getMaterials();
	var materialNames = Object.keys(materials);

	// Material selection menu
	this.materialSettings = {};
	this.materialSettings["material selection"] = materialName;
	var GUI = this;

	this.materialFolder.add(this.materialSettings, 'material selection', materialNames).onChange( function(materialName) {

						// remove gui for current material
						var materialObj = snelly.getLoadedMaterial();
						materialObj.eraseGui(GUI.materialFolder);

						// load new material
				 		snelly.loadMaterial(materialName);

				 		// init gui for new material
				 		materialObj = snelly.getLoadedMaterial();
				 		materialObj.initGui(GUI.materialFolder);

				 	} );

	materialObj.initGui(this.materialFolder);
	this.materialFolder.open();

	this.gui.remember(this.materialSettings);
	return this.materialFolder;
}

<commit_msg>Use Euler angles in degrees.<commit_after>

var GUI = function()
{
	// Create dat gui
	this.gui = new dat.GUI();
	var gui = this.gui;

	this.createSceneSettings();
	this.createMaterialSettings();
	this.createEmissionSettings();
	this.createLightTracerSettings();
	this.createSurfaceRendererSettings();
}

function updateDisplay(gui) {
    for (var i in gui.__controllers) {
        gui.__controllers[i].updateDisplay();
    }
    for (var f in gui.__folders) {
        updateDisplay(gui.__folders[f]);
    }
}

GUI.prototype.sync = function()
{
	var laser = snelly.getLaser();
	this.emissionSettings.eulerAngles.x = laser.eulerAngles.x * 180.0/Math.PI;
	this.emissionSettings.eulerAngles.y = laser.eulerAngles.y * 180.0/Math.PI;
	this.emissionSettings.eulerAngles.z = laser.eulerAngles.z * 180.0/Math.PI;

	updateDisplay(this.gui);
}

GUI.prototype.createLightTracerSettings = function()
{
	this.lightTracerFolder = this.gui.addFolder('Light Tracer');
	this.lightTracerSettings = {};
	var lightTracer = snelly.getLightTracer();

	this.lightTracerSettings.enable = lightTracer.enabled;
	this.lightTracerSettings.exposure = 1.0;
	this.lightTracerSettings.gamma = 2.2;
	this.lightTracerSettings.maxPathLength = lightTracer.maxPathLength;
	this.lightTracerSettings.maxMarchSteps = lightTracer.maxMarchSteps;
	this.lightTracerSettings.rayBufferSize = lightTracer.raySize;

	this.lightTracerFolder.add(this.lightTracerSettings, 'enable').onChange( function(value) { lightTracer.enabled = value;  } );
	this.lightTracerFolder.add(this.lightTracerSettings, 'exposure', -2.0, 4.0, 0.01);
	this.lightTracerFolder.add(this.lightTracerSettings, 'gamma', 0.0, 4.0, 0.01);
	this.lightTracerFolder.add(this.lightTracerSettings, 'maxPathLength', 4, 1024).onChange( function(value) { lightTracer.maxPathLength = Math.floor(value); lightTracer.reset(); } );
	this.lightTracerFolder.add(this.lightTracerSettings, 'maxMarchSteps', 32, 1024).onChange( function(value) { lightTracer.maxMarchSteps = Math.floor(value); lightTracer.reset(); } );
	this.lightTracerFolder.add(this.lightTracerSettings, 'rayBufferSize', 64, 1024).onChange( function(value) { lightTracer.raySize = Math.floor(value);
																											    lightTracer.initStates();
																												lightTracer.reset(); } );
	this.gui.remember(this.lightTracerSettings);
	this.lightTracerFolder.open();
}

function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}

GUI.prototype.createSurfaceRendererSettings = function()
{
	this.surfaceRendererFolder = this.gui.addFolder('Surface Renderer');
	this.surfaceRendererSettings = {};
	var surfaceRenderer = snelly.getSurfaceRenderer();

	var renderModes = ['normals', 'blinn'];

	this.surfaceRendererFolder.add(surfaceRenderer, 'enable');
	this.surfaceRendererFolder.add(surfaceRenderer, 'depthTest');
	this.surfaceRendererFolder.add(surfaceRenderer, 'showBounds');

	this.surfaceRendererFolder.add(surfaceRenderer, 'renderMode', renderModes).onChange( function(renderMode) { surfaceRenderer.reset(); });
	this.surfaceRendererFolder.add(surfaceRenderer, 'surfaceAlpha', 0.0, 1.0);
	this.surfaceRendererFolder.add(surfaceRenderer, 'maxMarchSteps', 1, 1024).onChange( function(value) { surfaceRenderer.maxMarchSteps = Math.floor(value); surfaceRenderer.reset(); } );

	this.surfaceRendererSettings.diffuseCol1 = [surfaceRenderer.kd1[0]*255.0, surfaceRenderer.kd1[1]*255.0, surfaceRenderer.kd1[2]*255.0];
	this.surfaceRendererSettings.diffuseCol2 = [surfaceRenderer.kd2[0]*255.0, surfaceRenderer.kd2[1]*255.0, surfaceRenderer.kd2[2]*255.0];

	this.surfaceRendererFolder.addColor(this.surfaceRendererSettings, 'diffuseCol1').onChange( function(value)
	{
		if (typeof value==='string' || value instanceof String)
		{
			var color = hexToRgb(value);
			surfaceRenderer.kd1[0] = color.r / 255.0;
			surfaceRenderer.kd1[1] = color.g / 255.0;
			surfaceRenderer.kd1[2] = color.b / 255.0;
		}
		else
		{
			surfaceRenderer.kd1[0] = value[0] / 255.0;
			surfaceRenderer.kd1[1] = value[1] / 255.0;
			surfaceRenderer.kd1[2] = value[2] / 255.0;
		}
		surfaceRenderer.reset();
	});

	this.surfaceRendererFolder.addColor(this.surfaceRendererSettings, 'diffuseCol2').onChange( function(value)
	{
		if (typeof value==='string' || value instanceof String)
		{
			var color = hexToRgb(value);
			surfaceRenderer.kd2[0] = color.r / 255.0;
			surfaceRenderer.kd2[1] = color.g / 255.0;
			surfaceRenderer.kd2[2] = color.b / 255.0;
		}
		else
		{
			surfaceRenderer.kd2[0] = value[0] / 255.0;
			surfaceRenderer.kd2[1] = value[1] / 255.0;
			surfaceRenderer.kd2[2] = value[2] / 255.0;
		}
		surfaceRenderer.reset();
	});

	this.surfaceRendererFolder.add(surfaceRenderer, 'specPower', 1.0, 100.0).onChange( function(renderMode) { surfaceRenderer.reset(); });

	this.gui.remember(this.surfaceRendererSettings);
	this.surfaceRendererFolder.open();
}

GUI.prototype.createEmissionSettings = function()
{
	this.emissionFolder = this.gui.addFolder('Emission');
	var lightTracer = snelly.getLightTracer();
	var laser = snelly.getLaser();
	this.emissionSettings = {};
	this.emissionSettings.showLaserPointer = true;
	this.emissionSettings.spectrum = 'monochromatic';
	this.emissionSettings.emissionRadius = 0.01; // (scene-scale relative)

	this.emissionFolder.add(this.emissionSettings, 'showLaserPointer').onChange( function(value) { laser.toggleVisibility(value); } );
	this.emissionFolder.add(this.emissionSettings, 'emissionRadius', 0.0, 3.0).onChange( function(value)
	{
		var sceneObj = snelly.getLoadedScene();
		var sceneScale = sceneObj.getScale();
		laser.setEmissionRadius(sceneScale*value);
		lightTracer.reset();
	} );
	this.emissionFolder.add(laser, 'emissionSpread', 0.0, 45.0).onChange( function(value) { laser.setEmissionSpreadAngle(value); lightTracer.reset(); } );
	this.gui.remember(laser);

	// Spectrum selection
	var GUI = this;
	var spectrumObj = lightTracer.getLoadedSpectrum();
	var spectrumName = spectrumObj.getName();
	var spectra = lightTracer.getSpectra();
	var spectrumNames = Object.keys(spectra);

	this.emissionSettings["spectrum selection"] = spectrumName;
	this.emissionFolder.add(this.emissionSettings, 'spectrum selection', spectrumNames).onChange( function(spectrumName) {

						// remove gui for current spectrum
						var spectrumObj = lightTracer.getLoadedSpectrum();
						spectrumObj.eraseGui(GUI.emissionFolder);

						// load new scene
				 		lightTracer.loadSpectrum(spectrumName);

				 		// init gui for new scene
				 		spectrumObj = lightTracer.getLoadedSpectrum();
				 		spectrumObj.initGui(GUI.emissionFolder);

				 	} );

	spectrumObj.initGui(this.emissionFolder);

	// Laser transform sliders
	this.emissionTranslationFolder = this.emissionFolder.addFolder('Translation');
	var xT = this.emissionTranslationFolder.add(laser.group.position, 'x');
	var yT = this.emissionTranslationFolder.add(laser.group.position, 'y');
	var zT = this.emissionTranslationFolder.add(laser.group.position, 'z');
	xT.onChange( function(value)  { snelly.reset(); } );
	yT.onChange( function(value)  { snelly.reset(); } );
	zT.onChange( function(value)  { snelly.reset(); } );

	this.emissionSettings.eulerAngles = new THREE.Vector3();
	this.emissionSettings.eulerAngles.x = laser.eulerAngles.x * 180.0/Math.PI;
	this.emissionSettings.eulerAngles.y = laser.eulerAngles.y * 180.0/Math.PI;
	this.emissionSettings.eulerAngles.z = laser.eulerAngles.z * 180.0/Math.PI;

	this.emissionRotationFolder = this.emissionFolder.addFolder('Rotation');
	var xR = this.emissionRotationFolder.add(this.emissionSettings.eulerAngles, 'x', -180.0, 180.0);
	xR.onChange( function(value)
	{
		var euler = laser.getEuler();
		euler.x = value * Math.PI/180.0;
		laser.setEuler(euler);
		snelly.reset();
	} );
	var yR = this.emissionRotationFolder.add(this.emissionSettings.eulerAngles, 'y', -180.0, 180.0);
	yR.onChange( function(value)
	{
		var euler = laser.getEuler();
		euler.y = value * Math.PI/180.0;
		laser.setEuler(euler);
		snelly.reset();
	} );
	var zR = this.emissionRotationFolder.add(this.emissionSettings.eulerAngles, 'z', -180.0, 180.0);
	zR.onChange( function(value)
	{
		var euler = laser.getEuler();
		euler.z = value * Math.PI/180.0;
		laser.setEuler(euler);
		snelly.reset();
	} );

	this.gui.remember(this.emissionSettings);
	this.emissionFolder.open();
	return this.emissionFolder;
}

GUI.prototype.createSceneSettings = function()
{
	this.sceneFolder = this.gui.addFolder('Scene');
	var sceneObj = snelly.getLoadedScene();
	var sceneName = sceneObj.getName();
	scenes = snelly.getScenes();
	var sceneNames = Object.keys(scenes);

	// Scene selection menu
	this.sceneSettings = {};
	this.sceneSettings["scene selection"] = sceneName;
	var GUI = this;

	this.sceneFolder.add(this.sceneSettings, 'scene selection', sceneNames).onChange( function(sceneName) {

						// remove gui for current scene
						var sceneObj = snelly.getLoadedScene();
						sceneObj.eraseGui(GUI.sceneFolder);

						// load new scene
				 		snelly.loadScene(sceneName);

				 		// init gui for new scene
				 		sceneObj = snelly.getLoadedScene();
				 		sceneObj.initGui(GUI.sceneFolder);

				 	} );

	sceneObj.initGui(this.sceneFolder);
	this.sceneFolder.open();

	this.gui.remember(this.sceneSettings);
	return this.sceneFolder;
}

GUI.prototype.createMaterialSettings = function()
{
	this.materialFolder = this.gui.addFolder('Material');
	var materialObj = snelly.getLoadedMaterial();
	var materialName = materialObj.getName();
	materials = snelly.getMaterials();
	var materialNames = Object.keys(materials);

	// Material selection menu
	this.materialSettings = {};
	this.materialSettings["material selection"] = materialName;
	var GUI = this;

	this.materialFolder.add(this.materialSettings, 'material selection', materialNames).onChange( function(materialName) {

						// remove gui for current material
						var materialObj = snelly.getLoadedMaterial();
						materialObj.eraseGui(GUI.materialFolder);

						// load new material
				 		snelly.loadMaterial(materialName);

				 		// init gui for new material
				 		materialObj = snelly.getLoadedMaterial();
				 		materialObj.initGui(GUI.materialFolder);

				 	} );

	materialObj.initGui(this.materialFolder);
	this.materialFolder.open();

	this.gui.remember(this.materialSettings);
	return this.materialFolder;
}

<|endoftext|>