<commit_before>/* eslint-disable import/no-unresolved */
import React, {
  Component,
  PropTypes,
} from 'react';

import {
  NativeModules,
  Platform,
  StyleSheet,
  Text,
  TouchableHighlight,
  View,
} from 'react-native';
/* eslint-enable import/no-unresolved */

class Cell extends Component {
  constructor(props) {
    super(props);
    this.state = {
      fontSizeMultiplier: 1,
    };

    /** Deprecation messages */
    // eslint-disable-next-line
    if (props.cellTextColor) {
      console.warn('`<Cell cellTextColor="..." />` is deprecated. Use `<Cell backgroundColor="..." />` instead.');
    }
    // eslint-disable-next-line
    if (props.titleTintColor) {
      console.warn('`<Cell titleTintColor="..." />` is deprecated. Use `<Cell titleTextColor="..." />` instead.');
    }
    // eslint-disable-next-line
    if (props.cellstyle) {
      console.warn('`<Cell cellstyle="..." />` is deprecated. Use `<Cell cellStyle="..." />` instead.');
    }
  }

  componentWillMount() {
    /**
     * Need AccessibilityManager to access the current font size multiplier
     */
    if (Platform.OS !== 'ios' || !this.props.allowFontScaling) return;
    NativeModules.AccessibilityManager.getMultiplier((value) => {
      this.setState({
        fontSizeMultiplier: value,
      });
    });
  }
  render() {
    const {
      accessory,
      accessoryColor,
      backgroundColor,
      detail,
      highlightActiveOpacity,
      highlightUnderlayColor,
      isDisabled,
      leftDetailColor,
      rightDetailColor,
      title,
      titleTextColor,
    } = this.props;

    let { cellStyle } = this.props;
    /** Deprecation */
    // eslint-disable-next-line
    if (this.props.cellstyle) cellStyle = this.props.cellstyle;

    const isPressable = !!this.props.onPress;

    /* Declare and merge styles with props */
    const styleCell = [
      ...{},
      styles.cell,
      { backgroundColor, height: 44 * this.state.fontSizeMultiplier },
    ];
    const styleCell__subtitle = [
      ...{},
      styles.cell__subtitle,
      { backgroundColor, height: 44 * this.state.fontSizeMultiplier },
    ];

    const styleCell_title = isDisabled
      ? [...{}, styles.cell_title, styles.cell_text__disabled]
      : [...{}, styles.cell_title, { color: titleTextColor }];
    const styleCell_leftDetailTitle = isDisabled ?
      [...{}, styles.cell_leftDetailTitle, styles.cell_text__disabled]
      : [...{}, styles.cell_leftDetailTitle, { color: titleTextColor }];

    /* Apply color prop to accessories */
    const styleAccessory_checkmark = [
      ...{},
      styles.accessory_checkmark,
      { borderColor: accessoryColor },
    ];
    const styleAccessory_detail = [
      ...{},
      styles.accessory_detail,
      { borderColor: accessoryColor },
    ];
    const styleAccessory_detailText = [
      ...{},
      styles.accessory_detailText,
      { color: accessoryColor },
    ];
    const styleCell_leftDetail = [
      ...{},
      styles.cell_leftdetail,
      { color: leftDetailColor },
    ];
    const styleCell_rightDetail = [
      ...{},
      styles.cell_rightdetail,
      { color: rightDetailColor },
    ];

    /**
     * Render accessory
     * Currently available
     * @return {View} View with accessory
     */
    const renderAccessory = () => {
      switch (accessory) {
        case 'DisclosureIndicator':
          return (<View style={styles.accessory_disclosureIndicator} />);
        case 'Detail':
          return (
            <View style={styleAccessory_detail}>
              <Text style={styleAccessory_detailText}>i</Text>
            </View>
          );
        case 'DetailDisclosure':
          return (
            <View style={styles.accessory_detailDisclosure}>
              <View style={styleAccessory_detail}>
                <Text style={styleAccessory_detailText}>i</Text>
              </View>
              <View style={styles.accessory_disclosureIndicator} />
            </View>
          );
        case 'Checkmark':
          return (<View style={styleAccessory_checkmark} />);
        default:
          return null;
      }
    };

    /**
     * Render cell of type Basic
     * @return {View} View with Text and Accessory
     */
    const CellBasic = () => (
      <View style={styleCell}>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={styleCell_title}
        >
          {title}
        </Text>
        {renderAccessory()}
      </View>
    );

   /**
    * Render cell of type RightDetail
    * @return {View} View with Text, Text and Accessory
    */
    const CellRightDetail = () => (
      <View style={styleCell}>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={styleCell_title}
        >
          {title}
        </Text>
        <Text
          numberOfLines={1}
          style={isDisabled ? [...{}, styleCell_rightDetail, styles.cell_text__disabled] : styleCell_rightDetail}
        >
          {detail}
        </Text>
        {renderAccessory()}
      </View>
    );

    /**
    * Render cell of type LeftDetail
    * @return {View} View with Text, Text and Accessory
    */
    const CellLeftDetail = () => (
      <View style={styleCell}>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={isDisabled ? [...{}, styleCell_leftDetail, styles.cell_text__disabled] : styleCell_leftDetail}
        >
          {detail}
        </Text>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={styleCell_leftDetailTitle}
        >
          {title}
        </Text>
        {renderAccessory()}
      </View>
    );

    /**
     * Render cell of type Subtitle
     * @return {View} View with View, Text, Text and Accessory
     */
    const CellSubtitle = () => (
      <View style={styleCell__subtitle}>
        <View style={styles.cellinner__subtitle}>
          <Text
            allowFontScaling={this.props.allowFontScaling}
            numberOfLines={1}
            style={styleCell_title}
          >
            {title}
          </Text>
          <Text
            allowFontScaling={this.props.allowFontScaling}
            numberOfLines={1}
            style={isDisabled ? [...{}, styles.cell_subtitle, styles.cell_text__disabled] : styles.cell_subtitle}
          >
            {detail}
          </Text>
        </View>
        {renderAccessory()}
      </View>
    );

    /**
     * Render cell by type
     * @return {View} Returns the correct function to call
     */
    const renderCell = () => {
      let cellToRender = CellBasic;
      switch (cellStyle) {
        case 'Basic':
          cellToRender = CellBasic;
          break;
        case 'RightDetail':
          cellToRender = CellRightDetail;
          break;
        case 'LeftDetail':
          cellToRender = CellLeftDetail;
          break;
        case 'Subtitle':
          cellToRender = CellSubtitle;
          break;
        default:
          cellToRender = CellBasic;
      }

      if (isPressable && !isDisabled) {
        return (
          <TouchableHighlight
            activeOpacity={highlightActiveOpacity}
            onPress={this.props.onPress}
            underlayColor={highlightUnderlayColor}
          >
            {cellToRender()}
          </TouchableHighlight>
        );
      }
      return (<View>{cellToRender()}</View>);
    };

    return (
      <View>
        {renderCell()}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  cell: {
    justifyContent: 'center',
    paddingLeft: 15,
    paddingRight: 20,
    paddingTop: 10,
    paddingBottom: 10,
    flexDirection: 'row',
    alignItems: 'center',
    height: 44,
  },
  cell__subtitle: {
    height: 44,
    paddingLeft: 15,
    paddingRight: 20,
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 5,
  },
  cellinner__subtitle: {
    flexDirection: 'column',
    flex: 1,
  },
  cell_title: {
    fontSize: 16,
    flex: 1,
  },
  cell_leftDetailTitle: {
    fontSize: 12,
    flex: 1,
  },
  cell_leftdetail: {
    fontSize: 12,
    alignSelf: 'center',
    textAlign: 'right',
    marginRight: 5,
    width: 75,
  },
  cell_rightdetail: {
    fontSize: 16,
    alignSelf: 'center',
    color: '#8E8E93',
  },
  cell_subtitle: {
    fontSize: 11,
  },
  cell_text__disabled: {
    color: 'gray',
  },
  accessory_disclosureIndicator: {
    width: 10,
    height: 10,
    marginLeft: 7,
    backgroundColor: 'transparent',
    borderTopWidth: 1,
    borderRightWidth: 1,
    borderColor: '#c7c7cc',
    transform: [{
      rotate: '45deg',
    }],
  },
  accessory_detail: {
    width: 20,
    height: 20,
    marginLeft: 10,
    backgroundColor: 'transparent',
    borderWidth: 1,
    borderRadius: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  accessory_detailText: {
    fontSize: 15,
    fontWeight: '400',
    fontFamily: 'Georgia',
  },
  accessory_detailDisclosure: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  accessory_checkmark: {
    width: 13,
    height: 6,
    marginLeft: 7,
    backgroundColor: 'transparent',
    borderBottomWidth: 2,
    borderLeftWidth: 2,
    borderColor: '#007AFF',
    transform: [{
      rotate: '-45deg',
    }],
  },
});

Cell.propTypes = {
  accessory: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.string,
  ]),
  accessoryColor: PropTypes.string.isRequired,
  allowFontScaling: PropTypes.bool,
  cellStyle: PropTypes.string,
  backgroundColor: PropTypes.string.isRequired,
  detail: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
  ]),
  highlightActiveOpacity: PropTypes.number,
  highlightUnderlayColor: PropTypes.string,
  isDisabled: PropTypes.bool,
  leftDetailColor: PropTypes.string,
  rightDetailColor: PropTypes.string,
  title: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
  ]),
  titleTextColor: PropTypes.string,
  onPress: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.func,
  ]),
};

Cell.defaultProps = {
  accessory: false,
  accessoryColor: '#007AFF',
  allowFontScaling: true,
  cellStyle: 'Basic',
  backgroundColor: '#fff',
  detail: '',
  highlightActiveOpacity: 0.8,
  highlightUnderlayColor: 'black',
  isDisabled: false,
  leftDetailColor: '#007AFF',
  rightDetailColor: '#8E8E93',
  title: '',
  titleTextColor: '#000',
};

export default Cell;
<commit_msg>remove deprecations<commit_after>/* eslint-disable import/no-unresolved */
import React, {
  Component,
  PropTypes,
} from 'react';

import {
  NativeModules,
  Platform,
  StyleSheet,
  Text,
  TouchableHighlight,
  View,
} from 'react-native';
/* eslint-enable import/no-unresolved */

class Cell extends Component {
  constructor(props) {
    super(props);
    this.state = {
      fontSizeMultiplier: 1,
    };

  }

  componentWillMount() {
    /**
     * Need AccessibilityManager to access the current font size multiplier
     */
    if (Platform.OS !== 'ios' || !this.props.allowFontScaling) return;
    NativeModules.AccessibilityManager.getMultiplier((value) => {
      this.setState({
        fontSizeMultiplier: value,
      });
    });
  }
  render() {
    const {
      accessory,
      accessoryColor,
      backgroundColor,
      detail,
      highlightActiveOpacity,
      highlightUnderlayColor,
      isDisabled,
      leftDetailColor,
      rightDetailColor,
      title,
      titleTextColor,
    } = this.props;

    const { cellStyle } = this.props;

    const isPressable = !!this.props.onPress;

    /* Declare and merge styles with props */
    const styleCell = [
      ...{},
      styles.cell,
      { backgroundColor, height: 44 * this.state.fontSizeMultiplier },
    ];
    const styleCell__subtitle = [
      ...{},
      styles.cell__subtitle,
      { backgroundColor, height: 44 * this.state.fontSizeMultiplier },
    ];

    const styleCell_title = isDisabled
      ? [...{}, styles.cell_title, styles.cell_text__disabled]
      : [...{}, styles.cell_title, { color: titleTextColor }];
    const styleCell_leftDetailTitle = isDisabled ?
      [...{}, styles.cell_leftDetailTitle, styles.cell_text__disabled]
      : [...{}, styles.cell_leftDetailTitle, { color: titleTextColor }];

    /* Apply color prop to accessories */
    const styleAccessory_checkmark = [
      ...{},
      styles.accessory_checkmark,
      { borderColor: accessoryColor },
    ];
    const styleAccessory_detail = [
      ...{},
      styles.accessory_detail,
      { borderColor: accessoryColor },
    ];
    const styleAccessory_detailText = [
      ...{},
      styles.accessory_detailText,
      { color: accessoryColor },
    ];
    const styleCell_leftDetail = [
      ...{},
      styles.cell_leftdetail,
      { color: leftDetailColor },
    ];
    const styleCell_rightDetail = [
      ...{},
      styles.cell_rightdetail,
      { color: rightDetailColor },
    ];

    /**
     * Render accessory
     * Currently available
     * @return {View} View with accessory
     */
    const renderAccessory = () => {
      switch (accessory) {
        case 'DisclosureIndicator':
          return (<View style={styles.accessory_disclosureIndicator} />);
        case 'Detail':
          return (
            <View style={styleAccessory_detail}>
              <Text style={styleAccessory_detailText}>i</Text>
            </View>
          );
        case 'DetailDisclosure':
          return (
            <View style={styles.accessory_detailDisclosure}>
              <View style={styleAccessory_detail}>
                <Text style={styleAccessory_detailText}>i</Text>
              </View>
              <View style={styles.accessory_disclosureIndicator} />
            </View>
          );
        case 'Checkmark':
          return (<View style={styleAccessory_checkmark} />);
        default:
          return null;
      }
    };

    /**
     * Render cell of type Basic
     * @return {View} View with Text and Accessory
     */
    const CellBasic = () => (
      <View style={styleCell}>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={styleCell_title}
        >
          {title}
        </Text>
        {renderAccessory()}
      </View>
    );

   /**
    * Render cell of type RightDetail
    * @return {View} View with Text, Text and Accessory
    */
    const CellRightDetail = () => (
      <View style={styleCell}>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={styleCell_title}
        >
          {title}
        </Text>
        <Text
          numberOfLines={1}
          style={isDisabled ? [...{}, styleCell_rightDetail, styles.cell_text__disabled] : styleCell_rightDetail}
        >
          {detail}
        </Text>
        {renderAccessory()}
      </View>
    );

    /**
    * Render cell of type LeftDetail
    * @return {View} View with Text, Text and Accessory
    */
    const CellLeftDetail = () => (
      <View style={styleCell}>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={isDisabled ? [...{}, styleCell_leftDetail, styles.cell_text__disabled] : styleCell_leftDetail}
        >
          {detail}
        </Text>
        <Text
          allowFontScaling={this.props.allowFontScaling}
          numberOfLines={1}
          style={styleCell_leftDetailTitle}
        >
          {title}
        </Text>
        {renderAccessory()}
      </View>
    );

    /**
     * Render cell of type Subtitle
     * @return {View} View with View, Text, Text and Accessory
     */
    const CellSubtitle = () => (
      <View style={styleCell__subtitle}>
        <View style={styles.cellinner__subtitle}>
          <Text
            allowFontScaling={this.props.allowFontScaling}
            numberOfLines={1}
            style={styleCell_title}
          >
            {title}
          </Text>
          <Text
            allowFontScaling={this.props.allowFontScaling}
            numberOfLines={1}
            style={isDisabled ? [...{}, styles.cell_subtitle, styles.cell_text__disabled] : styles.cell_subtitle}
          >
            {detail}
          </Text>
        </View>
        {renderAccessory()}
      </View>
    );

    /**
     * Render cell by type
     * @return {View} Returns the correct function to call
     */
    const renderCell = () => {
      let cellToRender = CellBasic;
      switch (cellStyle) {
        case 'Basic':
          cellToRender = CellBasic;
          break;
        case 'RightDetail':
          cellToRender = CellRightDetail;
          break;
        case 'LeftDetail':
          cellToRender = CellLeftDetail;
          break;
        case 'Subtitle':
          cellToRender = CellSubtitle;
          break;
        default:
          cellToRender = CellBasic;
      }

      if (isPressable && !isDisabled) {
        return (
          <TouchableHighlight
            activeOpacity={highlightActiveOpacity}
            onPress={this.props.onPress}
            underlayColor={highlightUnderlayColor}
          >
            {cellToRender()}
          </TouchableHighlight>
        );
      }
      return (<View>{cellToRender()}</View>);
    };

    return (
      <View>
        {renderCell()}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  cell: {
    justifyContent: 'center',
    paddingLeft: 15,
    paddingRight: 20,
    paddingTop: 10,
    paddingBottom: 10,
    flexDirection: 'row',
    alignItems: 'center',
    height: 44,
  },
  cell__subtitle: {
    height: 44,
    paddingLeft: 15,
    paddingRight: 20,
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 5,
  },
  cellinner__subtitle: {
    flexDirection: 'column',
    flex: 1,
  },
  cell_title: {
    fontSize: 16,
    flex: 1,
  },
  cell_leftDetailTitle: {
    fontSize: 12,
    flex: 1,
  },
  cell_leftdetail: {
    fontSize: 12,
    alignSelf: 'center',
    textAlign: 'right',
    marginRight: 5,
    width: 75,
  },
  cell_rightdetail: {
    fontSize: 16,
    alignSelf: 'center',
    color: '#8E8E93',
  },
  cell_subtitle: {
    fontSize: 11,
  },
  cell_text__disabled: {
    color: 'gray',
  },
  accessory_disclosureIndicator: {
    width: 10,
    height: 10,
    marginLeft: 7,
    backgroundColor: 'transparent',
    borderTopWidth: 1,
    borderRightWidth: 1,
    borderColor: '#c7c7cc',
    transform: [{
      rotate: '45deg',
    }],
  },
  accessory_detail: {
    width: 20,
    height: 20,
    marginLeft: 10,
    backgroundColor: 'transparent',
    borderWidth: 1,
    borderRadius: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  accessory_detailText: {
    fontSize: 15,
    fontWeight: '400',
    fontFamily: 'Georgia',
  },
  accessory_detailDisclosure: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  accessory_checkmark: {
    width: 13,
    height: 6,
    marginLeft: 7,
    backgroundColor: 'transparent',
    borderBottomWidth: 2,
    borderLeftWidth: 2,
    borderColor: '#007AFF',
    transform: [{
      rotate: '-45deg',
    }],
  },
});

Cell.propTypes = {
  accessory: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.string,
  ]),
  accessoryColor: PropTypes.string.isRequired,
  allowFontScaling: PropTypes.bool,
  cellStyle: PropTypes.string,
  backgroundColor: PropTypes.string.isRequired,
  detail: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
  ]),
  highlightActiveOpacity: PropTypes.number,
  highlightUnderlayColor: PropTypes.string,
  isDisabled: PropTypes.bool,
  leftDetailColor: PropTypes.string,
  rightDetailColor: PropTypes.string,
  title: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
  ]),
  titleTextColor: PropTypes.string,
  onPress: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.func,
  ]),
};

Cell.defaultProps = {
  accessory: false,
  accessoryColor: '#007AFF',
  allowFontScaling: true,
  cellStyle: 'Basic',
  backgroundColor: '#fff',
  detail: '',
  highlightActiveOpacity: 0.8,
  highlightUnderlayColor: 'black',
  isDisabled: false,
  leftDetailColor: '#007AFF',
  rightDetailColor: '#8E8E93',
  title: '',
  titleTextColor: '#000',
};

export default Cell;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var moment = require('moment');

var timeago = require('./timeago')();

function init() {

  var openaps = {
    name: 'openaps'
    , label: 'OpenAPS'
    , pluginType: 'pill-status'
  };

  openaps.setProperties = function setProperties (sbx) {
    sbx.offerProperty('openaps', function setopenaps ( ) {
      var openapsStatus = _.findLast(sbx.data.devicestatus, function (status) {
        return sbx.entryMills(status) <= sbx.time && ('openaps' in status);
      });

      var loopStatus = checkLoopStatus(openapsStatus, sbx);
      openapsStatus = openapsStatus || {};
      openapsStatus.status = loopStatus;

      return openapsStatus;
    });
  };

  openaps.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.openaps;

    var info = [];

    function valueString (prefix, value) {
      return value ? prefix + value : '';
    }

    if ('enacted' === prop.status.code) {
      info.push({label: 'Status', value: 'enacted' + valueString(', reason: ', prop.openaps && prop.openaps.enacted && prop.openaps.enacted.reason)});
    } else if ('looping' === prop.status.code) {
      info.push({label: 'Status', value: 'looping' + valueString(', suggested: ', prop.openaps && prop.openaps.suggested && prop.openaps.suggested.reason)});
    } else if ('waiting' === prop.status.code) {
      info.push({label: 'Status', value: 'waiting'});
    }

    var recent = moment(sbx.time).subtract(15, 'minutes');

    if (prop.pump && prop.pump.clock) {
      var pumpTime = moment(prop.pump.clock);
      if (pumpTime.isAfter(recent)) {
        info.push({label: 'Pump Clock', value: pumpTime.format('LT')});
      }
    }

    if ('enacted' === prop.status.code && prop.openaps && prop.openaps.enacted) {
      var enactedTime = moment(prop.openaps.enacted.timestamp);
      if (prop.openaps.enacted.rate === 0 && prop.openaps.enacted.duration === 0) {
        info.push({label: 'Temp Basal Canceled', value: timeAt(sbx) + timeFormat(enactedTime, sbx)});
      } else {
        info.push({
          label: 'Temp Basal Started'
          , value: prop.openaps.enacted.rate.toFixed(3) +
            ' for ' + prop.openaps.enacted.duration +
            'm ' + timeAt(sbx) + timeFormat(enactedTime, sbx)});
      }
    }

    if (prop.openaps && prop.openaps.iob) {
      var iobTime = moment(prop.openaps.iob.timestamp);
      if (iobTime.isAfter(recent)) {
        info.push({label: 'IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.iob) + 'U ' + timeAt(sbx) + timeFormat(iobTime, sbx)});
        info.push({label: 'Bolus IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.bolusiob) + 'U'});
        info.push({label: 'Insulin Activity', value: prop.openaps.iob.activity.toFixed(2) + ' ' + sbx.unitsLabel + '/5m'});
      }
    }

    sbx.pluginBase.updatePillText(openaps, {
      value: timeFormat(prop.status.when, sbx)
      , label: 'OpenAPS ' + prop.status.symbol
      , info: info
    });
  };

  return openaps;

}

function checkLoopStatus (openapsStatus, sbx)  {

  var status = {
    symbol: '⚠'
    , code: 'warning'
  };

  var enactedLast = openapsStatus && openapsStatus.openaps.enacted && moment(openapsStatus.openaps.enacted.timestamp);
  var suggestedLast = openapsStatus && openapsStatus.openaps.suggested && moment(openapsStatus.openaps.suggested.timestamp);

  var last = openapsStatus && moment(openapsStatus.mills);
  var recent = moment(sbx.time).subtract(15, 'minutes');

  var enactedEnd = enactedLast && enactedLast.add(openapsStatus.openaps.enacted.duration, 'minutes');

  if ((!suggestedLast || enactedLast.isAfter(suggestedLast)) && ((enactedEnd && enactedEnd.isAfter(recent)) || (enactedLast && enactedLast.isAfter(recent)))) {
    status.symbol = '⌁';
    status.code = 'enacted';
  } else if (suggestedLast && suggestedLast.isAfter(recent)) {
    status.symbol = '↻';
    status.code = 'looping';
  } else if (last && last.isAfter(recent)) {
    status.symbol = '◉';
    status.code = 'waiting';
  } else if (last) {
    status.symbol = '⚠';
    status.code = 'warning';
  }

  if (last) {
    status.when = last;
  }

  return status;
}

function timeFormat (m, sbx) {

  var when;
  if (m && sbx.data.inRetroMode) {
    when = m.format('LT');
  } else if (m) {
    var ago = timeago.calcDisplay({mills: m.valueOf()}, sbx.time);
    when = (ago.value ? ago.value : '') + ago.shortLabel + (ago.shortLabel.length === 1 ? ' ago' : '')
  } else {
    when = 'unknown';
  }

  return when
}

function timeAt (sbx) {
  return sbx.data.inRetroMode ? '@ ' : ', ';
}

module.exports = init;<commit_msg>prefix when needed only<commit_after>'use strict';

var _ = require('lodash');
var moment = require('moment');

var timeago = require('./timeago')();

function init() {

  var openaps = {
    name: 'openaps'
    , label: 'OpenAPS'
    , pluginType: 'pill-status'
  };

  openaps.setProperties = function setProperties (sbx) {
    sbx.offerProperty('openaps', function setopenaps ( ) {
      var openapsStatus = _.findLast(sbx.data.devicestatus, function (status) {
        return sbx.entryMills(status) <= sbx.time && ('openaps' in status);
      });

      var loopStatus = checkLoopStatus(openapsStatus, sbx);
      openapsStatus = openapsStatus || {};
      openapsStatus.status = loopStatus;

      return openapsStatus;
    });
  };

  openaps.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.openaps;

    var info = [];

    function valueString (prefix, value) {
      return value ? prefix + value : '';
    }

    if ('enacted' === prop.status.code) {
      info.push({label: 'Status', value: 'enacted' + valueString(', reason: ', prop.openaps && prop.openaps.enacted && prop.openaps.enacted.reason)});
    } else if ('looping' === prop.status.code) {
      info.push({label: 'Status', value: 'looping' + valueString(', suggested: ', prop.openaps && prop.openaps.suggested && prop.openaps.suggested.reason)});
    } else if ('waiting' === prop.status.code) {
      info.push({label: 'Status', value: 'waiting'});
    }

    var recent = moment(sbx.time).subtract(15, 'minutes');

    if (prop.pump && prop.pump.clock) {
      var pumpTime = moment(prop.pump.clock);
      if (pumpTime.isAfter(recent)) {
        info.push({label: 'Pump Clock', value: pumpTime.format('LT')});
      }
    }

    if ('enacted' === prop.status.code && prop.openaps && prop.openaps.enacted) {
      var enactedTime = moment(prop.openaps.enacted.timestamp);
      if (prop.openaps.enacted.rate === 0 && prop.openaps.enacted.duration === 0) {
        info.push({label: 'Temp Basal Canceled', value: timeAt(false, sbx) + timeFormat(enactedTime, sbx)});
      } else {
        info.push({
          label: 'Temp Basal Started'
          , value: prop.openaps.enacted.rate.toFixed(3) +
            ' for ' + prop.openaps.enacted.duration +
            'm' + timeAt(true, sbx) + timeFormat(enactedTime, sbx)});
      }
    }

    if (prop.openaps && prop.openaps.iob) {
      var iobTime = moment(prop.openaps.iob.timestamp);
      if (iobTime.isAfter(recent)) {
        info.push({label: 'IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.iob) + 'U' + timeAt(true, sbx) + timeFormat(iobTime, sbx)});
        info.push({label: 'Bolus IOB', value: sbx.roundInsulinForDisplayFormat(prop.openaps.iob.bolusiob) + 'U'});
        info.push({label: 'Insulin Activity', value: prop.openaps.iob.activity.toFixed(2) + ' ' + sbx.unitsLabel + '/5m'});
      }
    }

    sbx.pluginBase.updatePillText(openaps, {
      value: timeFormat(prop.status.when, sbx)
      , label: 'OpenAPS ' + prop.status.symbol
      , info: info
    });
  };

  return openaps;

}

function checkLoopStatus (openapsStatus, sbx)  {

  var status = {
    symbol: '⚠'
    , code: 'warning'
  };

  var enactedLast = openapsStatus && openapsStatus.openaps.enacted && moment(openapsStatus.openaps.enacted.timestamp);
  var suggestedLast = openapsStatus && openapsStatus.openaps.suggested && moment(openapsStatus.openaps.suggested.timestamp);

  var last = openapsStatus && moment(openapsStatus.mills);
  var recent = moment(sbx.time).subtract(15, 'minutes');

  var enactedEnd = enactedLast && enactedLast.add(openapsStatus.openaps.enacted.duration, 'minutes');

  if ((!suggestedLast || enactedLast.isAfter(suggestedLast)) && ((enactedEnd && enactedEnd.isAfter(recent)) || (enactedLast && enactedLast.isAfter(recent)))) {
    status.symbol = '⌁';
    status.code = 'enacted';
  } else if (suggestedLast && suggestedLast.isAfter(recent)) {
    status.symbol = '↻';
    status.code = 'looping';
  } else if (last && last.isAfter(recent)) {
    status.symbol = '◉';
    status.code = 'waiting';
  } else if (last) {
    status.symbol = '⚠';
    status.code = 'warning';
  }

  if (last) {
    status.when = last;
  }

  return status;
}

function timeFormat (m, sbx) {

  var when;
  if (m && sbx.data.inRetroMode) {
    when = m.format('LT');
  } else if (m) {
    var ago = timeago.calcDisplay({mills: m.valueOf()}, sbx.time);
    when = (ago.value ? ago.value : '') + ago.shortLabel + (ago.shortLabel.length === 1 ? ' ago' : '')
  } else {
    when = 'unknown';
  }

  return when
}

function timeAt (prefix, sbx) {
  return sbx.data.inRetroMode ? (prefix ? ' ' : '') + '@ ' : (prefix ? ', ' : '');
}

module.exports = init;<|endoftext|>


<commit_before>// deps
var app = require('express.io')();
var cors = require('cors');
var mongo = require(__dirname + '/lib/database');
var config = require(__dirname + '/_config_');

var bodyParser = require('body-parser');

// request handlers
var log = require(__dirname + '/routes/log');
var tweets = require(__dirname + '/routes/tweets');
var stats = require(__dirname + '/routes/stats');
var email = require(__dirname + '/routes/email');
var websites = require(__dirname + '/routes/websites');

// io event broadcasters
var logBroadcaster = require(__dirname + '/routes/broadcast/logs');

//------------------------------------------------------------------------------

app.use(cors());
app.use(bodyParser.json())
app.http().io();

mongo.get().then(function(db) {
	//--------------------------------------------------------------------------
	// standard routes

	// index has some links
    app.get('/', function(req, res) {
        res.send('
StopTheSpies API server
 \
        		https://stopthespies.org \
        		https://github.com/stopthespies/');
    });

    app.get('/battleforthenet', function(req, response) {
	    db.collection('tweets').find({}, {sort : [["user.followers_count", 'desc']], limit : 200}, function(err, res) {
			if (err) {
				callback(err);
				return;
			}
			res.toArray(function(err, docs) {
				if (err) {
					callback(err);
					return;
				}
				var tweets = docs;
		        /*var tweets = docs.map(function(tweet){
		        	return {
						tweet: tweet.text,
						handle: tweet.user.screen_name,
						avatar: tweet.user.profile_image_url,
						link: 'https://twitter.com/#!/' + tweet.user.id + '/status/' + tweet._id + '/',
						retweet_link: 'https://twitter.com/intent/retweet?tweet_id=' + tweet._id,
						followers: tweet.user.followers_count
		        	}
		        });*/

		        response.send(tweets);
			});
		});
    })

	// emails are sent via POST
    app.post('/email', function(req, res) {
    	email.call(app, req, res);
	});

	// expose some JSON endpoints as APIs
    app.get('/stats', function(req, res) {
    	req.io.route('stats');
	});
    app.get('/tweets', function(req, res) {
    	req.io.route('tweets');
	});

    app.post('/websites', function(req, res) {
    	req.io.route('websites');
	});

	//--------------------------------------------------------------------------
	// socket events

	// read global stats
	app.io.route('stats', function(req) {
		console.log('read stats');
    	stats.call(app, req);
	});

	// read tweets
	app.io.route('tweets', function(req) {
		console.log('read tweets');
    	tweets.call(app, req);
	});

    // log events
    app.io.route('log', function(req) {
    	log.call(app, req);
	});

    // website events
    app.io.route('websites', function(req) {
    	websites.call(app, req);
	});

	//--------------------------------------------------------------------------
	// broadcast daemons

	logBroadcaster(app);

	//--------------------------------------------------------------------------
	// init server

    var port = config.server_port || 5000;
    app.listen(port, function() {
        console.log('Listening on ' + port);
    });
}, function(err) {
	throw err;
});

<commit_msg>fixed links<commit_after>// deps
var app = require('express.io')();
var cors = require('cors');
var mongo = require(__dirname + '/lib/database');
var config = require(__dirname + '/_config_');

var bodyParser = require('body-parser');

// request handlers
var log = require(__dirname + '/routes/log');
var tweets = require(__dirname + '/routes/tweets');
var stats = require(__dirname + '/routes/stats');
var email = require(__dirname + '/routes/email');
var websites = require(__dirname + '/routes/websites');

// io event broadcasters
var logBroadcaster = require(__dirname + '/routes/broadcast/logs');

//------------------------------------------------------------------------------

app.use(cors());
app.use(bodyParser.json())
app.http().io();

mongo.get().then(function(db) {
	//--------------------------------------------------------------------------
	// standard routes

	// index has some links
    app.get('/', function(req, res) {
        res.send('
StopTheSpies API server
 \
        		https://stopthespies.org \
        		https://github.com/stopthespies/');
    });

    app.get('/battleforthenet', function(req, response) {
	    db.collection('tweets').find({}, {sort : [["user.followers_count", 'desc']], limit : 200}, function(err, res) {
			if (err) {
				callback(err);
				return;
			}
			res.toArray(function(err, docs) {
				if (err) {
					callback(err);
					return;
				}
				var tweets = docs;
		        var tweets = docs.map(function(tweet){
		        	return {
						tweet: tweet.text,
						handle: tweet.user.screen_name,
						avatar: tweet.user.profile_image_url,
						link: 'https://twitter.com/#!/' + tweet.user.screen_name + '/status/' + (tweet.id_str || 'broken') + '/',
						retweet_link: 'https://twitter.com/intent/retweet?tweet_id=' + (tweet.id_str|| 'broken'),
						followers: tweet.user.followers_count
		        	}
		        });

		        response.send(tweets);
			});
		});
    })

	// emails are sent via POST
    app.post('/email', function(req, res) {
    	email.call(app, req, res);
	});

	// expose some JSON endpoints as APIs
    app.get('/stats', function(req, res) {
    	req.io.route('stats');
	});
    app.get('/tweets', function(req, res) {
    	req.io.route('tweets');
	});

    app.post('/websites', function(req, res) {
    	req.io.route('websites');
	});

	//--------------------------------------------------------------------------
	// socket events

	// read global stats
	app.io.route('stats', function(req) {
		console.log('read stats');
    	stats.call(app, req);
	});

	// read tweets
	app.io.route('tweets', function(req) {
		console.log('read tweets');
    	tweets.call(app, req);
	});

    // log events
    app.io.route('log', function(req) {
    	log.call(app, req);
	});

    // website events
    app.io.route('websites', function(req) {
    	websites.call(app, req);
	});

	//--------------------------------------------------------------------------
	// broadcast daemons

	logBroadcaster(app);

	//--------------------------------------------------------------------------
	// init server

    var port = config.server_port || 5000;
    app.listen(port, function() {
        console.log('Listening on ' + port);
    });
}, function(err) {
	throw err;
});

<|endoftext|>


<commit_before>
        //dep code
var   views = require('atom-space-pen-views')
    , SelectListView = views.SelectListView
        //lib code
    , extend = require('./utils/extend')
    , state = require('./lime-state')

function LimeStatus(_lime) {

    LimeStatus.__super__.constructor.apply(this, arguments);

    this.lime = _lime;
    this.status_items = [
        { tag:'lime',
            text:'lime file',
            desc:'none set'
        },
        { tag:'target',
            text:'lime target',
            desc:state.target },
        { tag:'debug',
            text:'Toggle debug build',
            desc:'currently: '+ state.flags.debug
        },
        { tag:'verbose',
            text:'Toggle verbose build',
            desc:'currently: '+ state.flags.verbose
        },
        { tag:'build-only',
            text:'Toggle build only',
            desc:'currently: '+ state.flags.build_only
        },
        { tag:'launch-only',
            text:'Toggle launch only',
            desc:'currently: '+ state.flags.launch_only
        },
        { tag:'use-simulator',
            text:'Toggle use simulator',
            desc:'currently: '+ state.flags.use_simulator
        }
    ];

    this.all_targets = {
        "mac": { target:'mac', text:'Mac', desc:'desktop, native mac app' },
        "linux": { target:'linux', text:'Linux', desc:'desktop, native linux app' },
        "windows": { target:'windows', text:'Windows', desc:'desktop, native windows app' },
        "neko": { target:'neko', text:'Neko', desc:'desktop, neko app' },
        "android": { target:'android', text:'Android', desc:'mobile, native android app' },
        "ios": { target:'ios', text:'iOS', desc:'mobile, native ios project' },
        "html5": { target:'html5', text:'HTML5', desc:'web, html5 based app' },
        "flash": { target:'flash', text:'Flash', desc:'flash app' },
        "blackberry": { target:'blackberry', text:'BlackBerry', desc:'mobile, blackberry app' },
        "tizen": { target:'tizen', text:'Tizen', desc:'tizen app' },
        "emscripten": { target:'emscripten', text:'Emscripten', desc:'emscripten app' },
        "webos": { target:'webos', text:'WebOS', desc:'webos app' }
    }

    this.unavailable_targets = {
        'windows':'mac, ios, linux',
        'linux':'mac, ios, windows',
        'mac':'windows, linux'
    }

    this.available_targets = {
        'windows': ['windows','html5','android', 'flash', 'neko', 'blackberry', 'tizen', 'emscripten', 'webos'],
        'mac': ['mac','html5','android','ios', 'flash', 'neko', 'blackberry', 'tizen', 'emscripten', 'webos'],
        'linux': ['linux','html5','android','ios', 'flash', 'neko', 'blackberry', 'tizen', 'emscripten', 'webos'],
    }

    this.targets = [
        { target:'unavailable', text:'unavailable from ' + state.system, desc:this.unavailable_targets[state.system] }
    ];

    var list = this.available_targets[state.system];
    for(var i = 0; i < list.length; ++i) {
        var t = list[i];
        this.targets.push( this.all_targets[t] );
    }

    this.menu_map = {};
    this.target_map = {};
    for(var i = 0; i < this.status_items.length; ++i) {
        this.menu_map[this.status_items[i].tag] = i;
    }

    for(var i = 0; i < this.targets.length; ++i) {
        this.target_map[this.targets[i].text.toLowerCase()] = i;
    }

}

extend(LimeStatus, SelectListView);

LimeStatus.prototype.update_flag = function(tag, state) {
    var index = this.menu_map[tag];
    var flag = this.status_items[index];
    flag.desc = 'currently: ' + state;
}

LimeStatus.prototype.update_lime_file = function(filename) {
    var index = this.menu_map['lime'];
    var item = this.status_items[index];
    item.desc = filename;
}

LimeStatus.prototype.update_target = function(targetname) {
    var index = this.menu_map['target'];
    var item = this.status_items[index];
    item.desc = targetname;
}

LimeStatus.prototype.show = function(show_targets) {

    this.panel = atom.workspace.addModalPanel({item: this});
    this.panel.show();
    this.storeFocusedElement();

    if(!state.valid) {
        atom.notifications.addWarning('lime / no lime file active');
    } else {
        if(show_targets) {
            this.showing_targets = true;
            this.setItems(this.targets);
        } else {
            this.showing_targets = false;
            this.setItems(this.status_items);
        }
    }

    this.focusFilterEditor();

}

LimeStatus.prototype.cancelled = function() {
    this.hide();
}

LimeStatus.prototype.hide = function() {
    this.panel.hide();
}

LimeStatus.prototype.viewForItem = function(item) {

     var res = '
';
         res += '
'+item.text+'
';
         res += '
'+item.desc+'
';
         res += '
';

     return res;
}

LimeStatus.prototype.confirmed = function(item) {

    this.cancel();

    if(this.showing_targets) {

        if(item.target != 'unavailable') {
            this.lime.set_target( item.target );
        }

    } else {

        switch(item.tag) {
            case 'lime':
                atom.open({pathsToOpen:[ item.desc ]});
            break;
            case 'target':
                this.show(true);
            break;
            case 'debug':
                state.flags.debug = !state.flags.debug;
                this.update_flag(item.tag, state.flags.debug);
            break;
            case 'verbose':
                state.flags.verbose = !state.flags.verbose;
                this.update_flag(item.tag, state.flags.verbose);
            break;
            case 'build-only':
                state.flags.build_only = !state.flags.build_only;
                if(state.flags.build_only) state.flags.launch_only = false;
                this.update_flag(item.tag, state.flags.build_only);
            break;
            case 'launch-only':
                state.flags.launch_only = !state.flags.launch_only;
                if(state.flags.launch_only) state.flags.build_only = false;
                this.update_flag(item.tag, state.flags.launch_only);
            break;
            case 'use-simulator':
                state.flags.use_simulator = !state.flags.use_simulator;
                this.update_flag(item.tag, state.flags.use_simulator);
            break;
        }
    }

}

module.exports = LimeStatus;
<commit_msg>Fixed search in SelectListView<commit_after>
        //dep code
var   views = require('atom-space-pen-views')
    , SelectListView = views.SelectListView
        //lib code
    , extend = require('./utils/extend')
    , state = require('./lime-state')

function LimeStatus(_lime) {

    LimeStatus.__super__.constructor.apply(this, arguments);

    this.lime = _lime;
    this.status_items = [
        { tag:'lime',
            text:'lime file',
            desc:'none set'
        },
        { tag:'target',
            text:'lime target',
            desc:state.target },
        { tag:'debug',
            text:'Toggle debug build',
            desc:'currently: '+ state.flags.debug
        },
        { tag:'verbose',
            text:'Toggle verbose build',
            desc:'currently: '+ state.flags.verbose
        },
        { tag:'build-only',
            text:'Toggle build only',
            desc:'currently: '+ state.flags.build_only
        },
        { tag:'launch-only',
            text:'Toggle launch only',
            desc:'currently: '+ state.flags.launch_only
        },
        { tag:'use-simulator',
            text:'Toggle use simulator',
            desc:'currently: '+ state.flags.use_simulator
        }
    ];

    this.all_targets = {
        "mac": { target:'mac', text:'Mac', desc:'desktop, native mac app' },
        "linux": { target:'linux', text:'Linux', desc:'desktop, native linux app' },
        "windows": { target:'windows', text:'Windows', desc:'desktop, native windows app' },
        "neko": { target:'neko', text:'Neko', desc:'desktop, neko app' },
        "android": { target:'android', text:'Android', desc:'mobile, native android app' },
        "ios": { target:'ios', text:'iOS', desc:'mobile, native ios project' },
        "html5": { target:'html5', text:'HTML5', desc:'web, html5 based app' },
        "flash": { target:'flash', text:'Flash', desc:'flash app' },
        "blackberry": { target:'blackberry', text:'BlackBerry', desc:'mobile, blackberry app' },
        "tizen": { target:'tizen', text:'Tizen', desc:'tizen app' },
        "emscripten": { target:'emscripten', text:'Emscripten', desc:'emscripten app' },
        "webos": { target:'webos', text:'WebOS', desc:'webos app' }
    }

    this.unavailable_targets = {
        'windows':'mac, ios, linux',
        'linux':'mac, ios, windows',
        'mac':'windows, linux'
    }

    this.available_targets = {
        'windows': ['windows','html5','android', 'flash', 'neko', 'blackberry', 'tizen', 'emscripten', 'webos'],
        'mac': ['mac','html5','android','ios', 'flash', 'neko', 'blackberry', 'tizen', 'emscripten', 'webos'],
        'linux': ['linux','html5','android','ios', 'flash', 'neko', 'blackberry', 'tizen', 'emscripten', 'webos'],
    }

    this.targets = [
        { target:'unavailable', text:'unavailable from ' + state.system, desc:this.unavailable_targets[state.system] }
    ];

    var list = this.available_targets[state.system];
    for(var i = 0; i < list.length; ++i) {
        var t = list[i];
        this.targets.push( this.all_targets[t] );
    }

    this.menu_map = {};
    this.target_map = {};
    for(var i = 0; i < this.status_items.length; ++i) {
        this.menu_map[this.status_items[i].tag] = i;
    }

    for(var i = 0; i < this.targets.length; ++i) {
        this.target_map[this.targets[i].text.toLowerCase()] = i;
    }

}

extend(LimeStatus, SelectListView);

LimeStatus.prototype.update_flag = function(tag, state) {
    var index = this.menu_map[tag];
    var flag = this.status_items[index];
    flag.desc = 'currently: ' + state;
}

LimeStatus.prototype.update_lime_file = function(filename) {
    var index = this.menu_map['lime'];
    var item = this.status_items[index];
    item.desc = filename;
}

LimeStatus.prototype.update_target = function(targetname) {
    var index = this.menu_map['target'];
    var item = this.status_items[index];
    item.desc = targetname;
}

LimeStatus.prototype.show = function(show_targets) {

    this.panel = atom.workspace.addModalPanel({item: this});
    this.panel.show();
    this.storeFocusedElement();

    if(!state.valid) {
        atom.notifications.addWarning('lime / no lime file active');
    } else {
        if(show_targets) {
            this.showing_targets = true;
            this.setItems(this.targets);
        } else {
            this.showing_targets = false;
            this.setItems(this.status_items);
        }
    }

    this.focusFilterEditor();

}

LimeStatus.prototype.cancelled = function() {
    this.hide();
}

LimeStatus.prototype.hide = function() {
    this.panel.hide();
}

LimeStatus.prototype.viewForItem = function(item) {

     var res = '
';
         res += '
'+item.text+'
';
         res += '
'+item.desc+'
';
         res += '
';

     return res;
}

LimeStatus.prototype.confirmed = function(item) {

    this.cancel();

    if(this.showing_targets) {

        if(item.target != 'unavailable') {
            this.lime.set_target( item.target );
        }

    } else {

        switch(item.tag) {
            case 'lime':
                atom.open({pathsToOpen:[ item.desc ]});
            break;
            case 'target':
                this.show(true);
            break;
            case 'debug':
                state.flags.debug = !state.flags.debug;
                this.update_flag(item.tag, state.flags.debug);
            break;
            case 'verbose':
                state.flags.verbose = !state.flags.verbose;
                this.update_flag(item.tag, state.flags.verbose);
            break;
            case 'build-only':
                state.flags.build_only = !state.flags.build_only;
                if(state.flags.build_only) state.flags.launch_only = false;
                this.update_flag(item.tag, state.flags.build_only);
            break;
            case 'launch-only':
                state.flags.launch_only = !state.flags.launch_only;
                if(state.flags.launch_only) state.flags.build_only = false;
                this.update_flag(item.tag, state.flags.launch_only);
            break;
            case 'use-simulator':
                state.flags.use_simulator = !state.flags.use_simulator;
                this.update_flag(item.tag, state.flags.use_simulator);
            break;
        }
    }
}

LimeStatus.prototype.getFilterKey = function() {
   return "text";
}

module.exports = LimeStatus;
<|endoftext|>


<commit_before>require('normalize.css/normalize.css');
require('styles/App.scss');

import React from 'react';
import ReactDOM from 'react-dom';

//得到圖片資訊
let imageDatas = require('json!../data/imageDatas.json');

// Immediately Invoked Function Expressions (IIFE)，自執行函數
imageDatas = ((imageDatasArr) => {
    for(var i = 0; i < imageDatasArr.length; i++) {
        let singleImageData = imageDatasArr[i];
        singleImageData.imageURL = require('../images/' + singleImageData.fileName);
        imageDatasArr[i].singleImageData;
    }

    return imageDatasArr;
})(imageDatas);

var getRangeRandom = (low, high) => Math.floor(Math.random() * (high - low) + low);

// 得到0~30度之間的任意正負值
var get30DegRandom = () => {
    let deg = '';
    deg = (Math.random() > 0.5) ? '' : '-';
    return deg + Math.ceil(Math.random() * 30);
};

//  圖片本身component
class ImgFigure extends React.Component{
    constructor (props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }

    //  imgFigure的點擊處理函數
    handleClick(e) {

        if (this.props.arrange.isCenter) {
            this.props.inverse();
        } else {
            this.props.center();
        }

        e.stopPropagation();
        e.preventDefault();
    }

    render () {

        var styleObj = {};

        //  如果props屬性中指定這張照片的位置
        if (this.props.arrange.pos) {
            styleObj = this.props.arrange.pos;
        }

        //  如果圖片旋轉角度不為0
        if (this.props.arrange.rotate) {
            (['Moz', 'Ms', 'Webkit', '']).forEach((value) => {
                styleObj[value + 'Transform'] = 'rotate(' + this.props.arrange.rotate + 'deg)';
            })
        }

        if (this.props.arrange.isCenter) {
            styleObj.zIndex = 50;
        }

        let imgFigureClassName = 'img-figure';
        imgFigureClassName += this.props.arrange.isInverse ? ' is-inverse' : '';

        return (

                        {this.props.data.title}

                            {this.props.data.desc}

        )
    }
}

class ControllerUnit extends React.Component {

    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick (e) {
        if (this.props.arrange.isCenter) {
            this.props.inverse();
        } else {
            this.props.center();
        }

        e.stopPropagation();
        e.preventDefault();
    }

    render () {
        let controllerUnitClassName = 'controller-unit';

        if (this.props.arrange.isCenter) {
            controllerUnitClassName += ' is-center';

            if (this.props.arrange.isInverse) {
                controllerUnitClassName += ' is-inverse'
            }
        }

        return (

        )
    }
}

//  大框架component
class GalleryByReactApp extends React.Component {

    constructor (props) {
        super(props);
        this.Constant = {
            centerPos: {
                left: 0,
                right: 0
            },
            hPosRange: { //水平方向取直範圍
                leftSecX: [0, 0],
                rightSecX: [0, 0],
                y: [0, 0]
            },
            vPosRange: { //垂直方向取直範圍
                x: [0, 0],
                topY: [0, 0]
            }
        };

        this.state = {
            // 存圖片的狀態訊息
            imgsArrangeArr: [
                // pos: {
                //     left: 0,
                //     top: 0
                // },
                // rotate: 0 ,  // 旋轉角度
                // isInverse: false, //  圖片正反面
                // isCenter: false //   圖片是否在中間
            ]
        };
    }

    //  旋轉圖片
    //  index是目前要旋轉的圖片的index值
    inverse (index) {
        return () => {
            let imgsArrangeArr = this.state.imgsArrangeArr;
            imgsArrangeArr[index].isInverse = !imgsArrangeArr[index].isInverse;
            this.setState({
                imgsArrangeArr: imgsArrangeArr
            })
        }
    }

    //  居中照片
    center (index) {
        return () => {
            this.rearrange(index);
        }
    }

    //  重新佈局所有圖片
    //  centerImage = 指定哪張圖要居中
    rearrange (centerIndex) {
        let imgsArrangeArr = this.state.imgsArrangeArr, //存所有圖片的狀態訊息
            Constant = this.Constant,
            centerPos = Constant.centerPos,
            hPosRange = Constant.hPosRange,
            vPosRange = Constant.vPosRange,
            hPosRangeLeftSecX = hPosRange.leftSecX,
            hPosRangeRightSecX = hPosRange.rightSecX,
            hPosRangeY = hPosRange.y,
            vPosRangeTopY = vPosRange.topY,
            vPosRangeX = vPosRange.x,

            imgsArrangeTopArr = [],
            topImgNum = Math.floor(Math.random() * 2), //取一個或不取
            topImgSpliceIndex = 0,

            // 從imgsArrangeArr中把centerIndex拿掉
            imgsArrangeCenterArr = imgsArrangeArr.splice(centerIndex, 1);

            //  首先居中centerIndex這張圖片，centerIndex不用旋轉
            imgsArrangeCenterArr[0] = {
                pos: centerPos,
                rotate: 0,
                isCenter: true
            };

        // 居中的圖片不用旋轉
        imgsArrangeCenterArr[0].rotate = 0;

        //  語出要放在上冊的圖片的狀態訊息
        topImgSpliceIndex = Math.floor(Math.random() * (imgsArrangeArr.length - topImgNum));
        imgsArrangeTopArr = imgsArrangeArr.splice(topImgSpliceIndex, topImgNum); // 從imgsArrangeArr把topImgSpliceIndex拿掉

        //  佈局位於上側的圖片
        imgsArrangeTopArr.forEach((value, index) => {
            imgsArrangeTopArr[index] = {
                pos: {
                    top: getRangeRandom(vPosRangeTopY[0], vPosRangeTopY[1]),
                    left: getRangeRandom(vPosRangeX[0], vPosRangeX[1])
                },
                rotate: get30DegRandom(),
                isCenter: false
            }
        });

        //  佈局位於兩側的圖片
        for (let i = 0, j = imgsArrangeArr.length, k = j / 2; i < j; i++) {
            let hPosRangeLORX = null;

            //  前半部分分佈在左邊，後半部分分佈在右邊
            if (i < k) {
                hPosRangeLORX = hPosRangeLeftSecX;
            } else {
                hPosRangeLORX = hPosRangeRightSecX;
            }

            imgsArrangeArr[i] = {
                pos: {
                    top: getRangeRandom(hPosRangeY[0], hPosRangeY[1]),
                    left: getRangeRandom(hPosRangeLORX[0], hPosRangeLORX[1])
                },
                rotate: get30DegRandom(),
                isCenter: false
            }
        }

        if(imgsArrangeTopArr && imgsArrangeArr[0]) {
            imgsArrangeArr.splice(topImgSpliceIndex, 0, imgsArrangeTopArr[0]); // 把上方的那張圖塞回imgsArrangeArr
        }

        imgsArrangeArr.splice(centerIndex, 0, imgsArrangeCenterArr[0]); // 把中間的圖也塞回imgsArrangeArr

        this.setState({
            imgsArrangeArr: imgsArrangeArr
        });

    }

    //  component 載入後計算每張圖片的位置
    componentDidMount () {

        //  拿到stage的大小
        let stageDOM = ReactDOM.findDOMNode(this.refs.stage),
            stageW = stageDOM.scrollWidth,
            stageH = stageDOM.scrollHeight,
            halfStageW = Math.ceil(stageW / 2),
            halfStageH = Math.ceil(stageH / 2);

        //  拿到一個imgFigure的大小
        let imgFigureDOM = ReactDOM.findDOMNode(this.refs.imgFigure0),
            imgW = imgFigureDOM.scrollWidth,
            imgH = imgFigureDOM.scrollHeight,
            halfImgW = Math.ceil(imgW / 2),
            halfImgH = Math.ceil(imgH / 2);

        //  計算中心點位置
        this.Constant.centerPos = {
            left: halfStageW - halfImgW,
            top: halfStageH - halfImgH
        };

        //計算左側、右側區域圖片散佈的取值範圍
        this.Constant.hPosRange.leftSecX[0] = -halfImgW;
        this.Constant.hPosRange.leftSecX[1] = halfStageW - halfImgW * 3;

        this.Constant.hPosRange.rightSecX[0] = halfStageW + halfImgW;
        this.Constant.hPosRange.rightSecX[1] = stageW - halfImgW;

        this.Constant.hPosRange.y[0] = -halfImgH;
        this.Constant.hPosRange.y[1] = stageH - halfImgH;

        //計算上側區域圖片散佈的取值範圍
        this.Constant.vPosRange.topY[0] = -halfImgH;
        this.Constant.vPosRange.topY[1] = halfStageH - halfImgH * 3;

        this.Constant.vPosRange.x[0] = halfStageW - imgW;
        this.Constant.vPosRange.x[1] = halfStageW;

        let num = Math.floor(Math.random() * 13);
        this.rearrange(num);
    }

    render() {

        var controllerUnits = [], imgFigures = [];

        imageDatas.forEach((value, index) => {

            //  把每張圖先定在左上角，初始化
            if(!this.state.imgsArrangeArr[index]) {
                this.state.imgsArrangeArr[index] = {
                    pos: {
                        left: 0,
                        top: 0
                    },
                    rotate: 0,
                    isInverse: false,
                    isCenter: false
                }
            }

            //產生DOM節點，放進array裡
            imgFigures.push(
                <ImgFigure
                    data={value}
                    key={index}
                    ref={'imgFigure' + index}
                    arrange={this.state.imgsArrangeArr[index]}
                    inverse={this.inverse(index)}
                    center={this.center(index)}
                />
            );
            controllerUnits.push(
                <ControllerUnit
                    key={index}
                    arrange={this.state.imgsArrangeArr[index]}
                    inverse={this.inverse(index)}
                    center={this.center(index)}
                />
            );
        });

        return (

                    {imgFigures}

                    {controllerUnits}

        );
  }

}

GalleryByReactApp.defaultProps = {
};

export default GalleryByReactApp;
<commit_msg>complete all things<commit_after>require('normalize.css/normalize.css');
require('styles/App.scss');

import React from 'react';
import ReactDOM from 'react-dom';

//得到圖片資訊
let imageDatas = require('json!../data/imageDatas.json');

// Immediately Invoked Function Expressions (IIFE)，自執行函數
imageDatas = ((imageDatasArr) => {
    for(var i = 0; i < imageDatasArr.length; i++) {
        let singleImageData = imageDatasArr[i];
        singleImageData.imageURL = require('../images/' + singleImageData.fileName);
        imageDatasArr[i].singleImageData;
    }

    return imageDatasArr;
})(imageDatas);

var getRangeRandom = (low, high) => Math.floor(Math.random() * (high - low) + low);

// 得到0~30度之間的任意正負值
var get30DegRandom = () => {
    let deg = '';
    deg = (Math.random() > 0.5) ? '' : '-';
    return deg + Math.ceil(Math.random() * 30);
};

//  圖片本身component
class ImgFigure extends React.Component{
    constructor (props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }

    //  imgFigure的點擊處理函數
    handleClick(e) {

        if (this.props.arrange.isCenter) {
            this.props.inverse();
        } else {
            this.props.center();
        }

        e.stopPropagation();
        e.preventDefault();
    }

    render () {

        var styleObj = {};

        //  如果props屬性中指定這張照片的位置
        if (this.props.arrange.pos) {
            styleObj = this.props.arrange.pos;
        }

        //  如果圖片旋轉角度不為0
        if (this.props.arrange.rotate) {
            (['Moz', 'ms', 'Webkit', '']).forEach((value) => {
                styleObj[value + 'Transform'] = 'rotate(' + this.props.arrange.rotate + 'deg)';
            })
        }

        if (this.props.arrange.isCenter) {
            styleObj.zIndex = 50;
        }

        let imgFigureClassName = 'img-figure';
        imgFigureClassName += this.props.arrange.isInverse ? ' is-inverse' : '';

        return (

                        {this.props.data.title}

                            {this.props.data.desc}

        )
    }
}

class ControllerUnit extends React.Component {

    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick (e) {
        if (this.props.arrange.isCenter) {
            this.props.inverse();
        } else {
            this.props.center();
        }

        e.stopPropagation();
        e.preventDefault();
    }

    render () {
        let controllerUnitClassName = 'controller-unit';

        if (this.props.arrange.isCenter) {
            controllerUnitClassName += ' is-center';

            if (this.props.arrange.isInverse) {
                controllerUnitClassName += ' is-inverse';
            }
        }

        return (

        )
    }
}

//  大框架component
class GalleryByReactApp extends React.Component {

    constructor (props) {
        super(props);
        this.Constant = {
            centerPos: {
                left: 0,
                right: 0
            },
            hPosRange: { //水平方向取直範圍
                leftSecX: [0, 0],
                rightSecX: [0, 0],
                y: [0, 0]
            },
            vPosRange: { //垂直方向取直範圍
                x: [0, 0],
                topY: [0, 0]
            }
        };

        this.state = {
            // 存圖片的狀態訊息
            imgsArrangeArr: [
                // pos: {
                //     left: 0,
                //     top: 0
                // },
                // rotate: 0 ,  // 旋轉角度
                // isInverse: false, //  圖片正反面
                // isCenter: false //   圖片是否在中間
            ]
        };
    }

    //  旋轉圖片
    //  index是目前要旋轉的圖片的index值
    inverse (index) {
        return () => {
            let imgsArrangeArr = this.state.imgsArrangeArr;
            imgsArrangeArr[index].isInverse = !imgsArrangeArr[index].isInverse;
            this.setState({
                imgsArrangeArr: imgsArrangeArr
            })
        }
    }

    //  居中照片
    center (index) {
        return () => {
            this.rearrange(index);
        }
    }

    //  重新佈局所有圖片
    //  centerImage = 指定哪張圖要居中
    rearrange (centerIndex) {
        let imgsArrangeArr = this.state.imgsArrangeArr, //存所有圖片的狀態訊息
            Constant = this.Constant,
            centerPos = Constant.centerPos,
            hPosRange = Constant.hPosRange,
            vPosRange = Constant.vPosRange,
            hPosRangeLeftSecX = hPosRange.leftSecX,
            hPosRangeRightSecX = hPosRange.rightSecX,
            hPosRangeY = hPosRange.y,
            vPosRangeTopY = vPosRange.topY,
            vPosRangeX = vPosRange.x,

            imgsArrangeTopArr = [],
            topImgNum = Math.floor(Math.random() * 2), //取一個或不取
            topImgSpliceIndex = 0,

            // 從imgsArrangeArr中把centerIndex拿掉
            imgsArrangeCenterArr = imgsArrangeArr.splice(centerIndex, 1);

            //  首先居中centerIndex這張圖片，centerIndex不用旋轉
            imgsArrangeCenterArr[0] = {
                pos: centerPos,
                rotate: 0,
                isCenter: true
            };

        // 居中的圖片不用旋轉
        imgsArrangeCenterArr[0].rotate = 0;

        //  語出要放在上冊的圖片的狀態訊息
        topImgSpliceIndex = Math.floor(Math.random() * (imgsArrangeArr.length - topImgNum));
        imgsArrangeTopArr = imgsArrangeArr.splice(topImgSpliceIndex, topImgNum); // 從imgsArrangeArr把topImgSpliceIndex拿掉

        //  佈局位於上側的圖片
        imgsArrangeTopArr.forEach((value, index) => {
            imgsArrangeTopArr[index] = {
                pos: {
                    top: getRangeRandom(vPosRangeTopY[0], vPosRangeTopY[1]),
                    left: getRangeRandom(vPosRangeX[0], vPosRangeX[1])
                },
                rotate: get30DegRandom(),
                isCenter: false
            }
        });

        //  佈局位於兩側的圖片
        for (let i = 0, j = imgsArrangeArr.length, k = j / 2; i < j; i++) {
            let hPosRangeLORX = null;

            //  前半部分分佈在左邊，後半部分分佈在右邊
            if (i < k) {
                hPosRangeLORX = hPosRangeLeftSecX;
            } else {
                hPosRangeLORX = hPosRangeRightSecX;
            }

            imgsArrangeArr[i] = {
                pos: {
                    top: getRangeRandom(hPosRangeY[0], hPosRangeY[1]),
                    left: getRangeRandom(hPosRangeLORX[0], hPosRangeLORX[1])
                },
                rotate: get30DegRandom(),
                isCenter: false
            }
        }

        if(imgsArrangeTopArr && imgsArrangeArr[0]) {
            imgsArrangeArr.splice(topImgSpliceIndex, 0, imgsArrangeTopArr[0]); // 把上方的那張圖塞回imgsArrangeArr
        }

        imgsArrangeArr.splice(centerIndex, 0, imgsArrangeCenterArr[0]); // 把中間的圖也塞回imgsArrangeArr

        this.setState({
            imgsArrangeArr: imgsArrangeArr
        });

    }

    //  component 載入後計算每張圖片的位置
    componentDidMount () {

        //  拿到stage的大小
        let stageDOM = ReactDOM.findDOMNode(this.refs.stage),
            stageW = stageDOM.scrollWidth,
            stageH = stageDOM.scrollHeight,
            halfStageW = Math.ceil(stageW / 2),
            halfStageH = Math.ceil(stageH / 2);

        //  拿到一個imgFigure的大小
        let imgFigureDOM = ReactDOM.findDOMNode(this.refs.imgFigure0),
            imgW = imgFigureDOM.scrollWidth,
            imgH = imgFigureDOM.scrollHeight,
            halfImgW = Math.ceil(imgW / 2),
            halfImgH = Math.ceil(imgH / 2);

        //  計算中心點位置
        this.Constant.centerPos = {
            left: halfStageW - halfImgW,
            top: halfStageH - halfImgH
        };

        //計算左側、右側區域圖片散佈的取值範圍
        this.Constant.hPosRange.leftSecX[0] = -halfImgW;
        this.Constant.hPosRange.leftSecX[1] = halfStageW - halfImgW * 3;

        this.Constant.hPosRange.rightSecX[0] = halfStageW + halfImgW;
        this.Constant.hPosRange.rightSecX[1] = stageW - halfImgW;

        this.Constant.hPosRange.y[0] = -halfImgH;
        this.Constant.hPosRange.y[1] = stageH - halfImgH;

        //計算上側區域圖片散佈的取值範圍
        this.Constant.vPosRange.topY[0] = -halfImgH;
        this.Constant.vPosRange.topY[1] = halfStageH - halfImgH * 3;

        this.Constant.vPosRange.x[0] = halfStageW - imgW;
        this.Constant.vPosRange.x[1] = halfStageW;

        let num = Math.floor(Math.random() * 13);
        this.rearrange(num);
    }

    render() {

        var controllerUnits = [], imgFigures = [];

        imageDatas.forEach((value, index) => {

            //  把每張圖先定在左上角，初始化
            if(!this.state.imgsArrangeArr[index]) {
                this.state.imgsArrangeArr[index] = {
                    pos: {
                        left: 0,
                        top: 0
                    },
                    rotate: 0,
                    isInverse: false,
                    isCenter: false
                }
            }

            //產生DOM節點，放進array裡
            imgFigures.push(
                <ImgFigure
                    data={value}
                    key={index} //  以防每次React都要渲染一次（因為React會比較virtual DOM前後差異）
                    ref={'imgFigure' + index}
                    arrange={this.state.imgsArrangeArr[index]}
                    inverse={this.inverse(index)}
                    center={this.center(index)}
                />
            );
            controllerUnits.push(
                <ControllerUnit
                    key={index}
                    arrange={this.state.imgsArrangeArr[index]}
                    inverse={this.inverse(index)}
                    center={this.center(index)}
                />
            );
        });

        return (

                    {imgFigures}

                    {controllerUnits}

        );
  }

}

GalleryByReactApp.defaultProps = {
};

export default GalleryByReactApp;
<|endoftext|>


<commit_before>/**
 * @file claim command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

let claimedUsers = [];
const specialIDs = require('../../data/specialIDs.json');

exports.exec = (Bastion, message) => {
  if (!claimedUsers.includes(message.author.id)) {
    let rewardAmount = Bastion.functions.getRandomInt(50, 100);

    if (message.guild.id === specialIDs.bastionGuild) {
      rewardAmount *= 2;

      if (message.member && message.member.roles.has(specialIDs.patronsRole)) {
        rewardAmount += 500;
      }
      else if (message.member && message.member.roles.has(specialIDs.donorsRole)) {
        rewardAmount += 100;
      }
    }

    Bastion.emit('userDebit', message.author, rewardAmount);
    claimedUsers.push(message.author.id);
    setTimeout(() => {
      claimedUsers.splice(claimedUsers.indexOf(message.author.id), 1);
    }, Bastion.functions.msUntilMidnight());

    /**
    * Send a message in the channel to let the user know that the operation was successful.
    */
    message.channel.send({
      embed: {
        color: Bastion.colors.GREEN,
        description: `${message.author} You've claimed your daily reward. Please check my message in your DM to see the reward amount.\n\n*If you want to get more Bastion Currencies when you use this command, check out the [FAQ](https://bastionbot.org/faq) on **How can I get more Bastion Currency with the claim/daily command?***`
      }
    }).catch(e => {
      Bastion.log.error(e);
    });

    /**
    * Let the user know by DM that their account has been debited.
    */
    let description;
    if (Bastion.user.id === '267035345537728512' && message.guild.id !== specialIDs.bastionGuild) {
      description = `Your account has been debited with **${rewardAmount}** Bastion Currencies.\n\nUse the \`claim\`/\`daily\` command in [Bastion HQ](https://discord.gg/fzx8fkt) to get 2x more rewards.`;
    }
    else {
      description = `Your account has been debited with **${rewardAmount}** Bastion Currencies.`;
    }
    message.author.send({
      embed: {
        color: Bastion.colors.GREEN,
        description: description
      }
    }).catch(e => {
      if (e.code !== 50007) {
        Bastion.log.error(e);
      }
    });
  }
  else {
    /**
     * Error condition is encountered.
     * @fires error
     */
    return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'cooldown'), Bastion.strings.error(message.guild.language, 'claimCooldown', true, message.author), message.channel);
  }
};

exports.config = {
  aliases: [ 'daily' ],
  enabled: true
};

exports.help = {
  name: 'claim',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'claim',
  example: []
};
<commit_msg>Don't show some claim message when it's unnecessary<commit_after>/**
 * @file claim command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license MIT
 */

let claimedUsers = [];
const specialIDs = require('../../data/specialIDs.json');

exports.exec = (Bastion, message) => {
  if (!claimedUsers.includes(message.author.id)) {
    let rewardAmount = Bastion.functions.getRandomInt(50, 100);

    let claimMessage, rewardMessage;
    if (Bastion.user.id === '267035345537728512') {
      claimMessage = `${message.author} You've claimed your daily reward. Please check my message in your DM to see the reward amount.\n\n*If you want to get more Bastion Currencies when you use this command, check out the [FAQ](https://bastionbot.org/faq) on **How can I get more Bastion Currency with the claim/daily command?***`;

      if (message.guild.id === specialIDs.bastionGuild) {
        rewardAmount *= 2;
        if (message.member && message.member.roles.has(specialIDs.patronsRole)) {
          rewardAmount += 500;
        }
        else if (message.member && message.member.roles.has(specialIDs.donorsRole)) {
          rewardAmount += 100;
        }

        rewardMessage = `Your account has been debited with **${rewardAmount}** Bastion Currencies.`;
      }
      else {
        rewardMessage = `Your account has been debited with **${rewardAmount}** Bastion Currencies.\n\nUse the \`claim\`/\`daily\` command in [Bastion HQ](https://discord.gg/fzx8fkt) to get 2x more rewards.`;
      }
    }
    else {
      claimMessage = `${message.author} You've claimed your daily reward. Please check my message in your DM to see the reward amount.`;
    }

    Bastion.emit('userDebit', message.author, rewardAmount);
    claimedUsers.push(message.author.id);
    setTimeout(() => {
      claimedUsers.splice(claimedUsers.indexOf(message.author.id), 1);
    }, Bastion.functions.msUntilMidnight());

    /**
    * Send a message in the channel to let the user know that the operation was successful.
    */
    message.channel.send({
      embed: {
        color: Bastion.colors.GREEN,
        description: claimMessage
      }
    }).catch(e => {
      Bastion.log.error(e);
    });

    /**
    * Let the user know by DM that their account has been debited.
    */
    message.author.send({
      embed: {
        color: Bastion.colors.GREEN,
        description: rewardMessage
      }
    }).catch(e => {
      if (e.code !== 50007) {
        Bastion.log.error(e);
      }
    });
  }
  else {
    /**
     * Error condition is encountered.
     * @fires error
     */
    return Bastion.emit('error', Bastion.strings.error(message.guild.language, 'cooldown'), Bastion.strings.error(message.guild.language, 'claimCooldown', true, message.author), message.channel);
  }
};

exports.config = {
  aliases: [ 'daily' ],
  enabled: true
};

exports.help = {
  name: 'claim',
  botPermission: '',
  userTextPermission: '',
  userVoicePermission: '',
  usage: 'claim',
  example: []
};
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Segment = this.Segment = Base.extend({
	/** @lends Segment# */
	beans: true,

	/**
	 * Creates a new Segment object.
	 *
	 * @name Segment
	 * @constructor
	 * @param {Point} [point={x: 0, y: 0}] the anchor point of the segment
	 * @param {Point} [handleIn={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the in tangent of the
	 *        segment.
	 * @param {Point} [handleOut={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the out tangent of the
	 *        segment.
	 *
	 * @example
	 * var handleIn = new Point(-40, -50);
	 * var handleOut = new Point(40, 50);
	 *
	 * var firstPoint = new Point(100, 50);
	 * var firstSegment = new Segment(firstPoint, null, handleOut);
	 *
	 * var secondPoint = new Point(200, 50);
	 * var secondSegment = new Segment(secondPoint, handleIn, null);
	 *
	 * var path = new Path();
	 * path.segments = [firstSegment, secondSegment];
	 * path.strokeColor = 'black';
	 *
	 * @class The Segment object represents a part of a path which is
	 * described by the {@link Path#segments} array. Every segment of a
	 * path corresponds to an anchor point (anchor points are the path handles
	 * that are visible when the path is selected).
	 */
	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		var count = arguments.length,
			createPoint = SegmentPoint.create,
			point, handleIn, handleOut;
		if (count == 0) {
			// Nothing
		} else if (count == 1) {
			// TODO: If beans are not activated, this won't copy from existing
			// segments. OK?
			if (arg0.point) {
				point = arg0.point;
				handleIn = arg0.handleIn;
				handleOut = arg0.handleOut;
			} else {
				point = arg0;
			}
		} else if (count < 6) {
			if (count == 2 && !arg1.x) {
				point = [ arg0, arg1 ];
			} else {
				point = arg0;
				// Doesn't matter if these arguments exist, SegmentPointcreate
				// produces creates points with (0, 0) otherwise
				handleIn = arg1;
				handleOut = arg2;
			}
		} else if (count == 6) {
			point = [ arg0, arg1 ];
			handleIn = [ arg2, arg3 ];
			handleOut = [ arg4, arg5 ];
		}
		createPoint(this, '_point', point);
		createPoint(this, '_handleIn', handleIn);
		createPoint(this, '_handleOut', handleOut);
	},

	_changed: function(point) {
		if (!this._path)
			return;
		// Delegate changes to affected curves if they exist
		var curve = this._path._curves && this.getCurve(), other;
		if (curve) {
			curve._changed();
			// Get the other affected curve, which is the previous one for
			// _point or _handleIn changing when this segment is _segment1 of
			// the curve, for all other cases it's the next (e.g. _handleOut
			// when this segment is _segment2)
			if (other = (curve[point == this._point
					|| point == this._handleIn && curve._segment1 == this
					? 'getPrevious' : 'getNext']())) {
				other._changed();
			}
		}
		this._path._changed(ChangeFlags.GEOMETRY);
	},

	/**
	 * The anchor point of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the in tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleInIfSet: function() {
		return this._handleIn._x == 0 && this._handleIn._y == 0
			? null : this._handleIn;
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the out tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleOutIfSet: function() {
		return this._handleOut._x == 0 && this._handleOut._y == 0
			? null : this._handleOut;
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		var path = this._path,
			selected = !!selected, // convert to boolean
			state = this._selectionState,
			wasSelected = !!state,
			// For performance reasons use array indices to access the various
			// selection states: 0 = point, 1 = handleIn, 2 = handleOut
			selection = [
				!!(state & SelectionState.POINT),
				!!(state & SelectionState.HANDLE_IN),
				!!(state & SelectionState.HANDLE_OUT)
			];
		if (point == this._point) {
			if (selected) {
				// We're selecting point, deselect the handles
				selection[1] = selection[2] = false;
			} else {
				var previous = this.getPrevious(),
					next = this.getNext();
				// When deselecting a point, the handles get selected instead
				// depending on the selection state of their neighbors.
				selection[1] = previous && (previous._point.isSelected()
						|| previous._handleOut.isSelected());
				selection[2] = next && (next._point.isSelected()
						|| next._handleIn.isSelected());
			}
			selection[0] = selected;
		} else {
			var index = point == this._handleIn ? 1 : 2;
			if (selection[index] != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					selection[0] = false;
				selection[index] = selected;
			}
		}
		this._selectionState = (selection[0] ? SelectionState.POINT : 0)
				| (selection[1] ? SelectionState.HANDLE_IN : 0)
				| (selection[2] ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// project._selectedItems
		if (path && wasSelected != !!this._selectionState)
			path._updateSelection(this);
	},

	/**
	 * Specifies whether the {@link #point} of the segment is selected.
	 * @type Boolean
	 * @bean
	 */
	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	/**
	 * {@grouptitle Hierarchy}
	 *
	 * The index of the segment in the {@link Path#segments} array that the
	 * segment belongs to.
	 *
	 * @type Number
	 * @bean
	 */
	getIndex: function() {
		return this._index !== undefined ? this._index : null;
	},

	/**
	 * The path that the segment belongs to.
	 *
	 * @type Path
	 * @bean
	 */
	getPath: function() {
		return this._path || null;
	},

	/**
	 * The curve that the segment belongs to.
	 *
	 * @type Curve
	 * @bean
	 */
	getCurve: function() {
		if (this._path) {
			var index = this._index;
			// The last segment of an open path belongs to the last curve
			if (!this._path._closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index] || null;
		}
		return null;
	},

	/**
	 * {@grouptitle Sibling Segments}
	 *
	 * The next segment in the {@link Path#segments} array that the segment
	 * belongs to. If the segments belongs to a closed path, the first segment
	 * is returned for the last segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index + 1]
				|| this._path._closed && segments[0]) || null;
	},

	/**
	 * The previous segment in the {@link Path#segments} array that the
	 * segment belongs to. If the segments belongs to a closed path, the last
	 * segment is returned for the first segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index - 1]
				|| this._path._closed && segments[segments.length - 1]) || null;
	},

	/**
	 * Returns the reversed the segment, without modifying the segment itself.
	 * @return {Segment} the reversed segment
	 */
	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	/**
	 * Removes the segment from the path that it belongs to.
	 */
	remove: function() {
		return this._path ? !!this._path.removeSegment(this._index) : false;
	},

	/**
	 * @return {String} A string representation of the segment.
	 */
	toString: function() {
		var parts = [ 'point: ' + this._point ];
		if (!this._handleIn.isZero())
			parts.push('handleIn: ' + this._handleIn);
		if (!this._handleOut.isZero())
			parts.push('handleOut: ' + this._handleOut);
		return '{ ' + parts.join(', ') + ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If a matrix is defined, only transform handles if they are set.
			// This saves some computation time. If no matrix is set, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  matrix && this.getHandleInIfSet() || this._handleIn,
			handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
			x = point._x,
			y = point._y,
			i = 2;
		coords[0] = x;
		coords[1] = y;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[i++] = handleIn._x + x;
			coords[i++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[i++] = handleOut._x + x;
			coords[i++] = handleOut._y + y;
		}
		if (matrix) {
			matrix._transformCoordinates(coords, 0, coords, 0, i / 2);
			x = coords[0];
			y = coords[1];
			if (change) {
				// If change is true, we need to set the new values back
				point._x = x;
				point._y = y;
				i  = 2;
				if (handleIn) {
					handleIn._x = coords[i++] - x;
					handleIn._y = coords[i++] - y;
				}
				if (handleOut) {
					handleOut._x = coords[i++] - x;
					handleOut._y = coords[i++] - y;
				}
			} else {
				// We want to receive the results in coords, so make sure
				// handleIn and out are defined too, even if they're 0
				if (!handleIn) {
					coords[i++] = x;
					coords[i++] = y;
				}
				if (!handleOut) {
					coords[i++] = x;
					coords[i++] = y;
				}
			}
		}
	}
});
<commit_msg>Improve Segment constructor example.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Segment = this.Segment = Base.extend({
	/** @lends Segment# */
	beans: true,

	/**
	 * Creates a new Segment object.
	 *
	 * @name Segment
	 * @constructor
	 * @param {Point} [point={x: 0, y: 0}] the anchor point of the segment
	 * @param {Point} [handleIn={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the in tangent of the
	 *        segment.
	 * @param {Point} [handleOut={x: 0, y: 0}] the handle point relative to the
	 *        anchor point of the segment that describes the out tangent of the
	 *        segment.
	 *
	 * @example {@paperscript}
	 * var handleIn = new Point(-80, -100);
	 * var handleOut = new Point(80, 100);
	 *
	 * var firstPoint = new Point(100, 50);
	 * var firstSegment = new Segment(firstPoint, null, handleOut);
	 *
	 * var secondPoint = new Point(300, 50);
	 * var secondSegment = new Segment(secondPoint, handleIn, null);
	 *
	 * var path = new Path(firstSegment, secondSegment);
	 * path.strokeColor = 'black';
	 *
	 * @class The Segment object represents a part of a path which is
	 * described by the {@link Path#segments} array. Every segment of a
	 * path corresponds to an anchor point (anchor points are the path handles
	 * that are visible when the path is selected).
	 */
	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		var count = arguments.length,
			createPoint = SegmentPoint.create,
			point, handleIn, handleOut;
		if (count == 0) {
			// Nothing
		} else if (count == 1) {
			// TODO: If beans are not activated, this won't copy from existing
			// segments. OK?
			if (arg0.point) {
				point = arg0.point;
				handleIn = arg0.handleIn;
				handleOut = arg0.handleOut;
			} else {
				point = arg0;
			}
		} else if (count < 6) {
			if (count == 2 && !arg1.x) {
				point = [ arg0, arg1 ];
			} else {
				point = arg0;
				// Doesn't matter if these arguments exist, SegmentPointcreate
				// produces creates points with (0, 0) otherwise
				handleIn = arg1;
				handleOut = arg2;
			}
		} else if (count == 6) {
			point = [ arg0, arg1 ];
			handleIn = [ arg2, arg3 ];
			handleOut = [ arg4, arg5 ];
		}
		createPoint(this, '_point', point);
		createPoint(this, '_handleIn', handleIn);
		createPoint(this, '_handleOut', handleOut);
	},

	_changed: function(point) {
		if (!this._path)
			return;
		// Delegate changes to affected curves if they exist
		var curve = this._path._curves && this.getCurve(), other;
		if (curve) {
			curve._changed();
			// Get the other affected curve, which is the previous one for
			// _point or _handleIn changing when this segment is _segment1 of
			// the curve, for all other cases it's the next (e.g. _handleOut
			// when this segment is _segment2)
			if (other = (curve[point == this._point
					|| point == this._handleIn && curve._segment1 == this
					? 'getPrevious' : 'getNext']())) {
				other._changed();
			}
		}
		this._path._changed(ChangeFlags.GEOMETRY);
	},

	/**
	 * The anchor point of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the in tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleInIfSet: function() {
		return this._handleIn._x == 0 && this._handleIn._y == 0
			? null : this._handleIn;
	},

	/**
	 * The handle point relative to the anchor point of the segment that
	 * describes the out tangent of the segment.
	 *
	 * @type Point
	 * @bean
	 */
	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isColinear(this._handleOut);
	},

	getHandleOutIfSet: function() {
		return this._handleOut._x == 0 && this._handleOut._y == 0
			? null : this._handleOut;
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		var path = this._path,
			selected = !!selected, // convert to boolean
			state = this._selectionState,
			wasSelected = !!state,
			// For performance reasons use array indices to access the various
			// selection states: 0 = point, 1 = handleIn, 2 = handleOut
			selection = [
				!!(state & SelectionState.POINT),
				!!(state & SelectionState.HANDLE_IN),
				!!(state & SelectionState.HANDLE_OUT)
			];
		if (point == this._point) {
			if (selected) {
				// We're selecting point, deselect the handles
				selection[1] = selection[2] = false;
			} else {
				var previous = this.getPrevious(),
					next = this.getNext();
				// When deselecting a point, the handles get selected instead
				// depending on the selection state of their neighbors.
				selection[1] = previous && (previous._point.isSelected()
						|| previous._handleOut.isSelected());
				selection[2] = next && (next._point.isSelected()
						|| next._handleIn.isSelected());
			}
			selection[0] = selected;
		} else {
			var index = point == this._handleIn ? 1 : 2;
			if (selection[index] != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					selection[0] = false;
				selection[index] = selected;
			}
		}
		this._selectionState = (selection[0] ? SelectionState.POINT : 0)
				| (selection[1] ? SelectionState.HANDLE_IN : 0)
				| (selection[2] ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// project._selectedItems
		if (path && wasSelected != !!this._selectionState)
			path._updateSelection(this);
	},

	/**
	 * Specifies whether the {@link #point} of the segment is selected.
	 * @type Boolean
	 * @bean
	 */
	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	/**
	 * {@grouptitle Hierarchy}
	 *
	 * The index of the segment in the {@link Path#segments} array that the
	 * segment belongs to.
	 *
	 * @type Number
	 * @bean
	 */
	getIndex: function() {
		return this._index !== undefined ? this._index : null;
	},

	/**
	 * The path that the segment belongs to.
	 *
	 * @type Path
	 * @bean
	 */
	getPath: function() {
		return this._path || null;
	},

	/**
	 * The curve that the segment belongs to.
	 *
	 * @type Curve
	 * @bean
	 */
	getCurve: function() {
		if (this._path) {
			var index = this._index;
			// The last segment of an open path belongs to the last curve
			if (!this._path._closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index] || null;
		}
		return null;
	},

	/**
	 * {@grouptitle Sibling Segments}
	 *
	 * The next segment in the {@link Path#segments} array that the segment
	 * belongs to. If the segments belongs to a closed path, the first segment
	 * is returned for the last segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index + 1]
				|| this._path._closed && segments[0]) || null;
	},

	/**
	 * The previous segment in the {@link Path#segments} array that the
	 * segment belongs to. If the segments belongs to a closed path, the last
	 * segment is returned for the first segment of the path.
	 *
	 * @type Segment
	 * @bean
	 */
	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this._index - 1]
				|| this._path._closed && segments[segments.length - 1]) || null;
	},

	/**
	 * Returns the reversed the segment, without modifying the segment itself.
	 * @return {Segment} the reversed segment
	 */
	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	/**
	 * Removes the segment from the path that it belongs to.
	 */
	remove: function() {
		return this._path ? !!this._path.removeSegment(this._index) : false;
	},

	/**
	 * @return {String} A string representation of the segment.
	 */
	toString: function() {
		var parts = [ 'point: ' + this._point ];
		if (!this._handleIn.isZero())
			parts.push('handleIn: ' + this._handleIn);
		if (!this._handleOut.isZero())
			parts.push('handleOut: ' + this._handleOut);
		return '{ ' + parts.join(', ') + ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If a matrix is defined, only transform handles if they are set.
			// This saves some computation time. If no matrix is set, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  matrix && this.getHandleInIfSet() || this._handleIn,
			handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
			x = point._x,
			y = point._y,
			i = 2;
		coords[0] = x;
		coords[1] = y;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[i++] = handleIn._x + x;
			coords[i++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[i++] = handleOut._x + x;
			coords[i++] = handleOut._y + y;
		}
		if (matrix) {
			matrix._transformCoordinates(coords, 0, coords, 0, i / 2);
			x = coords[0];
			y = coords[1];
			if (change) {
				// If change is true, we need to set the new values back
				point._x = x;
				point._y = y;
				i  = 2;
				if (handleIn) {
					handleIn._x = coords[i++] - x;
					handleIn._y = coords[i++] - y;
				}
				if (handleOut) {
					handleOut._x = coords[i++] - x;
					handleOut._y = coords[i++] - y;
				}
			} else {
				// We want to receive the results in coords, so make sure
				// handleIn and out are defined too, even if they're 0
				if (!handleIn) {
					coords[i++] = x;
					coords[i++] = y;
				}
				if (!handleOut) {
					coords[i++] = x;
					coords[i++] = y;
				}
			}
		}
	}
});
<|endoftext|>