<commit_before>'use strict';

const events = require('events');
const http = require('http');
const stream = require('stream');
const util = require('util');
const ws = require('ws');

const debug = util.debuglog('rdbg');

class Console extends stream.Readable {
  constructor(client) {
    super({
      objectMode: true
    });

    client.on('message', (method, params) => {
      if (method === 'Console.messageAdded') {
        this.push(params.message);
      } else if (method === 'Console.messagesCleared') {
        this.emit('clear');
      }
    });

    this._client = client;
  }

  _read() {
  }

  get client() {
    return this._client;
  }

  enable(callback) {
    this.client.request('Console.enable', error => {
      callback(error);
    });
  }

  disable(callback) {
    this.client.request('Console.enable', error => {
      callback(error);
    });
  }
}

module.exports.Console = Console;

class Debugger extends events.EventEmitter {
  constructor(client) {
    super();

    client.on('message', (method, params) => {
      if (method === 'Debugger.scriptParsed') {
        this.emit('scriptParse', params);
      } else if (method === 'Debugger.scriptFailedToParse') {
        this.emit('scriptError', params);
      } else if (method === 'Debugger.globalObjectCleared') {
        this.emit('clear');
      }
    });

    this._client = client;
  }

  get client() {
    return this._client;
  }

  enable(callback) {
    this.client.request('Debugger.enable', function(error) {
      callback(error);
    });
  }

  disable(callback) {
    this.client.request('Debugger.enable', function(error) {
      callback(error);
    });
  }

  pause(callback) {
    this.client.request('Debugger.pause', function(error) {
      callback(error);
    });
  }

  disable(callback) {
    this.client.request('Debugger.disable', function(error) {
      callback(error);
    });
  }

  canSetScriptSource(callback) {
    this.client.request('Debugger.canSetScriptSource', function(error, result) {
      if (error) {
        return callback(error);
      }

      callback(null, result.result);
    });
  }

  getScriptSource(scriptId, callback) {
    this.client.request('Debugger.getScriptSource', {
      scriptId: scriptId,
    }, function(error, result) {
      if (error) {
        return callback(error);
      }

      callback(null, result.scriptSource);
    });
  }

  setScriptSource(scriptId, source, callback) {
    this.client.request('Debugger.setScriptSource', {
      scriptId: scriptId,
      scriptSource: source
    }, callback);
  }
}

module.exports.Debugger = Debugger;

class Runtime extends events.EventEmitter {
  constructor(client) {
    super();

    client.on('message', (method, params) => {
    });

    this._client = client;
  }

  get client() {
    return this._client;
  }

  enable(callback) {
    this.client.request('Runtime.enable', error => {
      return callback(error);
    });
  }

  evaluate(cmd, options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = undefined;
    }

    this.client.request('Runtime.evaluate', Object.assign({}, {
      expression: cmd,
    }, options), (error, result) => {
      if (error) {
        return callback(error);
      }

      callback(null, result.result, result.wasThrown);
    });
  }
}

module.exports.Runtime = Runtime;

class Client extends events.EventEmitter {
  constructor() {
    super();

    this._counter = 0;
    this._callbacks = {};
    this._commands = {};
    this._socket = null;
  }

  get socket() {
    return this._socket;
  }

  get console() {
    if (!this._console) {
      this._console = new Console(this);
    }

    return this._console;
  }

  get debugger() {
    if (!this._debugger) {
      this._debugger = new Debugger(this);
    }

    return this._debugger;
  }

  get runtime() {
    if (!this._runtime) {
      this._runtime = new Runtime(this);
    }

    return this._runtime;
  }

  request(method, parameters, id, callback) {
    if (typeof parameters === 'function') {
      callback = parameters;
      parameters = undefined;
    }

    if (typeof id === 'function') {
      callback = id;
      id = undefined;
    }

    if (typeof parameters === 'undefined') {
      parameters = {};
    }

    if (typeof id === 'undefined') {
      id = this._counter++;
    }

    var request = {
      id: id,
      method: method,
      params: parameters,
    };

    this._callbacks[id] = callback;
    this._commands[id] = request;

    debug('request %s', util.inspect(request));
    this.emit('request', request);
    this._socket.send(JSON.stringify(request));
  }

  connect(uri, callback) {
    if (callback) {
      this.on('connect', callback);
    }

    let socket = ws.connect(uri);
    socket.once('open', () => {
      debug('connect');
      this.emit('connect');
    });

    socket.on('close', () => {
      debug('close');
      this.emit('close');
    });

    socket.on('error', (error) => {
      debug('error');
      this.emit('error', error);
    });

    socket.on('message', data => {
      try {
        var message = JSON.parse(data);

        if (message.id !== undefined) {
          var command = this._commands[message.id];
          var callback = this._callbacks[message.id];

          debug('response %s, %s', util.inspect(command), util.inspect(message));
          this.emit('response', command, message);

          if (callback) {
            if (message.error) {
              callback(message.error);
            } else {
              callback(null, message.result);
            }

            delete this._callbacks[message.id];
          }

          delete this._commands[message.id];

        } else {
          debug('message %s', util.inspect(message));
          this.emit('message', message.method, message.params);
        }
      } catch (error) {
        this.emit('error', error);
      }
    });

    this._socket = socket;
    this.emit('socket', socket);
  }

  close() {
    if (this.socket) {
      this.socket.close();
    }
  }
}

module.exports.Client = Client;

function connect(uri, callback) {
  var client = new Client();
  if (callback) {
    client.on('connect', callback);
  }

  client.connect(uri);

  return client;
}

module.exports.connect = connect;

function list(port, host, callback) {
  if (typeof host === 'function') {
    callback = host;
    host = undefined;
  }

  if (typeof host == 'undefined') {
    host = 'localhost';
  }

  var request = http.get({
    port: port,
    host: host,
    path: '/json/list'
  });

  request.on('response', (response) => {
    var data = '';

    response.on('data', (chunk) => {
      data += chunk;
    });

    response.on('end', () => {
      try {
        var targets = JSON.parse(data);
        return callback(null, targets);
      } catch (error) {
        return callback(error);
      }
    });
  });

  request.on('error', (error) => {
    return callback(error);
  });
}

module.exports.list = list;
module.exports.get = util.deprecate(list, 'rdbg.get: Use rdbg.list instead');
<commit_msg>lib: remove duplicate disable method<commit_after>'use strict';

const events = require('events');
const http = require('http');
const stream = require('stream');
const util = require('util');
const ws = require('ws');

const debug = util.debuglog('rdbg');

class Console extends stream.Readable {
  constructor(client) {
    super({
      objectMode: true
    });

    client.on('message', (method, params) => {
      if (method === 'Console.messageAdded') {
        this.push(params.message);
      } else if (method === 'Console.messagesCleared') {
        this.emit('clear');
      }
    });

    this._client = client;
  }

  _read() {
  }

  get client() {
    return this._client;
  }

  enable(callback) {
    this.client.request('Console.enable', error => {
      callback(error);
    });
  }

  disable(callback) {
    this.client.request('Console.enable', error => {
      callback(error);
    });
  }
}

module.exports.Console = Console;

class Debugger extends events.EventEmitter {
  constructor(client) {
    super();

    client.on('message', (method, params) => {
      if (method === 'Debugger.scriptParsed') {
        this.emit('scriptParse', params);
      } else if (method === 'Debugger.scriptFailedToParse') {
        this.emit('scriptError', params);
      } else if (method === 'Debugger.globalObjectCleared') {
        this.emit('clear');
      }
    });

    this._client = client;
  }

  get client() {
    return this._client;
  }

  enable(callback) {
    this.client.request('Debugger.enable', function(error) {
      callback(error);
    });
  }

  disable(callback) {
    this.client.request('Debugger.enable', function(error) {
      callback(error);
    });
  }

  pause(callback) {
    this.client.request('Debugger.pause', function(error) {
      callback(error);
    });
  }

  canSetScriptSource(callback) {
    this.client.request('Debugger.canSetScriptSource', function(error, result) {
      if (error) {
        return callback(error);
      }

      callback(null, result.result);
    });
  }

  getScriptSource(scriptId, callback) {
    this.client.request('Debugger.getScriptSource', {
      scriptId: scriptId,
    }, function(error, result) {
      if (error) {
        return callback(error);
      }

      callback(null, result.scriptSource);
    });
  }

  setScriptSource(scriptId, source, callback) {
    this.client.request('Debugger.setScriptSource', {
      scriptId: scriptId,
      scriptSource: source
    }, callback);
  }
}

module.exports.Debugger = Debugger;

class Runtime extends events.EventEmitter {
  constructor(client) {
    super();

    client.on('message', (method, params) => {
    });

    this._client = client;
  }

  get client() {
    return this._client;
  }

  enable(callback) {
    this.client.request('Runtime.enable', error => {
      return callback(error);
    });
  }

  evaluate(cmd, options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = undefined;
    }

    this.client.request('Runtime.evaluate', Object.assign({}, {
      expression: cmd,
    }, options), (error, result) => {
      if (error) {
        return callback(error);
      }

      callback(null, result.result, result.wasThrown);
    });
  }
}

module.exports.Runtime = Runtime;

class Client extends events.EventEmitter {
  constructor() {
    super();

    this._counter = 0;
    this._callbacks = {};
    this._commands = {};
    this._socket = null;
  }

  get socket() {
    return this._socket;
  }

  get console() {
    if (!this._console) {
      this._console = new Console(this);
    }

    return this._console;
  }

  get debugger() {
    if (!this._debugger) {
      this._debugger = new Debugger(this);
    }

    return this._debugger;
  }

  get runtime() {
    if (!this._runtime) {
      this._runtime = new Runtime(this);
    }

    return this._runtime;
  }

  request(method, parameters, id, callback) {
    if (typeof parameters === 'function') {
      callback = parameters;
      parameters = undefined;
    }

    if (typeof id === 'function') {
      callback = id;
      id = undefined;
    }

    if (typeof parameters === 'undefined') {
      parameters = {};
    }

    if (typeof id === 'undefined') {
      id = this._counter++;
    }

    var request = {
      id: id,
      method: method,
      params: parameters,
    };

    this._callbacks[id] = callback;
    this._commands[id] = request;

    debug('request %s', util.inspect(request));
    this.emit('request', request);
    this._socket.send(JSON.stringify(request));
  }

  connect(uri, callback) {
    if (callback) {
      this.on('connect', callback);
    }

    let socket = ws.connect(uri);
    socket.once('open', () => {
      debug('connect');
      this.emit('connect');
    });

    socket.on('close', () => {
      debug('close');
      this.emit('close');
    });

    socket.on('error', (error) => {
      debug('error');
      this.emit('error', error);
    });

    socket.on('message', data => {
      try {
        var message = JSON.parse(data);

        if (message.id !== undefined) {
          var command = this._commands[message.id];
          var callback = this._callbacks[message.id];

          debug('response %s, %s', util.inspect(command), util.inspect(message));
          this.emit('response', command, message);

          if (callback) {
            if (message.error) {
              callback(message.error);
            } else {
              callback(null, message.result);
            }

            delete this._callbacks[message.id];
          }

          delete this._commands[message.id];

        } else {
          debug('message %s', util.inspect(message));
          this.emit('message', message.method, message.params);
        }
      } catch (error) {
        this.emit('error', error);
      }
    });

    this._socket = socket;
    this.emit('socket', socket);
  }

  close() {
    if (this.socket) {
      this.socket.close();
    }
  }
}

module.exports.Client = Client;

function connect(uri, callback) {
  var client = new Client();
  if (callback) {
    client.on('connect', callback);
  }

  client.connect(uri);

  return client;
}

module.exports.connect = connect;

function list(port, host, callback) {
  if (typeof host === 'function') {
    callback = host;
    host = undefined;
  }

  if (typeof host == 'undefined') {
    host = 'localhost';
  }

  var request = http.get({
    port: port,
    host: host,
    path: '/json/list'
  });

  request.on('response', (response) => {
    var data = '';

    response.on('data', (chunk) => {
      data += chunk;
    });

    response.on('end', () => {
      try {
        var targets = JSON.parse(data);
        return callback(null, targets);
      } catch (error) {
        return callback(error);
      }
    });
  });

  request.on('error', (error) => {
    return callback(error);
  });
}

module.exports.list = list;
module.exports.get = util.deprecate(list, 'rdbg.get: Use rdbg.list instead');
<|endoftext|>


<commit_before>/* eslint-disable react/jsx-wrap-multilines */
/* eslint-disable react/forbid-prop-types */
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Text, View, StyleSheet } from 'react-native';
import * as Animatable from 'react-native-animatable';

import { DateRangeSelector } from '../widgets/DateRangeSelector';
import {
  ColdBreachIcon,
  DataTablePageView,
  FlexRow,
  FlexView,
  HotBreachIcon,
  Paper,
  SensorStatus,
} from '../widgets';

import { BreachManHappy } from '../widgets/BreachManHappy';
import { VaccineBarChart } from '../widgets/VaccineBarChart';
import { VaccineLineChart } from '../widgets/VaccineLineChart';

import { FridgeActions } from '../actions/FridgeActions';
import { AfterInteractions } from '../widgets/AfterInteractions';
import { IconButton } from '../widgets/IconButton';
import { BarChartIcon, LineChartIcon } from '../widgets/icons';

import {
  selectAverageTemperature,
  selectBreaches,
  selectColdCumulativeBreach,
  selectHotCumulativeBreach,
  selectLeastRecentTemperatureLogDate,
  selectMinAndMaxDomains,
  selectMinAndMaxLogs,
  selectMostRecentTemperatureLogDate,
  selectNumberOfColdConsecutiveBreaches,
  selectNumberOfHotConsecutiveBreaches,
  selectSelectedFridgeIsInColdBreach,
  selectSelectedFridgeIsInHotBreach,
  selectSelectedFridgeSensorIsLowBattery,
  selectSelectFridgeCurrentTemperature,
  selectTemperatureLogsFromDate,
  selectTemperatureLogsToDate,
  selectBreachBoundaries,
} from '../selectors/fridge';

import {
  DARKER_GREY,
  APP_FONT_FAMILY,
  BLUE_WHITE,
  COLD_BREACH_BLUE,
  DANGER_RED,
  WARMER_GREY,
} from '../globalStyles';
import { vaccineStrings } from '../localization/index';
import { SensorHeader } from '../widgets/SensorHeader';
import { BreachManUnhappy } from '../widgets/BreachManUnhappy';

const BREACH_MAN_UNHAPPY_SIZE = 400;
const NoBreachMessage = () => (
  <>
    <BreachManHappy />
    <Text style={localStyles.noBreachText}>{vaccineStrings.no_breaches}</Text>
  </>
);

const BreachCard = props => (
  <Paper
    {...props}
    style={localStyles.card}
    contentContainerStyle={{ alignItems: 'center', paddingBottom: 10 }}
  />
);

const EmptyComponent = ({ sensorName }) => (
  <FlexView justifyContent="center" alignItems="center" flex={1}>
    <BreachManUnhappy size={BREACH_MAN_UNHAPPY_SIZE} />
    <Text style={localStyles.emptyText}>
      No temperatures logged for {sensorName} within the given date range
    </Text>
  </FlexView>
);

EmptyComponent.propTypes = {
  sensorName: PropTypes.string.isRequired,
};

export const FridgeDetailPageComponent = ({
  breaches,
  minLine,
  maxLine,
  minDomain,
  maxDomain,
  onChangeToDate,
  onChangeFromDate,
  fromDate,
  toDate,
  minimumDate,
  maximumDate,
  numberOfHotBreaches,
  numberOfColdBreaches,
  hotCumulativeBreach,
  coldCumulativeBreach,
  averageTemperature,
  isInHotBreach,
  isInColdBreach,
  isLowBattery,
  currentTemperature,
  sensor,
  breachBoundaries,
}) => {
  const [chartType, setChartType] = useState('bar');
  const getIconButton = type => {
    const iconStyle = { color: chartType === type ? WARMER_GREY : DARKER_GREY };
    return (
      <IconButton
        onPress={() => setChartType(type)}
        Icon={
          type === 'line' ? <LineChartIcon style={iconStyle} /> : <BarChartIcon style={iconStyle} />
        }
        containerStyle={{ padding: 8 }}
      />
    );
  };

  if (!minLine.length || !maxLine.length) {
    return (
      <AfterInteractions>
        <View style={localStyles.container}>
          <View style={localStyles.topRow}>
            <DateRangeSelector
              containerStyle={localStyles.datePickerContainer}
              initialStartDate={fromDate}
              initialEndDate={toDate}
              onChangeToDate={onChangeToDate}
              onChangeFromDate={onChangeFromDate}
              minimumDate={minimumDate}
              maximumDate={maximumDate}
            />
          </View>
          <EmptyComponent sensorName={sensor.name} />
        </View>
      </AfterInteractions>
    );
  }

  return (
    <DataTablePageView>
      <AfterInteractions>
        <View style={localStyles.container}>
          <View style={localStyles.topRow}>
            <DateRangeSelector
              containerStyle={localStyles.datePickerContainer}
              initialStartDate={fromDate}
              initialEndDate={toDate}
              onChangeToDate={onChangeToDate}
              onChangeFromDate={onChangeFromDate}
              minimumDate={minimumDate}
              maximumDate={maximumDate}
            />
            <View style={localStyles.buttonContainer}>
              {getIconButton('line')}
              {getIconButton('bar')}
            </View>
          </View>

          <Paper
            height={300}
            contentContainerStyle={{ flex: 1, marginTop: 20 }}
            Header={<SensorHeader showTitle showCog sensor={sensor} />}
          >
            <AfterInteractions>
              <FlexRow alignItems="center">
                {chartType === 'line' && (
                  <VaccineLineChart
                    breaches={breaches}
                    minLine={minLine}
                    maxLine={maxLine}
                    minDomain={minDomain}
                    maxDomain={maxDomain}
                    breachBoundaries={breachBoundaries}
                  />
                )}
                {chartType === 'bar' && (
                  <VaccineBarChart
                    breaches={breaches}
                    minLine={minLine}
                    maxLine={maxLine}
                    minDomain={minDomain}
                    maxDomain={maxDomain}
                    breachBoundaries={breachBoundaries}
                  />
                )}
                <SensorStatus
                  isInHotBreach={isInHotBreach}
                  isInColdBreach={isInColdBreach}
                  isLowBattery={isLowBattery}
                  currentTemp={currentTemperature}
                />
              </FlexRow>
            </AfterInteractions>
          </Paper>

          <Animatable.View animation="fadeIn" duration={3000} useNativeDriver>
            <FlexRow>
              <BreachCard headerText={vaccineStrings.cumulative_breach}>
                {coldCumulativeBreach ? (
                  <>
                    <Text style={localStyles.coldText}>{coldCumulativeBreach}</Text>
                    <ColdBreachIcon color={COLD_BREACH_BLUE} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>

              <BreachCard headerText={vaccineStrings.consecutive_breach}>
                {numberOfColdBreaches ? (
                  <>
                    <Text style={localStyles.coldText}>{numberOfColdBreaches}</Text>
                    <ColdBreachIcon color={COLD_BREACH_BLUE} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>

              <BreachCard headerText={vaccineStrings.average_temperature}>
                <Text style={[localStyles.hotText, { color: DARKER_GREY }]}>
                  {averageTemperature}
                </Text>
              </BreachCard>

              <BreachCard headerText={vaccineStrings.cumulative_breach}>
                {hotCumulativeBreach ? (
                  <>
                    <Text style={localStyles.hotText}>{hotCumulativeBreach}</Text>
                    <HotBreachIcon color={DANGER_RED} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>

              <BreachCard headerText={vaccineStrings.consecutive_breach}>
                {numberOfHotBreaches ? (
                  <>
                    <Text style={localStyles.hotText}>{numberOfHotBreaches}</Text>
                    <HotBreachIcon color={DANGER_RED} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>
            </FlexRow>
          </Animatable.View>
        </View>
      </AfterInteractions>
    </DataTablePageView>
  );
};

const stateToProps = (state, props) => {
  const { route } = props;
  const { params } = route;
  const { fridge: fridgeProp } = params;
  const { sensor } = fridgeProp;
  const { fridge } = state;
  const { code } = fridge;

  const breaches = selectBreaches(state);
  const { minLine, maxLine } = selectMinAndMaxLogs(state);
  const { minDomain, maxDomain } = selectMinAndMaxDomains(state);
  const fromDate = selectTemperatureLogsFromDate(state);
  const toDate = selectTemperatureLogsToDate(state);
  const minimumDate = selectLeastRecentTemperatureLogDate(state);
  const maximumDate = selectMostRecentTemperatureLogDate(state);
  const numberOfHotBreaches = selectNumberOfHotConsecutiveBreaches(state);
  const numberOfColdBreaches = selectNumberOfColdConsecutiveBreaches(state);
  const averageTemperature = selectAverageTemperature(state);
  const hotCumulativeBreach = selectHotCumulativeBreach(state);
  const coldCumulativeBreach = selectColdCumulativeBreach(state);
  const isInHotBreach = selectSelectedFridgeIsInHotBreach(state);
  const isInColdBreach = selectSelectedFridgeIsInColdBreach(state);
  const isLowBattery = selectSelectedFridgeSensorIsLowBattery(state);
  const currentTemperature = selectSelectFridgeCurrentTemperature(state);
  const breachBoundaries = selectBreachBoundaries(state);

  return {
    sensor,
    fridge: fridgeProp,
    averageTemperature,
    coldCumulativeBreach,
    hotCumulativeBreach,
    numberOfColdBreaches,
    numberOfHotBreaches,
    code,
    breaches,
    minLine,
    maxLine,
    minDomain,
    maxDomain,
    fromDate,
    toDate,
    minimumDate,
    maximumDate,
    isInHotBreach,
    isInColdBreach,
    isLowBattery,
    currentTemperature,
    breachBoundaries,
  };
};

const dispatchToProps = dispatch => ({
  onChangeToDate: date => dispatch(FridgeActions.changeToDate(date)),
  onChangeFromDate: date => dispatch(FridgeActions.changeFromDate(date)),
});

FridgeDetailPageComponent.defaultProps = {
  coldCumulativeBreach: null,
  currentTemperature: null,
  hotCumulativeBreach: null,
  maximumDate: null,
  minimumDate: null,
};

FridgeDetailPageComponent.propTypes = {
  breaches: PropTypes.object.isRequired,
  minLine: PropTypes.array.isRequired,
  maxLine: PropTypes.array.isRequired,
  minDomain: PropTypes.number.isRequired,
  maxDomain: PropTypes.number.isRequired,
  onChangeToDate: PropTypes.func.isRequired,
  onChangeFromDate: PropTypes.func.isRequired,
  fromDate: PropTypes.instanceOf(Date).isRequired,
  toDate: PropTypes.instanceOf(Date).isRequired,
  minimumDate: PropTypes.instanceOf(Date),
  maximumDate: PropTypes.instanceOf(Date),
  numberOfHotBreaches: PropTypes.number.isRequired,
  numberOfColdBreaches: PropTypes.number.isRequired,
  hotCumulativeBreach: PropTypes.string,
  coldCumulativeBreach: PropTypes.string,
  averageTemperature: PropTypes.string.isRequired,
  isInHotBreach: PropTypes.bool.isRequired,
  isInColdBreach: PropTypes.bool.isRequired,
  isLowBattery: PropTypes.bool.isRequired,
  currentTemperature: PropTypes.number,
  sensor: PropTypes.object.isRequired,
  breachBoundaries: PropTypes.object.isRequired,
};

const localStyles = StyleSheet.create({
  container: { padding: 20, flex: 1, backgroundColor: BLUE_WHITE },
  datePickerContainer: { width: 250, borderRadius: 50, height: 50 },
  card: { flex: 1 },
  hotText: {
    color: DANGER_RED,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  coldText: {
    color: COLD_BREACH_BLUE,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  emptyText: {
    fontSize: 28,
    color: DARKER_GREY,
    fontFamily: APP_FONT_FAMILY,
    fontWeight: 'bold',
  },
  noBreachText: {
    fontSize: 12,
    color: DARKER_GREY,
    fontFamily: APP_FONT_FAMILY,
  },
  buttonContainer: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
  topRow: { flexDirection: 'row' },
});

export const FridgeDetailPage = connect(stateToProps, dispatchToProps)(FridgeDetailPageComponent);
<commit_msg>localise<commit_after>/* eslint-disable react/jsx-wrap-multilines */
/* eslint-disable react/forbid-prop-types */
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Text, View, StyleSheet } from 'react-native';
import * as Animatable from 'react-native-animatable';

import { DateRangeSelector } from '../widgets/DateRangeSelector';
import {
  ColdBreachIcon,
  DataTablePageView,
  FlexRow,
  FlexView,
  HotBreachIcon,
  Paper,
  SensorStatus,
} from '../widgets';

import { BreachManHappy } from '../widgets/BreachManHappy';
import { VaccineBarChart } from '../widgets/VaccineBarChart';
import { VaccineLineChart } from '../widgets/VaccineLineChart';

import { FridgeActions } from '../actions/FridgeActions';
import { AfterInteractions } from '../widgets/AfterInteractions';
import { IconButton } from '../widgets/IconButton';
import { BarChartIcon, LineChartIcon } from '../widgets/icons';

import {
  selectAverageTemperature,
  selectBreaches,
  selectColdCumulativeBreach,
  selectHotCumulativeBreach,
  selectLeastRecentTemperatureLogDate,
  selectMinAndMaxDomains,
  selectMinAndMaxLogs,
  selectMostRecentTemperatureLogDate,
  selectNumberOfColdConsecutiveBreaches,
  selectNumberOfHotConsecutiveBreaches,
  selectSelectedFridgeIsInColdBreach,
  selectSelectedFridgeIsInHotBreach,
  selectSelectedFridgeSensorIsLowBattery,
  selectSelectFridgeCurrentTemperature,
  selectTemperatureLogsFromDate,
  selectTemperatureLogsToDate,
  selectBreachBoundaries,
} from '../selectors/fridge';

import {
  DARKER_GREY,
  APP_FONT_FAMILY,
  BLUE_WHITE,
  COLD_BREACH_BLUE,
  DANGER_RED,
  WARMER_GREY,
} from '../globalStyles';
import { vaccineStrings } from '../localization/index';
import { SensorHeader } from '../widgets/SensorHeader';
import { BreachManUnhappy } from '../widgets/BreachManUnhappy';

const BREACH_MAN_UNHAPPY_SIZE = 400;
const NoBreachMessage = () => (
  <>
    <BreachManHappy />
    <Text style={localStyles.noBreachText}>{vaccineStrings.no_breaches}</Text>
  </>
);

const BreachCard = props => (
  <Paper
    {...props}
    style={localStyles.card}
    contentContainerStyle={{ alignItems: 'center', paddingBottom: 10 }}
  />
);

const EmptyComponent = ({ sensorName }) => (
  <FlexView style={localStyles.emptyComponent}>
    <BreachManUnhappy size={BREACH_MAN_UNHAPPY_SIZE} />
    <Text>{sensorName}</Text>
    <Text style={localStyles.emptyText}>{vaccineStrings.oops_no_temperatures}</Text>
  </FlexView>
);

EmptyComponent.propTypes = {
  sensorName: PropTypes.string.isRequired,
};

export const FridgeDetailPageComponent = ({
  breaches,
  minLine,
  maxLine,
  minDomain,
  maxDomain,
  onChangeToDate,
  onChangeFromDate,
  fromDate,
  toDate,
  minimumDate,
  maximumDate,
  numberOfHotBreaches,
  numberOfColdBreaches,
  hotCumulativeBreach,
  coldCumulativeBreach,
  averageTemperature,
  isInHotBreach,
  isInColdBreach,
  isLowBattery,
  currentTemperature,
  sensor,
  breachBoundaries,
}) => {
  const [chartType, setChartType] = useState('bar');
  const getIconButton = type => {
    const iconStyle = { color: chartType === type ? WARMER_GREY : DARKER_GREY };
    return (
      <IconButton
        onPress={() => setChartType(type)}
        Icon={
          type === 'line' ? <LineChartIcon style={iconStyle} /> : <BarChartIcon style={iconStyle} />
        }
        containerStyle={{ padding: 8 }}
      />
    );
  };

  if (!minLine.length || !maxLine.length) {
    return (
      <AfterInteractions>
        <View style={localStyles.container}>
          <View style={localStyles.topRow}>
            <DateRangeSelector
              containerStyle={localStyles.datePickerContainer}
              initialStartDate={fromDate}
              initialEndDate={toDate}
              onChangeToDate={onChangeToDate}
              onChangeFromDate={onChangeFromDate}
              minimumDate={minimumDate}
              maximumDate={maximumDate}
            />
          </View>
          <EmptyComponent sensorName={sensor.name} />
        </View>
      </AfterInteractions>
    );
  }

  return (
    <DataTablePageView>
      <AfterInteractions>
        <View style={localStyles.container}>
          <View style={localStyles.topRow}>
            <DateRangeSelector
              containerStyle={localStyles.datePickerContainer}
              initialStartDate={fromDate}
              initialEndDate={toDate}
              onChangeToDate={onChangeToDate}
              onChangeFromDate={onChangeFromDate}
              minimumDate={minimumDate}
              maximumDate={maximumDate}
            />
            <View style={localStyles.buttonContainer}>
              {getIconButton('line')}
              {getIconButton('bar')}
            </View>
          </View>

          <Paper
            height={300}
            contentContainerStyle={{ flex: 1, marginTop: 20 }}
            Header={<SensorHeader showTitle showCog sensor={sensor} />}
          >
            <AfterInteractions>
              <FlexRow alignItems="center">
                {chartType === 'line' && (
                  <VaccineLineChart
                    breaches={breaches}
                    minLine={minLine}
                    maxLine={maxLine}
                    minDomain={minDomain}
                    maxDomain={maxDomain}
                    breachBoundaries={breachBoundaries}
                  />
                )}
                {chartType === 'bar' && (
                  <VaccineBarChart
                    breaches={breaches}
                    minLine={minLine}
                    maxLine={maxLine}
                    minDomain={minDomain}
                    maxDomain={maxDomain}
                    breachBoundaries={breachBoundaries}
                  />
                )}
                <SensorStatus
                  isInHotBreach={isInHotBreach}
                  isInColdBreach={isInColdBreach}
                  isLowBattery={isLowBattery}
                  currentTemp={currentTemperature}
                />
              </FlexRow>
            </AfterInteractions>
          </Paper>

          <Animatable.View animation="fadeIn" duration={3000} useNativeDriver>
            <FlexRow>
              <BreachCard headerText={vaccineStrings.cumulative_breach}>
                {coldCumulativeBreach ? (
                  <>
                    <Text style={localStyles.coldText}>{coldCumulativeBreach}</Text>
                    <ColdBreachIcon color={COLD_BREACH_BLUE} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>

              <BreachCard headerText={vaccineStrings.consecutive_breach}>
                {numberOfColdBreaches ? (
                  <>
                    <Text style={localStyles.coldText}>{numberOfColdBreaches}</Text>
                    <ColdBreachIcon color={COLD_BREACH_BLUE} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>

              <BreachCard headerText={vaccineStrings.average_temperature}>
                <Text style={[localStyles.hotText, { color: DARKER_GREY }]}>
                  {averageTemperature}
                </Text>
              </BreachCard>

              <BreachCard headerText={vaccineStrings.cumulative_breach}>
                {hotCumulativeBreach ? (
                  <>
                    <Text style={localStyles.hotText}>{hotCumulativeBreach}</Text>
                    <HotBreachIcon color={DANGER_RED} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>

              <BreachCard headerText={vaccineStrings.consecutive_breach}>
                {numberOfHotBreaches ? (
                  <>
                    <Text style={localStyles.hotText}>{numberOfHotBreaches}</Text>
                    <HotBreachIcon color={DANGER_RED} />
                  </>
                ) : (
                  <NoBreachMessage />
                )}
              </BreachCard>
            </FlexRow>
          </Animatable.View>
        </View>
      </AfterInteractions>
    </DataTablePageView>
  );
};

const stateToProps = (state, props) => {
  const { route } = props;
  const { params } = route;
  const { fridge: fridgeProp } = params;
  const { sensor } = fridgeProp;
  const { fridge } = state;
  const { code } = fridge;

  const breaches = selectBreaches(state);
  const { minLine, maxLine } = selectMinAndMaxLogs(state);
  const { minDomain, maxDomain } = selectMinAndMaxDomains(state);
  const fromDate = selectTemperatureLogsFromDate(state);
  const toDate = selectTemperatureLogsToDate(state);
  const minimumDate = selectLeastRecentTemperatureLogDate(state);
  const maximumDate = selectMostRecentTemperatureLogDate(state);
  const numberOfHotBreaches = selectNumberOfHotConsecutiveBreaches(state);
  const numberOfColdBreaches = selectNumberOfColdConsecutiveBreaches(state);
  const averageTemperature = selectAverageTemperature(state);
  const hotCumulativeBreach = selectHotCumulativeBreach(state);
  const coldCumulativeBreach = selectColdCumulativeBreach(state);
  const isInHotBreach = selectSelectedFridgeIsInHotBreach(state);
  const isInColdBreach = selectSelectedFridgeIsInColdBreach(state);
  const isLowBattery = selectSelectedFridgeSensorIsLowBattery(state);
  const currentTemperature = selectSelectFridgeCurrentTemperature(state);
  const breachBoundaries = selectBreachBoundaries(state);

  return {
    sensor,
    fridge: fridgeProp,
    averageTemperature,
    coldCumulativeBreach,
    hotCumulativeBreach,
    numberOfColdBreaches,
    numberOfHotBreaches,
    code,
    breaches,
    minLine,
    maxLine,
    minDomain,
    maxDomain,
    fromDate,
    toDate,
    minimumDate,
    maximumDate,
    isInHotBreach,
    isInColdBreach,
    isLowBattery,
    currentTemperature,
    breachBoundaries,
  };
};

const dispatchToProps = dispatch => ({
  onChangeToDate: date => dispatch(FridgeActions.changeToDate(date)),
  onChangeFromDate: date => dispatch(FridgeActions.changeFromDate(date)),
});

FridgeDetailPageComponent.defaultProps = {
  coldCumulativeBreach: null,
  currentTemperature: null,
  hotCumulativeBreach: null,
  maximumDate: null,
  minimumDate: null,
};

FridgeDetailPageComponent.propTypes = {
  breaches: PropTypes.object.isRequired,
  minLine: PropTypes.array.isRequired,
  maxLine: PropTypes.array.isRequired,
  minDomain: PropTypes.number.isRequired,
  maxDomain: PropTypes.number.isRequired,
  onChangeToDate: PropTypes.func.isRequired,
  onChangeFromDate: PropTypes.func.isRequired,
  fromDate: PropTypes.instanceOf(Date).isRequired,
  toDate: PropTypes.instanceOf(Date).isRequired,
  minimumDate: PropTypes.instanceOf(Date),
  maximumDate: PropTypes.instanceOf(Date),
  numberOfHotBreaches: PropTypes.number.isRequired,
  numberOfColdBreaches: PropTypes.number.isRequired,
  hotCumulativeBreach: PropTypes.string,
  coldCumulativeBreach: PropTypes.string,
  averageTemperature: PropTypes.string.isRequired,
  isInHotBreach: PropTypes.bool.isRequired,
  isInColdBreach: PropTypes.bool.isRequired,
  isLowBattery: PropTypes.bool.isRequired,
  currentTemperature: PropTypes.number,
  sensor: PropTypes.object.isRequired,
  breachBoundaries: PropTypes.object.isRequired,
};

const localStyles = StyleSheet.create({
  container: { padding: 20, flex: 1, backgroundColor: BLUE_WHITE },
  datePickerContainer: { width: 250, borderRadius: 50, height: 50 },
  card: { flex: 1 },
  hotText: {
    color: DANGER_RED,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  coldText: {
    color: COLD_BREACH_BLUE,
    fontSize: 50,
    fontFamily: APP_FONT_FAMILY,
  },
  emptyText: {
    fontSize: 28,
  },
  emptyComponent: {
    alignItems: 'center',
    color: DARKER_GREY,
    flex: 1,
    fontFamily: APP_FONT_FAMILY,
    fontWeight: 'bold',
    justifyContent: 'center',
  },
  noBreachText: {
    fontSize: 12,
    color: DARKER_GREY,
    fontFamily: APP_FONT_FAMILY,
  },
  buttonContainer: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
  topRow: { flexDirection: 'row' },
});

export const FridgeDetailPage = connect(stateToProps, dispatchToProps)(FridgeDetailPageComponent);
<|endoftext|>


<commit_before>/* @flow weak */

/**
 * OfflineMobile Android
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import { StyleSheet, View } from 'react-native';
import globalStyles, { SUSSOL_ORANGE } from '../globalStyles';

import {
  Cell,
  CheckableCell,
  DataTable,
  EditableCell,
  Header,
  HeaderCell,
  Row,
} from '../widgets/DataTable';

import Icon from 'react-native-vector-icons/Ionicons';
import { ListView } from 'realm/react-native';
import { SearchBar } from '../widgets';

/**
 * Provides a generic implementation of a standard page in mSupply Mobile, which
 * contains a searchable table. Should always be overridden, in particular the
 * following methods and instance variables (fields):
 * @method getUpdatedData(searchTerm, sortBy, isAscending) Should return updated data
 * @method renderCell(key, item) Should define what to render in a cell with the
 *         											 given column key and database item
 * @method onRowPress(key, item) Should define behaviour when a row is pressed,
 *         											 don't override if row should not be pressable
 * @method onEndEditing(key, item, newValue) Handles user input to an editable cell
 *         											 don't override if row should not be pressable
 * @field  {array}  columns      An array of objects defining each of the columns.
 *         											 Each column must contain: key, width, title. Each
 *         											 may optionally also contain a boolean 'sortable'.
 * @state  {ListView.DataSource} dataSource    DataTable input, used to update rows
 *         																		 being rendered
 * @state  {string}              searchTerm    Current term user has entered in search bar
 * @state  {string}              sortBy        The property to sort by (is selected
 *                                             by column press).
 * @state  {boolean}             isAscending   Direction sortBy should sort
 *                                             (ascending/descending:true/false).
 * N.B. Take care to call parent method if overriding any of the react life cycle methods.
 */
export class GenericTablePage extends React.Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource: dataSource,
      searchTerm: '',
      sortBy: '',
      isAscending: true,
      selection: [],
    };
    this.columns = null;
    this.dataTypesDisplayed = [];
    this.onSearchChange = this.onSearchChange.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.onDatabaseEvent = this.onDatabaseEvent.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.renderCell = this.renderCell.bind(this);
    this.refreshData = this.refreshData.bind(this);
  }

  /**
   * If overridden, first line of this method should be duplicated. May need to be overridden to
   * populate selection in state if CheckableCells are used and need to
   * remember their selected state.
   */
  componentWillMount() {
    this.databaseListenerId = this.props.database.addListener(this.onDatabaseEvent);
    this.refreshData();
  }

  componentWillUnmount() {
    this.props.database.removeListener(this.databaseListenerId);
  }

  onDatabaseEvent(changeType, recordType) {
    if (this.dataTypesDisplayed.indexOf(recordType) >= 0) this.refreshData();
  }

  onSearchChange(event) {
    const term = event.nativeEvent.text;
    this.setState({ searchTerm: term }, this.refreshData);
    this.refreshData();
  }

  onColumnSort(sortBy) {
    if (this.state.sortBy === sortBy) { // Changed column sort direction
      this.setState({ isAscending: !this.state.isAscending }, this.refreshData);
    } else { // Changed sorting column
      this.setState({
        sortBy: sortBy,
        isAscending: true,
      }, this.refreshData);
    }
  }

  /**
   * Adds/removes item.id to/from the selection array in state. Must call this within any overrides.
   * i.e. super.onCheckablePress(item);
   */
  onCheckablePress(item) {
    const newSelection = [...this.state.selection];
    if (newSelection.indexOf(item.id) >= 0) {
      newSelection.splice(newSelection.indexOf(item.id), 1);
    } else {
      newSelection.push(item.id);
    }
    this.setState({ selection: newSelection });
  }

  refreshData() {
    const { dataSource, searchTerm, sortBy, isAscending } = this.state;
    const data = this.getUpdatedData(searchTerm, sortBy, isAscending);
    this.setState({ dataSource: dataSource.cloneWithRows(data) });
  }

/**
 * Cell formats:
 * 1. item.name;
 * 2. {
 *      type: 'cell',
 *      cellContents: item.name,
 *    };
 * 3. {
 *      type: 'editable',
 *      cellContents: transactionItem.totalQuantity,
 *      editable: !this.props.transaction.isFinalised,
 *    };
 * 4. {
 *      type: 'checkable',
 *    };
 * 5. {
 *      type: 'checkable',
 *      icon: 'md-remove-circle', // will use for both Checked and NotChecked, only colour changes
 *    };
 * 6. {
 *      type: 'checkable',
 *      iconChecked: 'md-radio-button-on',
 *      iconNotChecked: 'md-radio-button-off',
 *    };
 */
  renderCell() {
    return 'DEFAULT CELL';
  }

  renderHeader() {
    const headerCells = [];
    this.columns.forEach((column) => {
      const sortFunction = column.sortable ? () => this.onColumnSort(column.key) : null;
      headerCells.push(
        <HeaderCell
          key={column.key}
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={column.width}
          onPress={sortFunction}
          isAscending={this.state.isAscending}
          isSelected={this.state.sortBy === column.key}
          text={column.title}
        />
      );
    });
    return (
      <Header style={globalStyles.header}>
        {headerCells}
      </Header>
    );
  }

  renderRow(item) {
    const cells = [];
    this.columns.forEach((column) => {
      const renderedCell = this.renderCell(column.key, item);
      let cell;
      switch (renderedCell.type) {
        case 'custom':
          cell = renderedCell.cell;
          break;
        case 'checkable': {
          const isChecked = renderedCell.isChecked ?
            renderedCell.isChecked : this.state.selection.indexOf(item.id) >= 0;
          let iconChecked;
          let iconNotChecked;
          if (renderedCell.iconChecked && renderedCell.iconNotChecked) {
            iconChecked = renderedCell.iconChecked;
            iconNotChecked = renderedCell.iconNotChecked;
          } else if (renderedCell.icon) {
            iconChecked = renderedCell.icon;
            iconNotChecked = renderedCell.icon;
          } else {
            iconChecked = 'md-radio-button-on';
            iconNotChecked = 'md-radio-button-off';
          }
          cell = (
            <CheckableCell
              style={[
                globalStyles.dataTableCell,
                globalStyles.dataTableCheckableCell,
              ]}
              width={column.width}
              onPress={() => this.onCheckablePress(item)}
              renderIsChecked={<Icon name={iconChecked} size={15} color={SUSSOL_ORANGE} />}
              renderIsNotChecked={<Icon name={iconNotChecked} size={15} color={'grey'} />}
              isChecked={isChecked}
            />
          );
          break;
        }
        case 'editable':
          cell = (
            <EditableCell
              key={column.key}
              style={globalStyles.dataTableCell}
              textStyle={globalStyles.dataTableText}
              width={column.width}
              onEndEditing={this.onEndEditing &&
                            ((target, value) => this.onEndEditing(column.key, target, value))}
              target={item}
              value={renderedCell.cellContents}
            />
          );
          break;
        case 'finalised':
        case 'cell':
        default:
          cell = (
            <Cell
              key={column.key}
              style={globalStyles.dataTableCell}
              textStyle={globalStyles.dataTableText}
              width={column.width}
            >
              {renderedCell.cellContents ? renderedCell.cellContents : renderedCell}
            </Cell>
          );
      }
      cells.push(cell);
    });
    return (
      <Row
        style={globalStyles.dataTableRow}
        onPress={this.onRowPress && (() => this.onRowPress(item))}
      >
        {cells}
      </Row>
    );
  }

  renderSearchBar() {
    return (
      <SearchBar
        onChange={(event) => this.onSearchChange(event)}
      />);
  }

  renderDataTable() {
    return (
      <DataTable
        style={globalStyles.dataTable}
        listViewStyle={localStyles.listView}
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        renderHeader={this.renderHeader}
      />);
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.pageTopSectionContainer}>
            {this.renderSearchBar()}
          </View>
          {this.renderDataTable()}
        </View>
      </View>
    );
  }
}

GenericTablePage.propTypes = {
  database: React.PropTypes.object,
};

const localStyles = StyleSheet.create({
  listView: {
    flex: 1,
  },
  rightMostCell: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<commit_msg>Added key to CheckableCell to fix warning<commit_after>/* @flow weak */

/**
 * OfflineMobile Android
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import { StyleSheet, View } from 'react-native';
import globalStyles, { SUSSOL_ORANGE } from '../globalStyles';

import {
  Cell,
  CheckableCell,
  DataTable,
  EditableCell,
  Header,
  HeaderCell,
  Row,
} from '../widgets/DataTable';

import Icon from 'react-native-vector-icons/Ionicons';
import { ListView } from 'realm/react-native';
import { SearchBar } from '../widgets';

/**
 * Provides a generic implementation of a standard page in mSupply Mobile, which
 * contains a searchable table. Should always be overridden, in particular the
 * following methods and instance variables (fields):
 * @method getUpdatedData(searchTerm, sortBy, isAscending) Should return updated data
 * @method renderCell(key, item) Should define what to render in a cell with the
 *         											 given column key and database item
 * @method onRowPress(key, item) Should define behaviour when a row is pressed,
 *         											 don't override if row should not be pressable
 * @method onEndEditing(key, item, newValue) Handles user input to an editable cell
 *         											 don't override if row should not be pressable
 * @field  {array}  columns      An array of objects defining each of the columns.
 *         											 Each column must contain: key, width, title. Each
 *         											 may optionally also contain a boolean 'sortable'.
 * @state  {ListView.DataSource} dataSource    DataTable input, used to update rows
 *         																		 being rendered
 * @state  {string}              searchTerm    Current term user has entered in search bar
 * @state  {string}              sortBy        The property to sort by (is selected
 *                                             by column press).
 * @state  {boolean}             isAscending   Direction sortBy should sort
 *                                             (ascending/descending:true/false).
 * N.B. Take care to call parent method if overriding any of the react life cycle methods.
 */
export class GenericTablePage extends React.Component {
  constructor(props) {
    super(props);
    const dataSource = new ListView.DataSource({
      rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
      dataSource: dataSource,
      searchTerm: '',
      sortBy: '',
      isAscending: true,
      selection: [],
    };
    this.columns = null;
    this.dataTypesDisplayed = [];
    this.onSearchChange = this.onSearchChange.bind(this);
    this.onColumnSort = this.onColumnSort.bind(this);
    this.onDatabaseEvent = this.onDatabaseEvent.bind(this);
    this.renderHeader = this.renderHeader.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.renderCell = this.renderCell.bind(this);
    this.refreshData = this.refreshData.bind(this);
  }

  /**
   * If overridden, first line of this method should be duplicated. May need to be overridden to
   * populate selection in state if CheckableCells are used and need to
   * remember their selected state.
   */
  componentWillMount() {
    this.databaseListenerId = this.props.database.addListener(this.onDatabaseEvent);
    this.refreshData();
  }

  componentWillUnmount() {
    this.props.database.removeListener(this.databaseListenerId);
  }

  onDatabaseEvent(changeType, recordType) {
    if (this.dataTypesDisplayed.indexOf(recordType) >= 0) this.refreshData();
  }

  onSearchChange(event) {
    const term = event.nativeEvent.text;
    this.setState({ searchTerm: term }, this.refreshData);
    this.refreshData();
  }

  onColumnSort(sortBy) {
    if (this.state.sortBy === sortBy) { // Changed column sort direction
      this.setState({ isAscending: !this.state.isAscending }, this.refreshData);
    } else { // Changed sorting column
      this.setState({
        sortBy: sortBy,
        isAscending: true,
      }, this.refreshData);
    }
  }

  /**
   * Adds/removes item.id to/from the selection array in state. Must call this within any overrides.
   * i.e. super.onCheckablePress(item);
   */
  onCheckablePress(item) {
    const newSelection = [...this.state.selection];
    if (newSelection.indexOf(item.id) >= 0) {
      newSelection.splice(newSelection.indexOf(item.id), 1);
    } else {
      newSelection.push(item.id);
    }
    this.setState({ selection: newSelection });
  }

  refreshData() {
    const { dataSource, searchTerm, sortBy, isAscending } = this.state;
    const data = this.getUpdatedData(searchTerm, sortBy, isAscending);
    this.setState({ dataSource: dataSource.cloneWithRows(data) });
  }

/**
 * Cell formats:
 * 1. item.name;
 * 2. {
 *      type: 'cell',
 *      cellContents: item.name,
 *    };
 * 3. {
 *      type: 'editable',
 *      cellContents: transactionItem.totalQuantity,
 *      editable: !this.props.transaction.isFinalised,
 *    };
 * 4. {
 *      type: 'checkable',
 *    };
 * 5. {
 *      type: 'checkable',
 *      icon: 'md-remove-circle', // will use for both Checked and NotChecked, only colour changes
 *    };
 * 6. {
 *      type: 'checkable',
 *      iconChecked: 'md-radio-button-on',
 *      iconNotChecked: 'md-radio-button-off',
 *    };
 */
  renderCell() {
    return 'DEFAULT CELL';
  }

  renderHeader() {
    const headerCells = [];
    this.columns.forEach((column) => {
      const sortFunction = column.sortable ? () => this.onColumnSort(column.key) : null;
      headerCells.push(
        <HeaderCell
          key={column.key}
          style={globalStyles.dataTableHeaderCell}
          textStyle={globalStyles.dataTableText}
          width={column.width}
          onPress={sortFunction}
          isAscending={this.state.isAscending}
          isSelected={this.state.sortBy === column.key}
          text={column.title}
        />
      );
    });
    return (
      <Header style={globalStyles.header}>
        {headerCells}
      </Header>
    );
  }

  renderRow(item) {
    const cells = [];
    this.columns.forEach((column) => {
      const renderedCell = this.renderCell(column.key, item);
      let cell;
      switch (renderedCell.type) {
        case 'custom':
          cell = renderedCell.cell;
          break;
        case 'checkable': {
          const isChecked = renderedCell.isChecked ?
            renderedCell.isChecked : this.state.selection.indexOf(item.id) >= 0;
          let iconChecked;
          let iconNotChecked;
          if (renderedCell.iconChecked && renderedCell.iconNotChecked) {
            iconChecked = renderedCell.iconChecked;
            iconNotChecked = renderedCell.iconNotChecked;
          } else if (renderedCell.icon) {
            iconChecked = renderedCell.icon;
            iconNotChecked = renderedCell.icon;
          } else {
            iconChecked = 'md-radio-button-on';
            iconNotChecked = 'md-radio-button-off';
          }
          cell = (
            <CheckableCell
              key={column.key}
              style={[
                globalStyles.dataTableCell,
                globalStyles.dataTableCheckableCell,
              ]}
              width={column.width}
              onPress={() => this.onCheckablePress(item)}
              renderIsChecked={<Icon name={iconChecked} size={15} color={SUSSOL_ORANGE} />}
              renderIsNotChecked={<Icon name={iconNotChecked} size={15} color={'grey'} />}
              isChecked={isChecked}
            />
          );
          break;
        }
        case 'editable':
          cell = (
            <EditableCell
              key={column.key}
              style={globalStyles.dataTableCell}
              textStyle={globalStyles.dataTableText}
              width={column.width}
              onEndEditing={this.onEndEditing &&
                            ((target, value) => this.onEndEditing(column.key, target, value))}
              target={item}
              value={renderedCell.cellContents}
            />
          );
          break;
        case 'finalised':
        case 'cell':
        default:
          cell = (
            <Cell
              key={column.key}
              style={globalStyles.dataTableCell}
              textStyle={globalStyles.dataTableText}
              width={column.width}
            >
              {renderedCell.cellContents ? renderedCell.cellContents : renderedCell}
            </Cell>
          );
      }
      cells.push(cell);
    });
    return (
      <Row
        style={globalStyles.dataTableRow}
        onPress={this.onRowPress && (() => this.onRowPress(item))}
      >
        {cells}
      </Row>
    );
  }

  renderSearchBar() {
    return (
      <SearchBar
        onChange={(event) => this.onSearchChange(event)}
      />);
  }

  renderDataTable() {
    return (
      <DataTable
        style={globalStyles.dataTable}
        listViewStyle={localStyles.listView}
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        renderHeader={this.renderHeader}
      />);
  }

  render() {
    return (
      <View style={globalStyles.pageContentContainer}>
        <View style={globalStyles.container}>
          <View style={globalStyles.pageTopSectionContainer}>
            {this.renderSearchBar()}
          </View>
          {this.renderDataTable()}
        </View>
      </View>
    );
  }
}

GenericTablePage.propTypes = {
  database: React.PropTypes.object,
};

const localStyles = StyleSheet.create({
  listView: {
    flex: 1,
  },
  rightMostCell: {
    borderRightWidth: 0,
  },
  dataTable: {
    flex: 1,
  },
});
<|endoftext|>


<commit_before>
var dom      = exports.dom = require("./jsdom/level3/index").dom,
    fs       = require("fs"),
    pkg      = JSON.parse(fs.readFileSync(__dirname + "/../package.json")),
    request  = require('request'),
    URL      = require('url');

var style = require('./jsdom/level2/style');
exports.defaultLevel = dom.level3.html;
exports.browserAugmentation = require("./jsdom/browser/index").browserAugmentation;
exports.windowAugmentation = require("./jsdom/browser/index").windowAugmentation;

exports.debugMode = false;

var createWindow = exports.createWindow = require("./jsdom/browser/index").createWindow;

exports.__defineGetter__('version', function() {
  return pkg.version;
});

exports.jsdom = function (html, level, options) {

  options = options || {};
  level   = level || exports.defaultLevel;

  if (!options.url) {
    options.url = (module.parent.id === 'jsdom') ?
                  module.parent.parent.filename  :
                  module.parent.filename;
  }

  if (options.features && options.features.QuerySelector) {
    require("./jsdom/selectors/index").applyQuerySelectorPrototype(level);
  }

  var browser = exports.browserAugmentation(level, options),
      doc     = (browser.HTMLDocument)             ?
                 new browser.HTMLDocument(options) :
                 new browser.Document(options);

  exports.applyDocumentFeatures(doc, options.features);

  if (!!html) {
    doc.write(html + '');
  } else {
    doc.write('');
  }

  if (doc.close && !options.deferClose) {
    doc.close();
  }

  // Kept for backwards-compatibility. The window is lazily created when
  // document.parentWindow or document.defaultView is accessed.
  doc.createWindow = function() {
    // Remove ourself
    if (doc.createWindow) {
      delete doc.createWindow;
    }
    return doc.parentWindow;
  };

  return doc;
};

exports.html = function(html, level, options) {
  html += '';

  // TODO: cache a regex and use it here instead
  //       or make the parser handle it
  var htmlLowered = html.toLowerCase();

  // body
  if (!~htmlLowered.indexOf('<body')) {
    html = '' + html + '';
  }

  // html
  if (!~htmlLowered.indexOf('<html')) {
    html = '' + html + '';
  }
  return exports.jsdom(html, level, options);
};

exports.availableDocumentFeatures = [
  'FetchExternalResources',
  'ProcessExternalResources',
  'MutationEvents',
  'QuerySelector'
];

exports.defaultDocumentFeatures = {
  "FetchExternalResources"   : ['script'/*, 'img', 'css', 'frame', 'link'*/],
  "ProcessExternalResources" : ['script'/*, 'frame', 'iframe'*/],
  "MutationEvents"           : '2.0',
  "QuerySelector"            : false
};

exports.applyDocumentFeatures = function(doc, features) {
  var i, maxFeatures = exports.availableDocumentFeatures.length,
      defaultFeatures = exports.defaultDocumentFeatures,
      j,
      k,
      featureName,
      featureSource;

  features = features || {};

  for (i=0; i<maxFeatures; i++) {
    featureName = exports.availableDocumentFeatures[i];
    if (typeof features[featureName] !== 'undefined') {
      featureSource = features[featureName];
    } else if (defaultFeatures[featureName]) {
      featureSource = defaultFeatures[featureName];
    } else {
      continue;
    }

    doc.implementation.removeFeature(featureName);

    if (typeof featureSource !== 'undefined') {
      if (featureSource instanceof Array) {
        k = featureSource.length;
        for (j=0; j<k; j++) {
          doc.implementation.addFeature(featureName, featureSource[j]);
        }
      } else {
        doc.implementation.addFeature(featureName, featureSource);
      }
    }
  }
};

exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {

  if (!window || !window.document) { return; }

  var args = Array.prototype.slice.call(arguments),
      callback = (typeof(args[args.length - 1]) === 'function') && args.pop(),
      path,
      jQueryTag = window.document.createElement("script");

  if (args.length > 1 && typeof(args[1] === 'string')) {
    path = args[1];
  }

  var features = window.document.implementation._features;

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ["1.0"]);
  jQueryTag.src = path || 'http://code.jquery.com/jquery-latest.js';
  window.document.body.appendChild(jQueryTag);

  jQueryTag.onload = function() {
    if (callback) {
      callback(window, window.jQuery);
    }

    window.document.implementation._features = features;
  };

  return window;
};

exports.env = exports.jsdom.env = function() {
  var
  args        = Array.prototype.slice.call(arguments),
  config      = exports.env.processArguments(args),
  callback    = config.done,
  processHTML = function(err, html) {

    html += '';
    if(err) {
      return callback(err);
    }

    config.scripts = config.scripts || [];
    if (typeof config.scripts === 'string') {
      config.scripts = [config.scripts];
    }

    var
    window     = exports.html(html, null, {
      features : {
        'FetchExternalResources' : false,
        'ProcessExternalResources' : false
      }
    }).createWindow(),
    features   = window.document.implementation._features,
    docsLoaded = 0,
    totalDocs  = config.scripts.length,
    readyState = null,
    errors     = null;

    if (!window || !window.document) {
      return callback(new Error('JSDOM: a window object could not be created.'));
    }

    window.document.implementation.addFeature('FetchExternalResources', ['script']);
    window.document.implementation.addFeature('ProcessExternalResources', ['script']);
    window.document.implementation.addFeature('MutationEvents', ['1.0']);

    if (totalDocs > 0) {
      config.scripts.forEach(function(src) {
        var
        script = window.document.createElement('script'),
        scriptComplete = function() {
          docsLoaded++;
          if (docsLoaded >= totalDocs) {
            window.document.implementation._features = features;
            callback(errors, window);
          }
        };

        script.onload = function() {
          process.nextTick(scriptComplete);
        };

        script.onerror = function(e) {
          if (!errors) {
            errors = [];
          }
          errors.push(e.error || e.message);
          // nextTick so that an exception within scriptComplete won't cause
          // another script onerror (which would be an infinite loop)
          process.nextTick(scriptComplete);
        };

        script.src = src;
        window.document.documentElement.appendChild(script);
        window.document.documentElement.removeChild(script);
      });
    } else {
      callback(errors, window);
    }
  };

  config.html += '';

  // Handle markup
  if (config.html.indexOf("\n") > 0 || config.html.match(/^\W*</)) {
    processHTML(null, config.html);

  // Handle url/file
  } else {
    var url = URL.parse(config.html);
    if (url.hostname) {
      request({uri: url, encoding: config.encoding || 'utf8'}, function(err, request, body) {
        processHTML(err, body);
      });
    } else {
      fs.readFile(url.pathname, processHTML);
    }
  }
};

/*
  Since jsdom.env() is a helper for quickly and easily setting up a
  window with scripts and such already loaded into it, the arguments
  should be fairly flexible.  Here are the requirements

  1) collect `html` (url, string, or file on disk)  (STRING)
  2) load `code` into the window (array of scripts) (ARRAY)
  3) callback when resources are `done`             (FUNCTION)
  4) configuration                                  (OBJECT)

  Rules:
  + if there is one argument it had better be an object with atleast
    a `html` and `done` property (other properties are gravy)

  + arguments above are pulled out of the arguments and put into the
    config object that is returned
*/
exports.env.processArguments = function(args) {
  if (!args || !args.length || args.length < 1) {
    throw new Error('No arguments passed to jsdom.env().');
  }

  var
  props = {
    'html'    : true,
    'done'    : true,
    'scripts' : false,
    'config'  : false
  },
  propKeys = Object.keys(props),
  config = {
    code : []
  },
  l    = args.length
  ;
  if (l === 1) {
    config = args[0];
  } else {
    args.forEach(function(v) {
      var type = typeof v;
      if (!v) {
        return;
      }
      if (type === 'string' || v + '' === v) {
        config.html = v;
      } else if (type === 'object') {
        // Array
        if (v.length && v[0]) {
          config.scripts = v;
        } else {
          // apply missing required properties if appropriate
          propKeys.forEach(function(req) {

            if (typeof v[req] !== 'undefined' &&
                typeof config[req] === 'undefined') {

              config[req] = v[req];
              delete v[req];
            }
          });
          config.config = v;
        }
      } else if (type === 'function') {
        config.done = v;
      }
    });
  }

  propKeys.forEach(function(req) {
    var required = props[req];
    if (required && typeof config[req] === 'undefined') {
      throw new Error("jsdom.env requires a '" + req + "' argument");
    }
  });
  return config;
};
<commit_msg>Added configuration property 'headers' for HTTP request headers. Example usage:<commit_after>
var dom      = exports.dom = require("./jsdom/level3/index").dom,
    fs       = require("fs"),
    pkg      = JSON.parse(fs.readFileSync(__dirname + "/../package.json")),
    request  = require('request'),
    URL      = require('url');

var style = require('./jsdom/level2/style');
exports.defaultLevel = dom.level3.html;
exports.browserAugmentation = require("./jsdom/browser/index").browserAugmentation;
exports.windowAugmentation = require("./jsdom/browser/index").windowAugmentation;

exports.debugMode = false;

var createWindow = exports.createWindow = require("./jsdom/browser/index").createWindow;

exports.__defineGetter__('version', function() {
  return pkg.version;
});

exports.jsdom = function (html, level, options) {

  options = options || {};
  level   = level || exports.defaultLevel;

  if (!options.url) {
    options.url = (module.parent.id === 'jsdom') ?
                  module.parent.parent.filename  :
                  module.parent.filename;
  }

  if (options.features && options.features.QuerySelector) {
    require("./jsdom/selectors/index").applyQuerySelectorPrototype(level);
  }

  var browser = exports.browserAugmentation(level, options),
      doc     = (browser.HTMLDocument)             ?
                 new browser.HTMLDocument(options) :
                 new browser.Document(options);

  exports.applyDocumentFeatures(doc, options.features);

  if (!!html) {
    doc.write(html + '');
  } else {
    doc.write('');
  }

  if (doc.close && !options.deferClose) {
    doc.close();
  }

  // Kept for backwards-compatibility. The window is lazily created when
  // document.parentWindow or document.defaultView is accessed.
  doc.createWindow = function() {
    // Remove ourself
    if (doc.createWindow) {
      delete doc.createWindow;
    }
    return doc.parentWindow;
  };

  return doc;
};

exports.html = function(html, level, options) {
  html += '';

  // TODO: cache a regex and use it here instead
  //       or make the parser handle it
  var htmlLowered = html.toLowerCase();

  // body
  if (!~htmlLowered.indexOf('<body')) {
    html = '' + html + '';
  }

  // html
  if (!~htmlLowered.indexOf('<html')) {
    html = '' + html + '';
  }
  return exports.jsdom(html, level, options);
};

exports.availableDocumentFeatures = [
  'FetchExternalResources',
  'ProcessExternalResources',
  'MutationEvents',
  'QuerySelector'
];

exports.defaultDocumentFeatures = {
  "FetchExternalResources"   : ['script'/*, 'img', 'css', 'frame', 'link'*/],
  "ProcessExternalResources" : ['script'/*, 'frame', 'iframe'*/],
  "MutationEvents"           : '2.0',
  "QuerySelector"            : false
};

exports.applyDocumentFeatures = function(doc, features) {
  var i, maxFeatures = exports.availableDocumentFeatures.length,
      defaultFeatures = exports.defaultDocumentFeatures,
      j,
      k,
      featureName,
      featureSource;

  features = features || {};

  for (i=0; i<maxFeatures; i++) {
    featureName = exports.availableDocumentFeatures[i];
    if (typeof features[featureName] !== 'undefined') {
      featureSource = features[featureName];
    } else if (defaultFeatures[featureName]) {
      featureSource = defaultFeatures[featureName];
    } else {
      continue;
    }

    doc.implementation.removeFeature(featureName);

    if (typeof featureSource !== 'undefined') {
      if (featureSource instanceof Array) {
        k = featureSource.length;
        for (j=0; j<k; j++) {
          doc.implementation.addFeature(featureName, featureSource[j]);
        }
      } else {
        doc.implementation.addFeature(featureName, featureSource);
      }
    }
  }
};

exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {

  if (!window || !window.document) { return; }

  var args = Array.prototype.slice.call(arguments),
      callback = (typeof(args[args.length - 1]) === 'function') && args.pop(),
      path,
      jQueryTag = window.document.createElement("script");

  if (args.length > 1 && typeof(args[1] === 'string')) {
    path = args[1];
  }

  var features = window.document.implementation._features;

  window.document.implementation.addFeature('FetchExternalResources', ['script']);
  window.document.implementation.addFeature('ProcessExternalResources', ['script']);
  window.document.implementation.addFeature('MutationEvents', ["1.0"]);
  jQueryTag.src = path || 'http://code.jquery.com/jquery-latest.js';
  window.document.body.appendChild(jQueryTag);

  jQueryTag.onload = function() {
    if (callback) {
      callback(window, window.jQuery);
    }

    window.document.implementation._features = features;
  };

  return window;
};

exports.env = exports.jsdom.env = function() {
  var
  args        = Array.prototype.slice.call(arguments),
  config      = exports.env.processArguments(args),
  callback    = config.done,
  processHTML = function(err, html) {

    html += '';
    if(err) {
      return callback(err);
    }

    config.scripts = config.scripts || [];
    if (typeof config.scripts === 'string') {
      config.scripts = [config.scripts];
    }

    var
    window     = exports.html(html, null, {
      features : {
        'FetchExternalResources' : false,
        'ProcessExternalResources' : false
      }
    }).createWindow(),
    features   = window.document.implementation._features,
    docsLoaded = 0,
    totalDocs  = config.scripts.length,
    readyState = null,
    errors     = null;

    if (!window || !window.document) {
      return callback(new Error('JSDOM: a window object could not be created.'));
    }

    window.document.implementation.addFeature('FetchExternalResources', ['script']);
    window.document.implementation.addFeature('ProcessExternalResources', ['script']);
    window.document.implementation.addFeature('MutationEvents', ['1.0']);

    if (totalDocs > 0) {
      config.scripts.forEach(function(src) {
        var
        script = window.document.createElement('script'),
        scriptComplete = function() {
          docsLoaded++;
          if (docsLoaded >= totalDocs) {
            window.document.implementation._features = features;
            callback(errors, window);
          }
        };

        script.onload = function() {
          process.nextTick(scriptComplete);
        };

        script.onerror = function(e) {
          if (!errors) {
            errors = [];
          }
          errors.push(e.error || e.message);
          // nextTick so that an exception within scriptComplete won't cause
          // another script onerror (which would be an infinite loop)
          process.nextTick(scriptComplete);
        };

        script.src = src;
        window.document.documentElement.appendChild(script);
        window.document.documentElement.removeChild(script);
      });
    } else {
      callback(errors, window);
    }
  };

  config.html += '';

  // Handle markup
  if (config.html.indexOf("\n") > 0 || config.html.match(/^\W*</)) {
    processHTML(null, config.html);

  // Handle url/file
  } else {
    var url = URL.parse(config.html);
    if (url.hostname) {
      request({ uri: url,
                encoding: config.encoding || 'utf8',
                headers: config.headers || {}
              },
              function(err, request, body) {
                processHTML(err, body);
      });
    } else {
      fs.readFile(url.pathname, processHTML);
    }
  }
};

/*
  Since jsdom.env() is a helper for quickly and easily setting up a
  window with scripts and such already loaded into it, the arguments
  should be fairly flexible.  Here are the requirements

  1) collect `html` (url, string, or file on disk)  (STRING)
  2) load `code` into the window (array of scripts) (ARRAY)
  3) callback when resources are `done`             (FUNCTION)
  4) configuration                                  (OBJECT)

  Rules:
  + if there is one argument it had better be an object with atleast
    a `html` and `done` property (other properties are gravy)

  + arguments above are pulled out of the arguments and put into the
    config object that is returned
*/
exports.env.processArguments = function(args) {
  if (!args || !args.length || args.length < 1) {
    throw new Error('No arguments passed to jsdom.env().');
  }

  var
  props = {
    'html'    : true,
    'done'    : true,
    'scripts' : false,
    'config'  : false
  },
  propKeys = Object.keys(props),
  config = {
    code : []
  },
  l    = args.length
  ;
  if (l === 1) {
    config = args[0];
  } else {
    args.forEach(function(v) {
      var type = typeof v;
      if (!v) {
        return;
      }
      if (type === 'string' || v + '' === v) {
        config.html = v;
      } else if (type === 'object') {
        // Array
        if (v.length && v[0]) {
          config.scripts = v;
        } else {
          // apply missing required properties if appropriate
          propKeys.forEach(function(req) {

            if (typeof v[req] !== 'undefined' &&
                typeof config[req] === 'undefined') {

              config[req] = v[req];
              delete v[req];
            }
          });
          config.config = v;
        }
      } else if (type === 'function') {
        config.done = v;
      }
    });
  }

  propKeys.forEach(function(req) {
    var required = props[req];
    if (required && typeof config[req] === 'undefined') {
      throw new Error("jsdom.env requires a '" + req + "' argument");
    }
  });
  return config;
};
<|endoftext|>


<commit_before>/**
 * Exports factor functions for creating numeric expressions.
 * @module Parser/Ast/expressions
 **/
let typenames = require('./typenames.js');

/** @class BinExpr **/
let BinExpr = {};

BinExpr.prototype = {
  /** @lends BinExpr **/

  /**
   * Writes the expression of two terms as a string.
   * @return {String} String representation of a binary expression.
   **/
  toString : function() {
    return this.type() + "(" +
      this.term1.toString() + ", " +
      this.term2.toString() + ")";
  },
};

/** @class SubExpr **/
let SubExpr = {};

SubExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends SubExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.SUB_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs - rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) - this.rhs.execute(sysvars);
  }
};

/** @class AddExpr **/
let AddExpr = {};

AddExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends AddExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.ADD_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs + rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) + this.rhs.execute(sysvars);
  }
};

/** @class MulExpr **/
let MulExpr = {};

MulExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends MulExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.MUL_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs * rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) * this.rhs.execute(sysvars);
  }
};

/** @class DivExpr **/
let DivExpr = {};

DivExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends DivExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.DIV_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs / rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) / this.rhs.execute(sysvars);
  }
};

/** @class PowExpr **/
let PowExpr = {};

PowExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends PowExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.POW_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of math.pow(lhs, rhs).
   **/
  execute : function(sysvars) {
    return Math.pow(this.lhs.execute(sysvars), this.rhs.execute(sysvars));
  }
};

/** @class UMinusExpr **/
let UMinusExpr = {};

UMinusExpr.prototype = {
  /** @lends UMinusExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.UMINUS_EXPR; },

  /**
   * Executes the unary minus expression.
   * @param {Object} sysvars - The bot's state as it is represented to the DNA.
   * @return {Number} Result of -expression.
   **/
  execute : function(sysvars) {
    return -this.expression.execute(sysvars);
  },
};

/** @class Literal **/
let Literal = {};

Literal.prototype = {
  /** @lends Literal **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.LITERAL; },

  /**
   * Simply returns the value of the literal.
   * @return {Number} The literal's value.
   **/
  execute : function() {
    return this.value;
  },
};

/** @class Variable **/
let Variable = {};

Variable.prototype = {
  /** @lends Variable **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.VARIABLE; },

  /**
   * Reads the value stored in the variables location
   * in the sysvars.
   * @param {Object} sysvars - The bot dna's view of the bot's state.
   * @return {Number} The value of the variable (0 if undefined).
   **/
  execute : function(sysvars) {
    return sysvars[this.name] || 0;
  },
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {DivExpr} The expression.
 **/
module.exports.createDivExpr = function(lhs, rhs) {
  return {
    __proto__ : DivExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {MulExpr} The expression.
 **/
module.exports.createMulExpr = function(lhs, rhs) {
  return {
    __proto__ : MulExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {AddExpr} The expression.
 **/
module.exports.createAddExpr = function(lhs, rhs) {
  return {
    __proto__ : AddExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {SubExpr} The expression.
 **/
module.exports.createSubExpr = function(lhs, rhs) {
  return {
    __proto__ : SubExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {PowExpr} The expression.
 **/
module.exports.createPowExpr = function(lhs, rhs) {
  return {
    __proto__ : PowExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} expression - The expression to negate.
 * @return {UMinusExpr} The expression.
 **/
module.exports.createUMinusExpr = function(expression) {
  return {
    __proto__  : UMinusExpr.prototype,
    expression : expression,
  };
};

/**
 * Creates the ast node representing a numeric literal.
 * @param {Number} value - The literal value.
 * @return {Literal} The literal expression node.
 **/
module.exports.createLiteral = function(value) {
  return {
    __proto__ : Literal.prototype,
    value     : value,
  };
};

/**
 * Creates the ast node representing a variable.
 * @param {String} name - The variable name.
 * @return {Variable} The Variable node.
 **/
module.exports.createVariable = function(name) {
  return {
    __proto__ : Variable.prototype,
    name      : name,
  };
};
<commit_msg>added tostring methods to the unary expressions<commit_after>/**
 * Exports factor functions for creating numeric expressions.
 * @module Parser/Ast/expressions
 **/
let typenames = require('./typenames.js');

/** @class BinExpr **/
let BinExpr = {};

BinExpr.prototype = {
  /** @lends BinExpr **/

  /**
   * Writes the expression of two terms as a string.
   * @return {String} String representation of a binary expression.
   **/
  toString : function() {
    return this.type() + "(" +
      this.term1.toString() + ", " +
      this.term2.toString() + ")";
  },
};

/** @class SubExpr **/
let SubExpr = {};

SubExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends SubExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.SUB_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs - rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) - this.rhs.execute(sysvars);
  }
};

/** @class AddExpr **/
let AddExpr = {};

AddExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends AddExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.ADD_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs + rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) + this.rhs.execute(sysvars);
  }
};

/** @class MulExpr **/
let MulExpr = {};

MulExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends MulExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.MUL_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs * rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) * this.rhs.execute(sysvars);
  }
};

/** @class DivExpr **/
let DivExpr = {};

DivExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends DivExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.DIV_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of lhs / rhs.
   **/
  execute : function(sysvars) {
    return this.lhs.execute(sysvars) / this.rhs.execute(sysvars);
  }
};

/** @class PowExpr **/
let PowExpr = {};

PowExpr.prototype = {
  __proto__ : BinExpr.prototype,
  /** @lends PowExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.POW_EXPR; },

  /**
   * Executes the expression.
   * @param {Object} sysvars - The bot's state as it is represented tothe DNA.
   * @return {Number} Result of math.pow(lhs, rhs).
   **/
  execute : function(sysvars) {
    return Math.pow(this.lhs.execute(sysvars), this.rhs.execute(sysvars));
  }
};

/** @class UMinusExpr **/
let UMinusExpr = {};

UMinusExpr.prototype = {
  /** @lends UMinusExpr **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.UMINUS_EXPR; },

  /**
   * Executes the unary minus expression.
   * @param {Object} sysvars - The bot's state as it is represented to the DNA.
   * @return {Number} Result of -expression.
   **/
  execute : function(sysvars) {
    return -this.expression.execute(sysvars);
  },

  /**
   * Creates a human-readable representation of the unary expression.
   * @return {String} Pretty-printed form of this node.
   **/
  toString : function() {
    return "Minus(" + this.expression.toString() + ")";
  },
};

/** @class Literal **/
let Literal = {};

Literal.prototype = {
  /** @lends Literal **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.LITERAL; },

  /**
   * Simply returns the value of the literal.
   * @return {Number} The literal's value.
   **/
  execute : function() {
    return this.value;
  },

  /**
   * Creates a human-readable representation of the literal.
   * @return {String} Pretty-printed form of this node.
   **/
  toString : function() {
    return "Literal(" + this.value + ")";
  },
};

/** @class Variable **/
let Variable = {};

Variable.prototype = {
  /** @lends Variable **/

  /**
   * Retrieves the expression's type.
   **/
  type : function() { return typenames.VARIABLE; },

  /**
   * Reads the value stored in the variables location
   * in the sysvars.
   * @param {Object} sysvars - The bot dna's view of the bot's state.
   * @return {Number} The value of the variable (0 if undefined).
   **/
  execute : function(sysvars) {
    return sysvars[this.name] || 0;
  },

  /**
   * Creates a human-readable representation of the variable.
   * @return {String} Pretty-printed form of this node.
   **/
  toString : function() {
    return "Variable(" + this.name + ")";
  },
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {DivExpr} The expression.
 **/
module.exports.createDivExpr = function(lhs, rhs) {
  return {
    __proto__ : DivExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {MulExpr} The expression.
 **/
module.exports.createMulExpr = function(lhs, rhs) {
  return {
    __proto__ : MulExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {AddExpr} The expression.
 **/
module.exports.createAddExpr = function(lhs, rhs) {
  return {
    __proto__ : AddExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {SubExpr} The expression.
 **/
module.exports.createSubExpr = function(lhs, rhs) {
  return {
    __proto__ : SubExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} lhs - The left-hand side of the expression.
 * @param {Expression} rhs - The right-hand side of the expression.
 * @return {PowExpr} The expression.
 **/
module.exports.createPowExpr = function(lhs, rhs) {
  return {
    __proto__ : PowExpr.prototype,
    lhs       : lhs,
    rhs       : rhs,
  };
};

/**
 * Creates the ast node specified.
 * @param {Expression} expression - The expression to negate.
 * @return {UMinusExpr} The expression.
 **/
module.exports.createUMinusExpr = function(expression) {
  return {
    __proto__  : UMinusExpr.prototype,
    expression : expression,
  };
};

/**
 * Creates the ast node representing a numeric literal.
 * @param {Number} value - The literal value.
 * @return {Literal} The literal expression node.
 **/
module.exports.createLiteral = function(value) {
  return {
    __proto__ : Literal.prototype,
    value     : value,
  };
};

/**
 * Creates the ast node representing a variable.
 * @param {String} name - The variable name.
 * @return {Variable} The Variable node.
 **/
module.exports.createVariable = function(name) {
  return {
    __proto__ : Variable.prototype,
    name      : name,
  };
};
<|endoftext|>