<commit_before>var Application = require('../models/application');
var grant = require('./grant');
var Room = require('../models/room');

function getQuery(room, start, end, status) {
    return {
        room: room,
        status: status ? status : 'accepted',
        $or: [
            {
                startTime: {
                    $gte: start,
                    $lte: end
                }
            },
            {
                endTime: {
                    $gte: start,
                    $lte: end
                }
            }
        ]
    };
}

module.exports = require('express').Router()
    // view applications
    .get('/room/:room', function(req, res, next) {
        var start = req.query.start;
        var end = req.query.end;
        if (!start || !end) {
            // get applications in 24 hours
            start = Date.now();
            end = start + 86400000;
        }
        query = getQuery(req.params.room, start, end);
        Application.find(query, function(err, applications) {
            if (err) {
                console.log('find applications error', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                applications.forEach(function(v) { v.passport = null });
                res.json({
                    code: 0,
                    msg: 'ok',
                    body: applications
                });
            }
        });
    })
    // add application
    .post('/', grant.allowApplicant) // allow applicant
    .post('/', function(req, res, next) {
        if (req.body.end - req.body.start >= 0) { // minimum time
            Application.find(getQuery(req.body.room, req.body.start, req.body.end), function(err, applications) {
                if (err) {
                    console.log('find applications error', err);
                    res.json({
                        code: -1,
                        msg: 'err',
                        body: {}
                    });
                } else if (applications.length == 0) {
                    new Application({
                        title: req.body.title,
                        description: req.body.description,
                        applicant: req.session.userId,
                        room: req.body.room,
                        startTime: req.body.start,
                        endTime: req.body.end,
                    }).save(function(err, application) {
                        if (err) {
                            console.log('save applications error', err);
                            res.json({
                                code: -1,
                                msg: 'err',
                                body: {}
                            });
                        } else {
                            res.json({
                                code: 0,
                                msg: 'ok',
                                body: {}
                            });
                        }
                    });
                } else {
                    applications.forEach(function(v) { v.passport = null })
                    res.json({
                        code: 1,
                        msg: 'busy',
                        body: applications
                    });
                }
            });
        } else {
            res.json({
                code: 2,
                msg: 'time too short',
                body: {}
            });
        }
    })
    .get('/:_id', function(req, res, next) {
        Application.findById(req.params._id, function(err, application) {
            if (err) {
                console.log('get application err', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                // only the applicant and the gatekeeper can view the passport
                if (req.session.user) {
                    if (req.session.user.group == 'gatekeeper') {
                        Room.findById(application.room, function(err, room) {
                            if (err) {
                                console.log('gatekeeper check room err', err);
                                res.json({
                                    code: -1,
                                    msg: 'err',
                                    body: {}
                                });
                            } else {
                                if (room.gatekeeper != req.session.user._id) {
                                    application.passport = null;
                                }
                                res.json({
                                    code: 0,
                                    msg: 'ok',
                                    body: application
                                });
                            }
                        })
                    } else {
                        if (req.session.user._id != application.applicant) {
                            application.passport = null;
                        }
                        res.json({
                            code: 0,
                            msg: 'ok',
                            body: application
                        });
                    }
                } else {
                    application.passport = null;
                    res.json({
                        code: 0,
                        msg: 'ok',
                        body: application
                    });
                }
            }
        });
    })
    // only the administrator can update the application's status
    .put('/:_id', grant.allowAdministrator)
    .put('/:_id', function(req, res, next) {
        var status = req.body.status;
        if (status == 'accepted') {
            // 6 digits random integer string
            var passport = ('000000' + ~~(Math.random() * 1000000)).slice(-6);
        } else {
            var passport = null;
        }
        Application.findByIdAndUpdate(req.params._id, {
            $set: {
                status: status,
                passport: passport,
            }
        }, function(err, application) {
            if (err) {
                console.log('update application err', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                if (status == 'accepted') {
                    // failed others conflict applications
                    Application.findAndUpdate(getQuery(application.room, application.startTime, application.endTime), {
                        $set: {
                            status: 'failed',
                            passport: null
                        }
                    }, function(err, application) {
                        if (err) {
                            console.log('failed other applications err', err);
                            res.json({
                                code: -1,
                                msg: 'err',
                                body: {}
                            })
                        } else {
                            res.json({
                                code: 0,
                                msg: 'ok',
                                body: {}
                            })
                        }
                    })
                } else {
                    res.json({
                        code: 0,
                        msg: 'ok',
                        body: {}
                    });
                }
            }
        });
    })
    .delete('/:_id', grant.allowAdministrator)
    .delete('/:_id', function(req, res, next) {
        Application.remove({ _id: req.params._id }, function(err, application) {
            if (err) {
                console.log('delete application err', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                res.json({
                    code: 0,
                    msg: '',
                    body: application
                });
            }
        })
    })<commit_msg>使用中间件修复一些小逻辑问题<commit_after>var Application = require('../models/application');
var grant = require('./grant');
var Room = require('../models/room');

function getQuery(room, start, end, status) {
    return {
        room: room,
        status: status ? status : 'accepted',
        $or: [
            {
                startTime: {
                    $gte: start,
                    $lte: end
                }
            },
            {
                endTime: {
                    $gte: start,
                    $lte: end
                }
            }
        ]
    };
}

function updateApplication() {
    Application.findByIdAndUpdate(req.params._id, {
        $set: {
            status: status,
            passport: passport,
        }
    }, function(err, application) {
        if (err) {
            console.log('update application err', err);
            res.json({
                code: -1,
                msg: 'err',
                body: {}
            });
        } else {
            if (status == 'accepted') {
            } else {
                res.json({
                    code: 0,
                    msg: 'ok',
                    body: {}
                });
            }
        }
    });
}

module.exports = require('express').Router()
    // view applications
    .get('/room/:room', function(req, res, next) {
        var start = req.query.start;
        var end = req.query.end;
        if (!start || !end) {
            // get applications in 24 hours
            start = Date.now();
            end = start + 86400000;
        }
        query = getQuery(req.params.room, start, end);
        Application.find(query, function(err, applications) {
            if (err) {
                console.log('find applications error', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                applications.forEach(function(v) { v.passport = null });
                res.json({
                    code: 0,
                    msg: 'ok',
                    body: applications
                });
            }
        });
    })
    // add application
    .post('/', grant.allowApplicant) // allow applicant
    .post('/', function(req, res, next) {
        if (req.body.end - req.body.start >= 0) { // minimum time
            Application.find(getQuery(req.body.room, req.body.start, req.body.end), function(err, applications) {
                if (err) {
                    console.log('find applications error', err);
                    res.json({
                        code: -1,
                        msg: 'err',
                        body: {}
                    });
                } else if (applications.length == 0) {
                    new Application({
                        title: req.body.title,
                        description: req.body.description,
                        applicant: req.session.userId,
                        room: req.body.room,
                        startTime: req.body.start,
                        endTime: req.body.end,
                    }).save(function(err, application) {
                        if (err) {
                            console.log('save applications error', err);
                            res.json({
                                code: -1,
                                msg: 'err',
                                body: {}
                            });
                        } else {
                            res.json({
                                code: 0,
                                msg: 'ok',
                                body: {}
                            });
                        }
                    });
                } else {
                    applications.forEach(function(v) { v.passport = null })
                    res.json({
                        code: 1,
                        msg: 'busy',
                        body: applications
                    });
                }
            });
        } else {
            res.json({
                code: 2,
                msg: 'time too short',
                body: {}
            });
        }
    })
    .get('/:_id', function(req, res, next) {
        Application.findById(req.params._id, function(err, application) {
            if (err) {
                console.log('get application err', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                // only the applicant and the gatekeeper can view the passport
                if (req.session.user) {
                    if (req.session.user.group == 'gatekeeper') {
                        Room.findById(application.room, function(err, room) {
                            if (err) {
                                console.log('gatekeeper check room err', err);
                                res.json({
                                    code: -1,
                                    msg: 'err',
                                    body: {}
                                });
                            } else {
                                if (room.gatekeeper != req.session.user._id) {
                                    application.passport = null;
                                }
                                res.json({
                                    code: 0,
                                    msg: 'ok',
                                    body: application
                                });
                            }
                        })
                    } else {
                        if (req.session.user._id != application.applicant) {
                            application.passport = null;
                        }
                        res.json({
                            code: 0,
                            msg: 'ok',
                            body: application
                        });
                    }
                } else {
                    application.passport = null;
                    res.json({
                        code: 0,
                        msg: 'ok',
                        body: application
                    });
                }
            }
        });
    })
    // only the administrator can update the application's status
    .put('/:_id', grant.allowAdministrator)
    .put('/:_id', function(req, res, next) {
        var status = req.body.status;
        if (status == 'accepted') {
            // 6 digits random integer string
            req.body.passport = ('000000' + ~~(Math.random() * 1000000)).slice(-6);
            // failed others conflict applications
            Application.findAndUpdate(getQuery(application.room, application.startTime, application.endTime), {
                $set: {
                    status: 'failed',
                    passport: null
                }
            }, function(err, application) {
                if (err) {
                    console.log('failed other applications err', err);
                    res.json({
                        code: -1,
                        msg: 'err',
                        body: {}
                    })
                } else {
                    // and then accept the application
                    next();
                }
            })
        } else {
            req.body.passport = null;
            next();
        }
    })
    .put('/:_id', function(req, res, next) {
        var status = req.body.status;
        var passport = req.body.passport;
        Application.findByIdAndUpdate(req.params._id, {
            $set: {
                status: status,
                passport: passport,
            }
        }, function(err, application) {
            if (err) {
                console.log('update application err', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                res.json({
                    code: 0,
                    msg: 'ok',
                    body: {}
                });
            }
        });
    })
    .delete('/:_id', grant.allowAdministrator)
    .delete('/:_id', function(req, res, next) {
        Application.remove({ _id: req.params._id }, function(err, application) {
            if (err) {
                console.log('delete application err', err);
                res.json({
                    code: -1,
                    msg: 'err',
                    body: {}
                });
            } else {
                res.json({
                    code: 0,
                    msg: '',
                    body: application
                });
            }
        })
    })<|endoftext|>


<commit_before>import React, { Component } from 'react';
import settings from '../lib/settings';
import _ from 'lodash';
import deburr from 'lodash/deburr';
import queryString from 'query-string';
import classNames from 'classnames';
import Downshift from 'downshift';
import { withStyles } from '@material-ui/core/styles';

import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import TextField from '@material-ui/core/TextField';
import GridList from '@material-ui/core/GridList';
import InputLabel from '@material-ui/core/InputLabel';
import FormControl from '@material-ui/core/FormControl';
import RaisedButton from '@material-ui/core/Button';
import Select from '@material-ui/core/Select';
import Input from '@material-ui/core/Input';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import SearchIcon from '@material-ui/icons/Search';
import MenuItem from '@material-ui/core/MenuItem';
import InputAdornment from '@material-ui/core/InputAdornment';
import IconButton from '@material-ui/core/IconButton';
import GridListTile from '@material-ui/core/GridListTile';
import GridListTileBar from '@material-ui/core/GridListTileBar';
import ExpansionPanel from '@material-ui/core/ExpansionPanel';
import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Typography from '@material-ui/core/Typography';
import LooTile from './LooTile';
import CloseOutlined from '@material-ui/icons/CloseOutlined';
import { Link, navigate } from '@reach/router';

const styles = theme => ({
  gridRoot: {
    display: 'flex',
    flexWrap: 'wrap',
    justifyContent: 'space-around',
    overflow: 'hidden',
  },
  gridList: {
    width: '100%',
    height: 500,
  },
  card: {
    margin: '1em',
  },
  input: {
    minWidth: '10em',
  },
  buttons: {
    display: 'flex',
    justifyContent: 'flex-end',
  },
  heading: {
    fontSize: theme.typography.pxToRem(15),
    fontWeight: theme.typography.fontWeightRegular,
  },
  searchForm: {
    marginTop: theme.spacing.unit * 3,
    marginBottom: theme.spacing.unit * 3,
    padding: theme.spacing.unit * 2,
    [theme.breakpoints.up(600 + theme.spacing.unit * 3 * 2)]: {
      marginTop: theme.spacing.unit * 6,
      marginBottom: theme.spacing.unit * 6,
      padding: theme.spacing.unit * 3,
    },
  },
  button: {
    marginTop: theme.spacing.unit * 3,
    marginLeft: theme.spacing.unit,
  },
});

function renderInput(inputProps) {
  const { InputProps, classes, ref, ...other } = inputProps;

  return (
    <TextField
      InputProps={{
        inputRef: ref,
        classes: {
          root: classes.inputRoot,
        },
        ...InputProps,
      }}
      {...other}
    />
  );
}

function renderSuggestion({
  suggestion,
  index,
  itemProps,
  highlightedIndex,
  selectedItem,
}) {
  const isHighlighted = highlightedIndex === index;
  const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1;

  return (
    <MenuItem
      {...itemProps}
      key={suggestion.label}
      selected={isHighlighted}
      component="div"
      style={{
        fontWeight: isSelected ? 500 : 400,
      }}
    >
      {suggestion.label}
    </MenuItem>
  );
}

class Search extends Component {
  constructor(props) {
    super(props);

    this.searchDefaults = {
      text: '',
      order: 'desc',
      to_date: '',
      from_date: '',
      attributions: '',
      area_name: '',
    };

    const parsedQuery = queryString.parse(this.props.location.search);
    this.state = {
      searching: false,
      searchParams: {
        ...this.searchDefaults,
        // Apply values from query string.
        ...parsedQuery,
      },
      areas: [],
    };

    this.submit = this.submit.bind(this);
    this.doSearch = this.doSearch.bind(this);
    this.fetchAreaData = this.fetchAreaData.bind(this);
    this.fetchContributorData = this.fetchContributorData.bind(this);
    this.updateSearchParam = this.updateSearchParam.bind(this);
    this.updateSearchField = this.updateSearchField.bind(this);
    this.getSuggestions = this.getSuggestions.bind(this);
  }

  componentDidMount() {
    this.doSearch(this.state.searchParams);
    this.fetchContributorData();
    this.fetchAreaData();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.location.search !== this.props.location.search) {
      const parsedQuery = queryString.parse(this.props.location.search);
      this.setState(
        {
          searchParams: {
            ...this.searchDefaults,
            ...parsedQuery,
          },
        },
        this.doSearch.bind(this)
      );
    }
  }

  /**
   *
   * Performs a search given the provided query object and attaches results to state.
   *
   * @param {*} q
   */
  async doSearch(q) {
    q = q || this.state.searchParams;
    if (!_.isEmpty(q)) {
      this.setState({ searching: true });
      try {
        const res = await fetch(
          settings.getItem('apiUrl') +
            '/search?' +
            queryString.stringify(_.pickBy(q))
        );
        const results = await res.json();
        this.setState({ results });
      } catch (err) {
        console.error(err);
      } finally {
        this.setState({ searching: false });
      }
    }
  }

  /**
   * Retreives a flattened list of Areas and Area Types and attaches to state.
   */
  async fetchAreaData() {
    const searchUrl = settings.getItem('apiUrl') + '/admin_geo/areas';
    const response = await fetch(searchUrl);
    const result = await response.json();
    result.All = _.uniq(_.flatten(_.values(result))).map(x => ({ label: x }));
    this.setState({
      areas: result.All,
    });
  }

  /**
   * Fetches a list of contributors and attaches to state.
   */
  async fetchContributorData() {
    const searchUrl = settings.getItem('apiUrl') + '/statistics/contributors';
    const response = await fetch(searchUrl);
    const result = await response.json();
    const contributors = Object.keys(result).map(x => ({ label: x }));
    this.setState({
      contributors: contributors,
    });
  }

  /**
   * Navigates to updated query string and submits a search to the API.
   *
   * Omits any empty search paramaters from the search.
   */
  async submit() {
    const omitEmpty = _.pickBy(this.state.searchParams);

    // If everything is empty, ensure that we at least specify the `text` param.
    if (_.isEmpty(omitEmpty)) {
      omitEmpty.text = '';
    }

    const query = queryString.stringify(omitEmpty);
    await navigate(`search?${query}`);
    this.doSearch();
  }

  /**
   *
   * Helper to update specific fields with value of event.
   *
   * @param {*} key
   * @param {*} evt
   */
  updateSearchField(key, evt) {
    this.updateSearchParam(key, evt.target.value);
  }

  /**
   *
   * Update an individual entry in the search state.
   *
   * @param {*} key
   * @param {*} val
   */
  updateSearchParam(key, val) {
    var searchParams = {
      ...this.state.searchParams,
      [key]: val,
    };

    this.setState({
      searchParams,
    });
  }

  /**
   * Determines whether the user has conducted an advanced search.
   */
  get advancedSearch() {
    const advancedParams = [
      'area_name',
      'attributions',
      'from_date',
      'to_date',
    ];
    const currentParams = this.state.searchParams;
    return advancedParams.some(advancedParam => !!currentParams[advancedParam]);
  }

  /**
   *
   * Returns an array of suggestions based upon an input string and provided array.
   *
   * @param {*} data
   * @param {*} value
   */
  getSuggestions(data, value) {
    const inputValue = deburr(value.trim()).toLowerCase();
    const inputLength = inputValue.length;
    let count = 0;

    return inputLength === 0
      ? []
      : data.filter(suggestion => {
          const keep =
            count < 5 &&
            suggestion.label.slice(0, inputLength).toLowerCase() === inputValue;

          if (keep) {
            count += 1;
          }

          return keep;
        });
  }

  render() {
    const { classes } = this.props;
    return (

        <Paper className={classNames(classes.paper, classes.searchForm)}>
          <Grid container spacing={24}>
            <Grid item xs={12} sm={7} md={9}>
              <FormControl className={classes.formControl} fullWidth>
                <TextField
                  id="text"
                  label="Search in all text fields"
                  name="text"
                  value={this.state.searchParams.text}
                  onChange={_.partial(this.updateSearchField, 'text')}
                />
              </FormControl>
            </Grid>

            <Grid item xs={12} sm={5} md={3}>
              <FormControl className={classes.formControl} fullWidth>
                <InputLabel htmlFor="order">Search Order</InputLabel>
                <Select
                  id="order"
                  className={classes.input}
                  value={this.state.searchParams.order}
                  onChange={_.partial(this.updateSearchField, 'order')}
                  input={<Input name="order" id="order-helper" />}
                >
                  <MenuItem value={'desc'} key={0}>
                    Newest First
                  </MenuItem>
                  <MenuItem value={'asc'} key={1}>
                    Oldest First
                  </MenuItem>
                </Select>
              </FormControl>
            </Grid>
            <Grid item xs={12}>
              <ExpansionPanel defaultExpanded={this.advancedSearch}>
                <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
                  <Typography className={classes.heading}>
                    Advanced Options
                  </Typography>
                </ExpansionPanelSummary>
                <ExpansionPanelDetails>
                  <Grid container spacing={24}>
                    <Grid item xs={12} sm={6}>
                      <FormControl fullWidth>
                        <Downshift
                          id="area_name-search"
                          onChange={_.partial(
                            this.updateSearchParam,
                            'area_name'
                          )}
                          selectedItem={this.state.searchParams.area_name}
                        >
                          {({
                            getInputProps,
                            getItemProps,
                            isOpen,
                            inputValue,
                            selectedItem,
                            highlightedIndex,
                            clearSelection,
                          }) => (

                              {renderInput({
                                fullWidth: true,
                                classes,
                                InputProps: getInputProps({
                                  placeholder: 'Search Areas',
                                  endAdornment: (
                                    <InputAdornment position="end">
                                      <IconButton
                                        aria-label="Clear area input box"
                                        onClick={clearSelection}
                                      >
                                        <CloseOutlined />
                                      </IconButton>
                                    </InputAdornment>
                                  ),
                                }),
                              })}
                              {isOpen ? (
                                <Paper className={classes.paper} square>
                                  {this.getSuggestions(
                                    this.state.areas,
                                    inputValue
                                  ).map((suggestion, index) =>
                                    renderSuggestion({
                                      suggestion,
                                      index,
                                      itemProps: getItemProps({
                                        item: suggestion.label,
                                      }),
                                      highlightedIndex,
                                      selectedItem,
                                    })
                                  )}
                                </Paper>
                              ) : null}

                          )}
                        </Downshift>
                      </FormControl>
                    </Grid>

                    <Grid item xs={12} sm={6}>
                      <FormControl fullWidth>
                        <Downshift
                          id="attribution-search"
                          onChange={_.partial(
                            this.updateSearchParam,
                            'attributions'
                          )}
                          selectedItem={this.state.searchParams.attributions}
                        >
                          {({
                            getInputProps,
                            getItemProps,
                            isOpen,
                            inputValue,
                            selectedItem,
                            highlightedIndex,
                            clearSelection,
                          }) => (

                              {renderInput({
                                fullWidth: true,
                                classes,
                                InputProps: getInputProps({
                                  placeholder: 'Search Contributors',
                                  endAdornment: (
                                    <InputAdornment position="end">
                                      <IconButton
                                        aria-label="Clear contributor input box"
                                        onClick={clearSelection}
                                      >
                                        <CloseOutlined />
                                      </IconButton>
                                    </InputAdornment>
                                  ),
                                }),
                              })}
                              {isOpen ? (
                                <Paper className={classes.paper} square>
                                  {this.getSuggestions(
                                    this.state.contributors,
                                    inputValue
                                  ).map((suggestion, index) =>
                                    renderSuggestion({
                                      suggestion,
                                      index,
                                      itemProps: getItemProps({
                                        item: suggestion.label,
                                      }),
                                      highlightedIndex,
                                      selectedItem,
                                    })
                                  )}
                                </Paper>
                              ) : null}

                          )}
                        </Downshift>
                      </FormControl>
                    </Grid>

                    <Grid item xs={12} sm={6}>
                      <FormControl className={classes.formControl} fullWidth>
                        <TextField
                          id="from_date"
                          label="Updated After"
                          type="date"
                          value={this.state.searchParams.from_date}
                          onChange={_.partial(
                            this.updateSearchField,
                            'from_date'
                          )}
                          InputLabelProps={{
                            shrink: true,
                          }}
                        />
                      </FormControl>
                    </Grid>

                    <Grid item xs={12} sm={6}>
                      <FormControl className={classes.formControl} fullWidth>
                        <TextField
                          id="to_date"
                          label="Updated Before"
                          type="date"
                          value={this.state.searchParams.to_date}
                          onChange={_.partial(
                            this.updateSearchField,
                            'to_date'
                          )}
                          InputLabelProps={{
                            shrink: true,
                          }}
                        />
                      </FormControl>
                    </Grid>
                  </Grid>
                </ExpansionPanelDetails>
              </ExpansionPanel>
            </Grid>
          </Grid>

            <RaisedButton
              variant="contained"
              color="primary"
              className={classes.button}
              onClick={this.submit}
              disabled={this.state.searching}
            >
              <SearchIcon className={classes.rightIcon} />
              Search
            </RaisedButton>

        </Paper>

        <Card className={classes.card}>
          <CardContent>
            {this.state.results &&
              this.state.results.docs && (

                  <GridList className={classes.gridList} cellHeight={180}>
                    {this.state.results.docs.map(l => {
                      return (
                        <GridListTile key={l._id} style={{ width: '33.3%' }}>
                          <LooTile loo={l} />
                          <Link to={`../loos/${l._id}`}>
                            <GridListTileBar
                              title={l.properties.name || 'Unnamed'}
                            />
                          </Link>
                        </GridListTile>
                      );
                    })}
                  </GridList>

              )}
          </CardContent>
        </Card>

    );
  }
}

export default withStyles(styles)(Search);
<commit_msg>refactor: remove unnecessary bool conversion<commit_after>import React, { Component } from 'react';
import settings from '../lib/settings';
import _ from 'lodash';
import deburr from 'lodash/deburr';
import queryString from 'query-string';
import classNames from 'classnames';
import Downshift from 'downshift';
import { withStyles } from '@material-ui/core/styles';

import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import TextField from '@material-ui/core/TextField';
import GridList from '@material-ui/core/GridList';
import InputLabel from '@material-ui/core/InputLabel';
import FormControl from '@material-ui/core/FormControl';
import RaisedButton from '@material-ui/core/Button';
import Select from '@material-ui/core/Select';
import Input from '@material-ui/core/Input';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import SearchIcon from '@material-ui/icons/Search';
import MenuItem from '@material-ui/core/MenuItem';
import InputAdornment from '@material-ui/core/InputAdornment';
import IconButton from '@material-ui/core/IconButton';
import GridListTile from '@material-ui/core/GridListTile';
import GridListTileBar from '@material-ui/core/GridListTileBar';
import ExpansionPanel from '@material-ui/core/ExpansionPanel';
import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Typography from '@material-ui/core/Typography';
import LooTile from './LooTile';
import CloseOutlined from '@material-ui/icons/CloseOutlined';
import { Link, navigate } from '@reach/router';

const styles = theme => ({
  gridRoot: {
    display: 'flex',
    flexWrap: 'wrap',
    justifyContent: 'space-around',
    overflow: 'hidden',
  },
  gridList: {
    width: '100%',
    height: 500,
  },
  card: {
    margin: '1em',
  },
  input: {
    minWidth: '10em',
  },
  buttons: {
    display: 'flex',
    justifyContent: 'flex-end',
  },
  heading: {
    fontSize: theme.typography.pxToRem(15),
    fontWeight: theme.typography.fontWeightRegular,
  },
  searchForm: {
    marginTop: theme.spacing.unit * 3,
    marginBottom: theme.spacing.unit * 3,
    padding: theme.spacing.unit * 2,
    [theme.breakpoints.up(600 + theme.spacing.unit * 3 * 2)]: {
      marginTop: theme.spacing.unit * 6,
      marginBottom: theme.spacing.unit * 6,
      padding: theme.spacing.unit * 3,
    },
  },
  button: {
    marginTop: theme.spacing.unit * 3,
    marginLeft: theme.spacing.unit,
  },
});

function renderInput(inputProps) {
  const { InputProps, classes, ref, ...other } = inputProps;

  return (
    <TextField
      InputProps={{
        inputRef: ref,
        classes: {
          root: classes.inputRoot,
        },
        ...InputProps,
      }}
      {...other}
    />
  );
}

function renderSuggestion({
  suggestion,
  index,
  itemProps,
  highlightedIndex,
  selectedItem,
}) {
  const isHighlighted = highlightedIndex === index;
  const isSelected = (selectedItem || '').indexOf(suggestion.label) > -1;

  return (
    <MenuItem
      {...itemProps}
      key={suggestion.label}
      selected={isHighlighted}
      component="div"
      style={{
        fontWeight: isSelected ? 500 : 400,
      }}
    >
      {suggestion.label}
    </MenuItem>
  );
}

class Search extends Component {
  constructor(props) {
    super(props);

    this.searchDefaults = {
      text: '',
      order: 'desc',
      to_date: '',
      from_date: '',
      attributions: '',
      area_name: '',
    };

    const parsedQuery = queryString.parse(this.props.location.search);
    this.state = {
      searching: false,
      searchParams: {
        ...this.searchDefaults,
        // Apply values from query string.
        ...parsedQuery,
      },
      areas: [],
    };

    this.submit = this.submit.bind(this);
    this.doSearch = this.doSearch.bind(this);
    this.fetchAreaData = this.fetchAreaData.bind(this);
    this.fetchContributorData = this.fetchContributorData.bind(this);
    this.updateSearchParam = this.updateSearchParam.bind(this);
    this.updateSearchField = this.updateSearchField.bind(this);
    this.getSuggestions = this.getSuggestions.bind(this);
  }

  componentDidMount() {
    this.doSearch(this.state.searchParams);
    this.fetchContributorData();
    this.fetchAreaData();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.location.search !== this.props.location.search) {
      const parsedQuery = queryString.parse(this.props.location.search);
      this.setState(
        {
          searchParams: {
            ...this.searchDefaults,
            ...parsedQuery,
          },
        },
        this.doSearch.bind(this)
      );
    }
  }

  /**
   *
   * Performs a search given the provided query object and attaches results to state.
   *
   * @param {*} q
   */
  async doSearch(q) {
    q = q || this.state.searchParams;
    if (!_.isEmpty(q)) {
      this.setState({ searching: true });
      try {
        const res = await fetch(
          settings.getItem('apiUrl') +
            '/search?' +
            queryString.stringify(_.pickBy(q))
        );
        const results = await res.json();
        this.setState({ results });
      } catch (err) {
        console.error(err);
      } finally {
        this.setState({ searching: false });
      }
    }
  }

  /**
   * Retreives a flattened list of Areas and Area Types and attaches to state.
   */
  async fetchAreaData() {
    const searchUrl = settings.getItem('apiUrl') + '/admin_geo/areas';
    const response = await fetch(searchUrl);
    const result = await response.json();
    result.All = _.uniq(_.flatten(_.values(result))).map(x => ({ label: x }));
    this.setState({
      areas: result.All,
    });
  }

  /**
   * Fetches a list of contributors and attaches to state.
   */
  async fetchContributorData() {
    const searchUrl = settings.getItem('apiUrl') + '/statistics/contributors';
    const response = await fetch(searchUrl);
    const result = await response.json();
    const contributors = Object.keys(result).map(x => ({ label: x }));
    this.setState({
      contributors: contributors,
    });
  }

  /**
   * Navigates to updated query string and submits a search to the API.
   *
   * Omits any empty search paramaters from the search.
   */
  async submit() {
    const omitEmpty = _.pickBy(this.state.searchParams);

    // If everything is empty, ensure that we at least specify the `text` param.
    if (_.isEmpty(omitEmpty)) {
      omitEmpty.text = '';
    }

    const query = queryString.stringify(omitEmpty);
    await navigate(`search?${query}`);
    this.doSearch();
  }

  /**
   *
   * Helper to update specific fields with value of event.
   *
   * @param {*} key
   * @param {*} evt
   */
  updateSearchField(key, evt) {
    this.updateSearchParam(key, evt.target.value);
  }

  /**
   *
   * Update an individual entry in the search state.
   *
   * @param {*} key
   * @param {*} val
   */
  updateSearchParam(key, val) {
    var searchParams = {
      ...this.state.searchParams,
      [key]: val,
    };

    this.setState({
      searchParams,
    });
  }

  /**
   * Determines whether the user has conducted an advanced search.
   */
  get advancedSearch() {
    const advancedParams = [
      'area_name',
      'attributions',
      'from_date',
      'to_date',
    ];
    const currentParams = this.state.searchParams;
    return advancedParams.some(advancedParam => currentParams[advancedParam]);
  }

  /**
   *
   * Returns an array of suggestions based upon an input string and provided array.
   *
   * @param {*} data
   * @param {*} value
   */
  getSuggestions(data, value) {
    const inputValue = deburr(value.trim()).toLowerCase();
    const inputLength = inputValue.length;
    let count = 0;

    return inputLength === 0
      ? []
      : data.filter(suggestion => {
          const keep =
            count < 5 &&
            suggestion.label.slice(0, inputLength).toLowerCase() === inputValue;

          if (keep) {
            count += 1;
          }

          return keep;
        });
  }

  render() {
    const { classes } = this.props;
    return (

        <Paper className={classNames(classes.paper, classes.searchForm)}>
          <Grid container spacing={24}>
            <Grid item xs={12} sm={7} md={9}>
              <FormControl className={classes.formControl} fullWidth>
                <TextField
                  id="text"
                  label="Search in all text fields"
                  name="text"
                  value={this.state.searchParams.text}
                  onChange={_.partial(this.updateSearchField, 'text')}
                />
              </FormControl>
            </Grid>

            <Grid item xs={12} sm={5} md={3}>
              <FormControl className={classes.formControl} fullWidth>
                <InputLabel htmlFor="order">Search Order</InputLabel>
                <Select
                  id="order"
                  className={classes.input}
                  value={this.state.searchParams.order}
                  onChange={_.partial(this.updateSearchField, 'order')}
                  input={<Input name="order" id="order-helper" />}
                >
                  <MenuItem value={'desc'} key={0}>
                    Newest First
                  </MenuItem>
                  <MenuItem value={'asc'} key={1}>
                    Oldest First
                  </MenuItem>
                </Select>
              </FormControl>
            </Grid>
            <Grid item xs={12}>
              <ExpansionPanel defaultExpanded={this.advancedSearch}>
                <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
                  <Typography className={classes.heading}>
                    Advanced Options
                  </Typography>
                </ExpansionPanelSummary>
                <ExpansionPanelDetails>
                  <Grid container spacing={24}>
                    <Grid item xs={12} sm={6}>
                      <FormControl fullWidth>
                        <Downshift
                          id="area_name-search"
                          onChange={_.partial(
                            this.updateSearchParam,
                            'area_name'
                          )}
                          selectedItem={this.state.searchParams.area_name}
                        >
                          {({
                            getInputProps,
                            getItemProps,
                            isOpen,
                            inputValue,
                            selectedItem,
                            highlightedIndex,
                            clearSelection,
                          }) => (

                              {renderInput({
                                fullWidth: true,
                                classes,
                                InputProps: getInputProps({
                                  placeholder: 'Search Areas',
                                  endAdornment: (
                                    <InputAdornment position="end">
                                      <IconButton
                                        aria-label="Clear area input box"
                                        onClick={clearSelection}
                                      >
                                        <CloseOutlined />
                                      </IconButton>
                                    </InputAdornment>
                                  ),
                                }),
                              })}
                              {isOpen ? (
                                <Paper className={classes.paper} square>
                                  {this.getSuggestions(
                                    this.state.areas,
                                    inputValue
                                  ).map((suggestion, index) =>
                                    renderSuggestion({
                                      suggestion,
                                      index,
                                      itemProps: getItemProps({
                                        item: suggestion.label,
                                      }),
                                      highlightedIndex,
                                      selectedItem,
                                    })
                                  )}
                                </Paper>
                              ) : null}

                          )}
                        </Downshift>
                      </FormControl>
                    </Grid>

                    <Grid item xs={12} sm={6}>
                      <FormControl fullWidth>
                        <Downshift
                          id="attribution-search"
                          onChange={_.partial(
                            this.updateSearchParam,
                            'attributions'
                          )}
                          selectedItem={this.state.searchParams.attributions}
                        >
                          {({
                            getInputProps,
                            getItemProps,
                            isOpen,
                            inputValue,
                            selectedItem,
                            highlightedIndex,
                            clearSelection,
                          }) => (

                              {renderInput({
                                fullWidth: true,
                                classes,
                                InputProps: getInputProps({
                                  placeholder: 'Search Contributors',
                                  endAdornment: (
                                    <InputAdornment position="end">
                                      <IconButton
                                        aria-label="Clear contributor input box"
                                        onClick={clearSelection}
                                      >
                                        <CloseOutlined />
                                      </IconButton>
                                    </InputAdornment>
                                  ),
                                }),
                              })}
                              {isOpen ? (
                                <Paper className={classes.paper} square>
                                  {this.getSuggestions(
                                    this.state.contributors,
                                    inputValue
                                  ).map((suggestion, index) =>
                                    renderSuggestion({
                                      suggestion,
                                      index,
                                      itemProps: getItemProps({
                                        item: suggestion.label,
                                      }),
                                      highlightedIndex,
                                      selectedItem,
                                    })
                                  )}
                                </Paper>
                              ) : null}

                          )}
                        </Downshift>
                      </FormControl>
                    </Grid>

                    <Grid item xs={12} sm={6}>
                      <FormControl className={classes.formControl} fullWidth>
                        <TextField
                          id="from_date"
                          label="Updated After"
                          type="date"
                          value={this.state.searchParams.from_date}
                          onChange={_.partial(
                            this.updateSearchField,
                            'from_date'
                          )}
                          InputLabelProps={{
                            shrink: true,
                          }}
                        />
                      </FormControl>
                    </Grid>

                    <Grid item xs={12} sm={6}>
                      <FormControl className={classes.formControl} fullWidth>
                        <TextField
                          id="to_date"
                          label="Updated Before"
                          type="date"
                          value={this.state.searchParams.to_date}
                          onChange={_.partial(
                            this.updateSearchField,
                            'to_date'
                          )}
                          InputLabelProps={{
                            shrink: true,
                          }}
                        />
                      </FormControl>
                    </Grid>
                  </Grid>
                </ExpansionPanelDetails>
              </ExpansionPanel>
            </Grid>
          </Grid>

            <RaisedButton
              variant="contained"
              color="primary"
              className={classes.button}
              onClick={this.submit}
              disabled={this.state.searching}
            >
              <SearchIcon className={classes.rightIcon} />
              Search
            </RaisedButton>

        </Paper>

        <Card className={classes.card}>
          <CardContent>
            {this.state.results &&
              this.state.results.docs && (

                  <GridList className={classes.gridList} cellHeight={180}>
                    {this.state.results.docs.map(l => {
                      return (
                        <GridListTile key={l._id} style={{ width: '33.3%' }}>
                          <LooTile loo={l} />
                          <Link to={`../loos/${l._id}`}>
                            <GridListTileBar
                              title={l.properties.name || 'Unnamed'}
                            />
                          </Link>
                        </GridListTile>
                      );
                    })}
                  </GridList>

              )}
          </CardContent>
        </Card>

    );
  }
}

export default withStyles(styles)(Search);
<|endoftext|>


<commit_before>'use strict';

const express = require('express');
const router = express.Router();
const M2X = require('m2x');
const vincenty = require('node-vincenty');
const m2x = new M2X(process.env.M2X_KEY);
const streamName = 'check-points';

const deviceID = ( process.env.M2X_DEVICE_ID ) ? process.env.M2X_DEVICE_ID : '8b492694122cc040401c2d4f9a6a3b56';
// LEGACY CODE
// let masterPoint = {
//     name: 'Master Point',
//     // TEST (SACRED HEARTS CAFETERIA) 21.285471, -157.807362
//     lat: 21.2858534,
//     lon: -157.8064349,
//     active: false,
//     createdAt: Date.now(),
//   };
var gameCounter = 0;
var totalCounter = 0;
// NOTE: TIMES SUPERMARKET
// const testPoint = {
//     name: 'Test Point',
//     lat: 21.286615,
//     lon: -157.807592,
//     active: false,
//     createdAt: Date.now(),
//   }

router.route('/')
  // GET the current check-point stream status
  .get((req, res) => {
    m2x.devices.values(deviceID, streamName, (values) => {
      res.send(values);
    });
  });

router.route('/capture/lat/:lat/long/:lon')
  // GET capture route
  .get((req, res) => {
    let masterPoint = {};
    m2x.devices.location(deviceID,(result) => {
      masterPoint.lat = result.json.latitude;
      masterPoint.lon = result.json.longitude;
      const lat = req.params.lat;
      const lon = req.params.lon;
      // NOTE: Capture range.
      const distanceThreshold = 20;
      gameCounter++;
      totalCounter++;

      vincenty.distVincenty(lat, lon, masterPoint.lat, masterPoint.lon, (distance, initialBearing, finalBearing) => {
        if( distance < distanceThreshold && distance >= 0 ){
          // TODO: DO SOMETHING BETTER
          res.json({
            success: true,
            message: 'GOTTEM! It took a total of ' + gameCounter + ' total attempts since last found.',
          });
          gameCounter = 0;
        } else {
          res.json({
            success: false,
            message: 'NOPE! TRY AGAIN',
          });
        }
      });
    });
  });

router.route('/masterlocation')
  // GET device location for master point (DEVELOPMENT USE)
  // .get((req, res) => {
  //   m2x.devices.location(deviceID,(result) => {
  //     res.send(result.json);
  //   });
  // })

  // PUT updates master point to current device location specified
  .put((req, res) => {
    if( req.body.name === undefined ){
      req.body.name = "Master Point: " + Date.now();
    }
    if( req.body.elevation === undefined ){
      req.body.elevation = 17;
    }
    let newMasterPoint = {
      "name": req.body.name,
      "latitude": req.body.lat,
      "longitude": req.body.lon,
      "timestamp": Date.UTC(),
      "elevation": req.body.elevation
    };
    m2x.devices.updateLocation(deviceID, newMasterPoint,(result) => {
      res.send(result.json);
    });
  });

router.route('/reset')
  // STUBBED FOR FUTURE GAME ADMINISTRATION
  .get((req, res) => {
    res.send('stub');
  });

router.route('/lat/:lat/long/:lon')
  // GET the current check-point id's status and returns angle direction
  .get((req, res) => {
    let masterPoint = {};
    m2x.devices.location(deviceID,(result) => {
      masterPoint.lat = result.json.latitude;
      masterPoint.lon = result.json.longitude;
      const lat = req.params.lat;
      const lon = req.params.lon;
      gameCounter++;
      totalCounter++;
      let payload = {
        value: JSON.stringify(
          {
            // id: 'stub',
            lat: req.params.lat,
            lon: req.params.lon,
            value: 1, // NOTE: Maybe unnecessary if using a gameCounter on server.
            timestamp: Date.now(),
          }
        )
      };

      m2x.devices.setStreamValue(deviceID, streamName, payload, (result) => {
        console.log('result',result.json);
      });
      vincenty.distVincenty(lat, lon, masterPoint.lat, masterPoint.lon, (distance, initialBearing, finalBearing) => {
          res.json({
            finalBearing,
          });
      });
    });
  });

module.exports = router;
<commit_msg>set a default master point coord object to reference<commit_after>'use strict';

const express = require('express');
const router = express.Router();
const M2X = require('m2x');
const vincenty = require('node-vincenty');
const m2x = new M2X(process.env.M2X_KEY);
const streamName = 'check-points';

const deviceID = ( process.env.M2X_DEVICE_ID ) ? process.env.M2X_DEVICE_ID : '8b492694122cc040401c2d4f9a6a3b56';
let masterPointDefault = {
    // TEST (SACRED HEARTS CAFETERIA) 21.285471, -157.807362
    name: 'SACRED HEARTS CAFETERIA',
    lat: 21.285471,
    lon: -157.807362,
  };
var gameCounter = 0;
var totalCounter = 0;
// NOTE: TIMES SUPERMARKET
// const testPoint = {
//     name: 'Test Point',
//     lat: 21.286615,
//     lon: -157.807592,
//     active: false,
//     createdAt: Date.now(),
//   }

router.route('/')
  // GET the current check-point stream status
  .get((req, res) => {
    m2x.devices.values(deviceID, streamName, (values) => {
      res.send(values);
    });
  });

router.route('/capture/lat/:lat/long/:lon')
  // GET capture route
  .get((req, res) => {
    let masterPoint = {};
    m2x.devices.location(deviceID,(result) => {
      masterPoint.lat = result.json.latitude;
      masterPoint.lon = result.json.longitude;
      const lat = req.params.lat;
      const lon = req.params.lon;
      // NOTE: Capture range.
      const distanceThreshold = 20;
      gameCounter++;
      totalCounter++;

      vincenty.distVincenty(lat, lon, masterPoint.lat, masterPoint.lon, (distance, initialBearing, finalBearing) => {
        if( distance < distanceThreshold && distance >= 0 ){
          // TODO: DO SOMETHING BETTER
          res.json({
            success: true,
            message: 'GOTTEM! It took a total of ' + gameCounter + ' total attempts since last found.',
          });
          gameCounter = 0;
        } else {
          res.json({
            success: false,
            message: 'NOPE! TRY AGAIN',
          });
        }
      });
    });
  });

router.route('/masterlocation')
  // GET device location for master point (DEVELOPMENT USE)
  // .get((req, res) => {
  //   m2x.devices.location(deviceID,(result) => {
  //     res.send(result.json);
  //   });
  // })

  // PUT updates master point to current device location specified
  .put((req, res) => {
    if( req.body.name === undefined ){
      req.body.name = "Master Point: " + Date.now();
    }
    if( req.body.elevation === undefined ){
      req.body.elevation = 17;
    }
    let newMasterPoint = {
      "name": req.body.name,
      "latitude": req.body.lat,
      "longitude": req.body.lon,
      "timestamp": Date.UTC(),
      "elevation": req.body.elevation
    };
    m2x.devices.updateLocation(deviceID, newMasterPoint,(result) => {
      res.send(result.json);
    });
  });

router.route('/reset')
  // STUBBED FOR FUTURE GAME ADMINISTRATION
  .get((req, res) => {
    res.send('stub');
  });

router.route('/lat/:lat/long/:lon')
  // GET the current check-point id's status and returns angle direction
  .get((req, res) => {
    let masterPoint = {};
    m2x.devices.location(deviceID,(result) => {
      masterPoint.lat = result.json.latitude;
      masterPoint.lon = result.json.longitude;
      const lat = req.params.lat;
      const lon = req.params.lon;
      gameCounter++;
      totalCounter++;
      let payload = {
        value: JSON.stringify(
          {
            // id: 'stub',
            lat: req.params.lat,
            lon: req.params.lon,
            value: 1, // NOTE: Maybe unnecessary if using a gameCounter on server.
            timestamp: Date.now(),
          }
        )
      };

      m2x.devices.setStreamValue(deviceID, streamName, payload, (result) => {
        console.log('result',result.json);
      });
      vincenty.distVincenty(lat, lon, masterPoint.lat, masterPoint.lon, (distance, initialBearing, finalBearing) => {
          res.json({
            finalBearing,
          });
      });
    });
  });

module.exports = router;
<|endoftext|>


<commit_before>import { define, WeElement } from 'omi'
import logo from './logo.svg'
import '../app-intro'
import 'omiu/button'
import { Button } from 'antd'

define('my-app', class extends WeElement {
  static css = require('./_index.css')

  name = 'Omi'

  clickHandler2 = () => {
    this.name = 'Omio'
    this.update()
  }

  render() {
    return (

Welcome to {this.name}

        <app-intro />

    <Button type="primary" onClick={this.clickHandler2}>Primary</Button>
    <Button>Default</Button>
    <Button type="dashed">Dashed</Button>
    <Button type="danger">Danger</Button>

        <o-button style='width:200px;'>I am omiu button.</o-button>

    )
  }
})
<commit_msg>test Breadcrumb<commit_after>import { define, WeElement } from 'omi'
import logo from './logo.svg'
import '../app-intro'
import 'omiu/button'
import { Button, Breadcrumb } from 'antd'

define('my-app', class extends WeElement {
  static css = require('./_index.css')

  name = 'Omi'

  clickHandler2 = () => {
    this.name = 'Omio'
    this.update()
  }

  render() {
    return (

Welcome to {this.name}

        <app-intro />

        <Button type="primary" onClick={this.clickHandler2}>Primary</Button>
        <Button>Default</Button>
        <Button type="dashed">Dashed</Button>
        <Button type="danger">Danger</Button>

        <Breadcrumb>
          <Breadcrumb.Item>Home</Breadcrumb.Item>
          <Breadcrumb.Item>Application Center</Breadcrumb.Item>
          <Breadcrumb.Item>Application List</Breadcrumb.Item>
          <Breadcrumb.Item>An Application</Breadcrumb.Item>
        </Breadcrumb>

        <o-button style='width:200px;'>I am omiu button.</o-button>

    )
  }
})
<|endoftext|>


<commit_before>var Runner = require('../lib/runner/run.js');
var Logger = require('../lib/util/logger.js');
var Selenium = require('../lib/runner/selenium.js');
var fs = require('fs');
var path = require('path');
var util = require('util');

var SETTINGS_DEPRECTED_VAL = './settings.json';

function CliRunner(argv) {
  this.settings = null;
  this.argv = argv;
  this.test_settings = null;
  this.output_folder = '';
  this.parallelMode = false;
  this.parallelEnvs = null;
  this.cli = require('./_cli.js');
}

CliRunner.prototype = {
  init : function() {
    this
      .readSettings()
      .setOutputFolder()
      .parseTestSettings();

    return this;
  },

  /**
   * Read the provided config json file; defaults to settings.json if one isn't provided
   * @param {Object} argv
   */
  readSettings : function() {
    // use default settings.json file if we haven't received another value
    if (this.cli.command('config').isDefault(this.argv.c)) {
      var defaultValue = this.cli.command('config').defaults();
      var deprecatedValue = SETTINGS_DEPRECTED_VAL;

      if (fs.existsSync(defaultValue)) {
        this.argv.c = path.join(path.resolve('./'), this.argv.c);
      } else if (fs.existsSync(deprecatedValue)) {
        this.argv.c = path.join(path.resolve('./'), deprecatedValue);
      } else {
        var defaultFile = path.join(__dirname, this.argv.c);
        if (fs.existsSync(defaultFile)) {
          this.argv.c = defaultFile;
        } else {
          this.argv.c = path.join(__dirname, deprecatedValue);
        }
      }
    } else {
      this.argv.c = path.resolve(argv.c);
    }

    // reading the settings file
    try {
      this.settings = require(this.argv.c);
      this.replaceEnvVariables();
      this.manageSelenium = !this.isParallelMode() && this.settings.selenium && this.settings.selenium.start_process;
    } catch (ex) {
      Logger.error(ex);
      this.settings = {};
    }

    return this;
  },

  isParallelMode : function() {
    return process.env.__NIGHTWATCH_PARALLEL_MODE === '1';
  },

  /**
   * Looks for pattern ${VAR_NAME} in settings
   * @param {Object} [target]
   */
  replaceEnvVariables : function(target) {
    target = target || this.settings;
    for (var key in target) {
      switch(typeof target[key]) {
        case 'object':
          this.replaceEnvVariables(target[key]);
          break;
        case 'string':
          target[key] = target[key].replace(/\$\{(\w+)\}/g, function(match, varName) {
            return process.env[varName] || '${' + varName + '}';
          });
          break;
      }
    }

    return this;
  },

  /**
   * Reads globals from an external js or json file set in the settings file
   * @returns {*}
   */
  readExternalGlobals : function () {
    if (!this.settings.globals_path) {
      return this;
    }
    try {
      var fullPath = path.resolve(this.settings.globals_path);
      if (fs.existsSync(fullPath)) {
        var globals = require(fullPath);
        if (globals && globals.hasOwnProperty(this.argv.e)) {
          this.test_settings.globals = globals[this.argv.e];
        }
        return this;
      }

      throw new Error('External global file could not be located - using '+ this.settings.globals_path +'.');
    } catch (err) {
      err.message = 'Failed to load external global file: ' + err.message;
      throw err;
    }

  },

  /**
   * @returns {CliRunner}
   */
  setOutputFolder : function() {
    this.output_folder = this.cli.command('output').isDefault(this.argv.o) && this.settings.output_folder || this.argv.o;
    return this;
  },

  /**
   * @param err
   */
  globalErrorHandler : function(err) {
    if (err) {
      Logger.enable();
      if (!err.message) {
        err.message = 'There was an error while running the test.';
      }

      console.error(Logger.colors.red(err.message));
      if (err.data) {
        console.log(util.inspect(err.data, false, 0, true));
      }
      process.exit(1);
    }
  },

  /**
   * Returns the path where the tests are located
   * @returns {*}
   */
  getTestSource : function() {
    var testsource;
    if (typeof this.argv.t == 'string') {
      testsource =  (this.argv.t.indexOf(process.cwd()) === -1) ?
        path.join(process.cwd(), this.argv.t) :
        this.argv.t;
      if (testsource.substr(-3) != '.js') {
        testsource += '.js';
      }
      try {
        fs.statSync(testsource);
      } catch (err) {
        throw new Error('There was a problem reading the test file: ' + testsource);
      }
    } else if (typeof this.argv.g == 'string') {
      testsource = [this.argv.g];
    } else {
      testsource = this.settings.src_folders;
    }

    return testsource;
  },

  /**
   * Starts the selenium server process
   * @param {function} [callback]
   * @returns {CliRunner}
   */
  startSelenium : function(callback) {
    callback = callback || function() {};

    if (!this.manageSelenium) {
      callback();
      return this;
    }
    this.settings.parallelMode = this.parallelMode;
    var self = this;
    Selenium.startServer(this.settings, function(error, child, error_out, exitcode) {
      if (error) {
        Logger.error('There was an error while starting the Selenium server:');
        self.globalErrorHandler({
          message : error_out
        });
        return;
      }

      callback();

    });
    return this;
  },

  /**
   * Stops the selenium server if it is running
   * @returns {CliRunner}
   */
  stopSelenium : function() {
    if (this.manageSelenium) {
      Selenium.stopServer();
    }
    return this;
  },

  /**
   * Starts the test runner
   * @param source
   * @returns {CliRunner}
   */
  runTests : function() {
    if (this.parallelMode) {
      return this;
    }

    var source = this.getTestSource();
    var self = this;
    this.startSelenium(function() {
      Runner.run(source, self.test_settings, {
        output_folder : self.output_folder,
        src_folders : self.settings.src_folders
      }, function(err) {
        self
          .stopSelenium()
          .globalErrorHandler(err);
      });
    });

    return this;
  },

  inheritFromDefaultEnv : function() {
    var defaultEnv = this.settings.test_settings['default'] || {};
    for (var key in defaultEnv) {
      if (typeof this.test_settings[key] == 'undefined') {
        this.test_settings[key] = defaultEnv[key];
      }
    }
  },

  parseTestSettings : function() {
    // checking if the env passed is valid
    if (!this.settings.test_settings) {
      throw new Error('No testing environment specified.');
    }

    var envs = this.argv.e.split(',');
    for (var i = 0; i < envs.length; i++) {
      if (!(envs[i] in this.settings.test_settings)) {
        throw new Error('Invalid testing environment specified: ' + envs[i]);
      }
    }

    if (envs.length > 1) {
      this.setupParallelMode(envs);
      return this;
    }

    this.setTestSettings(this.argv.e);

    return this;
  },

  setTestSettings : function(env) {
    // picking the environment specific test settings
    this.test_settings = this.settings.test_settings[env];
    this.test_settings.custom_commands_path = this.settings.custom_commands_path || '';
    this.test_settings.custom_assertions_path = this.settings.custom_assertions_path || '';

    this.inheritFromDefaultEnv();

    // overwrite selenium settings per environment
    if (this.test_settings.selenium && typeof (this.test_settings.selenium) == 'object') {
      for (var prop in this.test_settings.selenium) {
        this.settings.selenium[prop] = this.test_settings.selenium[prop];
      }
    }

    this.mergeSeleniumOptions();

    // read the external globals, if any
    this.readExternalGlobals();

    if (this.argv.verbose) {
      this.test_settings.silent = false;
    }

    this.test_settings.output = this.test_settings.output || typeof this.test_settings.output === 'undefined';

    if (typeof this.argv.s == 'string') {
      this.test_settings.skipgroup = this.argv.s.split(',');
    }

    if (this.argv.f) {
      this.test_settings.filter = this.argv.f;
    }
  },

  /**
   * Backwards compatible method which attempts to merge deprecated driver specific options for selenium
   */
  mergeSeleniumOptions : function() {
    if (!this.manageSelenium) {
      return this;
    }
    this.settings.cli_args = {};

    var deprecationNotice = function(propertyName, newSettingName) {
      console.warn(Logger.colors.brown('DEPRECATION NOTICE: Property ' + propertyName + ' is deprecated since v0.5. Please' +
        ' use the "cli_args" object on the "selenium" property to define "' + newSettingName + '". E.g.:'));
      var demoObj = '{\n' +
        '  "cli_args": {\n' +
        '    "'+ Logger.colors.yellow(newSettingName) +'": "<VALUE>"\n' +
        '  }\n' +
        '}';

      console.log(demoObj, '\n');
    };

    if (this.test_settings.firefox_profile) {
      deprecationNotice('firefox_profile', 'webdriver.firefox.profile');
      this.settings.selenium.cli_args['webdriver.firefox.profile'] = this.test_settings.firefox_profile;
    }
    if (this.test_settings.chrome_driver) {
      deprecationNotice('chrome_driver', 'webdriver.chrome.driver');
      this.settings.selenium.cli_args['webdriver.chrome.driver'] = this.test_settings.chrome_driver;
    }
    if (this.test_settings.ie_driver) {
      deprecationNotice('ie_driver', 'webdriver.ie.driver');
      this.settings.selenium.cli_args['webdriver.ie.driver'] = this.test_settings.ie_driver;
    }
  },

  setupParallelMode : function(envs) {
    this.parallelMode = true;
    var self = this;

    this.startSelenium(function() {
      self.startParallelProcesses(envs, function() {
        self.stopSelenium();
      });
    });

    return this;
  },

  getChildProcessArgs : function() {
    var args = [];
    for (var i = 2; i < process.argv.length; i++) {
      if (process.argv[i] == '-e' || process.argv[i] == '--env') {
        i++
      } else {
        args.push(process.argv[i]);
      }
    }
    return args;
  },

  startParallelProcesses : function(envs, finishCallback) {
    var execFile = require('child_process').execFile, child, self = this;
    var mainModule = process.mainModule.filename;
    finishCallback = finishCallback || function() {};

    envs.forEach(function(item, index) {
      var cliArgs = self.getChildProcessArgs();
      cliArgs.push('-e', item, '__parallel-mode');
      var env = process.env;
      setTimeout(function() {
        env.__NIGHTWATCH_PARALLEL_MODE = 1;
        env.__NIGHTWATCH_ENV = item;

        child = execFile(mainModule, cliArgs, {
          cwd : process.cwd(),
          encoding: 'utf8',
          env : env
        }, function (error, stdout, stderr) {

        });

        child.stdout.on('data', function (data) {
          process.stdout.write(Logger.colors.light_gray('[' + item + ']', Logger.colors.background.red) + '\n  ' + data);
        });

        child.stderr.on('data', function (data) {
          process.stdout.write(Logger.colors.light_gray('[' + item + ']', Logger.colors.background.magenta) + '\n  ' + data);
        });

        child.on('close', function (code) {
          if (index == envs.length - 1) {
            finishCallback();
          }
        });
      }, index * 10);
    });
  }
};

module.exports = CliRunner;<commit_msg>removed the not useful error data from the output<commit_after>var Runner = require('../lib/runner/run.js');
var Logger = require('../lib/util/logger.js');
var Selenium = require('../lib/runner/selenium.js');
var fs = require('fs');
var path = require('path');
var util = require('util');

var SETTINGS_DEPRECTED_VAL = './settings.json';

function CliRunner(argv) {
  this.settings = null;
  this.argv = argv;
  this.test_settings = null;
  this.output_folder = '';
  this.parallelMode = false;
  this.parallelEnvs = null;
  this.cli = require('./_cli.js');
}

CliRunner.prototype = {
  init : function() {
    this
      .readSettings()
      .setOutputFolder()
      .parseTestSettings();

    return this;
  },

  /**
   * Read the provided config json file; defaults to settings.json if one isn't provided
   * @param {Object} argv
   */
  readSettings : function() {
    // use default settings.json file if we haven't received another value
    if (this.cli.command('config').isDefault(this.argv.c)) {
      var defaultValue = this.cli.command('config').defaults();
      var deprecatedValue = SETTINGS_DEPRECTED_VAL;

      if (fs.existsSync(defaultValue)) {
        this.argv.c = path.join(path.resolve('./'), this.argv.c);
      } else if (fs.existsSync(deprecatedValue)) {
        this.argv.c = path.join(path.resolve('./'), deprecatedValue);
      } else {
        var defaultFile = path.join(__dirname, this.argv.c);
        if (fs.existsSync(defaultFile)) {
          this.argv.c = defaultFile;
        } else {
          this.argv.c = path.join(__dirname, deprecatedValue);
        }
      }
    } else {
      this.argv.c = path.resolve(argv.c);
    }

    // reading the settings file
    try {
      this.settings = require(this.argv.c);
      this.replaceEnvVariables();
      this.manageSelenium = !this.isParallelMode() && this.settings.selenium && this.settings.selenium.start_process;
    } catch (ex) {
      Logger.error(ex);
      this.settings = {};
    }

    return this;
  },

  isParallelMode : function() {
    return process.env.__NIGHTWATCH_PARALLEL_MODE === '1';
  },

  /**
   * Looks for pattern ${VAR_NAME} in settings
   * @param {Object} [target]
   */
  replaceEnvVariables : function(target) {
    target = target || this.settings;
    for (var key in target) {
      switch(typeof target[key]) {
        case 'object':
          this.replaceEnvVariables(target[key]);
          break;
        case 'string':
          target[key] = target[key].replace(/\$\{(\w+)\}/g, function(match, varName) {
            return process.env[varName] || '${' + varName + '}';
          });
          break;
      }
    }

    return this;
  },

  /**
   * Reads globals from an external js or json file set in the settings file
   * @returns {*}
   */
  readExternalGlobals : function () {
    if (!this.settings.globals_path) {
      return this;
    }
    try {
      var fullPath = path.resolve(this.settings.globals_path);
      if (fs.existsSync(fullPath)) {
        var globals = require(fullPath);
        if (globals && globals.hasOwnProperty(this.argv.e)) {
          this.test_settings.globals = globals[this.argv.e];
        }
        return this;
      }

      throw new Error('External global file could not be located - using '+ this.settings.globals_path +'.');
    } catch (err) {
      err.message = 'Failed to load external global file: ' + err.message;
      throw err;
    }

  },

  /**
   * @returns {CliRunner}
   */
  setOutputFolder : function() {
    this.output_folder = this.cli.command('output').isDefault(this.argv.o) && this.settings.output_folder || this.argv.o;
    return this;
  },

  /**
   * @param err
   */
  globalErrorHandler : function(err) {
    if (err) {
      Logger.enable();
      if (!err.message) {
        err.message = 'There was an error while running the test.';
      }

      console.error(Logger.colors.red(err.message));

      process.exit(1);
    }
  },

  /**
   * Returns the path where the tests are located
   * @returns {*}
   */
  getTestSource : function() {
    var testsource;
    if (typeof this.argv.t == 'string') {
      testsource =  (this.argv.t.indexOf(process.cwd()) === -1) ?
        path.join(process.cwd(), this.argv.t) :
        this.argv.t;
      if (testsource.substr(-3) != '.js') {
        testsource += '.js';
      }
      try {
        fs.statSync(testsource);
      } catch (err) {
        throw new Error('There was a problem reading the test file: ' + testsource);
      }
    } else if (typeof this.argv.g == 'string') {
      testsource = [this.argv.g];
    } else {
      testsource = this.settings.src_folders;
    }

    return testsource;
  },

  /**
   * Starts the selenium server process
   * @param {function} [callback]
   * @returns {CliRunner}
   */
  startSelenium : function(callback) {
    callback = callback || function() {};

    if (!this.manageSelenium) {
      callback();
      return this;
    }
    this.settings.parallelMode = this.parallelMode;
    var self = this;
    Selenium.startServer(this.settings, function(error, child, error_out, exitcode) {
      if (error) {
        Logger.error('There was an error while starting the Selenium server:');
        self.globalErrorHandler({
          message : error_out
        });
        return;
      }

      callback();

    });
    return this;
  },

  /**
   * Stops the selenium server if it is running
   * @returns {CliRunner}
   */
  stopSelenium : function() {
    if (this.manageSelenium) {
      Selenium.stopServer();
    }
    return this;
  },

  /**
   * Starts the test runner
   * @param source
   * @returns {CliRunner}
   */
  runTests : function() {
    if (this.parallelMode) {
      return this;
    }

    var source = this.getTestSource();
    var self = this;
    this.startSelenium(function() {
      Runner.run(source, self.test_settings, {
        output_folder : self.output_folder,
        src_folders : self.settings.src_folders
      }, function(err) {
        self
          .stopSelenium()
          .globalErrorHandler(err);
      });
    });

    return this;
  },

  inheritFromDefaultEnv : function() {
    var defaultEnv = this.settings.test_settings['default'] || {};
    for (var key in defaultEnv) {
      if (typeof this.test_settings[key] == 'undefined') {
        this.test_settings[key] = defaultEnv[key];
      }
    }
  },

  parseTestSettings : function() {
    // checking if the env passed is valid
    if (!this.settings.test_settings) {
      throw new Error('No testing environment specified.');
    }

    var envs = this.argv.e.split(',');
    for (var i = 0; i < envs.length; i++) {
      if (!(envs[i] in this.settings.test_settings)) {
        throw new Error('Invalid testing environment specified: ' + envs[i]);
      }
    }

    if (envs.length > 1) {
      this.setupParallelMode(envs);
      return this;
    }

    this.setTestSettings(this.argv.e);

    return this;
  },

  setTestSettings : function(env) {
    // picking the environment specific test settings
    this.test_settings = this.settings.test_settings[env];
    this.test_settings.custom_commands_path = this.settings.custom_commands_path || '';
    this.test_settings.custom_assertions_path = this.settings.custom_assertions_path || '';

    this.inheritFromDefaultEnv();

    // overwrite selenium settings per environment
    if (this.test_settings.selenium && typeof (this.test_settings.selenium) == 'object') {
      for (var prop in this.test_settings.selenium) {
        this.settings.selenium[prop] = this.test_settings.selenium[prop];
      }
    }

    this.mergeSeleniumOptions();

    // read the external globals, if any
    this.readExternalGlobals();

    if (this.argv.verbose) {
      this.test_settings.silent = false;
    }

    this.test_settings.output = this.test_settings.output || typeof this.test_settings.output === 'undefined';

    if (typeof this.argv.s == 'string') {
      this.test_settings.skipgroup = this.argv.s.split(',');
    }

    if (this.argv.f) {
      this.test_settings.filter = this.argv.f;
    }
  },

  /**
   * Backwards compatible method which attempts to merge deprecated driver specific options for selenium
   */
  mergeSeleniumOptions : function() {
    if (!this.manageSelenium) {
      return this;
    }
    this.settings.cli_args = {};

    var deprecationNotice = function(propertyName, newSettingName) {
      console.warn(Logger.colors.brown('DEPRECATION NOTICE: Property ' + propertyName + ' is deprecated since v0.5. Please' +
        ' use the "cli_args" object on the "selenium" property to define "' + newSettingName + '". E.g.:'));
      var demoObj = '{\n' +
        '  "cli_args": {\n' +
        '    "'+ Logger.colors.yellow(newSettingName) +'": "<VALUE>"\n' +
        '  }\n' +
        '}';

      console.log(demoObj, '\n');
    };

    if (this.test_settings.firefox_profile) {
      deprecationNotice('firefox_profile', 'webdriver.firefox.profile');
      this.settings.selenium.cli_args['webdriver.firefox.profile'] = this.test_settings.firefox_profile;
    }
    if (this.test_settings.chrome_driver) {
      deprecationNotice('chrome_driver', 'webdriver.chrome.driver');
      this.settings.selenium.cli_args['webdriver.chrome.driver'] = this.test_settings.chrome_driver;
    }
    if (this.test_settings.ie_driver) {
      deprecationNotice('ie_driver', 'webdriver.ie.driver');
      this.settings.selenium.cli_args['webdriver.ie.driver'] = this.test_settings.ie_driver;
    }
  },

  setupParallelMode : function(envs) {
    this.parallelMode = true;
    var self = this;

    this.startSelenium(function() {
      self.startParallelProcesses(envs, function() {
        self.stopSelenium();
      });
    });

    return this;
  },

  getChildProcessArgs : function() {
    var args = [];
    for (var i = 2; i < process.argv.length; i++) {
      if (process.argv[i] == '-e' || process.argv[i] == '--env') {
        i++
      } else {
        args.push(process.argv[i]);
      }
    }
    return args;
  },

  startParallelProcesses : function(envs, finishCallback) {
    var execFile = require('child_process').execFile, child, self = this;
    var mainModule = process.mainModule.filename;
    finishCallback = finishCallback || function() {};

    envs.forEach(function(item, index) {
      var cliArgs = self.getChildProcessArgs();
      cliArgs.push('-e', item, '__parallel-mode');
      var env = process.env;
      setTimeout(function() {
        env.__NIGHTWATCH_PARALLEL_MODE = 1;
        env.__NIGHTWATCH_ENV = item;

        child = execFile(mainModule, cliArgs, {
          cwd : process.cwd(),
          encoding: 'utf8',
          env : env
        }, function (error, stdout, stderr) {

        });

        child.stdout.on('data', function (data) {
          process.stdout.write(Logger.colors.light_gray('[' + item + ']', Logger.colors.background.red) + '\n  ' + data);
        });

        child.stderr.on('data', function (data) {
          process.stdout.write(Logger.colors.light_gray('[' + item + ']', Logger.colors.background.magenta) + '\n  ' + data);
        });

        child.on('close', function (code) {
          if (index == envs.length - 1) {
            finishCallback();
          }
        });
      }, index * 10);
    });
  }
};

module.exports = CliRunner;<|endoftext|>