<commit_before>import React, { Component } from 'react';
import {
  View,
  ActivityIndicator,
  Dimensions,
  DeviceEventEmitter,
  Animated,
  Easing,
  StatusBar,
  Image,
  Text,
  Platform,
  TouchableHighlight
} from 'react-native';
import CONSTANTS from 'config/constants';

import MapView from 'react-native-maps';
import GeoPoint from 'geopoint';
import I18n from 'locales';

import ActionBtn from 'components/common/action-button';
import Theme from 'config/theme';
import tracker from 'helpers/googleAnalytics';
import styles from './styles';
import { SensorManager } from 'NativeModules'; // eslint-disable-line

const { RNLocation: Location } = require('NativeModules'); // eslint-disable-line

const { width, height } = Dimensions.get('window');

const ASPECT_RATIO = width / height;
const LATITUDE_DELTA = 10;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;

const markerImage = require('assets/marker.png');
const markerCompassRedImage = require('assets/compass_circle_red.png');
const alertGladImage = require('assets/alert-glad.png');
const alertViirsmage = require('assets/alert-viirs.png');
const alertWhiteImage = require('assets/alert-white.png');
const compassImage = require('assets/compass_direction.png');
const backgroundImage = require('assets/map_bg_gradient.png');
const backIconWhite = require('assets/previous_white.png');

function renderLoading() {
  return (
    <View style={[styles.container, styles.loader]}>
      <ActivityIndicator
        color={Theme.colors.color1}
        style={{ height: 80 }}
        size="large"
      />
    </View>
  );
}

function getGoogleMapsCoordinates(coordinates) {
  const cords = [];

  coordinates.forEach((cordinate) => {
    cords.push({
      latitude: cordinate.lat,
      longitude: cordinate.long
    });
  });

  return cords;
}

class Map extends Component {
  constructor(props) {
    super(props);
    const { params } = this.props.navigation.state;
    const intialCoords = params.center
      ? params.center
      : [CONSTANTS.maps.lng, CONSTANTS.maps.lat];

    this.afterRenderTimer = null;
    this.eventLocation = null;
    this.eventOrientation = null;
    this.state = {
      renderMap: false,
      lastPosition: null,
      heading: null,
      geoMarkerOpacity: new Animated.Value(0.3),
      region: {
        latitude: intialCoords.lat,
        longitude: intialCoords.lon,
        latitudeDelta: LATITUDE_DELTA,
        longitudeDelta: LONGITUDE_DELTA
      },
      alertSelected: null,
      alerts: params.features && params.features.length > 0 ? params.features.slice(0, 50) : [] // Provisional
    };
  }

  componentDidMount() {
    tracker.trackScreenView('Map');

    if (Platform.OS === 'ios') {
      Location.requestWhenInUseAuthorization();
      StatusBar.setBarStyle('light-content');
    }

    this.renderMap();
    this.geoLocate();
  }

  componentWillUnmount() {
    Location.stopUpdatingLocation();

    clearTimeout(this.afterRenderTimer);

    if (this.eventLocation) {
      this.eventLocation.remove();
    }

    if (this.eventOrientation) {
      this.eventOrientation.remove();
    }

    if (Platform.OS === 'ios') {
      StatusBar.setBarStyle('default');
      Location.stopUpdatingHeading();
    } else {
      SensorManager.stopOrientation();
    }
  }

  onAlertPress(alertSelected) {
    const alerts = [];

    this.state.alerts.forEach((alert) => {
      const newArea = Object.assign({}, alert);
      newArea.selected = alert.id === alertSelected.id;
      alerts.push(newArea);
    });

    this.setState({
      alerts,
      alertSelected
    });
  }

  onLayout = () => {
    if (!this.state.alertSelected) {
      if (this.afterRenderTimer) {
        clearTimeout(this.afterRenderTimer);
      }
      this.afterRenderTimer = setTimeout(() => {
        const { params } = this.props.navigation.state;
        if (params && params.features && params.features.length > 0) {
          this.map.fitToCoordinates(getGoogleMapsCoordinates(params.features), {
            edgePadding: { top: 100, right: 100, bottom: 100, left: 100 },
            animated: true
          });
        }
      }, 1000);
    }
  }

  geoLocate() {
    this.animateGeo();

    navigator.geolocation.getCurrentPosition(
      (location) => {
        this.setState({
          lastPosition: {
            latitude: location.coords.latitude,
            longitude: location.coords.longitude
          }
        });
      },
      (error) => console.info(error),
      { enableHighAccuracy: true, timeout: 30000, maximumAge: 0 }
    );

    Location.startUpdatingLocation();

    this.eventLocation = DeviceEventEmitter.addListener(
      'locationUpdated',
      (location) => {
        const coords = Platform.OS === 'ios' ? location.coords : location;
        this.setState({
          lastPosition: coords
        });
      }
    );

    if (Platform.OS === 'ios') {
      Location.startUpdatingHeading();
      this.eventOrientation = DeviceEventEmitter.addListener(
        'headingUpdated',
        (data) => {
          this.setState({ heading: parseInt(data.heading, 10) });
        }
      );
    } else {
      SensorManager.startOrientation(1000);
      this.eventOrientation = DeviceEventEmitter.addListener(
        'Orientation',
        (data) => {
          this.setState({
            heading: parseInt(data.azimuth, 10)
          });
        }
      );
    }
  }

  animateGeo() {
    Animated.sequence([
      Animated.timing(this.state.geoMarkerOpacity, {
        toValue: 0.4,
        easing: Easing.in(Easing.quad),
        duration: 800
      }),
      Animated.timing(this.state.geoMarkerOpacity, {
        toValue: 0.15,
        easing: Easing.out(Easing.quad),
        duration: 1000
      })
    ]).start(event => {
      if (event.finished) {
        this.animateGeo();
      }
    });
  }

  createReport = () => {
    const { lastPosition } = this.state;
    const form = `New-report-${Math.floor(Math.random() * 1000)}`;
    let latLng = '0,0';
    if (lastPosition) {
      latLng = `${lastPosition.latitude},${lastPosition.longitude}`;
    }
    this.props.createReport(form, latLng);
    this.props.navigate('NewReport', { form });
  };

  renderMap() {
    if (!this.state.renderMap) {
      this.setState({
        renderMap: true
      });
    }
  }

  renderFooter() {
    let distanceText = I18n.t('commonText.notAvailable');
    let positionText = '';
    let distance = 999999;
    const { lastPosition } = this.state;

    if (lastPosition) {
      const geoPoint = new GeoPoint(this.state.alertSelected.lat, this.state.alertSelected.long);
      const currentPoint = new GeoPoint(lastPosition.latitude, lastPosition.longitude);
      positionText = `${I18n.t('commonText.yourPosition')}: ${lastPosition.latitude}, ${lastPosition.longitude}`;
      distance = currentPoint.distanceTo(geoPoint, true).toFixed(4);
      distanceText = `${distance} ${I18n.t('commonText.kmAway')}`; // in Kilometers
    }

    const reportBtn = (
      <ActionBtn
        style={styles.footerButton}
        text={I18n.t('report.title')}
        onPress={this.createReport}
      />
    );
    return (
      <View style={styles.footer}>
        <Image
          style={styles.footerBg}
          source={backgroundImage}
        />
        <Text style={styles.footerTitle}>
          {distanceText}
        </Text>
        <Text style={[styles.footerTitle, styles.footerSubtitle]}>
          {positionText}
        </Text>
        {reportBtn}
      </View>
    );
  }

  renderFooterLoading() {
    return (!this.state.lastPosition &&
      <View style={styles.footer}>
        <Image
          style={styles.footerBg}
          source={backgroundImage}
        />
        <View style={styles.signalNotice}>
          <View style={styles.geoLocationContainer}>
            <Image
              style={styles.marker}
              source={markerCompassRedImage}
            />
            <Animated.View
              style={[styles.geoLocation, { opacity: this.state.geoMarkerOpacity }]}
            />
          </View>
          <Text style={styles.signalNoticeText}>{I18n.t('alerts.satelliteSignal')}</Text>
        </View>
      </View>
    );
  }

  render() {
    const { params } = this.props.navigation.state;
    const stopPropagation = thunk => e => {
      e.stopPropagation();
      thunk();
    };

    return (
      this.state.renderMap
      ?
        <View style={styles.container}>
          <View
            style={styles.header}
            pointerEvents={'box-none'}
          >
            <Image
              style={styles.headerBg}
              source={backgroundImage}
            />
            <Text style={styles.headerTitle}>
              {params.title}
            </Text>
            {this.state.alertSelected &&
              <Text style={styles.headerSubtitle}>
                {this.state.alertSelected.lat}, {this.state.alertSelected.long}
              </Text>
            }
            <TouchableHighlight
              style={styles.headerBtn}
              onPress={() => this.props.navigation.goBack()}
              underlayColor="transparent"
              activeOpacity={0.8}
            >
              <Image style={Theme.icon} source={backIconWhite} />
            </TouchableHighlight>
          </View>
          <MapView
            ref={(ref) => { this.map = ref; }}
            style={styles.map}
            provider={MapView.PROVIDER_GOOGLE}
            mapType="hybrid"
            rotateEnabled={false}
            initialRegion={this.state.region}
            onLayout={this.onLayout}
            moveOnMarkerPress={false}
          >
            {this.state.lastPosition &&
              <MapView.Marker.Animated
                image={markerImage}
                coordinate={this.state.lastPosition}
                style={{ zIndex: 2 }}
                pointerEvents={'none'}
              />
            }
            {this.state.lastPosition && this.state.heading
              ?
                <MapView.Marker
                  key={'compass'}
                  coordinate={this.state.lastPosition}
                  zIndex={1}
                  anchor={{ x: 0.5, y: 0.6 }}
                  pointerEvents={'none'}
                >
                  <Animated.Image
                    style={{
                      width: 94,
                      height: 94,
                      transform: [
                        { rotate: `${this.state.heading ? this.state.heading : '0'}deg` }
                      ]
                    }}
                    source={compassImage}
                  />
                </MapView.Marker>
              : null
            }
            {this.state.alerts.map((point, key) => {
              let image = alertGladImage;

              if (point.type === 'viirs') {
                image = alertViirsmage;
              }

              if (point.selected) {
                image = alertWhiteImage;
              }

              return (
                <MapView.Marker.Animated
                  key={key}
                  image={image}
                  style={{ opacity: 0.8 }}
                  coordinate={{
                    latitude: point.lat,
                    longitude: point.long
                  }}
                  onPress={stopPropagation(() => this.onAlertPress(point))}
                />
              );
            })
            }
          </MapView>
          {this.state.alertSelected
            ? this.renderFooter()
            : this.renderFooterLoading()
          }
        </View>
      :
        renderLoading()
    );
  }
}

Map.propTypes = {
  navigation: React.PropTypes.object.isRequired,
  navigate: React.PropTypes.func.isRequired,
  createReport: React.PropTypes.func.isRequired
};

Map.navigationOptions = {
  header: {
    visible: false
  }
};

export default Map;
<commit_msg>Increase the limit of markers on map<commit_after>import React, { Component } from 'react';
import {
  View,
  ActivityIndicator,
  Dimensions,
  DeviceEventEmitter,
  Animated,
  Easing,
  StatusBar,
  Image,
  Text,
  Platform,
  TouchableHighlight
} from 'react-native';
import CONSTANTS from 'config/constants';

import MapView from 'react-native-maps';
import GeoPoint from 'geopoint';
import I18n from 'locales';

import ActionBtn from 'components/common/action-button';
import Theme from 'config/theme';
import tracker from 'helpers/googleAnalytics';
import styles from './styles';
import { SensorManager } from 'NativeModules'; // eslint-disable-line

const { RNLocation: Location } = require('NativeModules'); // eslint-disable-line

const { width, height } = Dimensions.get('window');

const ASPECT_RATIO = width / height;
const LATITUDE_DELTA = 10;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;

const markerImage = require('assets/marker.png');
const markerCompassRedImage = require('assets/compass_circle_red.png');
const alertGladImage = require('assets/alert-glad.png');
const alertViirsmage = require('assets/alert-viirs.png');
const alertWhiteImage = require('assets/alert-white.png');
const compassImage = require('assets/compass_direction.png');
const backgroundImage = require('assets/map_bg_gradient.png');
const backIconWhite = require('assets/previous_white.png');

function renderLoading() {
  return (
    <View style={[styles.container, styles.loader]}>
      <ActivityIndicator
        color={Theme.colors.color1}
        style={{ height: 80 }}
        size="large"
      />
    </View>
  );
}

function getGoogleMapsCoordinates(coordinates) {
  const cords = [];

  coordinates.forEach((cordinate) => {
    cords.push({
      latitude: cordinate.lat,
      longitude: cordinate.long
    });
  });

  return cords;
}

class Map extends Component {
  constructor(props) {
    super(props);
    const { params } = this.props.navigation.state;
    const intialCoords = params.center
      ? params.center
      : [CONSTANTS.maps.lng, CONSTANTS.maps.lat];

    this.afterRenderTimer = null;
    this.eventLocation = null;
    this.eventOrientation = null;
    this.state = {
      renderMap: false,
      lastPosition: null,
      heading: null,
      geoMarkerOpacity: new Animated.Value(0.3),
      region: {
        latitude: intialCoords.lat,
        longitude: intialCoords.lon,
        latitudeDelta: LATITUDE_DELTA,
        longitudeDelta: LONGITUDE_DELTA
      },
      alertSelected: null,
      alerts: params.features && params.features.length > 0 ? params.features.slice(0, 120) : [] // Provisional
    };
  }

  componentDidMount() {
    tracker.trackScreenView('Map');

    if (Platform.OS === 'ios') {
      Location.requestWhenInUseAuthorization();
      StatusBar.setBarStyle('light-content');
    }

    this.renderMap();
    this.geoLocate();
  }

  componentWillUnmount() {
    Location.stopUpdatingLocation();

    clearTimeout(this.afterRenderTimer);

    if (this.eventLocation) {
      this.eventLocation.remove();
    }

    if (this.eventOrientation) {
      this.eventOrientation.remove();
    }

    if (Platform.OS === 'ios') {
      StatusBar.setBarStyle('default');
      Location.stopUpdatingHeading();
    } else {
      SensorManager.stopOrientation();
    }
  }

  onAlertPress(alertSelected) {
    const alerts = [];

    this.state.alerts.forEach((alert) => {
      const newArea = Object.assign({}, alert);
      newArea.selected = alert.id === alertSelected.id;
      alerts.push(newArea);
    });

    this.setState({
      alerts,
      alertSelected
    });
  }

  onLayout = () => {
    if (!this.state.alertSelected) {
      if (this.afterRenderTimer) {
        clearTimeout(this.afterRenderTimer);
      }
      this.afterRenderTimer = setTimeout(() => {
        const { params } = this.props.navigation.state;
        if (params && params.features && params.features.length > 0) {
          this.map.fitToCoordinates(getGoogleMapsCoordinates(params.features), {
            edgePadding: { top: 100, right: 100, bottom: 100, left: 100 },
            animated: true
          });
        }
      }, 1000);
    }
  }

  geoLocate() {
    this.animateGeo();

    navigator.geolocation.getCurrentPosition(
      (location) => {
        this.setState({
          lastPosition: {
            latitude: location.coords.latitude,
            longitude: location.coords.longitude
          }
        });
      },
      (error) => console.info(error),
      { enableHighAccuracy: true, timeout: 30000, maximumAge: 0 }
    );

    Location.startUpdatingLocation();

    this.eventLocation = DeviceEventEmitter.addListener(
      'locationUpdated',
      (location) => {
        const coords = Platform.OS === 'ios' ? location.coords : location;
        this.setState({
          lastPosition: coords
        });
      }
    );

    if (Platform.OS === 'ios') {
      Location.startUpdatingHeading();
      this.eventOrientation = DeviceEventEmitter.addListener(
        'headingUpdated',
        (data) => {
          this.setState({ heading: parseInt(data.heading, 10) });
        }
      );
    } else {
      SensorManager.startOrientation(1000);
      this.eventOrientation = DeviceEventEmitter.addListener(
        'Orientation',
        (data) => {
          this.setState({
            heading: parseInt(data.azimuth, 10)
          });
        }
      );
    }
  }

  animateGeo() {
    Animated.sequence([
      Animated.timing(this.state.geoMarkerOpacity, {
        toValue: 0.4,
        easing: Easing.in(Easing.quad),
        duration: 800
      }),
      Animated.timing(this.state.geoMarkerOpacity, {
        toValue: 0.15,
        easing: Easing.out(Easing.quad),
        duration: 1000
      })
    ]).start(event => {
      if (event.finished) {
        this.animateGeo();
      }
    });
  }

  createReport = () => {
    const { lastPosition } = this.state;
    const form = `New-report-${Math.floor(Math.random() * 1000)}`;
    let latLng = '0,0';
    if (lastPosition) {
      latLng = `${lastPosition.latitude},${lastPosition.longitude}`;
    }
    this.props.createReport(form, latLng);
    this.props.navigate('NewReport', { form });
  };

  renderMap() {
    if (!this.state.renderMap) {
      this.setState({
        renderMap: true
      });
    }
  }

  renderFooter() {
    let distanceText = I18n.t('commonText.notAvailable');
    let positionText = '';
    let distance = 999999;
    const { lastPosition } = this.state;

    if (lastPosition) {
      const geoPoint = new GeoPoint(this.state.alertSelected.lat, this.state.alertSelected.long);
      const currentPoint = new GeoPoint(lastPosition.latitude, lastPosition.longitude);
      positionText = `${I18n.t('commonText.yourPosition')}: ${lastPosition.latitude}, ${lastPosition.longitude}`;
      distance = currentPoint.distanceTo(geoPoint, true).toFixed(4);
      distanceText = `${distance} ${I18n.t('commonText.kmAway')}`; // in Kilometers
    }

    const reportBtn = (
      <ActionBtn
        style={styles.footerButton}
        text={I18n.t('report.title')}
        onPress={this.createReport}
      />
    );
    return (
      <View style={styles.footer}>
        <Image
          style={styles.footerBg}
          source={backgroundImage}
        />
        <Text style={styles.footerTitle}>
          {distanceText}
        </Text>
        <Text style={[styles.footerTitle, styles.footerSubtitle]}>
          {positionText}
        </Text>
        {reportBtn}
      </View>
    );
  }

  renderFooterLoading() {
    return (!this.state.lastPosition &&
      <View style={styles.footer}>
        <Image
          style={styles.footerBg}
          source={backgroundImage}
        />
        <View style={styles.signalNotice}>
          <View style={styles.geoLocationContainer}>
            <Image
              style={styles.marker}
              source={markerCompassRedImage}
            />
            <Animated.View
              style={[styles.geoLocation, { opacity: this.state.geoMarkerOpacity }]}
            />
          </View>
          <Text style={styles.signalNoticeText}>{I18n.t('alerts.satelliteSignal')}</Text>
        </View>
      </View>
    );
  }

  render() {
    const { params } = this.props.navigation.state;
    const stopPropagation = thunk => e => {
      e.stopPropagation();
      thunk();
    };

    return (
      this.state.renderMap
      ?
        <View style={styles.container}>
          <View
            style={styles.header}
            pointerEvents={'box-none'}
          >
            <Image
              style={styles.headerBg}
              source={backgroundImage}
            />
            <Text style={styles.headerTitle}>
              {params.title}
            </Text>
            {this.state.alertSelected &&
              <Text style={styles.headerSubtitle}>
                {this.state.alertSelected.lat}, {this.state.alertSelected.long}
              </Text>
            }
            <TouchableHighlight
              style={styles.headerBtn}
              onPress={() => this.props.navigation.goBack()}
              underlayColor="transparent"
              activeOpacity={0.8}
            >
              <Image style={Theme.icon} source={backIconWhite} />
            </TouchableHighlight>
          </View>
          <MapView
            ref={(ref) => { this.map = ref; }}
            style={styles.map}
            provider={MapView.PROVIDER_GOOGLE}
            mapType="hybrid"
            rotateEnabled={false}
            initialRegion={this.state.region}
            onLayout={this.onLayout}
            moveOnMarkerPress={false}
          >
            {this.state.lastPosition &&
              <MapView.Marker.Animated
                image={markerImage}
                coordinate={this.state.lastPosition}
                style={{ zIndex: 2 }}
                pointerEvents={'none'}
              />
            }
            {this.state.lastPosition && this.state.heading
              ?
                <MapView.Marker
                  key={'compass'}
                  coordinate={this.state.lastPosition}
                  zIndex={1}
                  anchor={{ x: 0.5, y: 0.6 }}
                  pointerEvents={'none'}
                >
                  <Animated.Image
                    style={{
                      width: 94,
                      height: 94,
                      transform: [
                        { rotate: `${this.state.heading ? this.state.heading : '0'}deg` }
                      ]
                    }}
                    source={compassImage}
                  />
                </MapView.Marker>
              : null
            }
            {this.state.alerts.map((point, key) => {
              let image = alertGladImage;

              if (point.type === 'viirs') {
                image = alertViirsmage;
              }

              if (point.selected) {
                image = alertWhiteImage;
              }

              return (
                <MapView.Marker.Animated
                  key={key}
                  image={image}
                  style={{ opacity: 0.8 }}
                  coordinate={{
                    latitude: point.lat,
                    longitude: point.long
                  }}
                  onPress={stopPropagation(() => this.onAlertPress(point))}
                />
              );
            })
            }
          </MapView>
          {this.state.alertSelected
            ? this.renderFooter()
            : this.renderFooterLoading()
          }
        </View>
      :
        renderLoading()
    );
  }
}

Map.propTypes = {
  navigation: React.PropTypes.object.isRequired,
  navigate: React.PropTypes.func.isRequired,
  createReport: React.PropTypes.func.isRequired
};

Map.navigationOptions = {
  header: {
    visible: false
  }
};

export default Map;
<|endoftext|>


<commit_before>// Dependencies
var B = require("./bloggify")
  , Utils = require(Bloggify.PATH_UTIL)
  , JsonDB = require("mongof")
  , Fs = require("fs")
  , Plugin = require(Bloggify.PATH_LIB + "/api/plugin")
  , Debug = require("bug-killer")
  , Path = require("path")
  ;

// Constants
const DEFAULT_CONFIG = require("./default");

/**
 * getConfig
 * Returns the parsed configuration.
 *
 * @name getConfig
 * @function
 * @return {Object} The Bloggify config.
 */
Bloggify.getConfig = function (path) {
    path = path || Bloggify.ROOT + "/conf/index.json";
    Bloggify.config = Utils.mergeRecursive(
        Utils.readJson(path)
      , DEFAULT_CONFIG
    );

    // Update the paths
    Bloggify.paths.site_dir = Path.resolve(path, "../..")
    Bloggify.paths.plugins = Bloggify.paths.site_dir + "/plugins";
    Bloggify.config.pathContent = Bloggify.paths.site_dir + Bloggify.config.content;

    // Attach log method
    Debug.config.logLevel = Bloggify.config.logLevel;
    Bloggify.log = function (msg, type) {
        type = type || "error";
        Debug.log(msg + (msg.stack ? "\n" + msg.stack : ""), type);
    };

    // Database constructor
    Bloggify.db = new JsonDB({
        uri: Bloggify.config.database.uri
    });

    return Bloggify.config;
};

// Init plugins
Bloggify.initPlugins = Plugin.initPlugins;

/**
 * initDbs
 * Initializes the MongoDB databases.
 *
 * @name initDbs
 * @function
 * @param {Function} callback The callback function.
 * @return {undefined}
 */
Bloggify.initDbs = function (callback) {

    // Handle async responses
    var complete = 0
      , clbed = false
      , clb = function (e, d) {
            if (clbed === true) { return; }
            if (e) {
                clbed = true;
                return callback(e);
            }
            if (e) { return Debug.log(e, "error"); }
            if (!--complete) {
                callback(null, null);
            }
        }
      ;

    // Init pages collection
    var pathPages = Bloggify.config.pathContent + Bloggify.config.pages + "/index.json";
    ++complete;
    Bloggify.pages = Bloggify.db.initCollection({
        inputFile: pathPages
      , outputFile: pathPages
      , collection: "pages"
      , autoInit: true
    }, function (err) {
        if (err) { return clb(err); }
        Bloggify.log("Inited pages collection.", "info");
        clb(null);
    });

    // Init articles collection
    var pathArticles = Bloggify.config.pathContent + Bloggify.config.articles + "/index.json";
    ++complete;
    Bloggify.articles = Bloggify.db.initCollection({
        inputFile: pathArticles
      , outputFile: pathArticles
      , collection: "articles"
      , autoInit: true
    }, function (err) {
        if (err) { return clb(err); }
        Bloggify.log("Inited articles collection.", "info");
        clb(null);
    });
};

/**
 * render
 * Renders the pages using the lien and data objects.
 *
 * @name render
 * @function
 * @param {Object} lien The lien object.
 * @param {Object} data Render data.
 * @return {undefined}
 */
Bloggify.render = function (lien, data) {

    data.lien = lien;
    data.config = Bloggify.config;
    data.require = require;

    var content = Bloggify.theme.render(data);
    var processor = Bloggify.processors[data.type];

    var processedAll = false;
    function processContent(arr) {
        var complete = 0;
        function checkComplete() {
            if (++complete === arr.length) {
                if (Bloggify.processors.all.length && processedAll === false) {
                    processedAll = true;
                    return processContent(Bloggify.processors.all);
                }
                lien.end(content);
            }
        }
        for (var i = 0; i < arr.length; ++i) {
            var cFun = arr[i];
            if (typeof cFun !== "function") {
                checkComplete();
                continue;
            }
            if (cFun.length <= 3) {
                content = cFun(lien, data, content);
                checkComplete();
            } else {
                cFun(lien, data, content, function (nc) {
                    content = nc;
                    checkComplete();
                });
            }
        }
    }

    if (data.type === "sitePage" && processor[lien.pathName] && processor[lien.pathName].length) {
        processContent(processor[lien.pathName]);
    } else if (processor && processor.length) {
        processContent(processor);
    } else if (Bloggify.processors.all.length) {
        processedAll = true;
        processContent(Bloggify.processors.all);
    } else {
        lien.end(content);
    }
};
<commit_msg>Minor<commit_after>// Dependencies
var B = require("./bloggify")
  , Utils = require(Bloggify.PATH_UTIL)
  , JsonDB = require("mongof")
  , Fs = require("fs")
  , Plugin = require(Bloggify.PATH_LIB + "/api/plugin")
  , Debug = require("bug-killer")
  , Path = require("path")
  ;

// Constants
const DEFAULT_CONFIG = require("./default");

/**
 * getConfig
 * Returns the parsed configuration.
 *
 * @name getConfig
 * @function
 * @return {Object} The Bloggify config.
 */
Bloggify.getConfig = function (path) {
    path = path || Bloggify.ROOT + "/conf/index.json";
    Bloggify.config = Utils.mergeRecursive(
        Utils.readJson(path)
      , DEFAULT_CONFIG
    );

    // Update the paths
    Bloggify.paths.site_dir = Path.resolve(path, "../..")
    Bloggify.paths.plugins = Bloggify.paths.site_dir + "/plugins";
    Bloggify.config.pathContent = Bloggify.paths.site_dir + Bloggify.config.content;

    // Attach log method
    Debug.config.logLevel = Bloggify.config.logLevel;
    Bloggify.log = function (msg, type) {
        type = type || "error";
        Debug.log(msg + (msg.stack ? "\n" + msg.stack : ""), type);
    };

    // Database constructor
    Bloggify.db = new JsonDB({
        uri: Bloggify.config.database.uri
    });

    return Bloggify.config;
};

// Init plugins
Bloggify.initPlugins = Plugin.initPlugins;

/**
 * initDbs
 * Initializes the MongoDB databases.
 *
 * @name initDbs
 * @function
 * @param {Function} callback The callback function.
 * @return {undefined}
 */
Bloggify.initDbs = function (callback) {

    // Handle async responses
    var complete = 0
      , clbed = false
      , clb = function (e, d) {
            if (clbed === true) { return; }
            if (e) {
                clbed = true;
                return callback(e);
            }
            if (e) { return Debug.log(e, "error"); }
            if (!--complete) {
                callback(null, null);
            }
        }
      ;

    // Init pages collection
    var pathPages = Bloggify.config.pathContent + Bloggify.config.pages + "/index.json";
    ++complete;
    Bloggify.pages = Bloggify.db.initCollection({
        inputFile: pathPages
      , outputFile: pathPages
      , collection: "pages"
      , autoInit: true
    }, function (err) {
        if (err) { return clb(err); }
        Bloggify.log("Inited pages collection.", "info");
        clb(null);
    });

    // Init articles collection
    var pathArticles = Bloggify.config.pathContent + Bloggify.config.articles + "/index.json";
    ++complete;
    Bloggify.articles = Bloggify.db.initCollection({
        inputFile: pathArticles
      , outputFile: pathArticles
      , collection: "articles"
      , autoInit: true
    }, function (err) {
        if (err) { return clb(err); }
        Bloggify.log("Inited articles collection.", "info");
        clb(null);
    });
};

/**
 * render
 * Renders the pages using the lien and data objects.
 *
 * @name render
 * @function
 * @param {Object} lien The lien object.
 * @param {Object} data Render data.
 * @return {undefined}
 */
Bloggify.render = function (lien, data) {

    data.lien = lien;
    data.config = Bloggify.config;
    data.require = require;

    var content = Bloggify.theme.render(data);
    var processor = Bloggify.processors[data.type];

    var processedAll = false;

    function processContent(arr) {
        var complete = 0;

        function checkComplete() {
            if (++complete === arr.length) {
                if (Bloggify.processors.all.length && processedAll === false) {
                    processedAll = true;
                    return processContent(Bloggify.processors.all);
                }
                lien.end(content);
            }
        }

        for (var i = 0; i < arr.length; ++i) {
            var cFun = arr[i];
            if (typeof cFun !== "function") {
                checkComplete();
                continue;
            }
            if (cFun.length <= 3) {
                content = cFun(lien, data, content);
                checkComplete();
            } else {
                cFun(lien, data, content, function (nc) {
                    content = nc;
                    checkComplete();
                });
            }
        }
    }

    if (data.type === "sitePage" && processor[lien.pathName] && processor[lien.pathName].length) {
        processContent(processor[lien.pathName]);
    } else if (processor && processor.length) {
        processContent(processor);
    } else if (Bloggify.processors.all.length) {
        processedAll = true;
        processContent(Bloggify.processors.all);
    } else {
        lien.end(content);
    }
};
<|endoftext|>


<commit_before>var uuid = require('uuid');
var client = require('../src/client');

module.exports = {
  'Append One Event To Stream Happy Path': function(test) {
    test.expect(2);
    var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
    this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, event)
        .then(function(result) {
          test.areEqual("nextExpectedVersion", result.nextExpectedVersion, 0);
          test.ok(result.logPosition, "No log position in result.");
          test.done();
        })
        .catch(function(err) {
          test.done(err);
        });
  },
  'Append Multiple Events To Stream Happy Path': function(test) {
    test.expect(2);
    const expectedVersion = 25;
    var events = [];
    for(var i = 0; i <= expectedVersion; i++) {
      if (i % 2 === 0)
        events.push(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent'));
      else
        events.push(client.createJsonEventData(uuid.v4(), {b: Math.random(), a: uuid.v4()}, null, 'otherEvent'));
    }
    this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, events)
        .then(function(result) {
          test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, expectedVersion);
          test.ok(result.logPosition, "No log position in result.");
          test.done();
        })
        .catch(function(err) {
          test.done(err);
        });
  },
  'Append To Stream Wrong Expected Version': function(test) {
    test.expect(1);
    var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
    this.conn.appendToStream(this.testStreamName, 10, event)
        .then(function(result) {
          test.fail("Append succeeded but should have failed.");
          test.done();
        })
        .catch(function(err) {
          var isWrongExpectedVersion = err instanceof client.WrongExpectedVersionError;
          test.ok(isWrongExpectedVersion, "Expected WrongExpectedVersionError, got " + err.constructor.name);
          if (isWrongExpectedVersion) return test.done();
          test.done(err);
        });
  },
  'Append To Stream Deleted': function(test) {
    test.expect(1);
    var self = this;
    this.conn.deleteStream(this.testStreamName, client.expectedVersion.noStream, true)
        .then(function() {
          var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
          return self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, event)
        })
        .then(function(result) {
          test.fail("Append succeeded but should have failed.");
          test.done();
        })
        .catch(function(err) {
          var isStreamDeleted = err instanceof client.StreamDeletedError;
          test.ok(isStreamDeleted, "Expected StreamDeletedError, got " + err.constructor.name);
          if (isStreamDeleted) return test.done();
          test.done(err);
        });
  },
  'Append To Stream Access Denied': function(test) {
    test.expect(1);
    var self = this;
    var metadata = {$acl: {$w: "$admins"}};
    this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.noStream, metadata)
        .then(function() {
          var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
          return self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, event)
        })
        .then(function(result) {
          test.fail("Append succeeded but should have failed.");
          test.done();
        })
        .catch(function(err) {
          var isStreamDeleted = err instanceof client.AccessDeniedError;
          test.ok(isStreamDeleted, "Expected AccessDeniedError, got " + err.constructor.name);
          if (isStreamDeleted) return test.done();
          test.done(err);
        });
  }
};

require('./common/base_test').init(module.exports);
<commit_msg>[test] append to stream stress test<commit_after>var uuid = require('uuid');
var client = require('../src/client');

module.exports = {
  'Append One Event To Stream Happy Path': function(test) {
    test.expect(2);
    var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
    this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, event)
        .then(function(result) {
          test.areEqual("nextExpectedVersion", result.nextExpectedVersion, 0);
          test.ok(result.logPosition, "No log position in result.");
          test.done();
        })
        .catch(function(err) {
          test.done(err);
        });
  },
  'Append Multiple Events To Stream Happy Path': function(test) {
    test.expect(2);
    const expectedVersion = 25;
    var events = [];
    for(var i = 0; i <= expectedVersion; i++) {
      if (i % 2 === 0)
        events.push(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent'));
      else
        events.push(client.createJsonEventData(uuid.v4(), {b: Math.random(), a: uuid.v4()}, null, 'otherEvent'));
    }
    this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, events)
        .then(function(result) {
          test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, expectedVersion);
          test.ok(result.logPosition, "No log position in result.");
          test.done();
        })
        .catch(function(err) {
          test.done(err);
        });
  },
  'Append Multiple Events To Stream Happy Path Stress Test': function(test) {
    test.expect(2);
    const expectedVersion = 1000;
    var events = [];
    for(var i = 0; i <= expectedVersion; i++) {
      if (i % 2 === 0)
        events.push(client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent'));
      else
        events.push(client.createJsonEventData(uuid.v4(), {b: Math.random(), a: uuid.v4()}, null, 'otherEvent'));
    }
    this.conn.appendToStream(this.testStreamName, client.expectedVersion.any, events)
        .then(function(result) {
          test.areEqual("result.nextExpectedVersion", result.nextExpectedVersion, expectedVersion);
          test.ok(result.logPosition, "No log position in result.");
          test.done();
        })
        .catch(function(err) {
          test.done(err);
        });
  },
  'Append To Stream Wrong Expected Version': function(test) {
    test.expect(1);
    var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
    this.conn.appendToStream(this.testStreamName, 10, event)
        .then(function(result) {
          test.fail("Append succeeded but should have failed.");
          test.done();
        })
        .catch(function(err) {
          var isWrongExpectedVersion = err instanceof client.WrongExpectedVersionError;
          test.ok(isWrongExpectedVersion, "Expected WrongExpectedVersionError, got " + err.constructor.name);
          if (isWrongExpectedVersion) return test.done();
          test.done(err);
        });
  },
  'Append To Stream Deleted': function(test) {
    test.expect(1);
    var self = this;
    this.conn.deleteStream(this.testStreamName, client.expectedVersion.noStream, true)
        .then(function() {
          var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
          return self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, event)
        })
        .then(function(result) {
          test.fail("Append succeeded but should have failed.");
          test.done();
        })
        .catch(function(err) {
          var isStreamDeleted = err instanceof client.StreamDeletedError;
          test.ok(isStreamDeleted, "Expected StreamDeletedError, got " + err.constructor.name);
          if (isStreamDeleted) return test.done();
          test.done(err);
        });
  },
  'Append To Stream Access Denied': function(test) {
    test.expect(1);
    var self = this;
    var metadata = {$acl: {$w: "$admins"}};
    this.conn.setStreamMetadataRaw(this.testStreamName, client.expectedVersion.noStream, metadata)
        .then(function() {
          var event = client.createJsonEventData(uuid.v4(), {a: Math.random(), b: uuid.v4()}, null, 'testEvent');
          return self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, event)
        })
        .then(function(result) {
          test.fail("Append succeeded but should have failed.");
          test.done();
        })
        .catch(function(err) {
          var isStreamDeleted = err instanceof client.AccessDeniedError;
          test.ok(isStreamDeleted, "Expected AccessDeniedError, got " + err.constructor.name);
          if (isStreamDeleted) return test.done();
          test.done(err);
        });
  }
};

require('./common/base_test').init(module.exports);
<|endoftext|>


<commit_before>/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  TouchableOpacity,
  View
} from 'react-native';

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

    this.state = {
      numberPlayers: 2,
      showInitialState: true,
      defaultHealth: 20,
    };

    // set this for callbacks
    this.handleSetNumberPlayers = this.handleSetNumberPlayers.bind(this);
    this.handleSetDefaultHealth = this.handleSetDefaultHealth.bind(this);
  }
  render() {
    let {showInitialState} = this.state;
    if (showInitialState) {
      return <MTGSetup
        onSetNumberPlayers={this.handleSetNumberPlayers}
        onSetDefaultHealth={this.handleSetDefaultHealth}
        defaultHealth={this.state.defaultHealth}
      />;
    } else {
      return <MTGLayout
        numberPlayers={this.state.numberPlayers}
        reset={() => this.handleReset()}
        defaultHealth={this.state.defaultHealth}
      />;
    }
  }

  handleReset() {
    this.setState({showInitialState: true});
  }

  handleSetNumberPlayers(numberPlayers) {
    console.log(numberPlayers)
    this.setState({
      numberPlayers: numberPlayers,
      showInitialState: false,
    });
  }

  handleSetDefaultHealth(health) {
    console.log(health)
    this.setState({
      defaultHealth: health
    });
  }
}

/**
 * props: {
   onSetNumberPlayers: func - callback to set the number of players and start
   onSetDefaultHealth: func - callback to set the starting health
   defaultHealth: number - the starting health
  }
 */
class MTGSetup extends Component {
  constructor(props) {
    super(props);
  }

  render () {
    console.log(this.props)
    let style20 = this.props.defaultHealth === 20 ? styles.buttonSolid : styles.button;
    let style40 = this.props.defaultHealth === 40 ? styles.buttonSolid : styles.button;
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>How many players?</Text>
        <TouchableOpacity onPress={() => this.props.onSetNumberPlayers(2)}>
          <Text style={styles.button}>two</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this.props.onSetNumberPlayers(3)}>
          <Text style={styles.button}>three</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this.props.onSetNumberPlayers(4)}>
          <Text style={styles.button}>four</Text>
        </TouchableOpacity>
        <Text style={styles.welcome}>Default health?</Text>
        <TouchableOpacity onPress={() => this.props.onSetDefaultHealth(20)}>
          <Text style={style20}>20</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this.props.onSetDefaultHealth(40)}>
          <Text style={style40}>40</Text>
        </TouchableOpacity>
      </View>
    )
  }
}
MTGSetup.propTypes = {
  onSetNumberPlayers: React.PropTypes.func,
  onSetDefaultHealth: React.PropTypes.func,
  defaultHealth: React.PropTypes.number,
};

/**
 * props: {
   numberPlayers: number - the number of players [2-4]
   reset: func - callback to reset the game
   defaultHealth: number - health to start with
  }
 */
class MTGLayout extends Component {
  constructor(props) {
    super(props);

    let players = {};
    for (var i = 1; i < this.props.numberPlayers + 1; i++) {
      players[i] = this.props.defaultHealth;
    }
    this.state = {
      players: players
    };

    this.addHealth = this.addHealth.bind(this);
    this.subHealth = this.subHealth.bind(this);
  }

  render () {
    let players = this.playerLayout();

    return (
      <View style={styles.container}>
        <TouchableOpacity style={styles.touchButton} onPress={() => this.props.reset()}>
          <Text style={styles.buttonText}>reset</Text>
        </TouchableOpacity>
        <View>
          {players}
        </View>
      </View>
    )
  }

  playerLayout() {
    let players = [];
    for (var playerNumber in this.state.players) {
      players.push({
        number: playerNumber,
        health: this.state.players[playerNumber],
      });
    }
    return players.map((p) => {
      return (
        <View key={p.number}>
          <TouchableOpacity onPress={() => this.subHealth(p.number)}>
            <Text >-</Text>
          </TouchableOpacity>
          <Text style={styles.button}>Player {p.number}: {p.health}</Text>
          <TouchableOpacity onPress={() => this.addHealth(p.number)}>
            <Text >+</Text>
          </TouchableOpacity>
        </View>
      );
    });
  }

  addHealth(playerNumber) {
    this.setState((previousState, currentProps) => {
      previousState.players[playerNumber] += 1;
      return previousState;
    });
  }
  subHealth(playerNumber) {
    this.setState((previousState, currentProps) => {
      previousState.players[playerNumber] -= 1;
      return previousState;
    });
  }
}
MTGLayout.propTypes = {
  numberPlayers: React.PropTypes.number,
  reset: React.PropTypes.func,
  defaultHealth: React.PropTypes.number,
};
MTGLayout.defaultProps = { defaultHealth: 40 };

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  welcome: {
    fontSize: 30,
    textAlign: 'center',
    color: '#34B8F1',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  // todo see if I can move the button styles to the TouchableOpacity since the
  // the Text button bleeds into the TouchableOpacity below it.
  button: {
    fontSize: 25,
    borderColor: '#34B8F1',
    borderWidth: 3,
    borderRadius: 5,
    textAlign: 'center',
    color: '#34B8F1',
    padding: 15,
    margin: 15,
  },
  buttonSolid: {
    fontSize: 25,
    borderColor: 'white',
    borderWidth: 3,
    borderRadius: 5,
    textAlign: 'center',
    color: 'white',
    backgroundColor: '#34B8F1',
    padding: 15,
    margin: 15,
    overflow: 'hidden',
  },
  touchButton: {
    borderColor: '#34B8F1',
    borderWidth: 3,
    borderRadius: 5,
    padding: 15,
    margin: 15,
  },
  buttonText: {
    color: '#34B8F1',
    textAlign: 'center',
    fontSize: 25,
  }
});

AppRegistry.registerComponent('MTG', () => MTG);
<commit_msg>better styles<commit_after>/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  TouchableOpacity,
  View
} from 'react-native';

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

    this.state = {
      numberPlayers: 2,
      showInitialState: true,
      defaultHealth: 20,
    };

    // set this for callbacks
    this.handleSetNumberPlayers = this.handleSetNumberPlayers.bind(this);
    this.handleSetDefaultHealth = this.handleSetDefaultHealth.bind(this);
  }
  render() {
    let {showInitialState} = this.state;
    if (showInitialState) {
      return <MTGSetup
        onSetNumberPlayers={this.handleSetNumberPlayers}
        onSetDefaultHealth={this.handleSetDefaultHealth}
        defaultHealth={this.state.defaultHealth}
      />;
    } else {
      return <MTGLayout
        numberPlayers={this.state.numberPlayers}
        reset={() => this.handleReset()}
        defaultHealth={this.state.defaultHealth}
      />;
    }
  }

  handleReset() {
    this.setState({showInitialState: true});
  }

  handleSetNumberPlayers(numberPlayers) {
    console.log(numberPlayers)
    this.setState({
      numberPlayers: numberPlayers,
      showInitialState: false,
    });
  }

  handleSetDefaultHealth(health) {
    console.log(health)
    this.setState({
      defaultHealth: health
    });
  }
}

/**
 * props: {
   onSetNumberPlayers: func - callback to set the number of players and start
   onSetDefaultHealth: func - callback to set the starting health
   defaultHealth: number - the starting health
  }
 */
class MTGSetup extends Component {
  constructor(props) {
    super(props);
  }

  render () {
    console.log(this.props)
    let style20 = this.props.defaultHealth === 20 ? styles.buttonSolid : styles.button;
    let style40 = this.props.defaultHealth === 40 ? styles.buttonSolid : styles.button;
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>How many players?</Text>
        <TouchableOpacity onPress={() => this.props.onSetNumberPlayers(2)}>
          <Text style={styles.button}>two</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this.props.onSetNumberPlayers(3)}>
          <Text style={styles.button}>three</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this.props.onSetNumberPlayers(4)}>
          <Text style={styles.button}>four</Text>
        </TouchableOpacity>
        <Text style={styles.welcome}>Default health?</Text>
        <TouchableOpacity onPress={() => this.props.onSetDefaultHealth(20)}>
          <Text style={style20}>20</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => this.props.onSetDefaultHealth(40)}>
          <Text style={style40}>40</Text>
        </TouchableOpacity>
      </View>
    )
  }
}
MTGSetup.propTypes = {
  onSetNumberPlayers: React.PropTypes.func,
  onSetDefaultHealth: React.PropTypes.func,
  defaultHealth: React.PropTypes.number,
};

/**
 * props: {
   numberPlayers: number - the number of players [2-4]
   reset: func - callback to reset the game
   defaultHealth: number - health to start with
  }
 */
class MTGLayout extends Component {
  constructor(props) {
    super(props);

    let players = {};
    for (var i = 1; i < this.props.numberPlayers + 1; i++) {
      players[i] = this.props.defaultHealth;
    }
    this.state = {
      players: players
    };

    this.addHealth = this.addHealth.bind(this);
    this.subHealth = this.subHealth.bind(this);
  }

  render () {
    let players = this.playerLayout();

    return (
      <View style={styles.container}>
        <TouchableOpacity style={styles.touchButton} onPress={() => this.props.reset()}>
          <Text style={styles.buttonText}>reset</Text>
        </TouchableOpacity>
        <View>
          {players}
        </View>
      </View>
    )
  }

  playerLayout() {
    let players = [];
    for (var playerNumber in this.state.players) {
      players.push({
        number: playerNumber,
        health: this.state.players[playerNumber],
      });
    }
    return players.map((p) => {
      return (
        <View key={p.number} style={styles.touchButton}>
          <TouchableOpacity onPress={() => this.subHealth(p.number)}>
            <Text style={styles.buttonText}>-</Text>
          </TouchableOpacity>
          <Text style={styles.buttonText}>Player {p.number}: {p.health}</Text>
          <TouchableOpacity onPress={() => this.addHealth(p.number)}>
            <Text style={styles.buttonText}>+</Text>
          </TouchableOpacity>
        </View>
      );
    });
  }

  addHealth(playerNumber) {
    this.setState((previousState, currentProps) => {
      previousState.players[playerNumber] += 1;
      return previousState;
    });
  }
  subHealth(playerNumber) {
    this.setState((previousState, currentProps) => {
      previousState.players[playerNumber] -= 1;
      return previousState;
    });
  }
}
MTGLayout.propTypes = {
  numberPlayers: React.PropTypes.number,
  reset: React.PropTypes.func,
  defaultHealth: React.PropTypes.number,
};
MTGLayout.defaultProps = { defaultHealth: 40 };

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  welcome: {
    fontSize: 30,
    textAlign: 'center',
    color: '#34B8F1',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  // todo see if I can move the button styles to the TouchableOpacity since the
  // the Text button bleeds into the TouchableOpacity below it.
  button: {
    fontSize: 25,
    borderColor: '#34B8F1',
    borderWidth: 3,
    borderRadius: 5,
    textAlign: 'center',
    color: '#34B8F1',
    padding: 15,
    margin: 15,
  },
  buttonSolid: {
    fontSize: 25,
    borderColor: 'white',
    borderWidth: 3,
    borderRadius: 5,
    textAlign: 'center',
    color: 'white',
    backgroundColor: '#34B8F1',
    padding: 15,
    margin: 15,
    overflow: 'hidden',
  },
  touchButton: {
    borderColor: '#34B8F1',
    borderWidth: 3,
    borderRadius: 5,
    padding: 15,
    margin: 15,
  },
  buttonText: {
    color: '#34B8F1',
    textAlign: 'center',
    fontSize: 25,
  }
});

AppRegistry.registerComponent('MTG', () => MTG);
<|endoftext|>


<commit_before>'use strict';

angular.module('publicApp')
  .controller('LoginCtrl', ['$scope', '$http', '$location', 'UserService', function ($scope, $http, $location, $user) {
    function checkAdminExists() {
      $http.get("/api/v1/gateway/settings").success(function(resp){
        if (resp.success) {
          $scope.adminExists = resp.settings.adminExists
        }
        $scope.loading = false
      })
    }

    checkAdminExists()

    $scope.loading = true
    $scope.adminExists = false
    $scope.admin = { email: '' }

    $scope.user = {}
    $scope.userService = $user

    $scope.logout = function() {
      $user.logout()
    }

    $scope.registerAdmin = function() {
      if ($scope.admin.email) {
        $http.post('/api/v1/admin/users', { email: $scope.admin.email }).success(function(resp) {
          console.log(resp)
        })
      }
    }

    $scope.login = function () {
			var name = $scope.user.name;
			var password = $scope.user.password;
      $user.login(name, password, function(err, session){
        if (!!session.username) {
					$location.path('/gateway/account');
				} else {
					$location.path('/admin/users/new');
				}
      })
		}
  }]);
<commit_msg>[FEATURE] Alert new admin of password and redirect to login.<commit_after>'use strict';

angular.module('publicApp')
  .controller('LoginCtrl', ['$scope', '$http', '$location', 'UserService', function ($scope, $http, $location, $user) {
    function checkAdminExists() {
      $http.get("/api/v1/gateway/settings").success(function(resp){
        if (resp.success) {
          $scope.adminExists = resp.settings.adminExists
        }
        $scope.loading = false
      })
    }

    checkAdminExists()

    $scope.loading = true
    $scope.adminExists = false
    $scope.admin = { email: '' }

    $scope.user = {}
    $scope.userService = $user

    $scope.logout = function() {
      $user.logout()
    }

    $scope.registerAdmin = function() {
      if ($scope.admin.email) {
        $http.post('/api/v1/admin/users', { email: $scope.admin.email }).success(function(resp) {
          alert("here is your admin password, save it right now! c582b4dd80459dc5fccacca210b3251297575bd86c2adc22d3384dc9a392d3d1");
          $location.path('/login');
          console.log(resp);
        })
      }
    }

    $scope.login = function () {
			var name = $scope.user.name;
			var password = $scope.user.password;
      $user.login(name, password, function(err, session){
        if (!!session.username) {
					$location.path('/gateway/account');
				} else {
					$location.path('/admin/users/new');
				}
      })
		}
  }]);
<|endoftext|>


<commit_before>import Declaration from "./declaration"
import * as PHPType from "../php-type"
import Method from "./method"
import Property from "./property"
import ClassConstant from "./classconstant"
import Identifier from "./identifier"
import TraitUse from "./traituse"
import {Context, ContextTypes, Doc, ParserStateOption} from "./node"
import * as PHPError from "../php-error"
export default class Class extends Declaration {
    /**
     * @type {Declaration[]}
     */
    get body() {
        return this.cacheNodeArray("body")
    }
    /**
     * @type {?Identifier}
     */
    get extends() {
        return this.cacheNode("extends")
    }
    /**
     * @type {Identifier[]}
     */
    get implements() {
        return this.cacheNode("implements")
    }
    /**
     * @type {boolean}
     */
    get isAbstract() {
        return this.node.isAbstract
    }
    /**
     * @type {boolean}
     */
    get isAnonymous() {
        return this.node.isAnonymous
    }
    /**
     * @type {boolean}
     */
    get isFinal() {
        return this.node.isFinal
    }

    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @param {Set<ParserStateOption.Base>} [parser_state]
     * @param {?Doc} [doc]
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, parser_state = new Set(), doc = null) {
        super.check(context, parser_state, doc)
        if(!this.name.match(/^([0-9A-Z]+[0-9a-z]*)+$/)) {
            // This does allow names like UPSPowerState
            this.throw(new PHPError.PSR1.S3ClassCase(), context)
        }
        if(!doc) {
            this.throw(new PHPError.NoDoc(), context)
        }
        let inner_context = context.childContext()
        let extended_class
        if(this.extends) {
            let extended_class_name = context.resolveNodeName(this.extends)
            try {
                extended_class = context.findClass(extended_class_name)
            } catch(e) {
                this.handleException(e, context)
            }
        }
        inner_context.classContext = inner_context.globalContext.addClass(
            context.resolveNodeName(this),
            extended_class,
            context.fileContext
        )
        inner_context.setThis()

        /** @type {?Doc} */
        let last_doc = null
        this.body.forEach(
            b => {
                let doc = last_doc
                if(b instanceof Doc) {
                    last_doc = b
                } else if(b instanceof Method) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof Property) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof ClassConstant) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        "public",
                        true,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof TraitUse) {
                    // Do nothing - loaded shortly
                } else {
                    b.check(inner_context, new Set(), last_doc)
                    last_doc = null
                }
            }
        )

        last_doc = null
        this.body.forEach(
            b => {
                if(b instanceof Doc) {
                    last_doc = b
                } else {
                    b.check(inner_context, new Set(), last_doc)
                    last_doc = null
                }
            }
        )
        return ContextTypes.empty
    }
}
<commit_msg>More clearly clean up temporary names at the end<commit_after>import Declaration from "./declaration"
import * as PHPType from "../php-type"
import Method from "./method"
import Property from "./property"
import ClassConstant from "./classconstant"
import Identifier from "./identifier"
import TraitUse from "./traituse"
import {Context, ContextTypes, Doc, ParserStateOption} from "./node"
import * as PHPError from "../php-error"
export default class Class extends Declaration {
    /**
     * @type {Declaration[]}
     */
    get body() {
        return this.cacheNodeArray("body")
    }
    /**
     * @type {?Identifier}
     */
    get extends() {
        return this.cacheNode("extends")
    }
    /**
     * @type {Identifier[]}
     */
    get implements() {
        return this.cacheNode("implements")
    }
    /**
     * @type {boolean}
     */
    get isAbstract() {
        return this.node.isAbstract
    }
    /**
     * @type {boolean}
     */
    get isAnonymous() {
        return this.node.isAnonymous
    }
    /**
     * @type {boolean}
     */
    get isFinal() {
        return this.node.isFinal
    }

    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @param {Set<ParserStateOption.Base>} [parser_state]
     * @param {?Doc} [doc]
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, parser_state = new Set(), doc = null) {
        super.check(context, parser_state, doc)
        if(!this.name.match(/^([0-9A-Z]+[0-9a-z]*)+$/)) {
            // This does allow names like UPSPowerState
            this.throw(new PHPError.PSR1.S3ClassCase(), context)
        }
        if(!doc) {
            this.throw(new PHPError.NoDoc(), context)
        }
        let inner_context = context.childContext()
        let extended_class
        if(this.extends) {
            let extended_class_name = context.resolveNodeName(this.extends)
            try {
                extended_class = context.findClass(extended_class_name)
            } catch(e) {
                this.handleException(e, context)
            }
        }
        inner_context.classContext = inner_context.globalContext.addClass(
            context.resolveNodeName(this),
            extended_class,
            context.fileContext
        )
        inner_context.setThis()

        /** @type {?Doc} */
        let last_doc = null
        this.body.forEach(
            b => {
                let doc = last_doc
                if(b instanceof Doc) {
                    last_doc = b
                } else if(b instanceof Method) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof Property) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof ClassConstant) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        "public",
                        true,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof TraitUse) {
                    // Do nothing - loaded shortly
                } else {
                    b.check(inner_context, new Set(), last_doc)
                    last_doc = null
                }
            }
        )

        last_doc = null
        this.body.forEach(
            b => {
                if(b instanceof Doc) {
                    last_doc = b
                } else {
                    if(b instanceof TraitUse) {
                        b.check(inner_context, new Set(), last_doc)
                    }
                    last_doc = null
                }
            }
        )
        Object.keys(inner_context.classContext.temporaryIdentifiers).forEach(name => {
            let ti = inner_context.classContext.temporaryIdentifiers[name]
            if(ti && !ti.compileStarted) {
                ti.compileStarted = true
                ti.compile()
                delete inner_context.classContext.temporaryIdentifiers[name]
            }
        })
        return ContextTypes.empty
    }
}
<|endoftext|>


<commit_before>import CartController from '../../../../.dist/controllers/components/cart-panel';
import { assert } from "chai";
import sinon from 'sinon';
import * as api from '../../../../.dist/lib/api-client';
import { Dispatcher } from 'consus-core/flux';

describe("CartController", () => {

    describe('checkInItem',()=> {
        let checkIn, dispatcherSpy;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            checkIn = sinon.stub(api, "checkIn");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKIN_SUCCESS" on success', () => {
            checkIn.returns(
                new Promise(resolve => {
                    resolve({itemAddress: "123456"});
                })
            );
            return CartController.checkInItem('123456', '123456').then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(0).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(0).args[0], "CHECKIN_SUCCESS");
            });
        });

        it('Should dispatch "ERROR" on failure', ()=> {
            checkIn.returns(
                new Promise((resolve, reject) => {
                    reject("Something Wrong");
                })
            );
            return CartController.checkInItem('123456', '123456').then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(0).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(0).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(0).args[1].error, 'Something Wrong');
            });
        });

        afterEach(() => {
            checkIn.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe('getItem',()=> {
        let dispatcherSpy, searchItem;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            searchItem = sinon.stub(api, "searchItem");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKOUT_ITEM_FOUND" on success', ()=> {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456", timestamp:100000000000000}]});

            searchItem.returns(
                new Promise(resolve => {
                    resolve({status: "AVAILABLE"});
                })
            );
            return CartController.getItem("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "CHECKOUT_ITEM_FOUND");
            })
        });

        it('Should dispatch "ERROR" if item is checked out', () => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: []});
            searchItem.returns(
                new Promise(resolve => {
                    resolve({
                        "address": "iGwEZUvfA",
                        "modelAddress": "m8y7nEtAe",
                        "status": "CHECKED_OUT"
                    })
                })
            );

            return CartController.getItem("iGwEZUvfA").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(1).args[1].error, 'This item is already checked out by another student.');
            });
        });

        afterEach(() => {
            searchItem.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe('getModel',()=> {
        let dispatcherSpy, searchModel;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            searchModel = sinon.stub(api, "searchModel");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKOUT_MODEL_FOUND" on success', ()=> {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456", timestamp:100000000000000}]});

            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        address: 123456,
                        allowCheckout: true,
                        inStock: 2
                    })
                })
            );
            return CartController.getModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "CHECKOUT_MODEL_FOUND");
            })
        });

        it('Should dispatch "ERROR" if no models in stock', () => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: []});
            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        name: "transistor",
                        address: 123456,
                        allowCheckout: true,
                        inStock: 0
                    })
                })
            );

            return CartController.getModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(1).args[1].error, 'All transistors have been checked out.');
            });
        });

        it('Should dispatch "ERROR" if checkout is not allowed', () => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: []});
            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        name: "transistor",
                        address: 123456,
                        allowCheckout: false,
                        inStock: 20
                    })
                })
            );

            return CartController.getModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(1).args[1].error, 'transistor is not available for checkout.');
            });
        });

        afterEach(() => {
            searchModel.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe('incrementModel',()=> {
        let dispatcherSpy, searchModel;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            Dispatcher.handleAction("CHECKOUT_MODEL_FOUND", {address:123456, allowCheckout: true, inStock: 2});
            searchModel = sinon.stub(api, "searchModel");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKOUT_DUPLICATE_MODEL" on success', ()=> {

            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        address: 123456,
                        allowCheckout: true,
                        inStock: 2
                    })
                })
            );
            return CartController.incrementModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(0).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(0).args[0], "CHECKOUT_DUPLICATE_MODEL");
            })
        });

        afterEach(() => {
            searchModel.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe("throwError", () => {
        let spy;
        before(() => {
            spy = sinon.spy(Dispatcher, "handleAction");
        });

        it("Should throw the error that it's given", () => {
            CartController.throwError("bad thing");
            assert.isTrue(spy.called);
            assert.strictEqual(spy.getCall(0).args.length, 2);
            assert.strictEqual(spy.getCall(0).args[0], "ERROR");
        });

        after(() => {
            spy.restore();
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

});
<commit_msg>Missing semicolon<commit_after>import CartController from '../../../../.dist/controllers/components/cart-panel';
import { assert } from "chai";
import sinon from 'sinon';
import * as api from '../../../../.dist/lib/api-client';
import { Dispatcher } from 'consus-core/flux';

describe("CartController", () => {

    describe('checkInItem',()=> {
        let checkIn, dispatcherSpy;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            checkIn = sinon.stub(api, "checkIn");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKIN_SUCCESS" on success', () => {
            checkIn.returns(
                new Promise(resolve => {
                    resolve({itemAddress: "123456"});
                })
            );
            return CartController.checkInItem('123456', '123456').then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(0).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(0).args[0], "CHECKIN_SUCCESS");
            });
        });

        it('Should dispatch "ERROR" on failure', ()=> {
            checkIn.returns(
                new Promise((resolve, reject) => {
                    reject("Something Wrong");
                })
            );
            return CartController.checkInItem('123456', '123456').then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(0).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(0).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(0).args[1].error, 'Something Wrong');
            });
        });

        afterEach(() => {
            checkIn.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe('getItem',()=> {
        let dispatcherSpy, searchItem;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            searchItem = sinon.stub(api, "searchItem");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKOUT_ITEM_FOUND" on success', ()=> {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456", timestamp:100000000000000}]});

            searchItem.returns(
                new Promise(resolve => {
                    resolve({status: "AVAILABLE"});
                })
            );
            return CartController.getItem("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "CHECKOUT_ITEM_FOUND");
            })
        });

        it('Should dispatch "ERROR" if item is checked out', () => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: []});
            searchItem.returns(
                new Promise(resolve => {
                    resolve({
                        "address": "iGwEZUvfA",
                        "modelAddress": "m8y7nEtAe",
                        "status": "CHECKED_OUT"
                    })
                })
            );

            return CartController.getItem("iGwEZUvfA").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(1).args[1].error, 'This item is already checked out by another student.');
            });
        });

        afterEach(() => {
            searchItem.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe('getModel',()=> {
        let dispatcherSpy, searchModel;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            searchModel = sinon.stub(api, "searchModel");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKOUT_MODEL_FOUND" on success', ()=> {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456", timestamp:100000000000000}]});

            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        address: 123456,
                        allowCheckout: true,
                        inStock: 2
                    })
                })
            );
            return CartController.getModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "CHECKOUT_MODEL_FOUND");
            })
        });

        it('Should dispatch "ERROR" if no models in stock', () => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: []});
            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        name: "transistor",
                        address: 123456,
                        allowCheckout: true,
                        inStock: 0
                    })
                })
            );

            return CartController.getModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(1).args[1].error, 'All transistors have been checked out.');
            });
        });

        it('Should dispatch "ERROR" if checkout is not allowed', () => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: []});
            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        name: "transistor",
                        address: 123456,
                        allowCheckout: false,
                        inStock: 20
                    })
                })
            );

            return CartController.getModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(1).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(1).args[0], "ERROR");
                assert.strictEqual(dispatcherSpy.getCall(1).args[1].error, 'transistor is not available for checkout.');
            });
        });

        afterEach(() => {
            searchModel.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe('incrementModel',()=> {
        let dispatcherSpy, searchModel;

        beforeEach(() => {
            Dispatcher.handleAction("STUDENT_FOUND", {items: [{itemAddress: "123456"}]});
            Dispatcher.handleAction("CHECKOUT_MODEL_FOUND", {address:123456, allowCheckout: true, inStock: 2});
            searchModel = sinon.stub(api, "searchModel");
            dispatcherSpy = sinon.spy(Dispatcher, "handleAction");
        });

        it('Should dispatch "CHECKOUT_DUPLICATE_MODEL" on success', ()=> {

            searchModel.returns(
                new Promise(resolve => {
                    resolve({
                        address: 123456,
                        allowCheckout: true,
                        inStock: 2
                    })
                })
            );
            return CartController.incrementModel("123456").then(() => {
                assert.isTrue(dispatcherSpy.called);
                assert.strictEqual(dispatcherSpy.getCall(0).args.length, 2);
                assert.strictEqual(dispatcherSpy.getCall(0).args[0], "CHECKOUT_DUPLICATE_MODEL");
            });
        });

        afterEach(() => {
            searchModel.restore();
            dispatcherSpy.restore();
            Dispatcher.handleAction("CLEAR_ALL_DATA");
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

    describe("throwError", () => {
        let spy;
        before(() => {
            spy = sinon.spy(Dispatcher, "handleAction");
        });

        it("Should throw the error that it's given", () => {
            CartController.throwError("bad thing");
            assert.isTrue(spy.called);
            assert.strictEqual(spy.getCall(0).args.length, 2);
            assert.strictEqual(spy.getCall(0).args[0], "ERROR");
        });

        after(() => {
            spy.restore();
            Dispatcher.handleAction("CLEAR_ERROR");
        });
    });

});
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'

import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'

import api from '../../../../services'

import { Row, Col, Spin, Collapse } from 'antd'
const Panel = Collapse.Panel
/*
There are two kinds of meetings:
- QA meetings (metrics, no votes)
- Voting meetings (votes, may have metrics but probably not)
*/
@connect(
    //  Might seem counterintuitive, but we're connecting to a manifest and pulling its proposal data.
    (state, props) => ({
      manifest: state.db.manifests[props.index],
      user: state.user
    }),
    dispatch => ({ api: bindActionCreators(api, dispatch) })
)
class Vote extends React.Component {
  render (
    { manifest } = this.props
  ) {
    const { proposal } = manifest
    const { id, title, organization, uac, year, number, date, comments, body } = proposal
    console.log(body)
    body && Object.keys(body.plan).forEach((key, i) => console.log(body.plan[key].current, body.plan[key].future))
    return (

        {!proposal
          ? <Spin size='large' tip='Loading...' />
          :

{uac ? title : `${title} (UAC)`}

            {uac &&
Universal Access Committee
}

For {organization}

{`ID: ${year}-${number}`}

Date: {date}

Endorsements: {comments.length}

            {body &&

                <Row gutter={32}>
                  <Col className='gutter-row' xs={24} md={12}>

Overview

{body.overview.abstract}

                  </Col>
                  <Col className='gutter-row' xs={24} md={12}>

Objectives

{body.overview.objectives}

Core Justification

{body.overview.justification}

                  </Col>
                </Row>

Impact

                <Row gutter={32}>
                  <Col className='gutter-row' xs={24} md={8}>

Academic Experience

{body.overview.impact.academic}

                  </Col>
                  <Col className='gutter-row' xs={24} md={8}>

Research Opportunities

{body.overview.impact.research}

                  </Col>
                  <Col className='gutter-row' xs={24} md={8}>

Career Development

{body.overview.impact.career}

                  </Col>
                </Row>

Project Plan

                {body.plan &&
                  <Collapse bordered={false} >
                    {Object.keys(body.plan).forEach((key, i) => (
                      <Panel header={i} key={i}>

Current

                          {body.plan[key].current}

Future

                          {body.plan[key].future}

                      </Panel>
                    ))}
                  </Collapse>
                }

            }

        }

    )
  }
}
Vote.propTypes = {
  manifest: PropTypes.object,
  user: PropTypes.object
}
export default Vote
<commit_msg>Summary component completed. Factoring out...<commit_after>import React from 'react'
import PropTypes from 'prop-types'

import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'

import api from '../../../../services'

import { Row, Col, Spin, Collapse } from 'antd'
const Panel = Collapse.Panel
/*
There are two kinds of meetings:
- QA meetings (metrics, no votes)
- Voting meetings (votes, may have metrics but probably not)
*/
@connect(
    //  Might seem counterintuitive, but we're connecting to a manifest and pulling its proposal data.
    (state, props) => ({
      manifest: state.db.manifests[props.index],
      user: state.user
    }),
    dispatch => ({ api: bindActionCreators(api, dispatch) })
)
class Vote extends React.Component {
  render (
    { manifest } = this.props
  ) {
    const { proposal } = manifest
    const { id, title, organization, uac, year, number, date, comments, body } = proposal
    //  For reasons unknown, we can't use Object.keys to iterate and create panels. Map works though. Perhaps it's a FP issue?
    const planKeys = Object.keys(body.plan)
    const planTitles = ['State Analysis', 'Availability', 'Implementation Strategy', 'Outreach Efforts', 'Risk Assessment']
    return (

        {!proposal
          ? <Spin size='large' tip='Loading...' />
          :

{uac ? title : `${title} (UAC)`}

            {uac &&
Universal Access Committee
}

For {organization}

{`ID: ${year}-${number}`}

Date: {date}

Endorsements: {comments.length}

            {body &&

                <Row gutter={32}>
                  <Col className='gutter-row' xs={24} md={12}>

Overview

{body.overview.abstract}

                  </Col>
                  <Col className='gutter-row' xs={24} md={12}>

Objectives

{body.overview.objectives}

Core Justification

{body.overview.justification}

                  </Col>
                </Row>

Impact

                <Row gutter={32}>
                  <Col className='gutter-row' xs={24} md={8}>

Academic Experience

{body.overview.impact.academic}

                  </Col>
                  <Col className='gutter-row' xs={24} md={8}>

Research Opportunities

{body.overview.impact.research}

                  </Col>
                  <Col className='gutter-row' xs={24} md={8}>

Career Development

{body.overview.impact.career}

                  </Col>
                </Row>

Project Plan

                <Collapse bordered={false} >
                  {planKeys.map((area, i) => (
                    <Panel header={planTitles[i]} key={i}>

Current

{body.plan[area].current}

Future

{body.plan[area].future}

                    </Panel>
                  ))}
                </Collapse>

            }

        }

    )
  }
}
Vote.propTypes = {
  manifest: PropTypes.object,
  user: PropTypes.object
}
export default Vote
<|endoftext|>