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

import { firebase } from 'app/firebase';
import { isString } from 'app/utils';
import { filter } from 'app/lodash';
import { Autocomplete, Button, FormGroup, InputField } from 'app/components';
import { ringsFormReset, ringsUpdate, ringsAutocompleteSelect } from 'app/redux/rings';

const block = purebem.of('rings-form');

const RingsForm = React.createClass({

    propTypes: {
        autocomplete: PropTypes.shape({
            expanded: PropTypes.bool,
            list: PropTypes.array
        }).isRequired,
        form: PropTypes.shape({
            size: PropTypes.string,
            serial: PropTypes.string
        }).isRequired,
        onAutocompleteUpdate: PropTypes.func.isRequired,
        onAutocompleteExpand: PropTypes.func.isRequired,
        onAutocompleteSelect: PropTypes.func.isRequired,
        onInput: PropTypes.func.isRequired,
        onReset: PropTypes.func.isRequired,
        projectId: PropTypes.string.isRequired,
        sizes: PropTypes.array.isRequired
    },

    componentWillMount() {
        const { projectId } = this.props;
        this.ringsRef = firebase.database().ref(`rings/${projectId}`);
    },

    getTrimmedValue(name, value) {
        switch (name) {
            case 'size':
                return value.trim().replace(',', '.');

            case 'serial':
                return value.trim().toUpperCase();
        }
    },

    getDecimalValue(value) {
        const pattern = /\.[0-9]+/;
        return pattern.test(value) ? value : `${value}.0`;
    },

    handleInput(evt) {
        const { name, value } = evt.target;
        const trimmedValue = this.getTrimmedValue(name, value);

        this.props.onInput(name, trimmedValue);

        if (name === 'size') {
            this.handleAutocomplete(value);
        }
    },

    handleAutocomplete(value) {
        const pattern = new RegExp(`^${value}`);
        const matches = filter(this.props.sizes, (o) => {
            return value && pattern.test(o.size);
        });

        this.props.onAutocompleteUpdate(matches);
    },

    handleSubmit(evt) {
        evt.preventDefault();
        const { form } = this.props;

        if (!isString(form.size) || !isString(form.serial)) {
            // display error message…
            return;
        }

        const size = this.getDecimalValue(form.size).replace('.', '_');
        this.ringsRef.child(`${size}`).update({ serial: form.serial });
        this.ringsRef.child(`${size}/history/${form.serial}`).set(true);
        this.props.onReset();
    },

    render() {
        const { autocomplete, form } = this.props;

        return (
            <form className={ block() } onSubmit={ this.handleSubmit }>
                <FormGroup
                    label="Ring Size"
                    type="inline">
                    <Autocomplete
                        name="size"
                        expanded={ autocomplete.expanded }
                        onChange={ this.handleInput }
                        onClick={ (value) => this.props.onInput('size', value) }
                        onExpand={ this.props.onAutocompleteExpand }
                        onSelect={ this.props.onAutocompleteSelect }
                        list={ autocomplete.list }
                        value={ form.size } />
                </FormGroup>
                <FormGroup
                    label="Serial No."
                    type="inline">
                    <InputField
                        name="serial"
                        onChange={ this.handleInput }
                        value={ form.serial } />
                </FormGroup>
                <FormGroup
                    label=" "
                    type="inline">
                    <Button
                        style="success"
                        type="submit">Update</Button>
                </FormGroup>
            </form>
        );
    }

});

const mapStateToProps = (state) => {
    return {
        autocomplete: state.rings.autocomplete,
        form: state.rings.form,
        sizes: state.rings.sizes.list
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        onInput: (name, value) => dispatch(ringsUpdate('form', { [name]: value })),
        onReset: () => dispatch(ringsFormReset()),
        onAutocompleteUpdate: (list) => dispatch(ringsUpdate('autocomplete', { list, expanded: !!list.length })),
        onAutocompleteExpand: (expanded) => dispatch(ringsUpdate('autocomplete', { expanded })),
        onAutocompleteSelect: (index) => dispatch(ringsAutocompleteSelect(index, { selected: true }))
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(RingsForm);
<commit_msg>clear rings form on unmount<commit_after>import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import purebem from 'purebem';

import { firebase } from 'app/firebase';
import { isString } from 'app/utils';
import { filter } from 'app/lodash';
import { Autocomplete, Button, FormGroup, InputField } from 'app/components';
import { ringsFormReset, ringsUpdate, ringsAutocompleteSelect } from 'app/redux/rings';

const block = purebem.of('rings-form');

const RingsForm = React.createClass({

    propTypes: {
        autocomplete: PropTypes.shape({
            expanded: PropTypes.bool,
            list: PropTypes.array
        }).isRequired,
        form: PropTypes.shape({
            size: PropTypes.string,
            serial: PropTypes.string
        }).isRequired,
        onAutocompleteUpdate: PropTypes.func.isRequired,
        onAutocompleteExpand: PropTypes.func.isRequired,
        onAutocompleteSelect: PropTypes.func.isRequired,
        onInput: PropTypes.func.isRequired,
        onReset: PropTypes.func.isRequired,
        projectId: PropTypes.string.isRequired,
        sizes: PropTypes.array.isRequired
    },

    componentWillMount() {
        const { projectId } = this.props;
        this.ringsRef = firebase.database().ref(`rings/${projectId}`);
    },

    componentWillUnmount() {
        this.props.onReset();
    },

    getTrimmedValue(name, value) {
        switch (name) {
            case 'size':
                return value.trim().replace(',', '.');

            case 'serial':
                return value.trim().toUpperCase();
        }
    },

    getDecimalValue(value) {
        const pattern = /\.[0-9]+/;
        return pattern.test(value) ? value : `${value}.0`;
    },

    handleInput(evt) {
        const { name, value } = evt.target;
        const trimmedValue = this.getTrimmedValue(name, value);

        this.props.onInput(name, trimmedValue);

        if (name === 'size') {
            this.handleAutocomplete(value);
        }
    },

    handleAutocomplete(value) {
        const pattern = new RegExp(`^${value}`);
        const matches = filter(this.props.sizes, (o) => {
            return value && pattern.test(o.size);
        });

        this.props.onAutocompleteUpdate(matches);
    },

    handleSubmit(evt) {
        evt.preventDefault();
        const { form } = this.props;

        if (!isString(form.size) || !isString(form.serial)) {
            // display error message…
            return;
        }

        const size = this.getDecimalValue(form.size).replace('.', '_');
        this.ringsRef.child(`${size}`).update({ serial: form.serial });
        this.ringsRef.child(`${size}/history/${form.serial}`).set(true);
        this.props.onReset();
    },

    render() {
        const { autocomplete, form } = this.props;

        return (
            <form className={ block() } onSubmit={ this.handleSubmit }>
                <FormGroup
                    label="Ring Size"
                    type="inline">
                    <Autocomplete
                        name="size"
                        expanded={ autocomplete.expanded }
                        onChange={ this.handleInput }
                        onClick={ (value) => this.props.onInput('size', value) }
                        onExpand={ this.props.onAutocompleteExpand }
                        onSelect={ this.props.onAutocompleteSelect }
                        list={ autocomplete.list }
                        value={ form.size } />
                </FormGroup>
                <FormGroup
                    label="Serial No."
                    type="inline">
                    <InputField
                        name="serial"
                        onChange={ this.handleInput }
                        value={ form.serial } />
                </FormGroup>
                <FormGroup
                    label=" "
                    type="inline">
                    <Button
                        style="success"
                        type="submit">Update</Button>
                </FormGroup>
            </form>
        );
    }

});

const mapStateToProps = (state) => {
    return {
        autocomplete: state.rings.autocomplete,
        form: state.rings.form,
        sizes: state.rings.sizes.list
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        onInput: (name, value) => dispatch(ringsUpdate('form', { [name]: value })),
        onReset: () => dispatch(ringsFormReset()),
        onAutocompleteUpdate: (list) => dispatch(ringsUpdate('autocomplete', { list, expanded: !!list.length })),
        onAutocompleteExpand: (expanded) => dispatch(ringsUpdate('autocomplete', { expanded })),
        onAutocompleteSelect: (index) => dispatch(ringsAutocompleteSelect(index, { selected: true }))
    };
};

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


<commit_before>/**
 * Created by carl.hand on 03/04/2017.
 */
import React, { Component, PropTypes } from 'react'

export default class Stopwatch extends React.Component {

    render() {
        return (

                    {this.props.time}

                <button onClick={this.props.shouldStart}>Start</button>
                <button onClick={this.props.reset}>Reset</button>

        );
    }
}

Stopwatch.PropTypes = {
    start: React.PropTypes.func.isRequired,
    reset: React.PropTypes.func.isRequired,
    time: React.PropTypes.number.isRequired
};<commit_msg>refactored Stopwatch to be a pure component<commit_after>/**
 * Created by carl.hand on 03/04/2017.
 */
import React, { Component, PropTypes } from 'react'

const Stopwatch = (props) => {

    return (

                {props.time}

            <button onClick={props.shouldStart}>Start</button>
            <button onClick={props.reset}>Reset</button>

    );
};

Stopwatch.PropTypes = {
    start: React.PropTypes.func.isRequired,
    reset: React.PropTypes.func.isRequired,
    time: React.PropTypes.number.isRequired
};

export default Stopwatch;<|endoftext|>


<commit_before>'use strict';

angular.module('myApp.dashboard', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/dashboard', {
    templateUrl: 'views/dashboard.html'
  });
}])

.factory('directiveService', ['$templateCache', function($templateCache){
	var serviceObject = {};
	var _directiveList = [];
	var _componentList = [];
	var _componentMap = {};
	var _sidebarMap = {};
	var _topbarMap = {};

	var _addDirectiveByElementTag = function(tag){
		if(!$templateCache.get(tag)){
			$templateCache.put(tag, "<"+tag+">"+"</"+tag+">");
			_directiveList.push(tag);
		}
	};

	var _addFullComponent = function(componentObject){
		if(!$templateCache.get(componentObject.tag())){
			$templateCache.put(componentObject.tag(), "<"+componentObject.tag()+">"+"</"+componentObject.tag()+">");

			$templateCache.put(componentObject.configTag(), "<"+componentObject.configTag()+">"+"</"+componentObject.configTag()+">");

			_componentList.push(componentObject);
			_componentMap[componentObject.tag()] = componentObject;
		}
	};

	var _addSideBarComponent = function(componentObject){
			$templateCache.put(componentObject.tag(), "<"+componentObject.tag()+">"+"</"+componentObject.tag()+">");

			$templateCache.put(componentObject.configTag(), "<"+componentObject.configTag()+">"+"</"+componentObject.configTag()+">");

			_sidebarMap[componentObject.tag()] = componentObject;
	};

	var _addTopBarComponent = function(componentObject){
			$templateCache.put(componentObject.tag(), "<"+componentObject.tag()+">"+"</"+componentObject.tag()+">");

			$templateCache.put(componentObject.configTag(), "<"+componentObject.configTag()+">"+"</"+componentObject.configTag()+">");

			_topbarMap[componentObject.tag()] = componentObject;
	};

	var _getDirectiveList = function(){
		return _directiveList;
	};

	var _getComponentList = function(){
		return _componentList;
	};

	var _getComponentMap = function(){
		return _componentMap;
	};

	var _getSideBarMap = function(){
		return _sidebarMap;
	};

	var _getTopBarMap = function(){
		return _topbarMap;
	};

	var _getCache = function(){
		return $templateCache;
	}

	serviceObject = {
		addDirectiveByElementTag : _addDirectiveByElementTag,
		getDirectiveList : _getDirectiveList,
		addFullComponent : _addFullComponent,
		addSideBarComponent: _addSideBarComponent,
		addTopBarComponent: _addTopBarComponent,
		getComponentList : _getComponentList,
		getComponentMap: _getComponentMap,
		getSideBarMap: _getSideBarMap,
		getTopBarMap: _getTopBarMap,
		getCache : _getCache
	}

	return serviceObject;
}])

.controller('dashboardController', [ '$scope', 'directiveService', function($scope, directiveService){
	var vm = this;
	vm.editSidebar = false;
	vm.editTopbar = false;
	vm.editMainView = false;
	vm.configDashboard = false;

	angular.extend(vm, directiveService);

	vm.divWrapper = function(){
		if(vm.configDashboard){
			return 'wrapper';
		}
		else return 'wrapper-no-right-padding';
	}

	vm.editTopbarClass = function(){
		if(vm.editTopbar){
			return 'alert alert-success';
		}
		else return 'alert alert-danger';
	};

	vm.editSidebarClass = function(){
		if(vm.editSidebar){
			return 'alert alert-success';
		}
		else return 'alert alert-danger';
	};

	vm.editMainViewClass = function(){
		if(vm.editMainView){
			return 'alert alert-success';
		}
		else return 'alert alert-danger';
	};

	vm.rightBarClass = function(){
		if(vm.configDashboard){
			return 'nav nav-sidebar';
		}
		else return 'hidden';
	}

	vm.config = function(){
		vm.configDashboard = !vm.configDashboard;
	}

	vm.getComponent = function(row, col){
		return vm.components[row][col];
	};

	vm.refresh = function(){
		$scope.$apply();
	};

	vm.isEmptyPanel = function(row, col){
		return !vm.components[row][col].tag || (vm.components[row][col].tag() == 'empty-panel');
	}

	vm.classes = function(row, col){
		if(vm.configDashboard){
			return 'col-md-4';
		}
		else if(vm.isEmptyPanel(row, col)){
			return 'hidden';
		}
		else{
			switch(col) {
				case 0:
					if(vm.isEmptyPanel(row, 1) && vm.isEmptyPanel(row, 2)){
						return 'col-md-12';
					}
					else if(!vm.isEmptyPanel(row, 1)){
						return 'col-md-4';
					}
					else{
						return 'col-md-8';
					}
				case 1:
					if(vm.isEmptyPanel(row, 0) && vm.isEmptyPanel(row, 2)){
						return 'col-md-12';
					}
					else if(!vm.isEmptyPanel(row, 0) && !vm.isEmptyPanel(row, 2)){
						return 'col-md-4';
					}
					else{
						return 'col-md-8';
					}
				case 2:
					if(vm.isEmptyPanel(row, 0) && vm.isEmptyPanel(row, 1)){
						return 'col-md-12';
					}
					//TODO - make an option for 'right alignment' where the rightmost column is width 8 when middle is missing.
					else{
						return 'col-md-4';
					}
			}
		}
	};

	vm.getSidebar = function(){
		return vm.sidebar;
	};

	vm.getTopbar = function(){
		return vm.topbar;
	};

	vm.sidebar = [];
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);

	vm.topbar = [];
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['a-very-specific-button']);

	vm.components = [];
	vm.components.push([directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel']]);
	vm.components.push([directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel']]);
	vm.components.push([directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel']]);

}])

.directive('navbar', [ function(){
	return {
		restrict: 'E',
		templateUrl: 'views/navbar.html'
	}
}])

.directive('paletteDashboardComponent', [ '$compile', function($compile){
	return{
		restrict: 'E',
		template: '',
		link: function(scope, el, attr){
			//add listener functions to the element.

			//add drop listeners to component containers.

			/* should be the same as below, but sometimes doesnt fire event....
			el[0].ondragenter = function(){

			};
			*/

			//sometimes dragenter event isnt fired when dragging???
			el[0].ondrag = function(){
				//only select panels in the main body (div page-content-wrapper)
				var panels = document.getElementById('page-content-wrapper').getElementsByTagName('panel-component');

				if(!angular.element(panels).controller().editMainView){
					var controller = angular.element(panels).controller();

					controller.editMainView=true;
					controller.refresh();

					angular.forEach(panels, function(panel, index){
						var jqElement = angular.element(panel);

						jqElement[0].ondragover= function(e){
							e.preventDefault();
							return false;
						};

						jqElement[0].ondrop= function(event){

							var row = +jqElement.attr('row');
							var column = +jqElement.attr('column');

							controller.components[row][column] = scope.component;

							controller.editMainView = false;
							controller.refresh();

							console.log('dashboard controller on drop:');
							console.log(controller);

							//remove listeners
							angular.forEach(panels, function(panel, index){
								angular.element(panel)[0].ondragover = null;
								angular.element(panel)[0].ondrop = null;

							});
						};
					});
				}
			};
		}
	}
}])

.directive('paletteSidebarComponent', [ '$compile', function($compile){
	return{
		restrict: 'E',
		template: '',
		link: function(scope, el, attr){
			//add listener functions to the element.

			//add drop listeners to component containers.

			/* should be the same as below, but sometimes doesnt fire event....
			el[0].ondragenter = function(){

			};
			*/

			//sometimes dragenter event isnt fired when dragging???
			el[0].ondrag = function(){
				//only select panels in the main body (div page-content-wrapper)
				var panels = document.getElementById('sidebar-wrapper').getElementsByTagName('sidebar-component');

				if(!angular.element(panels).controller().editSidebar){
					var controller = angular.element(panels).controller();

					controller.editSidebar=true;
					controller.refresh();

					angular.forEach(panels, function(panel, index){
						var jqElement = angular.element(panel);

						jqElement[0].ondragover= function(e){
							e.preventDefault();
							return false;
						};

						jqElement[0].ondrop= function(event){

							var index= +jqElement.attr('index');

							controller.sidebar[index] = scope.component;

							controller.editSidebar=false;
							controller.refresh();
							//remove listeners
							angular.forEach(panels, function(panel, index){
								angular.element(panel)[0].ondragover = null;
								angular.element(panel)[0].ondrop = null;

							});
						};
					});
				}
			};
		}
	}
}])

.directive('paletteTopbarComponent', [ '$compile', function($compile){
	return{
		restrict: 'E',
		template: '',
		link: function(scope, el, attr){
			//add listener functions to the element.

						//add drop listeners to component containers.

			/* should be the same as below, but sometimes doesnt fire event....
			el[0].ondragenter = function(){

			};
			*/

			//sometimes dragenter event isnt fired when dragging???
			el[0].ondrag = function(){
				//only select panels in the main body (div page-content-wrapper)
				var panels = document.getElementsByTagName('navbar')[0].getElementsByTagName('topbar-component');

				if(!angular.element(panels).controller().editTopbar){
					var controller = angular.element(panels).controller();

					controller.editTopbar=true;
					controller.refresh();

					angular.forEach(panels, function(panel, index){
						var jqElement = angular.element(panel);

						jqElement[0].ondragover= function(e){
							e.preventDefault();
							return false;
						};

						jqElement[0].ondrop= function(event){

							var index= +jqElement.attr('index');

							controller.topbar[index] = scope.component;

							controller.editTopbar = false;
							controller.refresh();

							//remove listeners
							angular.forEach(panels, function(panel, index){
								angular.element(panel)[0].ondragover = null;
								angular.element(panel)[0].ondrop = null;

							});
						};
					});
				}
			};
		}
	}
}]);
<commit_msg>all drag drop should work properly.<commit_after>'use strict';

angular.module('myApp.dashboard', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/dashboard', {
    templateUrl: 'views/dashboard.html'
  });
}])

.factory('directiveService', ['$templateCache', function($templateCache){
	var serviceObject = {};
	var _directiveList = [];
	var _componentList = [];
	var _componentMap = {};
	var _sidebarMap = {};
	var _topbarMap = {};

	var _addDirectiveByElementTag = function(tag){
		if(!$templateCache.get(tag)){
			$templateCache.put(tag, "<"+tag+">"+"</"+tag+">");
			_directiveList.push(tag);
		}
	};

	var _addFullComponent = function(componentObject){
		if(!$templateCache.get(componentObject.tag())){
			$templateCache.put(componentObject.tag(), "<"+componentObject.tag()+">"+"</"+componentObject.tag()+">");

			$templateCache.put(componentObject.configTag(), "<"+componentObject.configTag()+">"+"</"+componentObject.configTag()+">");

			_componentList.push(componentObject);
			_componentMap[componentObject.tag()] = componentObject;
		}
	};

	var _addSideBarComponent = function(componentObject){
			$templateCache.put(componentObject.tag(), "<"+componentObject.tag()+">"+"</"+componentObject.tag()+">");

			$templateCache.put(componentObject.configTag(), "<"+componentObject.configTag()+">"+"</"+componentObject.configTag()+">");

			_sidebarMap[componentObject.tag()] = componentObject;
	};

	var _addTopBarComponent = function(componentObject){
			$templateCache.put(componentObject.tag(), "<"+componentObject.tag()+">"+"</"+componentObject.tag()+">");

			$templateCache.put(componentObject.configTag(), "<"+componentObject.configTag()+">"+"</"+componentObject.configTag()+">");

			_topbarMap[componentObject.tag()] = componentObject;
	};

	var _getDirectiveList = function(){
		return _directiveList;
	};

	var _getComponentList = function(){
		return _componentList;
	};

	var _getComponentMap = function(){
		return _componentMap;
	};

	var _getSideBarMap = function(){
		return _sidebarMap;
	};

	var _getTopBarMap = function(){
		return _topbarMap;
	};

	var _getCache = function(){
		return $templateCache;
	}

	serviceObject = {
		addDirectiveByElementTag : _addDirectiveByElementTag,
		getDirectiveList : _getDirectiveList,
		addFullComponent : _addFullComponent,
		addSideBarComponent: _addSideBarComponent,
		addTopBarComponent: _addTopBarComponent,
		getComponentList : _getComponentList,
		getComponentMap: _getComponentMap,
		getSideBarMap: _getSideBarMap,
		getTopBarMap: _getTopBarMap,
		getCache : _getCache
	}

	return serviceObject;
}])

.controller('dashboardController', [ '$scope', 'directiveService', function($scope, directiveService){
	var vm = this;
	vm.editSidebar = false;
	vm.editTopbar = false;
	vm.editMainView = false;
	vm.configDashboard = false;

	angular.extend(vm, directiveService);

	vm.divWrapper = function(){
		if(vm.configDashboard){
			return 'wrapper';
		}
		else return 'wrapper-no-right-padding';
	}

	vm.editTopbarClass = function(){
		if(vm.editTopbar){
			return 'alert alert-success';
		}
		else return 'alert alert-danger';
	};

	vm.editSidebarClass = function(){
		if(vm.editSidebar){
			return 'alert alert-success';
		}
		else return 'alert alert-danger';
	};

	vm.editMainViewClass = function(){
		if(vm.editMainView){
			return 'alert alert-success';
		}
		else return 'alert alert-danger';
	};

	vm.rightBarClass = function(){
		if(vm.configDashboard){
			return 'nav nav-sidebar';
		}
		else return 'hidden';
	}

	vm.config = function(){
		vm.configDashboard = !vm.configDashboard;
	}

	vm.getComponent = function(row, col){
		return vm.components[row][col];
	};

	vm.refresh = function(){
		$scope.$apply();
	};

	vm.isEmptyPanel = function(row, col){
		return !vm.components[row][col].tag || (vm.components[row][col].tag() == 'empty-panel');
	}

	vm.classes = function(row, col){
		if(vm.configDashboard){
			return 'col-md-4';
		}
		else if(vm.isEmptyPanel(row, col)){
			return 'hidden';
		}
		else{
			switch(col) {
				case 0:
					if(vm.isEmptyPanel(row, 1) && vm.isEmptyPanel(row, 2)){
						return 'col-md-12';
					}
					else if(!vm.isEmptyPanel(row, 1)){
						return 'col-md-4';
					}
					else{
						return 'col-md-8';
					}
				case 1:
					if(vm.isEmptyPanel(row, 0) && vm.isEmptyPanel(row, 2)){
						return 'col-md-12';
					}
					else if(!vm.isEmptyPanel(row, 0) && !vm.isEmptyPanel(row, 2)){
						return 'col-md-4';
					}
					else{
						return 'col-md-8';
					}
				case 2:
					if(vm.isEmptyPanel(row, 0) && vm.isEmptyPanel(row, 1)){
						return 'col-md-12';
					}
					//TODO - make an option for 'right alignment' where the rightmost column is width 8 when middle is missing.
					else{
						return 'col-md-4';
					}
			}
		}
	};

	vm.getSidebar = function(){
		return vm.sidebar;
	};

	vm.getTopbar = function(){
		return vm.topbar;
	};

	vm.sidebar = [];
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);
	vm.sidebar.push(vm.getSideBarMap()['empty-row']);

	vm.topbar = [];
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['empty-col']);
	vm.topbar.push(vm.getTopBarMap()['a-very-specific-button']);

	vm.components = [];
	vm.components.push([directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel']]);
	vm.components.push([directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel']]);
	vm.components.push([directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel'],directiveService.getComponentMap()['empty-panel']]);

}])

.directive('navbar', [ function(){
	return {
		restrict: 'E',
		templateUrl: 'views/navbar.html'
	}
}])

.directive('paletteDashboardComponent', [ '$compile', function($compile){
	return{
		restrict: 'E',
		template: '',
		link: function(scope, el, attr){
			//add listener functions to the element.

			//add drop listeners to component containers.

			/* should be the same as below, but sometimes doesnt fire event....
			el[0].ondragenter = function(){

			};
			*/

			el[0].ondragend = function(){
				var panels = document.getElementById('page-content-wrapper').getElementsByTagName('panel-component');

				if(angular.element(panels).controller().editMainView){

					var controller = angular.element(panels).controller();
					controller.editMainView=false;
					controller.refresh();

					//remove listeners
					angular.forEach(panels, function(panel, index){
						angular.element(panel)[0].ondragover = null;
						angular.element(panel)[0].ondrop = null;

					});
				};
			};

			el[0].ondrag = function(){
				//only select panels in the main body (div page-content-wrapper)
				var panels = document.getElementById('page-content-wrapper').getElementsByTagName('panel-component');

				if(!angular.element(panels).controller().editMainView){
					var controller = angular.element(panels).controller();

					controller.editMainView=true;
					controller.refresh();

					angular.forEach(panels, function(panel, index){
						var jqElement = angular.element(panel);

						jqElement[0].ondragover= function(e){
							e.preventDefault();
							return false;
						};

						jqElement[0].ondrop= function(event){

							var row = +jqElement.attr('row');
							var column = +jqElement.attr('column');

							controller.components[row][column] = scope.component;

							controller.editMainView = false;
							controller.refresh();
						};
					});
				}
			};
		}
	}
}])

.directive('paletteSidebarComponent', [ '$compile', function($compile){
	return{
		restrict: 'E',
		template: '',
		link: function(scope, el, attr){
			//add listener functions to the element.

			//add drop listeners to component containers.

			/* should be the same as below, but sometimes doesnt fire event....
			el[0].ondragenter = function(){

			};
			*/

			el[0].ondragend = function(){
				var panels = document.getElementById('sidebar-wrapper').getElementsByTagName('sidebar-component');

				if(angular.element(panels).controller().editSidebar){

					var controller = angular.element(panels).controller();
					controller.editSidebar=false;
					controller.refresh();

					//remove listeners
					angular.forEach(panels, function(panel, index){
						angular.element(panel)[0].ondragover = null;
						angular.element(panel)[0].ondrop = null;

					});
				};
			};

			//sometimes dragenter event isnt fired when dragging???
			el[0].ondrag = function(){
				//only select panels in the main body (div page-content-wrapper)
				var panels = document.getElementById('sidebar-wrapper').getElementsByTagName('sidebar-component');

				if(!angular.element(panels).controller().editSidebar){
					var controller = angular.element(panels).controller();

					controller.editSidebar=true;
					controller.refresh();

					angular.forEach(panels, function(panel, index){
						var jqElement = angular.element(panel);

						jqElement[0].ondragover= function(e){
							e.preventDefault();
							return false;
						};

						jqElement[0].ondrop= function(event){

							var index= +jqElement.attr('index');

							controller.sidebar[index] = scope.component;

							controller.editSidebar=false;
							controller.refresh();
							//remove listeners
							angular.forEach(panels, function(panel, index){
								angular.element(panel)[0].ondragover = null;
								angular.element(panel)[0].ondrop = null;

							});
						};
					});
				}
			};
		}
	}
}])

.directive('paletteTopbarComponent', [ '$compile', function($compile){
	return{
		restrict: 'E',
		template: '',
		link: function(scope, el, attr){
			//add listener functions to the element.

						//add drop listeners to component containers.

			/* should be the same as below, but sometimes doesnt fire event....
			el[0].ondragenter = function(){

			};
			*/

			el[0].ondragend = function(){
				var panels = document.getElementsByTagName('navbar')[0].getElementsByTagName('topbar-component');

				if(angular.element(panels).controller().editTopbar){

					var controller = angular.element(panels).controller();
					controller.editTopbar=false;
					controller.refresh();

					//remove listeners
					angular.forEach(panels, function(panel, index){
						angular.element(panel)[0].ondragover = null;
						angular.element(panel)[0].ondrop = null;

					});
				};
			};

			el[0].ondrag = function(){
				//only select panels in the main body (div page-content-wrapper)
				var panels = document.getElementsByTagName('navbar')[0].getElementsByTagName('topbar-component');

				if(!angular.element(panels).controller().editTopbar){
					var controller = angular.element(panels).controller();

					controller.editTopbar=true;
					controller.refresh();

					angular.forEach(panels, function(panel, index){
						var jqElement = angular.element(panel);

						jqElement[0].ondragover= function(e){
							e.preventDefault();
							return false;
						};

						jqElement[0].ondrop= function(event){

							var index= +jqElement.attr('index');

							controller.topbar[index] = scope.component;

							controller.editTopbar = false;
							controller.refresh();

							//remove listeners
							angular.forEach(panels, function(panel, index){
								angular.element(panel)[0].ondragover = null;
								angular.element(panel)[0].ondrop = null;

							});
						};
					});
				}
			};
		}
	}
}]);
<|endoftext|>


<commit_before>import {Observable as O} from 'rx';

import {preventDefault, parseDataAttr, pluckDataAttr} from '../../lib/utils';

const touchTarget = (evt) =>
  document.elementFromPoint(
    evt.changedTouches[0].clientX,
    evt.changedTouches[0].clientY
  )
;
export default (DOM) => {
  const mouseUp$ = O.merge(
    O.fromEvent(document, 'touchend'),
    O.fromEvent(document, 'mouseup')
  );
  const mouseEnter$ = O.merge(
    DOM
      .select('.kv-cell-atom[data-kv-offset]')
      .events('mouseenter')
      .map((evt) => ({
        evt,
        offset: parseInt(evt.target.dataset.kvOffset, 10),
      }))
    ,
    DOM
      .select('.kv-cell-atom[data-kv-offset]')
      .events('touchmove')
      .map((evt) => ({
        evt,
        offset: parseInt(touchTarget(evt).dataset.kvOffset, 10),
      }))
  );
  const mousedown$ = O.merge(
      DOM
        .select('.kv-cell-atom[data-kv-offset]')
        .events('touchstart'),
      DOM
        .select('.kv-cell-atom[data-kv-offset]')
        .events('mousedown')
        .filter((evt) => !!evt.shiftKey)
    )
    .map((evt) => ({
      evt,
      offset: parseInt(evt.target.dataset.kvOffset, 10),
      output: parseInt(evt.target.dataset.kvOutput, 10),
    }))
    .do(({evt}) => evt.preventDefault())
    .filter(({offset}) => !isNaN(offset))
    ;
  const drag$ = mousedown$
    .flatMap(({offset, output}) =>
      O.just({
        output: output,
        startOffset: offset,
        targetOffset: offset,
      }).concat(
        mouseEnter$
        .do(({evt}) => evt.preventDefault())
        .filter(({offset: o}) => isFinite(o))
        .distinctUntilChanged(
          ({offset: o}) => o,
          (a, b) => a === b
        )
        .map(({offset: targetOffset}) => ({
          output,
          startOffset: offset,
          targetOffset,
        })
      )
      ).takeUntil(
        mouseUp$.do(preventDefault)
      )
    );

  const dragEnd$ = drag$
    .sample(mouseUp$);

  return {
    addInput$:
      DOM
        .select('[data-kv-counter="increment"]')
        .events('click')
        .map(() => true),
    removeInput$:
      DOM
        .select('[data-kv-counter="decrement"]')
        .events('click')
        .map(() => true),
    cycleValue$:
      DOM
        .select('.kv-cell-atom[data-kv-offset]')
        .events('click')
        .filter((evt) => !evt.shiftKey)
        .map((evt) => ({
          reverse: evt.altKey,
          output: parseInt(evt.target.dataset.kvOutput, 10),
          offset: parseInt(evt.target.dataset.kvOffset, 10),
        })),
    removeLoop$:
      DOM
        .select('[data-loop-index]')
        .events('click')
        .do(preventDefault)
        .map(parseDataAttr('loopIndex'))
        .filter(isFinite),
    tryLoop$:
      drag$,
    addLoop$:
      dragEnd$,
    addOutput$:
      DOM
        .select('[data-kv-add-output]')
        .events('click')
        .do(preventDefault)
        .map(() => true),
    removeOutput$:
      DOM
        .select('[data-kv-remove-output]')
        .events('click')
        .do(preventDefault)
        .do((e) => e.stopPropagation())
        .map(parseDataAttr('kvRemoveOutput'))
        .filter(isFinite),
    selectOutput$:
      DOM
        .select('[data-kv-output]')
        .events('click')
        .do(preventDefault)
        .do((e) => e.stopPropagation())
        .map(parseDataAttr('kvOutput'))
        .filter(isFinite),
    switchMode$:
      DOM
        .select('[data-kv-mode]')
        .events('click')
        .do(preventDefault)
        .map((evt) => evt.target.dataset.kvMode),
    help$:
      DOM
        .select('[data-help]')
        .events('click')
        .do(preventDefault)
        .map(pluckDataAttr('help'))
        .map((val) => val === 'true'),
  };
};
<commit_msg>rename streams to be more generic<commit_after>import {Observable as O} from 'rx';

import {preventDefault, parseDataAttr, pluckDataAttr} from '../../lib/utils';

const touchTarget = (evt) =>
  document.elementFromPoint(
    evt.changedTouches[0].clientX,
    evt.changedTouches[0].clientY
  )
;
export default (DOM) => {
  const pointerUp$ = O.merge(
    O.fromEvent(document, 'touchend'),
    O.fromEvent(document, 'mouseup')
  );
  const pointerEnter$ = O.merge(
    DOM
      .select('.kv-cell-atom[data-kv-offset]')
      .events('mouseenter')
      .map((evt) => ({
        evt,
        offset: parseInt(evt.target.dataset.kvOffset, 10),
      }))
    ,
    DOM
      .select('.kv-cell-atom[data-kv-offset]')
      .events('touchmove')
      .map((evt) => ({
        evt,
        offset: parseInt(touchTarget(evt).dataset.kvOffset, 10),
      }))
  );
  const pointerDown$ = O.merge(
      DOM
        .select('.kv-cell-atom[data-kv-offset]')
        .events('touchstart'),
      DOM
        .select('.kv-cell-atom[data-kv-offset]')
        .events('mousedown')
        .filter((evt) => !!evt.shiftKey)
    )
    .map((evt) => ({
      evt,
      offset: parseInt(evt.target.dataset.kvOffset, 10),
      output: parseInt(evt.target.dataset.kvOutput, 10),
    }))
    .do(({evt}) => evt.preventDefault())
    .filter(({offset}) => !isNaN(offset))
    ;
  const drag$ = pointerDown$
    .flatMap(({offset, output}) =>
      O.just({
        output: output,
        startOffset: offset,
        targetOffset: offset,
      }).concat(
        pointerEnter$
        .filter(({offset: o}) => !isNaN(o))
        .do(({evt}) => evt.preventDefault())
        .distinctUntilChanged(
          ({offset: o}) => o,
          (a, b) => a === b
        )
        .map(({offset: targetOffset}) => ({
          output,
          startOffset: offset,
          targetOffset,
        })
      )
      ).takeUntil(
        pointerUp$
      )
    );

  const dragEnd$ = drag$
    .sample(pointerUp$);

  return {
    addInput$:
      DOM
        .select('[data-kv-counter="increment"]')
        .events('click')
        .map(() => true),
    removeInput$:
      DOM
        .select('[data-kv-counter="decrement"]')
        .events('click')
        .map(() => true),
    cycleValue$:
      DOM
        .select('.kv-cell-atom[data-kv-offset]')
        .events('click')
        .filter((evt) => !evt.shiftKey)
        .map((evt) => ({
          reverse: evt.altKey,
          output: parseInt(evt.target.dataset.kvOutput, 10),
          offset: parseInt(evt.target.dataset.kvOffset, 10),
        })),
    removeLoop$:
      DOM
        .select('[data-loop-index]')
        .events('click')
        .do(preventDefault)
        .map(parseDataAttr('loopIndex'))
        .filter(isFinite),
    tryLoop$:
      drag$,
    addLoop$:
      dragEnd$,
    addOutput$:
      DOM
        .select('[data-kv-add-output]')
        .events('click')
        .do(preventDefault)
        .map(() => true),
    removeOutput$:
      DOM
        .select('[data-kv-remove-output]')
        .events('click')
        .do(preventDefault)
        .do((e) => e.stopPropagation())
        .map(parseDataAttr('kvRemoveOutput'))
        .filter(isFinite),
    selectOutput$:
      DOM
        .select('[data-kv-output]')
        .events('click')
        .do(preventDefault)
        .do((e) => e.stopPropagation())
        .map(parseDataAttr('kvOutput'))
        .filter(isFinite),
    switchMode$:
      DOM
        .select('[data-kv-mode]')
        .events('click')
        .do(preventDefault)
        .map((evt) => evt.target.dataset.kvMode),
    help$:
      DOM
        .select('[data-help]')
        .events('click')
        .do(preventDefault)
        .map(pluckDataAttr('help'))
        .map((val) => val === 'true'),
  };
};
<|endoftext|>


<commit_before>var xr = require('xr');
var { routes } = require('../constants');
var actions = require('../actions');

module.exports = React.createClass({

    onSubmit: function() {
        var orgName = React.findDOMNode(this.refs.organizationName).value;
        actions.organization.fetchRepositories(orgName);
    },

    render: function() {
        return

            <input type="text" ref="organizationName" placeholder="Organization Name" />
            <button ref="searchButton" onClick={this.onSubmit}>Search</button>

    }

});
<commit_msg>Capture enter key and submit form<commit_after>var xr = require('xr');
var { routes } = require('../constants');
var actions = require('../actions');

module.exports = React.createClass({

    onKeyPress: function(e) {
        if (e.keyCode === 13) {
            this.onSubmit();
        }
    },

    onSubmit: function() {
        var orgName = React.findDOMNode(this.refs.organizationName).value;
        actions.organization.fetchRepositories(orgName);
    },

    render: function() {
        return

            <input type="text" ref="organizationName" placeholder="Organization Name" onKeyUp={this.onKeyPress}/>
            <button ref="searchButton" onClick={this.onSubmit}>Search</button>

    }

});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';

import Panel from '../shared/Panel';
import Button from '../shared/Button';
import TeamForm from './Form';

import TeamUpdateMutation from '../../mutations/TeamUpdate';
import GraphQLErrors from '../../constants/GraphQLErrors';
import FlashesStore from '../../stores/FlashesStore';

class TeamEdit extends React.Component {
  static propTypes = {
    team: PropTypes.shape({
      name: PropTypes.string.isRequired,
      slug: PropTypes.string.isRequired,
      description: PropTypes.string,
      privacy: PropTypes.string.isRequired,
      isDefaultTeam: PropTypes.bool.isRequired,
      defaultMemberRole: PropTypes.string.isRequired,
      organization: PropTypes.shape({
        name: PropTypes.string.isRequired,
        slug: PropTypes.string.isRequired
      }).isRequired
    }).isRequired
  };

  static contextTypes = {
    router: PropTypes.object.isRequired
  };

  state = {
    name: this.props.team.name,
    description: this.props.team.description,
    privacy: this.props.team.privacy,
    isDefaultTeam: this.props.team.isDefaultTeam,
    defaultMemberRole: this.props.team.defaultMemberRole,
    saving: false,
    errors: null
  };

  render() {
    return (
      <form onSubmit={this.handleFormSubmit}>
        <TeamForm
          uuid={this.props.team.uuid}
          onChange={this.handleFormChange}
          errors={this.state.errors}
          name={this.state.name}
          description={this.state.description}
          privacy={this.state.privacy}
          isDefaultTeam={this.state.isDefaultTeam}
          saving={this.state.saving}
          button={this.state.saving ? "Saving team…" : "Save Team"}
        />
      </form>
    );
  }

  handleFormChange = (field, value) => {
    const state = {};
    state[field] = value;

    this.setState(state);
  };

  handleFormSubmit = (evt) => {
    evt.preventDefault();

    this.setState({ saving: true });

    const mutation = new TeamUpdateMutation({
      team: this.props.team,
      name: this.state.name,
      description: this.state.description,
      privacy: this.state.privacy,
      isDefaultTeam: this.state.isDefaultTeam,
      defaultMemberRole: this.state.defaultMemberRole
    });

    Relay.Store.commitUpdate(mutation, {
      onSuccess: this.handleMutationSuccess,
      onFailure: this.handleMutationError
    });
  };

  handleMutationError = (transaction) => {
    const error = transaction.getError();
    if (error) {
      if (error.source && error.source.type === GraphQLErrors.RECORD_VALIDATION_ERROR) {
        this.setState({ errors: transaction.getError().source.errors });
      } else {
        FlashesStore.flash(FlashesStore.ERROR, transaction.getError());
      }
    }

    this.setState({ saving: false });
  };

  handleMutationSuccess = (response) => {
    this.setState({ saving: false });

    FlashesStore.flash(FlashesStore.SUCCESS, "Settings for this team have been saved successfully")
  };
}

export default Relay.createContainer(TeamEdit, {
  fragments: {
    team: () => Relay.QL`
      fragment on Team {
        ${TeamUpdateMutation.getFragment('team')}
        uuid
        name
        slug
        description
        privacy
        isDefaultTeam
        defaultMemberRole
        organization {
          name
          slug
        }
      }
    `
  }
});
<commit_msg>Need to redirect to handle slug changes<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';

import Panel from '../shared/Panel';
import Button from '../shared/Button';
import TeamForm from './Form';

import TeamUpdateMutation from '../../mutations/TeamUpdate';
import GraphQLErrors from '../../constants/GraphQLErrors';
import FlashesStore from '../../stores/FlashesStore';

class TeamEdit extends React.Component {
  static propTypes = {
    team: PropTypes.shape({
      name: PropTypes.string.isRequired,
      slug: PropTypes.string.isRequired,
      description: PropTypes.string,
      privacy: PropTypes.string.isRequired,
      isDefaultTeam: PropTypes.bool.isRequired,
      defaultMemberRole: PropTypes.string.isRequired,
      organization: PropTypes.shape({
        name: PropTypes.string.isRequired,
        slug: PropTypes.string.isRequired
      }).isRequired
    }).isRequired
  };

  static contextTypes = {
    router: PropTypes.object.isRequired
  };

  state = {
    name: this.props.team.name,
    description: this.props.team.description,
    privacy: this.props.team.privacy,
    isDefaultTeam: this.props.team.isDefaultTeam,
    defaultMemberRole: this.props.team.defaultMemberRole,
    saving: false,
    errors: null
  };

  render() {
    return (
      <form onSubmit={this.handleFormSubmit}>
        <TeamForm
          uuid={this.props.team.uuid}
          onChange={this.handleFormChange}
          errors={this.state.errors}
          name={this.state.name}
          description={this.state.description}
          privacy={this.state.privacy}
          isDefaultTeam={this.state.isDefaultTeam}
          saving={this.state.saving}
          button={this.state.saving ? "Saving team…" : "Save Team"}
        />
      </form>
    );
  }

  handleFormChange = (field, value) => {
    const state = {};
    state[field] = value;

    this.setState(state);
  };

  handleFormSubmit = (evt) => {
    evt.preventDefault();

    this.setState({ saving: true });

    const mutation = new TeamUpdateMutation({
      team: this.props.team,
      name: this.state.name,
      description: this.state.description,
      privacy: this.state.privacy,
      isDefaultTeam: this.state.isDefaultTeam,
      defaultMemberRole: this.state.defaultMemberRole
    });

    Relay.Store.commitUpdate(mutation, {
      onSuccess: this.handleMutationSuccess,
      onFailure: this.handleMutationError
    });
  };

  handleMutationError = (transaction) => {
    const error = transaction.getError();
    if (error) {
      if (error.source && error.source.type === GraphQLErrors.RECORD_VALIDATION_ERROR) {
        this.setState({ errors: transaction.getError().source.errors });
      } else {
        FlashesStore.flash(FlashesStore.ERROR, transaction.getError());
      }
    }

    this.setState({ saving: false });
  };

  handleMutationSuccess = (response) => {
    this.setState({ saving: false });

    FlashesStore.flash(FlashesStore.SUCCESS, "Settings for this team have been saved successfully")

    // Update the URL with the latest version of the slug
    this.context.router.push(`/organizations/${this.props.team.organization.slug}/teams/${response.teamUpdate.team.slug}/settings`);
  };
}

export default Relay.createContainer(TeamEdit, {
  fragments: {
    team: () => Relay.QL`
      fragment on Team {
        ${TeamUpdateMutation.getFragment('team')}
        uuid
        name
        slug
        description
        privacy
        isDefaultTeam
        defaultMemberRole
        organization {
          name
          slug
        }
      }
    `
  }
});
<|endoftext|>


<commit_before>/**
 *
 * App
 *
 * This component is the skeleton around the actual pages, and should only
 * contain code that should be seen on all pages. (e.g. navigation bar)
 */

import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import { Link, browserHistory } from 'react-router';
import ReactGA from 'react-ga';

import { isIOS, isMobile } from 'react-device-detect';

import Helmet from 'react-helmet';
import styled from 'styled-components';

import Header from 'components/Header';
import Footer from 'components/Footer';
import withProgressBar from 'components/ProgressBar';
import Img from 'containers/App/Img';
import Banner from 'components/Header/banner.png';
import AppSnackBar from 'containers/AppSnackBar';
import { getAppIcon } from 'utils/functions';

import { MdChevronLeft,
  MdSearch,
  MdApps,
} from 'react-icons/lib/md';

import { FaNewspaperO } from 'react-icons/lib/fa';

import Paper from 'material-ui/Paper';
import Tooltip from 'material-ui/Tooltip';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';
import IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';
import Button from 'material-ui/Button';
import Hidden from 'material-ui/Hidden';
import Drawer from 'material-ui/Drawer';
import Divider from 'material-ui/Divider';
import Grid from 'material-ui/Grid';
import { withStyles } from 'material-ui/styles';
import List, { ListItem } from 'material-ui/List';
import ListSubheader from 'material-ui/List/ListSubheader';
import withWidth from 'material-ui/utils/withWidth';

import { apps, appBar, suBranding, version, whiteLabel } from 'utils/constants';
import { theme } from 'utils/theme';

import { styles } from './styles';

const AppWrapper = styled.div`
  max-width: calc(1200px + 16px * 2);
  margin: 0 auto;
  display: flex;
  min-height: 100%;
  padding: 0 0px;
  flex-direction: column;
`;

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      drawerOpen: false,
    };
    this.handleDrawerToggle = this.handleDrawerToggle.bind(this);
  }
  handleDrawerToggle() {
    this.setState({
      drawerOpen: !this.state.drawerOpen,
    });
  }

  render() {
    const drawer = (

          <Button color="primary" onClick={this.handleDrawerToggle}>
            <Link to="/" className={this.props.classes.menuLink}>
              {whiteLabel ? null :

              }
              {whiteLabel ?

Catalysis Browser beta v{version}

                  :

Catalysis-Hub.org v{version}

              }
            </Link>
          </Button>

        <Divider className={this.props.classes.divider} />

        <List
          subheader={<ListSubheader className={this.props.classes.subListHeader}>
            <Link to="/energies" onClick={this.handleDrawerToggle} className={this.props.classes.topMenuLink}>
              <MdSearch />{'\u00A0\u00A0 '}Search
            </Link>
          </ListSubheader>}
        >
        </List>

        <Divider className={this.props.classes.divider} />

        <List
          subheader={<ListSubheader className={this.props.classes.subListHeader}>
            <Link to="/appsIndex" onClick={this.handleDrawerToggle} className={this.props.classes.topMenuLink}>
              <MdApps />{'\u00A0\u00A0 '}Apps
            </Link>
          </ListSubheader>}
        >
          {apps.map((app, i) => (
            <div key={`app_${i}`}>
              <ListItem className={this.props.classes.menuItem}>
                <Link
                  to={app.route}
                  onClick={this.handleDrawerToggle}
                  className={!_.isEmpty(app.route) ? this.props.classes.menuLink : this.props.classes.inactiveLink}
                >
                  {getAppIcon(app.title)} {app.title}

                </Link>
              </ListItem>
              {_.isEmpty(app.children) ? null :
              <List>
                {app.children.map((child, j) => (
                  <ListItem key={`child_${i}_${j}`}>
                    <Link
                      to={`${app.route}/${child.route}`}
                      onClick={this.handleDrawerToggle}
                      className={!_.isEmpty(child.route) ? this.props.classes.menuLink : this.props.classes.inactiveLink}
                    >
                      {child.title}
                    </Link>
                  </ListItem>
                    )
                    )}
              </List>
              }

          )

          )}
        </List>

        <Divider className={this.props.classes.divider} />

    );

    return (

        <AppSnackBar />
        {suBranding === false || appBar === true ? null :

            <ReactGA.OutboundLink
              to="http://www.stanford.edu"
              eventLabel="http://www.stanford.edu"
              style={{ margin: 10 }}
              target="_blank"
            >

            </ReactGA.OutboundLink>

        }
        {appBar === false ? null :

          <AppBar position="fixed" className={this.props.classes.appBar}>
            <Toolbar>
              <Grid
                container
                direction="row"
                justify="space-between"
                className={this.props.classes.toolbar}
              >
                {isMobile ?
                  <Grid
                    item sm={1} md={1} lg={1}
                    className={this.props.classes.appBarGridItem}
                  >
                    { (!isIOS || this.props.history === null) ? null :
                    <IconButton onClick={browserHistory.goBack} color="inherit" aria-label="Back" className={this.props.classes.backLink} >
                      <MdChevronLeft />
                    </IconButton>

                          }
                    <Tooltip title="Open menu">
                      <IconButton onClick={this.handleDrawerToggle} color="inherit" aria-label="Menu" className={`${this.props.classes.navIconHide} ${this.props.classes.textLink}`}>
                        {/* onClick event has to be on IconButton to work w/ Firefox. */}
                        <MenuIcon />
                      </IconButton>
                    </Tooltip>
                  </Grid>
                        : '\u00A0\u00A0'
                    }
                <Grid
                  item sm={4} md={isMobile ? 5 : 6} lg={isMobile ? 6 : 7}
                  className={this.props.classes.appBarGridItem}
                >
                  <Grid container direction="row" justify={isIOS ? 'space-around' : 'space-between'}>
                    <Grid item>
                      <Grid container direction="column" justify="center">
                        <Hidden xsDown>
                          <Grid item>
                            <Tooltip title={`v${version}`}>
                              <Link to="/" className={this.props.classes.appBarTitle} >
                                <Typography type="body1" color="inherit" className={this.props.classes.appBarTitle} >
                                  {whiteLabel ? `${this.props.location.pathname}` : 'Catalysis-Hub.Org'}
                                </Typography>
                              </Link>
                            </Tooltip>
                          </Grid>
                        </Hidden>
                      </Grid>
                    </Grid>
                  </Grid>
                </Grid>

                <Grid
                  item sm={4} md={4} lg={4}
                  className={this.props.classes.appBarGridItem}
                >

                  <Grid container direction="row" justify="flex-end" >
                    <Grid
                      item className={(
                  window.location.href.endsWith('/publications') ||
                  window.location.href.indexOf('/publications') > -1 ||
                  window.location.href.endsWith('/about') ||
                  window.location.href.endsWith('/')
                ) ? null : this.props.classes.activeAppBarLink}
                    >
                      <Link to="/appsIndex" className={this.props.classes.textLink}>
                        <Tooltip title="Open Apps Index">

                            <IconButton className={this.props.classes.barIcon} size="small" color="inherit" aria-label="Open Apps Index" >
                              <MdApps />
                            </IconButton>

Apps

                        </Tooltip>
                      </Link>
                    </Grid>

                    <Grid item className={window.location.href.indexOf('/publications') === -1 ? null : this.props.classes.activeAppBarLink}>
                      <Link to="/publications" className={this.props.classes.textLink}>
                        <Tooltip title="Browse Publications with Geometries">

                            <IconButton className={this.props.classes.barIcon} size="small" color="inherit" aria-label="Open List of Publications" >
                              <FaNewspaperO />
                            </IconButton>

Publications

                        </Tooltip>
                      </Link>
                    </Grid>

                    <Grid item className={window.location.href.indexOf('/about') === -1 ? null : this.props.classes.activeAppBarLink}>
                      <Link to="/about" className={this.props.classes.textLink}>
                        <Tooltip title="About This Website">

About

                        </Tooltip>
                      </Link>
                    </Grid>

                    <Hidden mdDown>
                      <Grid item>
                        <Link to="http://docs.catalysis-hub.org" target="_blank" className={this.props.classes.textLink}>
                          <Tooltip title="See Website Documentation. Opens in new tab.">

Documentation

                          </Tooltip>
                        </Link>
                      </Grid>
                    </Hidden>
                  </Grid>
                </Grid>
              </Grid>
            </Toolbar>
          </AppBar>
          <Hidden xlUp >
            <Drawer
              type="temporary"
              anchor={theme.direction === 'rtl' ? 'right' : 'left'}
              open={this.state.drawerOpen}
              onClose={this.handleDrawerToggle}
              className={this.props.classes.drawerPaper}
              ModalProps={{
                keepMounted: true, // Better open performance on mobile.
              }}
            >
              { drawer }
            </Drawer>
          </Hidden>
          <Hidden lgDown implementation="css">
            <Paper >
              <Drawer
                type="permanent"
                open
                className={this.props.classes.drawerPaper}
              >
                { drawer }
              </Drawer>
            </Paper>
          </Hidden>

        }

          <AppWrapper>
            <Helmet
              className={this.props.classes.helmet}
              titleTemplate="%s - Catalysis-Hub.Org"
              defaultTitle="Catalysis-Hub.Org"
              title={`${(_.startCase(this.props.location.pathname.replace('/', '')).match(/[A-Z][a-z]+/g) || ['Index']).join(' ')} - Catalysis-Hub.Org`}
              meta={[
                { name: 'description', content: `Catalysis-Hub.org is a frontend for browsing the SUNCAT CatApp database containing thousands of first-principles calculations related to heterogeneous catalysis reactions on surface systems. Its goal is to allow comprehensive and user-friendly access to raw quantum chemical simulations guided by heterogeneous catalysis concepts and commonly used graphical representations such as scaling relations and activity maps. All reaction energies are derived from periodic plane-wave density functional theory calculations. An increasing number of calculations contain the corresponding optimized geometry as well as further calculational details such as exchange-correlation (XC) functional, basis set quality, and k-point sampling. Ultimately, the goal is to provide fully self-contained data for predicting experimental observations from electronic structure calculations and using software packages such as Quantum Espresso, GPAW, VASP, and FHI-aims. Input and output with other codes is supported through the Atomic Simulation Environment (ASE). It may also serve as a natural starting point for training and developing machine-learning based approaches accelerating quantum chemical simulations.
      Features include search for specific reaction energies, transition states, structures, exploration of scaling relations, activity maps, Pourbaix diagrams and machine learning models, as well as generation of novel bulk and surface structures. Calculations are linked to peer-review publications where available. The database can be queried via a GraphQL API that can also be accessed directly.
      All code pertaining to this project is hosted as open-source under a liberal MIT license on github to encourage derived work and collaboration. The frontend is developed using the React Javascript framework based on react boilerplate. New components (apps) can be quickly spun-off and added to the project. The backend is developed using the Flask Python framework providing the GraphQL API as well as further APIs for specific apps.
      As such Catalysis-Hub.org aims to serve as a starting point for trend studies and atomic based heterogeneous catalysis explorations.` },
                { name: 'robots', content: 'index,follow' },
                { name: 'keywords', content: 'heterogeneous catalysis,metals,density functional theory,scaling relations, activity maps,pourbaix diagrams,machine learning,quantum espresso,vasp,gpaw' },
                { name: 'DC.title', content: 'Catalysis-Hub.org' },
              ]}
              link={suBranding === false && appBar === false ? [] : [
                { rel: 'stylesheet', href: 'https://www.stanford.edu/su-identity/css/su-identity.css' },
                { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,200' },
              ]}
            />

            {React.Children.toArray(this.props.children)}
          </AppWrapper>

    );
  }
}

App.propTypes = {
  classes: PropTypes.object.isRequired,
  children: React.PropTypes.node,
  location: PropTypes.object.isRequired,
  history: PropTypes.object,
};
const mapStateToProps = (state) => ({
  history: state.get('route').get('locationBeforeTransitions'),
});

const mapDispatchToProps = () => ({
});

export default compose(
  withProgressBar,
  withWidth(),
  connect(mapStateToProps, mapDispatchToProps),
  withStyles(styles, { withTheme: true }),
)(App);
<commit_msg>Little styling update<commit_after>/**
 *
 * App
 *
 * This component is the skeleton around the actual pages, and should only
 * contain code that should be seen on all pages. (e.g. navigation bar)
 */

import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
import { Link, browserHistory } from 'react-router';
import ReactGA from 'react-ga';

import { isIOS, isMobile } from 'react-device-detect';

import Helmet from 'react-helmet';
import styled from 'styled-components';

import Header from 'components/Header';
import Footer from 'components/Footer';
import withProgressBar from 'components/ProgressBar';
import Img from 'containers/App/Img';
import Banner from 'components/Header/banner.png';
import AppSnackBar from 'containers/AppSnackBar';
import { getAppIcon } from 'utils/functions';

import { MdChevronLeft,
  MdSearch,
  MdApps,
} from 'react-icons/lib/md';

import { FaNewspaperO } from 'react-icons/lib/fa';

import Paper from 'material-ui/Paper';
import Tooltip from 'material-ui/Tooltip';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';
import IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';
import Button from 'material-ui/Button';
import Hidden from 'material-ui/Hidden';
import Drawer from 'material-ui/Drawer';
import Divider from 'material-ui/Divider';
import Grid from 'material-ui/Grid';
import { withStyles } from 'material-ui/styles';
import List, { ListItem } from 'material-ui/List';
import ListSubheader from 'material-ui/List/ListSubheader';
import withWidth from 'material-ui/utils/withWidth';

import { apps, appBar, suBranding, version, whiteLabel } from 'utils/constants';
import { theme } from 'utils/theme';

import { styles } from './styles';

const AppWrapper = styled.div`
  max-width: calc(1200px + 16px * 2);
  margin: 0 auto;
  display: flex;
  min-height: 100%;
  padding: 0 0px;
  flex-direction: column;
`;

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      drawerOpen: false,
    };
    this.handleDrawerToggle = this.handleDrawerToggle.bind(this);
  }
  handleDrawerToggle() {
    this.setState({
      drawerOpen: !this.state.drawerOpen,
    });
  }

  render() {
    const drawer = (

          <Button color="primary" onClick={this.handleDrawerToggle}>
            <Link to="/" className={this.props.classes.menuLink}>
              {whiteLabel ? null :

              }
              {whiteLabel ?

Catalysis Browser beta v{version}

                  :

Catalysis-Hub.org v{version}

              }
            </Link>
          </Button>

        <Divider className={this.props.classes.divider} />

        <List
          subheader={<ListSubheader className={this.props.classes.subListHeader}>
            <Link to="/energies" onClick={this.handleDrawerToggle} className={this.props.classes.topMenuLink}>
              <MdSearch />{'\u00A0\u00A0 '}Search
            </Link>
          </ListSubheader>}
        >
        </List>

        <Divider className={this.props.classes.divider} />

        <List
          subheader={<ListSubheader className={this.props.classes.subListHeader}>
            <Link to="/appsIndex" onClick={this.handleDrawerToggle} className={this.props.classes.topMenuLink}>
              <MdApps />{'\u00A0\u00A0 '}Apps
            </Link>
          </ListSubheader>}
        >
          {apps.map((app, i) => (
            <div key={`app_${i}`}>
              <ListItem className={this.props.classes.menuItem}>
                <Link
                  to={app.route}
                  onClick={this.handleDrawerToggle}
                  className={!_.isEmpty(app.route) ? this.props.classes.menuLink : this.props.classes.inactiveLink}
                >
                  {getAppIcon(app.title)} {app.title}

                </Link>
              </ListItem>
              {_.isEmpty(app.children) ? null :
              <List>
                {app.children.map((child, j) => (
                  <ListItem key={`child_${i}_${j}`}>
                    <Link
                      to={`${app.route}/${child.route}`}
                      onClick={this.handleDrawerToggle}
                      className={!_.isEmpty(child.route) ? this.props.classes.menuLink : this.props.classes.inactiveLink}
                    >
                      {child.title}
                    </Link>
                  </ListItem>
                    )
                    )}
              </List>
              }

          )

          )}
        </List>

        <Divider className={this.props.classes.divider} />

    );

    return (

        <AppSnackBar />
        {suBranding === false || appBar === true ? null :

            <ReactGA.OutboundLink
              to="http://www.stanford.edu"
              eventLabel="http://www.stanford.edu"
              style={{ margin: 10 }}
              target="_blank"
            >

            </ReactGA.OutboundLink>

        }
        {appBar === false ? null :

          <AppBar position="fixed" className={this.props.classes.appBar}>
            <Toolbar>
              <Grid
                container
                direction="row"
                justify="space-between"
                className={this.props.classes.toolbar}
              >
                {isMobile ?
                  <Grid
                    item sm={1} md={1} lg={1}
                    className={this.props.classes.appBarGridItem}
                  >
                    { (!isIOS || this.props.history === null) ? null :
                    <IconButton onClick={browserHistory.goBack} color="inherit" aria-label="Back" className={this.props.classes.backLink} >
                      <MdChevronLeft />
                    </IconButton>

                          }
                    <Tooltip title="Open menu">
                      <IconButton onClick={this.handleDrawerToggle} color="inherit" aria-label="Menu" className={`${this.props.classes.navIconHide} ${this.props.classes.textLink}`}>
                        {/* onClick event has to be on IconButton to work w/ Firefox. */}
                        <MenuIcon />
                      </IconButton>
                    </Tooltip>
                  </Grid>
                        : '\u00A0\u00A0'
                    }
                <Grid
                  item sm={4} md={isMobile ? 5 : 6} lg={isMobile ? 6 : 7}
                  className={this.props.classes.appBarGridItem}
                >
                  <Grid container direction="row" justify={isIOS ? 'space-around' : 'space-between'}>
                    <Grid item>
                      <Grid container direction="column" justify="center">
                        <Hidden xsDown>
                          <Grid item>
                            <Tooltip title={`v${version}`}>
                              <Link to="/" className={this.props.classes.appBarTitle} >
                                <Typography type="body1" color="inherit" className={this.props.classes.appBarTitle} >

                                    {whiteLabel ? `${this.props.location.pathname}` : 'Catalysis-Hub.Org'}

                                </Typography>
                              </Link>
                            </Tooltip>
                          </Grid>
                        </Hidden>
                      </Grid>
                    </Grid>
                  </Grid>
                </Grid>

                <Grid
                  item sm={4} md={4} lg={4}
                  className={this.props.classes.appBarGridItem}
                >

                  <Grid container direction="row" justify="flex-end" >
                    <Grid
                      item className={(
                  window.location.href.endsWith('/publications') ||
                  window.location.href.indexOf('/publications') > -1 ||
                  window.location.href.endsWith('/about') ||
                  window.location.href.endsWith('/')
                ) ? null : this.props.classes.activeAppBarLink}
                    >
                      <Link to="/appsIndex" className={this.props.classes.textLink}>
                        <Tooltip title="Open Apps Index">

                            <IconButton className={this.props.classes.barIcon} size="small" color="inherit" aria-label="Open Apps Index" >
                              <MdApps />
                            </IconButton>

Apps

                        </Tooltip>
                      </Link>
                    </Grid>

                    <Grid item className={window.location.href.indexOf('/publications') === -1 ? null : this.props.classes.activeAppBarLink}>
                      <Link to="/publications" className={this.props.classes.textLink}>
                        <Tooltip title="Browse Publications with Geometries">

                            <IconButton className={this.props.classes.barIcon} size="small" color="inherit" aria-label="Open List of Publications" >
                              <FaNewspaperO />
                            </IconButton>

Publications

                        </Tooltip>
                      </Link>
                    </Grid>

                    <Grid item className={window.location.href.indexOf('/about') === -1 ? null : this.props.classes.activeAppBarLink}>
                      <Link to="/about" className={this.props.classes.textLink}>
                        <Tooltip title="About This Website">

About

                        </Tooltip>
                      </Link>
                    </Grid>

                    <Hidden mdDown>
                      <Grid item>
                        <Link to="http://docs.catalysis-hub.org" target="_blank" className={this.props.classes.textLink}>
                          <Tooltip title="See Website Documentation. Opens in new tab.">

Documentation

                          </Tooltip>
                        </Link>
                      </Grid>
                    </Hidden>
                  </Grid>
                </Grid>
              </Grid>
            </Toolbar>
          </AppBar>
          <Hidden xlUp >
            <Drawer
              type="temporary"
              anchor={theme.direction === 'rtl' ? 'right' : 'left'}
              open={this.state.drawerOpen}
              onClose={this.handleDrawerToggle}
              className={this.props.classes.drawerPaper}
              ModalProps={{
                keepMounted: true, // Better open performance on mobile.
              }}
            >
              { drawer }
            </Drawer>
          </Hidden>
          <Hidden lgDown implementation="css">
            <Paper >
              <Drawer
                type="permanent"
                open
                className={this.props.classes.drawerPaper}
              >
                { drawer }
              </Drawer>
            </Paper>
          </Hidden>

        }

          <AppWrapper>
            <Helmet
              className={this.props.classes.helmet}
              titleTemplate="%s - Catalysis-Hub.Org"
              defaultTitle="Catalysis-Hub.Org"
              title={`${(_.startCase(this.props.location.pathname.replace('/', '')).match(/[A-Z][a-z]+/g) || ['Index']).join(' ')} - Catalysis-Hub.Org`}
              meta={[
                { name: 'description', content: `Catalysis-Hub.org is a frontend for browsing the SUNCAT CatApp database containing thousands of first-principles calculations related to heterogeneous catalysis reactions on surface systems. Its goal is to allow comprehensive and user-friendly access to raw quantum chemical simulations guided by heterogeneous catalysis concepts and commonly used graphical representations such as scaling relations and activity maps. All reaction energies are derived from periodic plane-wave density functional theory calculations. An increasing number of calculations contain the corresponding optimized geometry as well as further calculational details such as exchange-correlation (XC) functional, basis set quality, and k-point sampling. Ultimately, the goal is to provide fully self-contained data for predicting experimental observations from electronic structure calculations and using software packages such as Quantum Espresso, GPAW, VASP, and FHI-aims. Input and output with other codes is supported through the Atomic Simulation Environment (ASE). It may also serve as a natural starting point for training and developing machine-learning based approaches accelerating quantum chemical simulations.
      Features include search for specific reaction energies, transition states, structures, exploration of scaling relations, activity maps, Pourbaix diagrams and machine learning models, as well as generation of novel bulk and surface structures. Calculations are linked to peer-review publications where available. The database can be queried via a GraphQL API that can also be accessed directly.
      All code pertaining to this project is hosted as open-source under a liberal MIT license on github to encourage derived work and collaboration. The frontend is developed using the React Javascript framework based on react boilerplate. New components (apps) can be quickly spun-off and added to the project. The backend is developed using the Flask Python framework providing the GraphQL API as well as further APIs for specific apps.
      As such Catalysis-Hub.org aims to serve as a starting point for trend studies and atomic based heterogeneous catalysis explorations.` },
                { name: 'robots', content: 'index,follow' },
                { name: 'keywords', content: 'heterogeneous catalysis,metals,density functional theory,scaling relations, activity maps,pourbaix diagrams,machine learning,quantum espresso,vasp,gpaw' },
                { name: 'DC.title', content: 'Catalysis-Hub.org' },
              ]}
              link={suBranding === false && appBar === false ? [] : [
                { rel: 'stylesheet', href: 'https://www.stanford.edu/su-identity/css/su-identity.css' },
                { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,200' },
              ]}
            />

            {React.Children.toArray(this.props.children)}
          </AppWrapper>

    );
  }
}

App.propTypes = {
  classes: PropTypes.object.isRequired,
  children: React.PropTypes.node,
  location: PropTypes.object.isRequired,
  history: PropTypes.object,
};
const mapStateToProps = (state) => ({
  history: state.get('route').get('locationBeforeTransitions'),
});

const mapDispatchToProps = () => ({
});

export default compose(
  withProgressBar,
  withWidth(),
  connect(mapStateToProps, mapDispatchToProps),
  withStyles(styles, { withTheme: true }),
)(App);
<|endoftext|>


<commit_before>/**
 * Gets the repositories of the user from Github
 */

/* eslint-disable no-constant-condition */

import { take, call, put, select } from 'redux-saga/effects';
import { push } from 'react-router-redux';

import {
  LOAD_REPOS,
  LOAD_COLLECTIONS,
  LOAD_OAUTH,
  LOAD_OAUTH_SUCCESS,
  LOAD_ITEM,
  LOAD_ITEM_SUCCESS,
  LOAD_MODS,
  LOAD_SUBMISSIONS,

  LOG_OUT,
  LOG_OUT_SUCCESS,

  NEXT_STEP,

  SUBMIT_STEP,
  SUBMIT_STEP_SUCCESS,
  SKIP_STEP,
  SKIP_STEP_SUCCESS
} from 'containers/App/constants';

import {
  loadItem,
  itemLoaded, itemLoadingError,
  collectionsLoaded, collectionsLoadingError,
  stepSubmitted, stepSubmitError,
  stepSkipped, stepSkipError,
  modsLoaded, modsLoadingError,
  loadOAuth, oauthLoaded, oauthLoadingError,
  submissionsLoaded, submissionsLoadingError,
  reposLoaded, repoLoadingError,
  logOutSuccess, logOutError
} from 'containers/App/actions';

import request from 'utils/request';

import {
  selectUuid,
  selectCurrentStep,
  selectCurrentStepIndex,
} from 'containers/App/selectors';

// Bootstrap sagas
export default [
  resetItem,
  setRoute,
  getSubmissions,
  getItem,
  getCollections,
  getMods,
  getOAuth,
  submitStep,
  skipStep,
  getLogOut,
  getLogOutSuccess // TODO: this is not the right way!?
];

const API_URL = __CONFIG__.api.url

function isFunction(functionToCheck) {
  var getType = {};
  return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}

function* requestData(constant, getUrl, options) {
  while (true) {
    const action = yield take(constant);

    var url
    if (isFunction(getUrl)) {
      url = getUrl(action);
    } else {
      url = getUrl
    }

    if (url) {
      const defaultFetchOptions = {
        credentials: 'include'
      }

      var fetchOptions = options.fetchOptions

      // If options.fetchOptions is a function, call this function
      if (fetchOptions && isFunction(fetchOptions)) {
        fetchOptions = fetchOptions(action);
      } else if (!fetchOptions) {
        fetchOptions = {}
      }

      // Merge default fetchOptions
      fetchOptions = Object.assign(
        {},
        defaultFetchOptions,
        fetchOptions
      )

      // Use call from redux-saga for easier testing
      const result = yield call(request, url, fetchOptions);

      // We return an object in a specific format, see utils/request.js for more information
      if (result.err === undefined || result.err === null) {

        // If actionSuccess is present in options, call this function when
        //   request call is successful
        if (options.actionSuccess) {
          var actionSuccessParams = (action, resultData) => [resultData];

          if (options.actionSuccessParams) {
            actionSuccessParams = options.actionSuccessParams
          }

          yield put(options.actionSuccess.apply(null, actionSuccessParams(action, result.data)));
        }
      } else {
        // TODO: add actionErrorParams?
        if (options.actionError) {
          yield put(options.actionError({
            message: result.err.message,
            status: result.err.response ? result.err.response.status : -1,
            url: url
          }));
        }
      }
    }
  }
}

export function* setRoute() {
  while (true) {
    // yield take([SUBMIT_STEP_SUCCESS, SKIP_STEP_SUCCESS, NEXT_STEP, LOAD_ITEM_SUCCESS]);
    yield take(LOAD_ITEM_SUCCESS);

    // const stepIndex = yield select(selectCurrentStepIndex());
    const uuid = yield select(selectUuid());

    if (uuid) {
      var path = `/${uuid}`

      // if (stepIndex > 0) {
      //   const step = yield select(selectCurrentStep());
      //   path += `/${step}`
      // }

      yield(put(push(path)))
    }
  }
}

export function* resetItem() {
  while (true) {
    yield take([SUBMIT_STEP_SUCCESS, SKIP_STEP_SUCCESS, NEXT_STEP]);

    const stepIndex = yield select(selectCurrentStepIndex());

    if (stepIndex === 0) {
      // last step is reached after this event, state.steps.length === 0
      yield(put(loadItem()))
    }
  }
}

export function* getItem() {
  const getUrl = (action) => {
    var uuid = action.uuid;
    if (!uuid) {
      uuid = 'random'
    }
    return `${API_URL}items/${uuid}`;
  }

  yield* requestData(LOAD_ITEM, getUrl, {
    actionSuccess: itemLoaded,
    actionError: itemLoadingError
  })
}

export function* submitStep() {
  const getUrl = (action) => `${API_URL}items/${action.uuid}`;

  const fetchOptions = (action) => ({
    method: 'post',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      type: 'Feature',
      properties: {
        step: action.step,
        stepIndex: action.stepIndex,
        data: action.data
      },
      geometry: action.geometry
    })
  })

  yield* requestData(SUBMIT_STEP, getUrl, {
    fetchOptions,
    actionSuccess: stepSubmitted,
    actionSuccessParams: (action, resultData) => [
      action.uuid,
      action.step,
      action.stepIndex,
      action.data,
      action.geometry
    ],
    actionError: stepSubmitError
  });
}

export function* skipStep() {
  const getUrl = (action) => `${API_URL}items/${action.uuid}`;

  const fetchOptions = (action) => ({
    method: 'post',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      type: 'Feature',
      properties: {
        step: action.step,
        stepIndex: action.stepIndex,
        skipped: true
      }
    })
  });

  yield* requestData(SKIP_STEP, getUrl, {
    fetchOptions,
    actionSuccess: stepSkipped,
    actionSuccessParams: (action, resultData) => [
      action.uuid,
      action.step,
      action.stepIndex
    ],
    actionError: stepSkipError
  });
}

export function* getCollections() {
  yield* requestData(LOAD_COLLECTIONS, `${API_URL}collections`, {
    actionSuccess: collectionsLoaded,
    actionError: collectionsLoaded
  });
}

export function* getMods() {
  const getUrl = (action) => {
    const uuid = action.item.uuid;
    if (uuid) {
      return `${API_URL}items/${uuid}/mods`;
    } else {
      return null;
    }
  }

  yield* requestData(LOAD_ITEM_SUCCESS, getUrl, {
    actionSuccess: modsLoaded,
    actionError: modsLoadingError
  });
}

export function* getLogOut() {
  yield* requestData(LOG_OUT, `${API_URL}oauth/disconnect`, {
    actionSuccess: logOutSuccess,
    actionError: logOutError
  });
}

export function* getSubmissions() {
  yield* requestData(LOAD_OAUTH_SUCCESS, `${API_URL}submissions/count`, {
    actionSuccess: submissionsLoaded,
    actionError: submissionsLoadingError
  });
}

export function* getLogOutSuccess() {
  while (true) {
    yield take(LOG_OUT_SUCCESS);
    yield put(loadOAuth());
  }
}

export function* getOAuth() {
  yield* requestData(LOAD_OAUTH, `${API_URL}oauth`, {
    actionSuccess: oauthLoaded,
    actionError: oauthLoadingError
  })
}
<commit_msg>only accept valid uuids as url params<commit_after>/**
 * Gets the repositories of the user from Github
 */

/* eslint-disable no-constant-condition */

import { take, call, put, select } from 'redux-saga/effects';
import { push } from 'react-router-redux';

import {
  LOAD_REPOS,
  LOAD_COLLECTIONS,
  LOAD_OAUTH,
  LOAD_OAUTH_SUCCESS,
  LOAD_ITEM,
  LOAD_ITEM_SUCCESS,
  LOAD_MODS,
  LOAD_SUBMISSIONS,

  LOG_OUT,
  LOG_OUT_SUCCESS,

  NEXT_STEP,

  SUBMIT_STEP,
  SUBMIT_STEP_SUCCESS,
  SKIP_STEP,
  SKIP_STEP_SUCCESS
} from 'containers/App/constants';

import {
  loadItem,
  itemLoaded, itemLoadingError,
  collectionsLoaded, collectionsLoadingError,
  stepSubmitted, stepSubmitError,
  stepSkipped, stepSkipError,
  modsLoaded, modsLoadingError,
  loadOAuth, oauthLoaded, oauthLoadingError,
  submissionsLoaded, submissionsLoadingError,
  reposLoaded, repoLoadingError,
  logOutSuccess, logOutError
} from 'containers/App/actions';

import request from 'utils/request';

import {
  selectUuid,
  selectCurrentStep,
  selectCurrentStepIndex,
} from 'containers/App/selectors';

// Bootstrap sagas
export default [
  resetItem,
  setRoute,
  getSubmissions,
  getItem,
  getCollections,
  getMods,
  getOAuth,
  submitStep,
  skipStep,
  getLogOut,
  getLogOutSuccess // TODO: this is not the right way!?
];

const API_URL = __CONFIG__.api.url

function isFunction(functionToCheck) {
  var getType = {};
  return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}

function* requestData(constant, getUrl, options) {
  while (true) {
    const action = yield take(constant);

    var url
    if (isFunction(getUrl)) {
      url = getUrl(action);
    } else {
      url = getUrl
    }

    if (url) {
      const defaultFetchOptions = {
        credentials: 'include'
      }

      var fetchOptions = options.fetchOptions

      // If options.fetchOptions is a function, call this function
      if (fetchOptions && isFunction(fetchOptions)) {
        fetchOptions = fetchOptions(action);
      } else if (!fetchOptions) {
        fetchOptions = {}
      }

      // Merge default fetchOptions
      fetchOptions = Object.assign(
        {},
        defaultFetchOptions,
        fetchOptions
      )

      // Use call from redux-saga for easier testing
      const result = yield call(request, url, fetchOptions);

      // We return an object in a specific format, see utils/request.js for more information
      if (result.err === undefined || result.err === null) {

        // If actionSuccess is present in options, call this function when
        //   request call is successful
        if (options.actionSuccess) {
          var actionSuccessParams = (action, resultData) => [resultData];

          if (options.actionSuccessParams) {
            actionSuccessParams = options.actionSuccessParams
          }

          yield put(options.actionSuccess.apply(null, actionSuccessParams(action, result.data)));
        }
      } else {
        // TODO: add actionErrorParams?
        if (options.actionError) {
          yield put(options.actionError({
            message: result.err.message,
            status: result.err.response ? result.err.response.status : -1,
            url: url
          }));
        }
      }
    }
  }
}

export function* setRoute() {
  while (true) {
    // yield take([SUBMIT_STEP_SUCCESS, SKIP_STEP_SUCCESS, NEXT_STEP, LOAD_ITEM_SUCCESS]);
    yield take(LOAD_ITEM_SUCCESS);

    // const stepIndex = yield select(selectCurrentStepIndex());
    const uuid = yield select(selectUuid());

    if (uuid) {
      var path = `/${uuid}`

      // if (stepIndex > 0) {
      //   const step = yield select(selectCurrentStep());
      //   path += `/${step}`
      // }

      yield(put(push(path)))
    }
  }
}

export function* resetItem() {
  while (true) {
    yield take([SUBMIT_STEP_SUCCESS, SKIP_STEP_SUCCESS, NEXT_STEP]);

    const stepIndex = yield select(selectCurrentStepIndex());

    if (stepIndex === 0) {
      // last step is reached after this event, state.steps.length === 0
      yield(put(loadItem()))
    }
  }
}

export function* getItem() {
  const getUrl = (action) => {
    var uuid = action.uuid;
    const validUuid = /^\w{8}-(\w{4}-){3}\w{12}$/.test(uuid);

    if (uuid && !validUuid) {
      console.error(`Not a valid UUID: "${uuid}", using random item instead`);
    }

    if (!uuid || !validUuid) {
      uuid = 'random'
    }

    return `${API_URL}items/${uuid}`;
  }

  yield* requestData(LOAD_ITEM, getUrl, {
    actionSuccess: itemLoaded,
    actionError: itemLoadingError
  })
}

export function* submitStep() {
  const getUrl = (action) => `${API_URL}items/${action.uuid}`;

  const fetchOptions = (action) => ({
    method: 'post',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      type: 'Feature',
      properties: {
        step: action.step,
        stepIndex: action.stepIndex,
        data: action.data
      },
      geometry: action.geometry
    })
  })

  yield* requestData(SUBMIT_STEP, getUrl, {
    fetchOptions,
    actionSuccess: stepSubmitted,
    actionSuccessParams: (action, resultData) => [
      action.uuid,
      action.step,
      action.stepIndex,
      action.data,
      action.geometry
    ],
    actionError: stepSubmitError
  });
}

export function* skipStep() {
  const getUrl = (action) => `${API_URL}items/${action.uuid}`;

  const fetchOptions = (action) => ({
    method: 'post',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      type: 'Feature',
      properties: {
        step: action.step,
        stepIndex: action.stepIndex,
        skipped: true
      }
    })
  });

  yield* requestData(SKIP_STEP, getUrl, {
    fetchOptions,
    actionSuccess: stepSkipped,
    actionSuccessParams: (action, resultData) => [
      action.uuid,
      action.step,
      action.stepIndex
    ],
    actionError: stepSkipError
  });
}

export function* getCollections() {
  yield* requestData(LOAD_COLLECTIONS, `${API_URL}collections`, {
    actionSuccess: collectionsLoaded,
    actionError: collectionsLoaded
  });
}

export function* getMods() {
  const getUrl = (action) => {
    const uuid = action.item.uuid;
    if (uuid) {
      return `${API_URL}items/${uuid}/mods`;
    } else {
      return null;
    }
  }

  yield* requestData(LOAD_ITEM_SUCCESS, getUrl, {
    actionSuccess: modsLoaded,
    actionError: modsLoadingError
  });
}

export function* getLogOut() {
  yield* requestData(LOG_OUT, `${API_URL}oauth/disconnect`, {
    actionSuccess: logOutSuccess,
    actionError: logOutError
  });
}

export function* getSubmissions() {
  yield* requestData(LOAD_OAUTH_SUCCESS, `${API_URL}submissions/count`, {
    actionSuccess: submissionsLoaded,
    actionError: submissionsLoadingError
  });
}

export function* getLogOutSuccess() {
  while (true) {
    yield take(LOG_OUT_SUCCESS);
    yield put(loadOAuth());
  }
}

export function* getOAuth() {
  yield* requestData(LOAD_OAUTH, `${API_URL}oauth`, {
    actionSuccess: oauthLoaded,
    actionError: oauthLoadingError
  })
}
<|endoftext|>


<commit_before>capstone.controller("LoginCtrl", function($scope,$location,AuthFactory){
  //LOGIN FUNCTION
  $scope.login = ()=>{
    //CHECKING FOR USER INPUT IN LOGIN FORM
    if (($scope.user_email === undefined)&&($scope.user_password === undefined)){
      Materialize.toast("Please enter the email and password",2000)
    }
//AUTH CHECK FROM AUTHFACTORY
    AuthFactory.setter($scope.user_email,$scope.user_password)
    .then((data)=>{
      if(!data.message){
          Materialize.toast("logged in", 1000)
          $location.url("/publicresults")
          $scope.$apply()
      }
      else {
        Materialize.toast("Login email/password not found",2000)
        $location.url("/login")
      }

    })
  }
  //FORGOT PASSWORD FUNCTION
  $scope.forgotPassword = ()=>{
    firebase.auth().sendPasswordResetEmail($scope.user_email)
      .then(function() {
         alert("Email sent.... check your inbox")
        })
        .catch( function(error) {
          alert("Email not found")
        });
  }
})
<commit_msg>forgot password checked for email field and set focus to it<commit_after>capstone.controller("LoginCtrl", function($scope,$location,AuthFactory){
  //LOGIN FUNCTION
  $scope.login = ()=>{
    //CHECKING FOR USER INPUT IN LOGIN FORM
    if (($scope.user_email === undefined)&&($scope.user_password === undefined)){
      Materialize.toast("Please enter the email and password",2000)
    }
//AUTH CHECK FROM AUTHFACTORY
    AuthFactory.setter($scope.user_email,$scope.user_password)
    .then((data)=>{
      if(!data.message){
          Materialize.toast("logged in", 1000)
          $location.url("/publicresults")
          $scope.$apply()
      }
      else {
        Materialize.toast("Login email/password not found",2000)
        $location.url("/login")
      }

    })
  }
  //FORGOT PASSWORD FUNCTION
  $scope.forgotPassword = ()=>{
    if($scope.user_email){
      firebase.auth().sendPasswordResetEmail($scope.user_email)
      .then(function() {
         alert("Email sent.... check your inbox")
        })
        .catch( function(error) {
          alert("Email not found")
        });
    } else {
      Materialize.toast("Please enter the email address",2000)
      $("#email").focus()
    }
}
})
<|endoftext|>