<commit_before>import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field } from 'src'

const Input = props => {
  const { name, type, input } = props
  return <input type={type || 'text'} id={`field-` + name} name={name} {...input} />
}

class ResetPassword extends React.Component {

  constructor() {
    super()
    this.onSubmit = this.onSubmit.bind(this)
  }

  onSubmit() {
    // The <Form /> component has a `resetForm` method that you can access via refs
    // This pseudo code is supposed to show how you can do an async action like resetting
    // a password, then you can reset the form if you want
    return Promise.resolve().then(this.refs.myform.resetForm)
  }

  render() {
    return (
      <Form onSubmit={this.onSubmit} ref="myform">
        <Field name="password" component={Input} type="password" />

        <Field name="newPassword" component={Input} type="password" />

        <button type="submit">Change Password</button>
      </Form>
    )
  }
}

ReactDOM.render(<ResetPassword />, document.getElementById('root'))
<commit_msg>changing from string refs to function refs in example<commit_after>import React from 'react'
import ReactDOM from 'react-dom'
import { Form, Field } from 'src'

const Input = props => {
  const { name, type, input } = props
  return <input type={type || 'text'} id={`field-` + name} name={name} {...input} />
}

class ResetPassword extends React.Component {

  // Ref to form
  static myform

  constructor() {
    super()
    this.onSubmit = this.onSubmit.bind(this)
  }

  onSubmit() {
    // The <Form /> component has a `resetForm` method that you can access via refs
    // This pseudo code is supposed to show how you can do an async action like resetting
    // a password, then you can reset the form if you want
    return Promise.resolve().then(this.myform.resetForm)
  }

  render() {
    return (
      <Form onSubmit={this.onSubmit} ref={form => {this.myform = form}}>
        <Field name="password" component={Input} type="password" />

        <Field name="newPassword" component={Input} type="password" />

        <button type="submit">Change Password</button>
      </Form>
    )
  }
}

ReactDOM.render(<ResetPassword />, document.getElementById('root'))
<|endoftext|>


<commit_before>import { createSelector } from 'reselect'
import get from 'lodash.get'
import { networks } from '@zap/utils/crypto'
import { grpcService } from 'workers'
import { initAddresses } from './address'
import { putWallet, walletSelectors } from './wallet'
import { receiveCryptocurrency } from './ticker'

// ------------------------------------
// Constants
// ------------------------------------
export const GET_INFO = 'GET_INFO'
export const RECEIVE_INFO = 'RECEIVE_INFO'
export const SET_HAS_SYNCED = 'SET_HAS_SYNCED'

const networkInfo = {
  bitcoin: {
    mainnet: {
      id: 'mainnet',
      name: 'Mainnet',
      explorerUrl: 'https://blockstream.info',
      bitcoinJsNetwork: networks.bitcoin.mainnet,
      unitPrefix: '',
    },
    testnet: {
      id: 'testnet',
      name: 'Testnet',
      explorerUrl: 'https://blockstream.info/testnet',
      bitcoinJsNetwork: networks.bitcoin.testnet,
      unitPrefix: 't',
    },
  },
  litecoin: {
    mainnet: {
      id: 'mainnet',
      name: 'Mainnet',
      explorerUrl: 'https://insight.litecore.io',
      bitcoinJsNetwork: networks.litecoin.mainnet,
      unitPrefix: '',
    },
    testnet: {
      id: 'testnet',
      name: 'Testnet',
      explorerUrl: 'https://testnet.litecore.io',
      bitcoinJsNetwork: networks.litecoin.testnet,
      unitPrefix: 't',
    },
  },
}

// ------------------------------------
// Actions
// ------------------------------------
export function getInfo() {
  return {
    type: GET_INFO,
  }
}

export const setHasSynced = hasSynced => async (dispatch, getState) => {
  dispatch({ type: SET_HAS_SYNCED, hasSynced })

  const state = getState()
  const pubKey = get(state, 'info.data.identity_pubkey')

  if (pubKey) {
    const updated = await window.db.nodes.update(pubKey, { hasSynced })
    if (updated === 0) {
      // The reason for a result of 0 can be either that the provided key was not found, or if the provided data was
      // identical to existing data so that nothing was updated. If we got a 0, try to add a new entry for the node.
      try {
        await window.db.nodes.add({ id: pubKey, hasSynced })
      } catch (e) {
        // Do nothing if there was an error - this indicates that the item already exists and was unchanged.
      }
    }
  }
}

// Send IPC event for getinfo
export const fetchInfo = () => async dispatch => {
  dispatch(getInfo())
  const grpc = await grpcService
  const info = await grpc.services.Lightning.getInfo()
  dispatch(receiveInfo(info))
}

// Receive IPC event for info
export const receiveInfo = data => async (dispatch, getState) => {
  // Save the node info.
  dispatch({ type: RECEIVE_INFO, data })

  const state = getState()

  dispatch(receiveCryptocurrency(data.chains[0].chain))

  // Now that we have the node info, load it's sync state.
  const node = await window.db.nodes.get({ id: data.identity_pubkey })
  if (node) {
    dispatch(setHasSynced(node.hasSynced))
  }

  // Now that we have the node info, get the current wallet addresses.
  dispatch(initAddresses())

  // Update the active wallet settings with info discovered from getinfo.
  const chain = get(state, 'info.chain')
  const network = get(state, 'info.network')

  const wallet = walletSelectors.activeWalletSettings(state)
  if (wallet && (wallet.chain !== chain || wallet.network !== network)) {
    wallet.chain = chain
    wallet.network = network
    await dispatch(putWallet(wallet))
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [SET_HAS_SYNCED]: (state, { hasSynced }) => ({
    ...state,
    hasSynced,
  }),
  [GET_INFO]: state => ({ ...state, infoLoading: true }),
  [RECEIVE_INFO]: (state, { data }) => {
    const chain = get(data, 'chains[0].chain')
    const network = get(data, 'chains[0].network')
    return {
      ...state,
      infoLoading: false,
      infoLoaded: true,
      chain,
      network,
      data,
    }
  },
}

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
  infoLoading: false,
  infoLoaded: false,
  hasSynced: undefined,
  chain: null,
  network: null,
  data: {},
  networks: networkInfo,
}

// Selectors
const infoSelectors = {}
infoSelectors.chainSelector = state => state.info.chain
infoSelectors.networkSelector = state => state.info.network
infoSelectors.networksSelector = state => state.info.networks
infoSelectors.infoLoading = state => state.info.infoLoading
infoSelectors.infoLoaded = state => state.info.infoLoaded
infoSelectors.hasSynced = state => state.info.hasSynced

infoSelectors.networkInfo = createSelector(
  infoSelectors.chainSelector,
  infoSelectors.networkSelector,
  infoSelectors.networksSelector,
  (chain, network, networks) => get(networks, `${chain}.${network}`)
)

export { infoSelectors }

export default function infoReducer(state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<commit_msg>refactor: add nodePub selector<commit_after>import { createSelector } from 'reselect'
import get from 'lodash.get'
import { networks } from '@zap/utils/crypto'
import { grpcService } from 'workers'
import { initAddresses } from './address'
import { putWallet, walletSelectors } from './wallet'
import { receiveCryptocurrency } from './ticker'

// ------------------------------------
// Constants
// ------------------------------------
export const GET_INFO = 'GET_INFO'
export const RECEIVE_INFO = 'RECEIVE_INFO'
export const SET_HAS_SYNCED = 'SET_HAS_SYNCED'

const networkInfo = {
  bitcoin: {
    mainnet: {
      id: 'mainnet',
      name: 'Mainnet',
      explorerUrl: 'https://blockstream.info',
      bitcoinJsNetwork: networks.bitcoin.mainnet,
      unitPrefix: '',
    },
    testnet: {
      id: 'testnet',
      name: 'Testnet',
      explorerUrl: 'https://blockstream.info/testnet',
      bitcoinJsNetwork: networks.bitcoin.testnet,
      unitPrefix: 't',
    },
  },
  litecoin: {
    mainnet: {
      id: 'mainnet',
      name: 'Mainnet',
      explorerUrl: 'https://insight.litecore.io',
      bitcoinJsNetwork: networks.litecoin.mainnet,
      unitPrefix: '',
    },
    testnet: {
      id: 'testnet',
      name: 'Testnet',
      explorerUrl: 'https://testnet.litecore.io',
      bitcoinJsNetwork: networks.litecoin.testnet,
      unitPrefix: 't',
    },
  },
}

// ------------------------------------
// Actions
// ------------------------------------
export function getInfo() {
  return {
    type: GET_INFO,
  }
}

export const setHasSynced = hasSynced => async (dispatch, getState) => {
  dispatch({ type: SET_HAS_SYNCED, hasSynced })

  const state = getState()
  const pubKey = get(state, 'info.data.identity_pubkey')

  if (pubKey) {
    const updated = await window.db.nodes.update(pubKey, { hasSynced })
    if (updated === 0) {
      // The reason for a result of 0 can be either that the provided key was not found, or if the provided data was
      // identical to existing data so that nothing was updated. If we got a 0, try to add a new entry for the node.
      try {
        await window.db.nodes.add({ id: pubKey, hasSynced })
      } catch (e) {
        // Do nothing if there was an error - this indicates that the item already exists and was unchanged.
      }
    }
  }
}

// Send IPC event for getinfo
export const fetchInfo = () => async dispatch => {
  dispatch(getInfo())
  const grpc = await grpcService
  const info = await grpc.services.Lightning.getInfo()
  dispatch(receiveInfo(info))
}

// Receive IPC event for info
export const receiveInfo = data => async (dispatch, getState) => {
  // Save the node info.
  dispatch({ type: RECEIVE_INFO, data })

  const state = getState()

  dispatch(receiveCryptocurrency(data.chains[0].chain))

  // Now that we have the node info, load it's sync state.
  const node = await window.db.nodes.get({ id: data.identity_pubkey })
  if (node) {
    dispatch(setHasSynced(node.hasSynced))
  }

  // Now that we have the node info, get the current wallet addresses.
  dispatch(initAddresses())

  // Update the active wallet settings with info discovered from getinfo.
  const chain = get(state, 'info.chain')
  const network = get(state, 'info.network')

  const wallet = walletSelectors.activeWalletSettings(state)
  if (wallet && (wallet.chain !== chain || wallet.network !== network)) {
    wallet.chain = chain
    wallet.network = network
    await dispatch(putWallet(wallet))
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [SET_HAS_SYNCED]: (state, { hasSynced }) => ({
    ...state,
    hasSynced,
  }),
  [GET_INFO]: state => ({ ...state, infoLoading: true }),
  [RECEIVE_INFO]: (state, { data }) => {
    const chain = get(data, 'chains[0].chain')
    const network = get(data, 'chains[0].network')
    return {
      ...state,
      infoLoading: false,
      infoLoaded: true,
      chain,
      network,
      data,
    }
  },
}

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
  infoLoading: false,
  infoLoaded: false,
  hasSynced: undefined,
  chain: null,
  network: null,
  data: {},
  networks: networkInfo,
}

// Selectors
const infoSelectors = {}
infoSelectors.chainSelector = state => state.info.chain
infoSelectors.networkSelector = state => state.info.network
infoSelectors.networksSelector = state => state.info.networks
infoSelectors.infoLoading = state => state.info.infoLoading
infoSelectors.infoLoaded = state => state.info.infoLoaded
infoSelectors.hasSynced = state => state.info.hasSynced
infoSelectors.nodePub = state => {
  const parseFromDataUri = () => {
    const uri = get(state.info, 'data.uris[0]')
    return uri && uri.split('@')[0]
  }
  return get(state.info, 'data.identity_pubkey') || parseFromDataUri()
}

infoSelectors.networkInfo = createSelector(
  infoSelectors.chainSelector,
  infoSelectors.networkSelector,
  infoSelectors.networksSelector,
  (chain, network, networks) => get(networks, `${chain}.${network}`)
)

export { infoSelectors }

export default function infoReducer(state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<|endoftext|>


<commit_before>ms.geoJson = function(fetch) {
  var geoJson = ms.layer(load, unload),
      container = geoJson.container(),
      url,
      clip = true,
      clipId = "org.polymaps." + ms.id(),
      clipHref = "url(#" + clipId + ")",
      clipPath = container.insertBefore(ms.svg("clipPath"), container.firstChild),
      clipRect = clipPath.appendChild(ms.svg("rect")),
      scale = "auto",
      zoom = null,
      features,
      tileBackground = true,
      selection;

  container.setAttribute("fill-rule", "evenodd");
  clipPath.setAttribute("id", clipId);

  if (!arguments.length) fetch = ms.queue.json;

  function projection(proj) {
    var l = {lat: 0, lon: 0};
    return function(coordinates) {
      l.lat = coordinates[1];
      l.lon = coordinates[0];
      var p = proj(l);
      coordinates.x = p.x;
      coordinates.y = p.y;
      return p;
    };
  }

  function rescale(o, e, k) {
    return o.type in rescales && rescales[o.type](o, e, k);
  }

  var rescales = {

    Point: function (o, e, k) {
      var p = o.coordinates;
      e.setAttribute("transform", "translate(" + p.x + "," + p.y + ")" + k);
    },

    MultiPoint: function (o, e, k) {
      var c = o.coordinates,
          i = -1,
          n = p.length,
          x = e.firstChild,
          p;
      while (++i < n) {
        p = c[i];
        x.setAttribute("transform", "translate(" + p.x + "," + p.y + ")" + k);
        x = x.nextSibling;
      }
    }

  };

  // Create path projecting WGS84 spherical Mercator coordinates.
  function projectSpherical(tileProj) {
    return d3.geo.path().projection({
      stream: function(stream) {
        return {
          point: function(x, y) {
            // Latitudes at the poles (or beyond!) result in unrenderable NaN's and Infinities.
            var epsilon = 1.0e-6;
            y = Math.min(90 - epsilon, y);
            y = Math.max(-90 + epsilon, y);
            var p = tileProj.locationPoint({ lon: x, lat: y });
            stream.point(Math.round(2 * p.x) / 2, Math.round(2 * p.y) / 2);
          },
          sphere: function() { stream.sphere(); },
          lineStart: function() { stream.lineStart(); },
          lineEnd: function() { stream.lineEnd(); },
          polygonStart: function() { stream.polygonStart(); },
          polygonEnd: function() { stream.polygonEnd(); }
        };
      }
    });
  }

  function load(tile, proj) {
    var g = tile.element = ms.svg("g");

    var tileProj = proj(tile),
        path = projectSpherical(tileProj);

    tile.features = [];

    function update(data) {
      var updated = [];

      /* Fetch the next batch of features, if so directed. */
      if (data.next) tile.request = fetch(data.next.href, update);

      if (geoJson.tile() && tileBackground) {
        var tileSize = geoJson.map().tileSize();
        d3.select(g.insertBefore(ms.svg("rect"), g.firstChild))
          .attr("width", tileSize.x)
          .attr("height", tileSize.x)
          .attr("class", "tile-background");
      }

      draw(g, data, path, updated, tile);

      tile.ready = true;
      updated.push.apply(tile.features, updated);
      geoJson.dispatch({type: "load", tile: tile, features: updated});
    }

    if (url != null) {
      tile.request = fetch(typeof url == "function" ? url(tile) : url, update);
    } else {
      update({type: "FeatureCollection", features: features || []});
    }
  }

  function draw(g, data, path, updated, tile) {
    var update = d3.select(g)
      .selectAll("path")
      .data(data.features);

    update.exit()
      .remove();

    var enter = update
      .enter()
      .append("path");

    if (updated)
      enter.each(function(f) { updated.push({ element: this, data: f }); });

    if (selection)
      selection(update);

    var paths = [];
    update.each(function(f, i) {
      paths[i] = path(f);
    });
    update.attr("d", function(f, i) { return paths[i]; });
  }

  function unload(tile) {
    if (tile.request) tile.request.abort(true);
  }

  function move() {
    var zoom = geoJson.map().zoom(),
        tiles = geoJson.cache.locks(), // visible tiles
        key, // key in locks
        tile, // locks[key]
        features, // tile.features
        i, // current feature index
        n, // current feature count, features.length
        feature, // features[i]
        k; // scale transform
    if (scale == "fixed") {
      for (key in tiles) {
        if ((tile = tiles[key]).scale != zoom) {
          k = "scale(" + Math.pow(2, tile.zoom - zoom) + ")";
          i = -1;
          n = (features = tile.features).length;
          while (++i < n) rescale((feature = features[i]).data.geometry, feature.element, k);
          tile.scale = zoom;
        }
      }
    } else {
      for (key in tiles) {
        i = -1;
        n = (features = (tile = tiles[key]).features).length;
        while (++i < n) rescale((feature = features[i]).data.geometry, feature.element, "");
        delete tile.scale;
      }
    }
  }

  geoJson.tileBackground = function(x) {
    if (!arguments.length) return tileBackground;
    tileBackground = x;
    return geoJson;
  };

  geoJson.selection = function(x) {
    if (!arguments.length) return selection;
    selection = x;
    return geoJson.reshow();
  };

  geoJson.url = function(x) {
    if (!arguments.length) return url;
    url = typeof x == "string" && /{.}/.test(x) ? ms.url(x) : x;
    if (url != null) features = null;
    if (typeof url == "string") geoJson.tile(false);
    return geoJson.reload();
  };

  geoJson.features = function(x) {
    if (!arguments.length) return features;
    if (features = x) {
      url = null;
      geoJson.tile(false);
    }
    return geoJson.reload();
  };

  geoJson.clip = function(x) {
    if (!arguments.length) return clip;
    if (clip) container.removeChild(clipPath);
    if (clip = x) container.insertBefore(clipPath, container.firstChild);
    var locks = geoJson.cache.locks();
    for (var key in locks) {
      if (clip) locks[key].element.setAttribute("clip-path", clipHref);
      else locks[key].element.removeAttribute("clip-path");
    }
    return geoJson;
  };

  var __tile__ = geoJson.tile;
  geoJson.tile = function(x) {
    if (arguments.length && !x) geoJson.clip(x);
    return __tile__.apply(geoJson, arguments);
  };

  var __map__ = geoJson.map;
  geoJson.map = function(x) {
    if (x && clipRect) {
      var size = x.tileSize();
      clipRect.setAttribute("width", size.x);
      clipRect.setAttribute("height", size.y);
    }
    return __map__.apply(geoJson, arguments);
  };

  geoJson.scale = function(x) {
    if (!arguments.length) return scale;
    if (scale = x) geoJson.on("move", move);
    else geoJson.off("move", move);
    if (geoJson.map()) move();
    return geoJson;
  };

  geoJson.show = function(tile) {
    if (clip) tile.element.setAttribute("clip-path", clipHref);
    else tile.element.removeAttribute("clip-path");
    if (selection)
      selection(d3.select(tile.element).selectAll("path"));
    geoJson.dispatch({type: "show", tile: tile, features: tile.features});
    return geoJson;
  };

  geoJson.reshow = function() {
    var locks = geoJson.cache.locks();
    for (var key in locks) geoJson.show(locks[key]);
    return geoJson;
  };

  return geoJson;
};
<commit_msg>Display points as circles in order to allow users to change point sizes.<commit_after>ms.geoJson = function(fetch) {
  var geoJson = ms.layer(load, unload),
      container = geoJson.container(),
      url,
      clip = true,
      clipId = "org.polymaps." + ms.id(),
      clipHref = "url(#" + clipId + ")",
      clipPath = container.insertBefore(ms.svg("clipPath"), container.firstChild),
      clipRect = clipPath.appendChild(ms.svg("rect")),
      scale = "auto",
      zoom = null,
      pointRadius = 4.5,
      features,
      tileBackground = true,
      selection;

  container.setAttribute("fill-rule", "evenodd");
  clipPath.setAttribute("id", clipId);

  if (!arguments.length) fetch = ms.queue.json;

  function projection(proj) {
    var l = {lat: 0, lon: 0};
    return function(coordinates, c) {
      l.lat = coordinates[1];
      l.lon = coordinates[0];
      var p = proj(l);
      c.x = p.x;
      c.y = p.y;
      return p;
    };
  }

  function rescale(o, e, k) {
    var g = o.geometry;
    return g.type in rescales && rescales[g.type](o, e, k);
  }

  var rescales = {

    Point: function (o, e, k) {
      e.setAttribute("transform", "translate(" + o.x + "," + o.y + ")" + k);
    },

    MultiPoint: function (o, e, k) {
      e.setAttribute("transform", "translate(" + o.x + "," + o.y + ")" + k);
    }

  };

  // Create path projecting WGS84 spherical Mercator coordinates.
  function projectSpherical(tileProj) {
    return d3.geo.path().projection({
      stream: function(stream) {
        return {
          point: function(x, y) {
            // Latitudes at the poles (or beyond!) result in unrenderable NaN's and Infinities.
            var epsilon = 1.0e-6;
            y = Math.min(90 - epsilon, y);
            y = Math.max(-90 + epsilon, y);
            var p = tileProj.locationPoint({ lon: x, lat: y });
            stream.point(Math.round(2 * p.x) / 2, Math.round(2 * p.y) / 2);
          },
          sphere: function() { stream.sphere(); },
          lineStart: function() { stream.lineStart(); },
          lineEnd: function() { stream.lineEnd(); },
          polygonStart: function() { stream.polygonStart(); },
          polygonEnd: function() { stream.polygonEnd(); }
        };
      }
    });
  }

  function load(tile, proj) {
    var g = tile.element = ms.svg("g");

    var tileProj = proj(tile);

    tile.features = [];

    function update(data) {
      var updated = [];

      /* Fetch the next batch of features, if so directed. */
      if (data.next) tile.request = fetch(data.next.href, update);

      if (geoJson.tile() && tileBackground) {
        var tileSize = geoJson.map().tileSize();
        d3.select(g.insertBefore(ms.svg("rect"), g.firstChild))
          .attr("width", tileSize.x)
          .attr("height", tileSize.x)
          .attr("class", "tile-background");
      }

      draw(g, data, tileProj, updated, tile);

      tile.ready = true;
      updated.push.apply(tile.features, updated);
      geoJson.dispatch({type: "load", tile: tile, features: updated});
    }

    if (url != null) {
      tile.request = fetch(typeof url == "function" ? url(tile) : url, update);
    } else {
      update({type: "FeatureCollection", features: features || []});
    }
  }

  function copyObject(source) {
    var copy = {};
    for (var property in source) {
      copy[property] = source[property];
    }
    return copy;
  }

  function projectPoint(p, proj) {
    proj(p.geometry.coordinates, p);
    return p;
  }

  function projectPointsForMultiPoint(mp, proj) {
    var length = mp.geometry.coordinates.length;
    var points = [];
    for (var i = 0; i < length; i++) {
      var p = copyObject(mp);
      proj(p.geometry.coordinates[i], p);
      points.push(p);
    }
    return points;
  }

  function draw(g, data, tileProj, updated, tile) {
    var proj = projection(tileProj.locationPoint);

    var path = projectSpherical(tileProj);

    var pathFeatures = [],
        pointFeatures = [];

    data.features.forEach(function(f) {
      if (f.geometry.type === "Point")
        pointFeatures.push(projectPoint(f, proj));
      else if (f.geometry.type === "MultiPoint")
        pointFeatures.push.apply(pointFeatures, projectPointsForMultiPoint(f, proj));
      else
        pathFeatures.push(f);
    });

    var pathUpdate = d3.select(g)
      .selectAll("path")
      .data(pathFeatures)
      .enter()
      .append("path")
      .attr("d", function(f) { return path(f); });

    if (updated)
      pathUpdate.each(function(f) { updated.push({ element: this, data: f }); });

    var pointUpdate = d3.select(g)
      .selectAll("circle")
      .data(pointFeatures)
      .enter()
      .append("circle")
      .attr("transform", function(f) {
        return "translate(" + f.x + "," + f.y + ")";
      })
      .attr("r", pointRadius);

    if (updated)
      pointUpdate.each(function(f) { updated.push({ element: this, data: f }); });

    if (selection) {
      pathUpdate.push.apply(pathUpdate, pointUpdate);
      selection(pathUpdate);
    }
  }

  function unload(tile) {
    if (tile.request) tile.request.abort(true);
  }

  function move() {
    var zoom = geoJson.map().zoom(),
        tiles = geoJson.cache.locks(), // visible tiles
        key, // key in locks
        tile, // locks[key]
        features, // tile.features
        i, // current feature index
        n, // current feature count, features.length
        feature, // features[i]
        k; // scale transform
    if (scale == "fixed") {
      for (key in tiles) {
        if ((tile = tiles[key]).scale != zoom) {
          k = "scale(" + Math.pow(2, tile.zoom - zoom) + ")";
          i = -1;
          n = (features = tile.features).length;
          while (++i < n) rescale((feature = features[i]).data, feature.element, k);
          tile.scale = zoom;
        }
      }
    }
  }

  geoJson.tileBackground = function(x) {
    if (!arguments.length) return tileBackground;
    tileBackground = x;
    return geoJson;
  };

  geoJson.selection = function(x) {
    if (!arguments.length) return selection;
    selection = x;
    return geoJson.reshow();
  };

  geoJson.url = function(x) {
    if (!arguments.length) return url;
    url = typeof x == "string" && /{.}/.test(x) ? ms.url(x) : x;
    if (url != null) features = null;
    if (typeof url == "string") geoJson.tile(false);
    return geoJson.reload();
  };

  geoJson.features = function(x) {
    if (!arguments.length) return features;
    if (features = x) {
      url = null;
      geoJson.tile(false);
    }
    return geoJson.reload();
  };

  geoJson.clip = function(x) {
    if (!arguments.length) return clip;
    if (clip) container.removeChild(clipPath);
    if (clip = x) container.insertBefore(clipPath, container.firstChild);
    var locks = geoJson.cache.locks();
    for (var key in locks) {
      if (clip) locks[key].element.setAttribute("clip-path", clipHref);
      else locks[key].element.removeAttribute("clip-path");
    }
    return geoJson;
  };

  var __tile__ = geoJson.tile;
  geoJson.tile = function(x) {
    if (arguments.length && !x) geoJson.clip(x);
    return __tile__.apply(geoJson, arguments);
  };

  var __map__ = geoJson.map;
  geoJson.map = function(x) {
    if (x && clipRect) {
      var size = x.tileSize();
      clipRect.setAttribute("width", size.x);
      clipRect.setAttribute("height", size.y);
    }
    return __map__.apply(geoJson, arguments);
  };

  geoJson.scale = function(x) {
    if (!arguments.length) return scale;
    if (scale = x) geoJson.on("move", move);
    else geoJson.off("move", move);
    if (geoJson.map()) move();
    return geoJson;
  };

  geoJson.show = function(tile) {
    if (clip) tile.element.setAttribute("clip-path", clipHref);
    else tile.element.removeAttribute("clip-path");
    if (selection) {
      var s = d3.select(tile.element).selectAll("path");
      s.push.apply(s, d3.select(tile.element).selectAll("circle"));
      selection(s);
    }
    geoJson.dispatch({type: "show", tile: tile, features: tile.features});
    return geoJson;
  };

  geoJson.reshow = function() {
    var locks = geoJson.cache.locks();
    for (var key in locks) geoJson.show(locks[key]);
    return geoJson;
  };

  return geoJson;
};
<|endoftext|>


<commit_before>(function (window, undefined) {
    "use strict";
    // test for REM unit support
    var cssremunit =  function() {
        var div = document.createElement( 'div' );
            div.style.cssText = 'font-size: 1rem;';

        return (/rem/).test(div.style.fontSize);
    },

    // filter returned links for stylesheets
    isStyleSheet = function () {
        var styles = document.getElementsByTagName('link'),
            filteredLinks = [];

        for ( var i = 0; i < styles.length; i++) {
            if ( styles[i].rel.toLowerCase() === 'stylesheet' && styles[i].getAttribute('data-norem') === null ) {

                filteredLinks.push( styles[i].href );
            }
        }

        return filteredLinks;
    },

   processLinks = function () {
        if( links.length === 0 ){
            links = isStyleSheet(); // search for link tags and confirm it's a stylesheet
        }

        //prepare to match each link
        for( var i = 0; i < links.length; i++ ){
            xhr( links[i], storeCSS, links[i], i );
        }
    },

    storeCSS = function ( response, link ) {

        preCSS.push(response.responseText);
        CSSLinks.push(link);

        if( CSSLinks.length === links.length ){
            for( var j = 0; j <  CSSLinks.length; j++ ){
                matchCSS( preCSS[j], CSSLinks[j] );
            }

            if( ( links = importLinks.slice(0) ).length > 0 ){ //after finishing all current links, set links equal to the new imports found
                CSSLinks = [];
                preCSS = [];
                importLinks = [];
                processLinks();
            } else {
                buildCSS();
            }
        }
    },

    matchCSS = function ( sheetCSS, link ) { // collect all of the rules from the xhr response texts and match them to a pattern
        var clean = removeComments( removeMediaQueries(sheetCSS) ),
            pattern = /[\w\d\s\-\/\\\[\]:,.'"*()<>+~%#^$_=|@]+\{[\w\d\s\-\/\\%#:!;,.'"*()]+\d*\.?\d+rem[\w\d\s\-\/\\%#:!;,.'"*()]*\}/g, //find selectors that use rem in one or more of their rules
            current = clean.match(pattern),
            remPattern =/\d*\.?\d+rem/g,
            remCurrent = clean.match(remPattern),
            sheetPathPattern = /(.*\/)/,
            sheetPath = sheetPathPattern.exec(link)[0], //relative path to css file specified in @import
            importPattern = /@import (?:url\()?['"]?([^'\)"]*)['"]?\)?[^;]*/gm, //matches all @import variations outlined at: https://developer.mozilla.org/en-US/docs/Web/CSS/@import
            importStatement;

        while( (importStatement = importPattern.exec(sheetCSS)) !== null ){
            importLinks.push( sheetPath + importStatement[1] );
        }

        if( current !== null && current.length !== 0 ){
            found = found.concat( current ); // save all of the blocks of rules with rem in a property
            foundProps = foundProps.concat( remCurrent ); // save all of the properties with rem
        }
    },

    buildCSS = function () { // first build each individual rule from elements in the found array and then add it to the string of rules.
        var pattern = /[\w\d\s\-\/\\%#:,.'"*()]+\d*\.?\d+rem[\w\d\s\-\/\\%#:!,.'"*()]*[;}]/g; // find properties with rem values in them
        for( var i = 0; i < found.length; i++ ){
            rules = rules + found[i].substr(0,found[i].indexOf("{")+1); // save the selector portion of each rule with a rem value
            var current = found[i].match( pattern );
            for( var j = 0; j<current.length; j++ ){ // build a new set of with only the selector and properties that have rem in the value
                rules = rules + current[j];
                if( j === current.length-1 && rules[rules.length-1] !== "}" ){
                    rules = rules + "\n}";
                }
            }
        }

        parseCSS();
    },

    parseCSS = function () { // replace each set of parentheses with evaluated content
        for( var i = 0; i < foundProps.length; i++ ){
            css[i] = Math.round( parseFloat(foundProps[i].substr(0,foundProps[i].length-3)*fontSize) ) + 'px';
        }

        loadCSS();
    },

    loadCSS = function () { // replace and load the new rules
        for( var i = 0; i < css.length; i++ ){ // only run this loop as many times as css has entries
            if( css[i] ){
                rules = rules.replace( foundProps[i],css[i] ); // replace old rules with our processed rules
            }
        }
        var remcss = document.createElement( 'style' );
        remcss.setAttribute( 'type', 'text/css' );
        remcss.id = 'remReplace';
        document.getElementsByTagName( 'head' )[0].appendChild( remcss );   // create the new element
        if( remcss.styleSheet ) {
            remcss.styleSheet.cssText = rules; // IE8 will not support innerHTML on read-only elements, such as STYLE
        } else {
            remcss.appendChild( document.createTextNode( rules ) );
        }
    },

    xhr = function ( url, callback, i ) { // create new XMLHttpRequest object and run it

        try {
            var xhr = getXMLHttpRequest(),
                isOldIE = document.all;

            xhr.open( 'GET', url, true );
            xhr.send(null);

            // Better way for IE versions detection: http://tanalin.com/en/articles/ie-version-js/

            if ( !isOldIE || (isOldIE && window.XMLHttpRequest) ){ //If IE is greater than 6
                // This targets modern browsers and modern versions of IE,
                // which don't need the "new" keyword.
                xhr.onreadystatechange = function () {
                    if ( xhr.readyState === 4 ){
                        callback(xhr, i);
                    } // else { callback function on AJAX error }
                };
            } else {
                // This block targets old versions of IE, which require "new".
                xhr.onreadystatechange = new function() { //IE6 need the "new function()" syntax to work properly
                    if ( xhr.readyState === 4 ) {
                        callback( xhr, i );
                    } // else { callback function on AJAX error }
                };
            }
         } catch (e){
            if ( window.XDomainRequest ) {
                var xdr = new XDomainRequest();
                xdr.open('get', url);
                xdr.onload = function() {
                    callback(xdr, i);
                };
                xdr.onerror = function() {
                    return false; // xdr load fail
                };
                xdr.send();
            }
         }
    },

    removeComments = function ( css ) {
        var start = css.search(/\/\*/),
            end = css.search(/\*\//);
        while ( (start > -1) && (end > start) ) {
            css = css.substring(0, start) + css.substring(end + 2);
            start = css.search(/\/\*/);
            end = css.search(/\*\//);
        }
        return css;
    },

    // Test for Media Query support
    mediaQuery = function() {
        if (window.matchMedia || window.msMatchMedia) { return true; }
        return false;
    },

    // Remove queries.
    removeMediaQueries = function(css) {
        if (!mediaQuery()) {
            // If the browser doesn't support media queries, we find all @media declarations in the CSS and remove them.
            // Note: Since @rules can't be nested in the CSS spec, we're safe to just check for the closest following "}}" to the "@media".
            css = css.replace(/@media[\s\S]*?\}\s*\}/g, "");
        }

        return css;
    },

    getXMLHttpRequest = function () { // we're gonna check if our browser will let us use AJAX
        if (window.XMLHttpRequest) {
            return new XMLHttpRequest();
        } else { // if XMLHttpRequest doesn't work
            try {
                return new ActiveXObject("MSXML2.XMLHTTP"); // then we'll instead use AJAX through ActiveX for IE6/IE7
            } catch (e1) {
                try {
                    return new ActiveXObject("Microsoft.XMLHTTP"); // other microsoft
                } catch (e2) {
                    // No XHR at all...
                }
            }
        }
    };

    if( !cssremunit() ){ // this checks if the rem value is supported
        var rules = '', // initialize the rules variable in this scope so it can be used later
            links = [], // initialize the array holding the sheets urls for use later
            importLinks = [], //initialize the array holding the import sheet urls for use later
            found = [], // initialize the array holding the found rules for use later
            foundProps = [], // initialize the array holding the found properties for use later
            preCSS = [], // initialize array that holds css before being parsed
            CSSLinks = [], //initialize array holding css links returned from xhr
            css = [], // initialize the array holding the parsed rules for use later
            fontSize = '';

        fontSize = (function () {
                var who = document.createElement('div'),
                    currentSize,
                    body = document.body || document.createElement("body"),
                    size;

                who.style.cssText = 'line-height: 1; padding:0; position:absolute; visibility:hidden;';
                who.appendChild(document.createTextNode('M'));

                if (!document.body) {
                    document.documentElement.appendChild(body);
                }

                currentSize = body.style.fontSize;
                body.style.fontSize = '1em';
                body.appendChild(who);
                size = who.offsetHeight;
                body.style.fontSize = currentSize;

                if (!document.body) {
                    document.documentElement.removeChild(body);
                }
                else {
                    body.removeChild(who);
                }

                return size;
        }());

        processLinks();
    } // else { do nothing, you are awesome and have REM support }

})(window);
<commit_msg>Changes in original fontSize calculating<commit_after>(function (window, undefined) {
    "use strict";
    // test for REM unit support
    var cssremunit =  function() {
        var div = document.createElement( 'div' );
            div.style.cssText = 'font-size: 1rem;';

        return (/rem/).test(div.style.fontSize);
    },

    // filter returned links for stylesheets
    isStyleSheet = function () {
        var styles = document.getElementsByTagName('link'),
            filteredLinks = [];

        for ( var i = 0; i < styles.length; i++) {
            if ( styles[i].rel.toLowerCase() === 'stylesheet' && styles[i].getAttribute('data-norem') === null ) {

                filteredLinks.push( styles[i].href );
            }
        }

        return filteredLinks;
    },

   processLinks = function () {
        if( links.length === 0 ){
            links = isStyleSheet(); // search for link tags and confirm it's a stylesheet
        }

        //prepare to match each link
        for( var i = 0; i < links.length; i++ ){
            xhr( links[i], storeCSS, links[i], i );
        }
    },

    storeCSS = function ( response, link ) {

        preCSS.push(response.responseText);
        CSSLinks.push(link);

        if( CSSLinks.length === links.length ){
            for( var j = 0; j <  CSSLinks.length; j++ ){
                matchCSS( preCSS[j], CSSLinks[j] );
            }

            if( ( links = importLinks.slice(0) ).length > 0 ){ //after finishing all current links, set links equal to the new imports found
                CSSLinks = [];
                preCSS = [];
                importLinks = [];
                processLinks();
            } else {
                buildCSS();
            }
        }
    },

    matchCSS = function ( sheetCSS, link ) { // collect all of the rules from the xhr response texts and match them to a pattern
        var clean = removeComments( removeMediaQueries(sheetCSS) ),
            pattern = /[\w\d\s\-\/\\\[\]:,.'"*()<>+~%#^$_=|@]+\{[\w\d\s\-\/\\%#:!;,.'"*()]+\d*\.?\d+rem[\w\d\s\-\/\\%#:!;,.'"*()]*\}/g, //find selectors that use rem in one or more of their rules
            current = clean.match(pattern),
            remPattern =/\d*\.?\d+rem/g,
            remCurrent = clean.match(remPattern),
            sheetPathPattern = /(.*\/)/,
            sheetPath = sheetPathPattern.exec(link)[0], //relative path to css file specified in @import
            importPattern = /@import (?:url\()?['"]?([^'\)"]*)['"]?\)?[^;]*/gm, //matches all @import variations outlined at: https://developer.mozilla.org/en-US/docs/Web/CSS/@import
            importStatement;

        while( (importStatement = importPattern.exec(sheetCSS)) !== null ){
            importLinks.push( sheetPath + importStatement[1] );
        }

        if( current !== null && current.length !== 0 ){
            found = found.concat( current ); // save all of the blocks of rules with rem in a property
            foundProps = foundProps.concat( remCurrent ); // save all of the properties with rem
        }
    },

    buildCSS = function () { // first build each individual rule from elements in the found array and then add it to the string of rules.
        var pattern = /[\w\d\s\-\/\\%#:,.'"*()]+\d*\.?\d+rem[\w\d\s\-\/\\%#:!,.'"*()]*[;}]/g; // find properties with rem values in them
        for( var i = 0; i < found.length; i++ ){
            rules = rules + found[i].substr(0,found[i].indexOf("{")+1); // save the selector portion of each rule with a rem value
            var current = found[i].match( pattern );
            for( var j = 0; j<current.length; j++ ){ // build a new set of with only the selector and properties that have rem in the value
                rules = rules + current[j];
                if( j === current.length-1 && rules[rules.length-1] !== "}" ){
                    rules = rules + "\n}";
                }
            }
        }

        parseCSS();
    },

    parseCSS = function () { // replace each set of parentheses with evaluated content
        for( var i = 0; i < foundProps.length; i++ ){
            css[i] = Math.round( parseFloat(foundProps[i].substr(0,foundProps[i].length-3)*fontSize) ) + 'px';
        }

        loadCSS();
    },

    loadCSS = function () { // replace and load the new rules
        for( var i = 0; i < css.length; i++ ){ // only run this loop as many times as css has entries
            if( css[i] ){
                rules = rules.replace( foundProps[i],css[i] ); // replace old rules with our processed rules
            }
        }
        var remcss = document.createElement( 'style' );
        remcss.setAttribute( 'type', 'text/css' );
        remcss.id = 'remReplace';
        document.getElementsByTagName( 'head' )[0].appendChild( remcss );   // create the new element
        if( remcss.styleSheet ) {
            remcss.styleSheet.cssText = rules; // IE8 will not support innerHTML on read-only elements, such as STYLE
        } else {
            remcss.appendChild( document.createTextNode( rules ) );
        }
    },

    xhr = function ( url, callback, i ) { // create new XMLHttpRequest object and run it

        try {
            var xhr = getXMLHttpRequest(),
                isOldIE = document.all;

            xhr.open( 'GET', url, true );
            xhr.send(null);

            // Better way for IE versions detection: http://tanalin.com/en/articles/ie-version-js/

            if ( !isOldIE || (isOldIE && window.XMLHttpRequest) ){ //If IE is greater than 6
                // This targets modern browsers and modern versions of IE,
                // which don't need the "new" keyword.
                xhr.onreadystatechange = function () {
                    if ( xhr.readyState === 4 ){
                        callback(xhr, i);
                    } // else { callback function on AJAX error }
                };
            } else {
                // This block targets old versions of IE, which require "new".
                xhr.onreadystatechange = new function() { //IE6 need the "new function()" syntax to work properly
                    if ( xhr.readyState === 4 ) {
                        callback( xhr, i );
                    } // else { callback function on AJAX error }
                };
            }
         } catch (e){
            if ( window.XDomainRequest ) {
                var xdr = new XDomainRequest();
                xdr.open('get', url);
                xdr.onload = function() {
                    callback(xdr, i);
                };
                xdr.onerror = function() {
                    return false; // xdr load fail
                };
                xdr.send();
            }
         }
    },

    removeComments = function ( css ) {
        var start = css.search(/\/\*/),
            end = css.search(/\*\//);
        while ( (start > -1) && (end > start) ) {
            css = css.substring(0, start) + css.substring(end + 2);
            start = css.search(/\/\*/);
            end = css.search(/\*\//);
        }
        return css;
    },

    // Test for Media Query support
    mediaQuery = function() {
        if (window.matchMedia || window.msMatchMedia) { return true; }
        return false;
    },

    // Remove queries.
    removeMediaQueries = function(css) {
        if (!mediaQuery()) {
            // If the browser doesn't support media queries, we find all @media declarations in the CSS and remove them.
            // Note: Since @rules can't be nested in the CSS spec, we're safe to just check for the closest following "}}" to the "@media".
            css = css.replace(/@media[\s\S]*?\}\s*\}/g, "");
        }

        return css;
    },

    getXMLHttpRequest = function () { // we're gonna check if our browser will let us use AJAX
        if (window.XMLHttpRequest) {
            return new XMLHttpRequest();
        } else { // if XMLHttpRequest doesn't work
            try {
                return new ActiveXObject("MSXML2.XMLHTTP"); // then we'll instead use AJAX through ActiveX for IE6/IE7
            } catch (e1) {
                try {
                    return new ActiveXObject("Microsoft.XMLHTTP"); // other microsoft
                } catch (e2) {
                    // No XHR at all...
                }
            }
        }
    };

    if( !cssremunit() ){ // this checks if the rem value is supported
        var rules = '', // initialize the rules variable in this scope so it can be used later
            links = [], // initialize the array holding the sheets urls for use later
            importLinks = [], //initialize the array holding the import sheet urls for use later
            found = [], // initialize the array holding the found rules for use later
            foundProps = [], // initialize the array holding the found properties for use later
            preCSS = [], // initialize array that holds css before being parsed
            CSSLinks = [], //initialize array holding css links returned from xhr
            css = [], // initialize the array holding the parsed rules for use later
            fontSize = '';

        // Notice: rem is a "root em" that means that in case when html element size was changed by css
        // or style we should not change document.documentElement.fontSize to 1em - only body size should be changed
        // to 1em for calculation

        fontSize = (function () {
            var doc = document,
                docElement = doc.documentElement,
                body = doc.body || doc.createElement('body'),
                isFakeBody = !doc.body,
                div = doc.createElement('div'),
                currentSize = body.style.fontSize,
                size;

            if ( isFakeBody ) {
                docElement.appendChild( body );
            }

            div.style.cssText = 'width:1em; position:absolute; visibility:hidden; padding: 0;';

            body.style.fontSize = '1em';

            body.appendChild( div );
            size = div.offsetWidth;

            if ( isFakeBody ) {
                docElement.removeChild( body );
            }
            else {
                body.removeChild( div );
                body.style.fontSize = currentSize;
            }

            return size;
        }());

        processLinks();
    } // else { do nothing, you are awesome and have REM support }

})(window);
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import {DragDropContext, Droppable, Draggable} from 'react-beautiful-dnd';
import SectionHeading from '../components/SectionHeading';
import StudentPhoto from '../components/StudentPhoto';
import MockStudentPhoto from '../components/MockStudentPhoto';
import FountasAndPinnellLevelChart from './FountasAndPinnellLevelChart';

// TODO(kr) import path
import {
  reordered,
  insertedInto,
  UNPLACED_ROOM_KEY,
  initialStudentIdsByRoom,
  consistentlyPlacedInitialStudentIdsByRoom
} from '../class_lists/studentIdsByRoomFunctions';
import {
  DIBELS_DORF_WPM,
  DIBELS_DORF_ACC,
  F_AND_P_ENGLISH,
  INSTRUCTIONAL_NEEDS,
  readDoc
} from './readingData';

// For making and reviewing reading groups.
export default class CreateGroups extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      studentIdsByRoom: initialStudentIdsByRoom(groups(props.classrooms).length, props.readingStudents)
    };
    this.onDragEnd = this.onDragEnd.bind(this);
  }

  withMerged(students) {
    const {mtssNotes} = this.props;
    // const dibelsByStudentId = _.groupBy(dibelsDataPoints, 'student_id');
    const mtssByStudentId = _.groupBy(mtssNotes, 'student_id');
    return students.map(student => {
      return {
        ...student,
        // dibels: dibelsByStudentId[student.id] || [],
        mtss: mtssByStudentId[student.id] || []
      };
    });
  }

  onDragEnd(dragEndResult) {
    const {studentIdsByRoom} = this.state;
    const updatedStudentIdsByRoom = studentIdsByRoomAfterDrag(studentIdsByRoom, dragEndResult);
    this.setState({studentIdsByRoom: updatedStudentIdsByRoom});
  }

  // onDragEnd(dragEndResult) {
  //   // // dropped outside the list
  //   if (!result.destination) {
  //     return;
  //   }

  //   this.setState(
  //     reorderQuoteMap({
  //       quoteMap: this.state.quoteMap,
  //       source: result.source,
  //       destination: result.destination,
  //     }),
  //   );
  // }

  render() {
    const {readingStudents, classrooms} = this.props;
    const {studentIdsByRoom} = this.state;
    const students = this.withMerged(readingStudents);
    return (

        <SectionHeading>Reading Groups: 3rd grade at Arthur D. Healey</SectionHeading>
        <DragDropContext onDragEnd={this.onDragEnd}>

            {groups(classrooms).map(group => {
              const {groupKey} = group;
              const studentsInRoom = studentIdsByRoom[groupKey].map(studentId => {
                return _.find(students, { id: studentId });
              });
              return this.renderRow(group, studentsInRoom);
            })}

        </DragDropContext>

    );
  }

  renderRow(group, studentsInGroup) {
    const {text, groupKey} = group;
    return (

        {this.renderGroupName(groupKey, text, studentsInGroup)}
        <Droppable
          droppableId={groupKey}
          direction="horizontal"
          type="GROUP">
          {(provided, snapshot) => (

              {studentsInGroup.map(this.renderDraggable, this)}
              {provided.placeholder}

          )}
        </Droppable>

    );
  }

  renderDraggable(student, index) {
    return (
      <Draggable key={student.id} draggableId={`StudentCard:${student.id}`} index={index}>
        {(provided, snapshot) => (

            {this.renderItem(student)}

        )}
      </Draggable>
    );
  }

  /*
  return this.renderClickableStudentCard(student, {
  //   ref: provided.innerRef,
  //   placeholder: provided.placeholder,
  //   propsFromDraggable: {
  //     ...provided.draggableProps,
  //     ...provided.dragHandleProps
  //   }
  // });
  */

  // // Optionally pass arguments to make this work as a Draggable
  // renderItem(student, options = {}) {
  //   const {ref, placeholder, propsFromDraggable = {}} = options;
  //   return (
  //

  //

  //         {this.renderActual(student)}
  //

  //       {placeholder /* this preserves space when dragging */}
  //

  //   );
  // }

  renderGroupName(groupKey, classroomText, studentsInGroup) {
    const {doc} = this.props;
    const width = 100;
    const height = 22;
    const fnpLevels = _.sortBy(_.uniq(_.compact(studentsInGroup.map(student => {
      return readDoc(doc, student.id, F_AND_P_ENGLISH);
    }))));
    const wpms = _.sortBy(_.uniq(_.compact(studentsInGroup.map(student => {
      return readDoc(doc, student.id, DIBELS_DORF_WPM);
    }))));
    const accs = _.sortBy(_.uniq(_.compact(studentsInGroup.map(student => {
      return readDoc(doc, student.id, DIBELS_DORF_ACC);
    }))));
    return (

{classroomText}

F&P: {fnpLevels.join(' ')}

              <FountasAndPinnellLevelChart height={height} levels={fnpLevels} />

DORF accuracy:

{range(accs)}

DORF words/min:

{range(wpms)}

    );
  }

  renderItem(student) {
    const useMockPhoto = (this.props.useMockPhoto || window.location.search.indexOf('mock') !== -1);
    const photoProps = {
      key: student.id,
      style: styles.photoImage,
      student: student,
      fallbackEl: 😃,
      alt: '😃'
    };

    return (

        {useMockPhoto
          ? <MockStudentPhoto {...photoProps} />
          : <StudentPhoto {...photoProps} />}

{student.first_name}

    );
  }
}
CreateGroups.contextTypes = {
  districtKey: PropTypes.string.isRequired,
  nowFn: PropTypes.func.isRequired
};
CreateGroups.propTypes = {
  schoolName: PropTypes.string.isRequired,
  grade: PropTypes.string.isRequired,
  readingStudents: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired
  })).isRequired,
  classrooms: PropTypes.arrayOf(PropTypes.shape({
    text: PropTypes.string.isRequired
  })).isRequired,
  doc: PropTypes.object.isRequired,
  mtssNotes: PropTypes.arrayOf(PropTypes.shape({
    student_id: PropTypes.number.isRequired,
    id: PropTypes.number.isRequired,
    recorded_at: PropTypes.string.isRequired,
  })).isRequired,
  useMockPhoto: PropTypes.bool
};

const ROW_HEIGHT = 80;
const styles = {
  row: {
    display: 'flex',
    padding: 5,
    fontSize: 12,
    marginBottom: 5,
    marginRight: 5,
    height: ROW_HEIGHT,
    background: '#f8f8f8',
    color: 'black',
    border: '1px solid #eee'
  },
  droppable: {
    display: 'flex',
    flex: 1,
    backgroundColor: '#f8f8f8',
    overflowX: 'scroll'
  },
  photoContainer: {
    position: 'relative',
    width: 64,
    height: ROW_HEIGHT,
    marginRight: 1,
    fontSize: 32
  },
  fallbackSmiley: {
    position: 'absolute',
    display: 'flex',
    justifyContent: 'center',
    backgroundColor: '#333',
    opacity: 0.9
  },
  photoImage: {
    position: 'absolute',
    maxWidth: 64,
    maxHeight: ROW_HEIGHT,
    boxShadow: '2px 2px 1px #ccc'
  }
};

// Update the `studentIdsByRoom` map after a drag ends.
export function studentIdsByRoomAfterDrag(studentIdsByRoom, dragEndResult) {
  const {draggableId, source, destination} = dragEndResult;

  // Not moved
  if (destination === null) return studentIdsByRoom;

  const sourceStudentIds = studentIdsByRoom[source.droppableId];
  const destinationStudentIds = studentIdsByRoom[destination.droppableId];
  const draggableStudentId = _.find(sourceStudentIds, studentId => `StudentCard:${studentId}` === draggableId);

  // Moving within the same list
  if (source.droppableId === destination.droppableId) {
    return {
      ...studentIdsByRoom,
      [source.droppableId]: reordered(sourceStudentIds, source.index, destination.index)
    };
  }

  // Moving to another list
  if (source.droppableId !== destination.droppableId) {
    return {
      ...studentIdsByRoom,
      [source.droppableId]: _.without(sourceStudentIds, draggableStudentId),
      [destination.droppableId]: insertedInto(destinationStudentIds, destination.index, draggableStudentId)
    };
  }
}

// // relies on already sorted
// function tryLatest(key, list) {
//   const obj = _.first(list);
//   return obj ? obj[key] : null;
// }

// function latestDibels(student) {
//   return tryLatest('benchmark', student.dibels_results);
// }
// function latestFAndP(student) {
//   return tryLatest('instructional_level', student.f_and_p_assessments);
// }
// function latestStar(student) {
//   return tryLatest('percentile_rank', student.star_reading_results);
// }

// function tryDibels(dibels, grade, assessmentPeriod, assessmentKey) {
//   const d = _.find(dibels, {
//     grade,
//     assessment_period: assessmentPeriod,
//     assessment_key: assessmentKey,
//   });

//   return d ? d.data_point : null;
// }

function range(sortedValues) {
  if (sortedValues.length === 0) return 'none';
  if (sortedValues.length === 1) return _.first(sortedValues);
  return `${_.first(sortedValues)} - ${_.last(sortedValues)}`;
}

function groups(classrooms) {
  const unplacedGroup = {
    groupKey: UNPLACED_ROOM_KEY,
    text: 'Not yet placed'
  };
  return [unplacedGroup].concat(classrooms.map((classroom, index) => {
    return {
      ...classroom,
      groupKey: ['room', index].join(':')
    };
  }));
}<commit_msg>Styling on group name, instructional needs text<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import hash from 'object-hash';
import {DragDropContext, Droppable, Draggable} from 'react-beautiful-dnd';
import SectionHeading from '../components/SectionHeading';
import StudentPhoto from '../components/StudentPhoto';
import MockStudentPhoto from '../components/MockStudentPhoto';
import FountasAndPinnellLevelChart from './FountasAndPinnellLevelChart';

// TODO(kr) import path
import {
  reordered,
  insertedInto,
  UNPLACED_ROOM_KEY,
  initialStudentIdsByRoom,
  consistentlyPlacedInitialStudentIdsByRoom
} from '../class_lists/studentIdsByRoomFunctions';
import {
  DIBELS_DORF_WPM,
  DIBELS_DORF_ACC,
  F_AND_P_ENGLISH,
  INSTRUCTIONAL_NEEDS,
  readDoc
} from './readingData';

// For making and reviewing reading groups.
export default class CreateGroups extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      studentIdsByRoom: initialStudentIdsByRoom(groups(props.classrooms).length, props.readingStudents)
    };
    this.onDragEnd = this.onDragEnd.bind(this);
  }

  withMerged(students) {
    const {mtssNotes} = this.props;
    // const dibelsByStudentId = _.groupBy(dibelsDataPoints, 'student_id');
    const mtssByStudentId = _.groupBy(mtssNotes, 'student_id');
    return students.map(student => {
      return {
        ...student,
        // dibels: dibelsByStudentId[student.id] || [],
        mtss: mtssByStudentId[student.id] || []
      };
    });
  }

  onDragEnd(dragEndResult) {
    const {studentIdsByRoom} = this.state;
    const updatedStudentIdsByRoom = studentIdsByRoomAfterDrag(studentIdsByRoom, dragEndResult);
    this.setState({studentIdsByRoom: updatedStudentIdsByRoom});
  }

  // onDragEnd(dragEndResult) {
  //   // // dropped outside the list
  //   if (!result.destination) {
  //     return;
  //   }

  //   this.setState(
  //     reorderQuoteMap({
  //       quoteMap: this.state.quoteMap,
  //       source: result.source,
  //       destination: result.destination,
  //     }),
  //   );
  // }

  render() {
    const {readingStudents, classrooms} = this.props;
    const {studentIdsByRoom} = this.state;
    const students = this.withMerged(readingStudents);
    return (

        <SectionHeading>Reading Groups: 3rd grade at Arthur D. Healey</SectionHeading>
        <DragDropContext onDragEnd={this.onDragEnd}>

            {groups(classrooms).map((group, groupIndex) => {
              const {groupKey} = group;
              const studentsInRoom = studentIdsByRoom[groupKey].map(studentId => {
                return _.find(students, { id: studentId });
              });
              return this.renderRow(group, groupIndex, studentsInRoom);
            })}

        </DragDropContext>

    );
  }

  renderRow(group, groupIndex, studentsInGroup) {
    const {text, groupKey} = group;
    return (

        {this.renderGroupName(groupKey, groupIndex, text, studentsInGroup)}
        <Droppable
          droppableId={groupKey}
          direction="horizontal"
          type="GROUP">
          {(provided, snapshot) => (

              {studentsInGroup.map(this.renderDraggable, this)}
              {provided.placeholder}

          )}
        </Droppable>

    );
  }

  renderDraggable(student, index) {
    return (
      <Draggable key={student.id} draggableId={`StudentCard:${student.id}`} index={index}>
        {(provided, snapshot) => (

            {this.renderItem(student)}

        )}
      </Draggable>
    );
  }

  /*
  return this.renderClickableStudentCard(student, {
  //   ref: provided.innerRef,
  //   placeholder: provided.placeholder,
  //   propsFromDraggable: {
  //     ...provided.draggableProps,
  //     ...provided.dragHandleProps
  //   }
  // });
  */

  // // Optionally pass arguments to make this work as a Draggable
  // renderItem(student, options = {}) {
  //   const {ref, placeholder, propsFromDraggable = {}} = options;
  //   return (
  //

  //

  //         {this.renderActual(student)}
  //

  //       {placeholder /* this preserves space when dragging */}
  //

  //   );
  // }

  renderGroupName(groupKey, groupIndex, classroomText, studentsInGroup) {
    const {doc} = this.props;
    const width = 100;
    const height = 22;
    const fnpLevels = _.sortBy(_.uniq(_.compact(studentsInGroup.map(student => {
      return readDoc(doc, student.id, F_AND_P_ENGLISH);
    }))));
    const wpms = _.sortBy(_.uniq(_.compact(studentsInGroup.map(student => {
      return readDoc(doc, student.id, DIBELS_DORF_WPM);
    }))));
    const accs = _.sortBy(_.uniq(_.compact(studentsInGroup.map(student => {
      return readDoc(doc, student.id, DIBELS_DORF_ACC);
    }))));
    const instructionalNeeds = _.sortBy(_.uniq(_.compact(studentsInGroup.map(student => {
      return readDoc(doc, student.id, INSTRUCTIONAL_NEEDS);
    }))));
    return (

{classroomText}

F&P: {fnpLevels.join(' ')}

              <FountasAndPinnellLevelChart height={height} levels={fnpLevels} />

DORF accuracy:

{range(accs)}

DORF words/min:

{range(wpms)}

{instructionalNeeds.map(need => (
            {`“${need}”`}
          ))}

    );
  }

  renderItem(student) {
    const useMockPhoto = (this.props.useMockPhoto || window.location.search.indexOf('mock') !== -1);
    const photoProps = {
      key: student.id,
      style: styles.photoImage,
      student: student,
      fallbackEl: 😃,
      alt: '😃'
    };

    return (

        {useMockPhoto
          ? <MockStudentPhoto {...photoProps} />
          : <StudentPhoto {...photoProps} />}

{student.first_name}

    );
  }
}
CreateGroups.contextTypes = {
  districtKey: PropTypes.string.isRequired,
  nowFn: PropTypes.func.isRequired
};
CreateGroups.propTypes = {
  schoolName: PropTypes.string.isRequired,
  grade: PropTypes.string.isRequired,
  readingStudents: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired
  })).isRequired,
  classrooms: PropTypes.arrayOf(PropTypes.shape({
    text: PropTypes.string.isRequired
  })).isRequired,
  doc: PropTypes.object.isRequired,
  mtssNotes: PropTypes.arrayOf(PropTypes.shape({
    student_id: PropTypes.number.isRequired,
    id: PropTypes.number.isRequired,
    recorded_at: PropTypes.string.isRequired,
  })).isRequired,
  useMockPhoto: PropTypes.bool
};

const ROW_HEIGHT = 80;
const styles = {
  row: {
    display: 'flex',
    fontSize: 12,
    marginBottom: 5,
    marginRight: 5,
    height: ROW_HEIGHT,
    background: '#f8f8f8',
    color: 'black',
    border: '1px solid #eee'
  },
  droppable: {
    display: 'flex',
    flex: 1,
    backgroundColor: '#f8f8f8',
    overflowX: 'scroll'
  },
  photoContainer: {
    position: 'relative',
    width: 64,
    height: ROW_HEIGHT,
    marginRight: 1,
    fontSize: 32
  },
  fallbackSmiley: {
    position: 'absolute',
    display: 'flex',
    justifyContent: 'center',
    backgroundColor: '#333',
    opacity: 0.9
  },
  photoImage: {
    position: 'absolute',
    maxWidth: 64,
    maxHeight: ROW_HEIGHT,
    boxShadow: '2px 2px 1px #ccc'
  }
};

// Update the `studentIdsByRoom` map after a drag ends.
export function studentIdsByRoomAfterDrag(studentIdsByRoom, dragEndResult) {
  const {draggableId, source, destination} = dragEndResult;

  // Not moved
  if (destination === null) return studentIdsByRoom;

  const sourceStudentIds = studentIdsByRoom[source.droppableId];
  const destinationStudentIds = studentIdsByRoom[destination.droppableId];
  const draggableStudentId = _.find(sourceStudentIds, studentId => `StudentCard:${studentId}` === draggableId);

  // Moving within the same list
  if (source.droppableId === destination.droppableId) {
    return {
      ...studentIdsByRoom,
      [source.droppableId]: reordered(sourceStudentIds, source.index, destination.index)
    };
  }

  // Moving to another list
  if (source.droppableId !== destination.droppableId) {
    return {
      ...studentIdsByRoom,
      [source.droppableId]: _.without(sourceStudentIds, draggableStudentId),
      [destination.droppableId]: insertedInto(destinationStudentIds, destination.index, draggableStudentId)
    };
  }
}

// // relies on already sorted
// function tryLatest(key, list) {
//   const obj = _.first(list);
//   return obj ? obj[key] : null;
// }

// function latestDibels(student) {
//   return tryLatest('benchmark', student.dibels_results);
// }
// function latestFAndP(student) {
//   return tryLatest('instructional_level', student.f_and_p_assessments);
// }
// function latestStar(student) {
//   return tryLatest('percentile_rank', student.star_reading_results);
// }

// function tryDibels(dibels, grade, assessmentPeriod, assessmentKey) {
//   const d = _.find(dibels, {
//     grade,
//     assessment_period: assessmentPeriod,
//     assessment_key: assessmentKey,
//   });

//   return d ? d.data_point : null;
// }

function range(sortedValues) {
  if (sortedValues.length === 0) return 'none';
  if (sortedValues.length === 1) return _.first(sortedValues);
  return `${_.first(sortedValues)} - ${_.last(sortedValues)}`;
}

function groups(classrooms) {
  const unplacedGroup = {
    groupKey: UNPLACED_ROOM_KEY,
    text: 'Not placed'
  };
  return [unplacedGroup].concat(classrooms.map((classroom, index) => {
    return {
      ...classroom,
      groupKey: ['room', index].join(':')
    };
  }));
}

const colors = [
  '#31AB39',
  '#EB4B26',
  '#139DEA',
  '#333333',
  '#CDD71A',
  '#6A2987',
  '#fdbf6f',
  '#ff7f00',
  '#cab2d6',
  '#6a3d9a',
  '#ffff99',
  '#b15928',
];
function pickGroupColor(groupIndex) {
  // return colors[parseInt(hash(homeroomText), 16) % colors.length];
  return colors[groupIndex];
}
<|endoftext|>


<commit_before>'use strict'

// Library for making requests
const request = require('request')
const BigNumber = require('bignumber.js');

// Necessary node libraries to make sia.js emit events
const Util = require('util')
const EventEmitter = require('events')
const nodePath = require('path')

/**
 * SiadWrapper, a closure, initializes siad as a background process and
 * provides functions to interact with it
 * @class SiadWrapper
 */
function SiadWrapper () {
  // siad details with default values
  var settings = {
    detached: false,
    address: 'localhost:9980',
    rpcAddress: ':9981',
    hostAddress: ':9982',
    datadir: nodePath.join(__dirname, '..', 'Sia'),
    path: nodePath.join(__dirname, '..', 'Sia', process.platform === 'win32' ? 'siad.exe' : 'siad')
  }
  // Ensure precision for unit conversions
  BigNumber.config({ DECIMAL_PLACES: 30 })
  BigNumber.config({ EXPONENTIAL_AT: 1e+9 })
  // Unit conversion constants
  const hastingsPerSiacoin = new BigNumber(10).pow(24)

  // Tracks if siad was last known to be running or not
  var running = false
  // Keep reference to `this` to emit events from within contexts where `this`
  // does not point to this class
  var self = this
  // Inherit `EventEmitter` properties
  EventEmitter.call(this)

  /**
   * Relays calls to daemonAPI with the localhost:port address appended
   * @function SiadWrapper#call
   * @param {apiCall} call - function to run if Siad is running
   * @param {apiResponse} callback
   * @returns {object} request call object
   */
  function apiCall (call, callback) {
    // Interpret string-only calls. Will default to 'GET' requests
    if (typeof call === 'string') {
      call = { url: call }
    }

    // Setup request
    call.url = 'http://' + settings.address + call.url
    call.json = true
    call.headers = {
      'User-Agent': 'Sia-Agent'
    }

    // Return the request sent if the user wants to be creative and get more
    // information than what's passed to the callback
    // TODO: Don't understand why the setImmediate is needed, but without it,
    // some calls seem to not return right after another call but eventually do
    // return once another call is made. Some sort of resource blocking is
    // happening
    setImmediate(function () {
      request(call, function (err, response, body) {
        // The error from request should be null if siad is running
        running = !err

        // If siad puts out an error, pass it as first argument to callback
        if (!err && response.statusCode !== 200) {
          err = body
          body = null
        }

        // Return results to callback
        if (callback !== undefined) {
          callback(err, body)
        }
      })
    })
  }

  // Checks whether siad is running on the current address
  function checkIfSiadRunning (callback) {
    return apiCall('/daemon/version', function (err) {
      // There should be no reason this call would error if siad were running
      // and serving requests
      running = !err

      // Return result to callback
      if (callback !== undefined) {
        callback(running)
      }
    })
  }

  /**
   * Checks whether siad is running and runs ones of two callbacks
   * @function SiadWrapper#ifRunning
   * @param {callback} is - called if siad is running
   * @param {callback} not - called if siad is not running
   * @returns {object} request call object
   */
  function ifSiadRunning (is, not) {
    return checkIfSiadRunning(function (running) {
      if (running && is !== undefined) {
        is()
      } else if (not !== undefined) {
        not()
      }
    })
  }

  /**
   * Synchronous way to check if siad is running, may not be up to date
   * @function SiadWrapper#isRunning
   * @returns {boolean} whether siad is running
   */
  function isSiadRunning (callback) {
    return running
  }

  // Polls the siad API until it comes online
  function waitUntilLoaded (callback) {
    return ifSiadRunning(callback, function () {
      setTimeout(function () {
        waitUntilLoaded(callback)
      }, 1000)
    })
  }

  /**
   * Starts the daemon as a long running background process
   * @param {callback} callback - function to be run if successful
   * @returns {boolean} if start was attempted
   */
  function start (callback) {
    // Check if siad is already running
    if (running) {
      if (callback !== null) {
        callback(new Error('Attempted to start siad when it was already running'))
      }
      return false
    }

    // Check synchronously if siad doesn't exist at settings.path
    const fs = require('fs')
    try {
      require('fs').statSync(settings.path)
    } catch (e) {
      if (callback !== null) {
        callback(e)
      }
      return false
    }

    // Set siad folder as configured settings.datadir
    var processOptions = {
      cwd: settings.datadir
    }

    // If the detached option is set, spawn siad as a separate process to be
    // run in the background after the parent process has closed
    if (settings.detached) {
      let log = nodePath.join(settings.datadir, 'out.log')
      let out = fs.openSync(log, 'a')
      let err = fs.openSync(log, 'a')
      processOptions.detached = true
      processOptions.stdio = [ 'ignore', out, err ]
    }

    // Spawn siad
    const Process = require('child_process').spawn
    var daemonProcess = new Process(settings.path, [
      '--api-addr=' + settings.address,
      '--rpc-addr=' + settings.rpcAddress,
      '--host-addr=' + settings.hostAddress,
      '--sia-directory=' + settings.datadir
    ], processOptions)

    // Exclude it from the parent process' event loop if detached
    if (settings.detached) {
      daemonProcess.unref()
    } else {
      // Listen for siad events and emit them from wrapper
      // TODO: Attach these to siad if it's already running
      var childProcessEvents = ['close', 'disconnect', 'error', 'message']
      childProcessEvents.forEach(function (ev) {
        daemonProcess.on(ev, function (arg1, arg2) {
          self.emit(ev, arg1, arg2)
        })
      })
      daemonProcess.on('exit', function (code) {
        running = false
        self.emit('exit', code)
      })
    }

    // Wait until siad finishes loading to call callback
    waitUntilLoaded(callback)
    return true
  }

  /**
   * Sends a stop call to the daemon
   * @param {callback} callback - function to be run if successful
   * @returns {boolean} if stop was attempted, should always be true
   */
  function stop (callback) {
    apiCall('/daemon/stop', function (err) {
      if (!err) {
        running = false
      }
      if (callback !== undefined) {
        callback(err)
      }
    })
    return true
  }

  /**
   * Sets the member variables based on the passed config. Checks if siad is
   * running on the new configuration so running should be up to date for
   * the callback
   * @param {config} c - the config object derived from config.json
   * @param {callback} callback - first argument is any errors, second argument
   * is the new configuration
   * @returns {object} siad configuration object
   */
  function configure (newSettings, callback) {
    for (let key in settings) {
      // Set passed in settings within siad
      if (settings.hasOwnProperty(key) && newSettings.hasOwnProperty(key)) {
        settings[key] = newSettings[key]
      } else if (settings.hasOwnProperty(key)) {
        // Set settings to sync with siad
        newSettings[key] = settings[key]
      }
    }
    if (callback !== undefined) {
      checkIfSiadRunning(function (check) {
        callback(null, settings)
      })
    }
    return settings
  }

  /**
   * Downloads siad and siac to a specified or default location
   * @param {string} path - An optional location of where to download to
   * @param {callback} callback
   * @returns {boolean} if download was started, should always be true
   */
  function download (path, callback) {
    if (typeof path === 'string') {
      settings.path = path
    } else {
      // the first argument is either undefined or the callback
      callback = path
      path = settings.path
    }
    return require('./download.js')(path, callback)
  }

  // Sia unit conversion functions.
  // Convert Hastings (the base unit) to Siacoin.  Use strings as input to avoid precision loss.
  // @param {hastings :string}
  // @return BigNumber
  function hastingsToSiacoins (hastings) {
    const hastingsNumber = new BigNumber(hastings)
    return hastingsNumber.dividedBy(hastingsPerSiacoin)
  }
  // Convert Siacoin to Hastings.  Use strings as input to avoid precision loss.
  // @params {siacoin :string}
  // @return BigNumber
  function siacoinsToHastings (siacoins) {
    const siacoinNumber = new BigNumber(siacoins)
    return siacoinNumber.multiply(hastingsPerSiacoin)
  }

  // Make certain members public
  this.call = apiCall
  this.ifRunning = ifSiadRunning
  this.isRunning = isSiadRunning
  this.start = start
  this.stop = stop
  this.configure = configure
  this.download = download
  this.siacoinsToHastings = siacoinsToHastings
  this.hastingsToSiacoins = hastingsToSiacoins
}

// Inherit functions from `EventEmitter`'s prototype
Util.inherits(SiadWrapper, EventEmitter)

module.exports = new SiadWrapper()
<commit_msg>change multiply to times<commit_after>'use strict'

// Library for making requests
const request = require('request')
const BigNumber = require('bignumber.js')

// Necessary node libraries to make sia.js emit events
const Util = require('util')
const EventEmitter = require('events')
const nodePath = require('path')

/**
 * SiadWrapper, a closure, initializes siad as a background process and
 * provides functions to interact with it
 * @class SiadWrapper
 */
function SiadWrapper () {
  // siad details with default values
  var settings = {
    detached: false,
    address: 'localhost:9980',
    rpcAddress: ':9981',
    hostAddress: ':9982',
    datadir: nodePath.join(__dirname, '..', 'Sia'),
    path: nodePath.join(__dirname, '..', 'Sia', process.platform === 'win32' ? 'siad.exe' : 'siad')
  }
  // Ensure precision for unit conversions
  BigNumber.config({ DECIMAL_PLACES: 30 })
  BigNumber.config({ EXPONENTIAL_AT: 1e+9 })
  // Unit conversion constants
  const hastingsPerSiacoin = new BigNumber(10).pow(24)

  // Tracks if siad was last known to be running or not
  var running = false
  // Keep reference to `this` to emit events from within contexts where `this`
  // does not point to this class
  var self = this
  // Inherit `EventEmitter` properties
  EventEmitter.call(this)

  /**
   * Relays calls to daemonAPI with the localhost:port address appended
   * @function SiadWrapper#call
   * @param {apiCall} call - function to run if Siad is running
   * @param {apiResponse} callback
   * @returns {object} request call object
   */
  function apiCall (call, callback) {
    // Interpret string-only calls. Will default to 'GET' requests
    if (typeof call === 'string') {
      call = { url: call }
    }

    // Setup request
    call.url = 'http://' + settings.address + call.url
    call.json = true
    call.headers = {
      'User-Agent': 'Sia-Agent'
    }

    // Return the request sent if the user wants to be creative and get more
    // information than what's passed to the callback
    // TODO: Don't understand why the setImmediate is needed, but without it,
    // some calls seem to not return right after another call but eventually do
    // return once another call is made. Some sort of resource blocking is
    // happening
    setImmediate(function () {
      request(call, function (err, response, body) {
        // The error from request should be null if siad is running
        running = !err

        // If siad puts out an error, pass it as first argument to callback
        if (!err && response.statusCode !== 200) {
          err = body
          body = null
        }

        // Return results to callback
        if (callback !== undefined) {
          callback(err, body)
        }
      })
    })
  }

  // Checks whether siad is running on the current address
  function checkIfSiadRunning (callback) {
    return apiCall('/daemon/version', function (err) {
      // There should be no reason this call would error if siad were running
      // and serving requests
      running = !err

      // Return result to callback
      if (callback !== undefined) {
        callback(running)
      }
    })
  }

  /**
   * Checks whether siad is running and runs ones of two callbacks
   * @function SiadWrapper#ifRunning
   * @param {callback} is - called if siad is running
   * @param {callback} not - called if siad is not running
   * @returns {object} request call object
   */
  function ifSiadRunning (is, not) {
    return checkIfSiadRunning(function (running) {
      if (running && is !== undefined) {
        is()
      } else if (not !== undefined) {
        not()
      }
    })
  }

  /**
   * Synchronous way to check if siad is running, may not be up to date
   * @function SiadWrapper#isRunning
   * @returns {boolean} whether siad is running
   */
  function isSiadRunning (callback) {
    return running
  }

  // Polls the siad API until it comes online
  function waitUntilLoaded (callback) {
    return ifSiadRunning(callback, function () {
      setTimeout(function () {
        waitUntilLoaded(callback)
      }, 1000)
    })
  }

  /**
   * Starts the daemon as a long running background process
   * @param {callback} callback - function to be run if successful
   * @returns {boolean} if start was attempted
   */
  function start (callback) {
    // Check if siad is already running
    if (running) {
      if (callback !== null) {
        callback(new Error('Attempted to start siad when it was already running'))
      }
      return false
    }

    // Check synchronously if siad doesn't exist at settings.path
    const fs = require('fs')
    try {
      require('fs').statSync(settings.path)
    } catch (e) {
      if (callback !== null) {
        callback(e)
      }
      return false
    }

    // Set siad folder as configured settings.datadir
    var processOptions = {
      cwd: settings.datadir
    }

    // If the detached option is set, spawn siad as a separate process to be
    // run in the background after the parent process has closed
    if (settings.detached) {
      let log = nodePath.join(settings.datadir, 'out.log')
      let out = fs.openSync(log, 'a')
      let err = fs.openSync(log, 'a')
      processOptions.detached = true
      processOptions.stdio = [ 'ignore', out, err ]
    }

    // Spawn siad
    const Process = require('child_process').spawn
    var daemonProcess = new Process(settings.path, [
      '--api-addr=' + settings.address,
      '--rpc-addr=' + settings.rpcAddress,
      '--host-addr=' + settings.hostAddress,
      '--sia-directory=' + settings.datadir
    ], processOptions)

    // Exclude it from the parent process' event loop if detached
    if (settings.detached) {
      daemonProcess.unref()
    } else {
      // Listen for siad events and emit them from wrapper
      // TODO: Attach these to siad if it's already running
      var childProcessEvents = ['close', 'disconnect', 'error', 'message']
      childProcessEvents.forEach(function (ev) {
        daemonProcess.on(ev, function (arg1, arg2) {
          self.emit(ev, arg1, arg2)
        })
      })
      daemonProcess.on('exit', function (code) {
        running = false
        self.emit('exit', code)
      })
    }

    // Wait until siad finishes loading to call callback
    waitUntilLoaded(callback)
    return true
  }

  /**
   * Sends a stop call to the daemon
   * @param {callback} callback - function to be run if successful
   * @returns {boolean} if stop was attempted, should always be true
   */
  function stop (callback) {
    apiCall('/daemon/stop', function (err) {
      if (!err) {
        running = false
      }
      if (callback !== undefined) {
        callback(err)
      }
    })
    return true
  }

  /**
   * Sets the member variables based on the passed config. Checks if siad is
   * running on the new configuration so running should be up to date for
   * the callback
   * @param {config} c - the config object derived from config.json
   * @param {callback} callback - first argument is any errors, second argument
   * is the new configuration
   * @returns {object} siad configuration object
   */
  function configure (newSettings, callback) {
    for (let key in settings) {
      // Set passed in settings within siad
      if (settings.hasOwnProperty(key) && newSettings.hasOwnProperty(key)) {
        settings[key] = newSettings[key]
      } else if (settings.hasOwnProperty(key)) {
        // Set settings to sync with siad
        newSettings[key] = settings[key]
      }
    }
    if (callback !== undefined) {
      checkIfSiadRunning(function (check) {
        callback(null, settings)
      })
    }
    return settings
  }

  /**
   * Downloads siad and siac to a specified or default location
   * @param {string} path - An optional location of where to download to
   * @param {callback} callback
   * @returns {boolean} if download was started, should always be true
   */
  function download (path, callback) {
    if (typeof path === 'string') {
      settings.path = path
    } else {
      // the first argument is either undefined or the callback
      callback = path
      path = settings.path
    }
    return require('./download.js')(path, callback)
  }

  // Sia unit conversion functions.
  // Convert Hastings (the base unit) to Siacoin.  Use strings as input to avoid precision loss.
  // @param {hastings :string}
  // @return BigNumber
  function hastingsToSiacoins (hastings) {
    const hastingsNumber = new BigNumber(hastings)
    return hastingsNumber.dividedBy(hastingsPerSiacoin)
  }
  // Convert Siacoin to Hastings.  Use strings as input to avoid precision loss.
  // @params {siacoin :string}
  // @return BigNumber
  function siacoinsToHastings (siacoins) {
    const siacoinNumber = new BigNumber(siacoins)
    return siacoinNumber.times(hastingsPerSiacoin)
  }

  // Make certain members public
  this.call = apiCall
  this.ifRunning = ifSiadRunning
  this.isRunning = isSiadRunning
  this.start = start
  this.stop = stop
  this.configure = configure
  this.download = download
  this.siacoinsToHastings = siacoinsToHastings
  this.hastingsToSiacoins = hastingsToSiacoins
}

// Inherit functions from `EventEmitter`'s prototype
Util.inherits(SiadWrapper, EventEmitter)

module.exports = new SiadWrapper()
<|endoftext|>


<commit_before>/*
    This is a rudimentary CSV parser, for a more complete example see csv.js
    In this simple version, the data must not contain any coma
    and double quotes are not processed.
**/
// Parser reference
var self = this
// Current row
var data = []

// Define the parser rules
var eol = ['\n','\r\n']
var sep = options.separator || ','
// Handlers are used instead of events
atok
    // Ignore comments
    .ignore(true) // On rule match, do not do anything, skip the token
        .addRule('#', eol, 'comment')
    .ignore() // Turn the ignore property off
    // Anything else is data
    // Rule definition:
    // first argument: always an exact match. To match anything, use ''
    // next arguments: array of possible matches (first match wins)
    .addRule('', { firstOf: [ sep ].concat(eol) }, function (token, idx) {
        // token=the matched data
        // idx=when using array of patterns, the index of the matched pattern
        if (token === 'error') {
            // Build the error message using positioning
            var msg = self.trackData(token)
            self.emit('error', new Error(msg.message))
            return
        }
        // Add the data
        data.push(token)
        // EOL reached, the parser emits the row
        if (idx > 0) {
            self.emit('data', data)
            data = []
        }
    })<commit_msg>Version 0.0.1<commit_after>/*
    This is a rudimentary CSV parser, for a more complete example see csv.js
    In this simple version, the data must not contain any coma
    and double quotes are not processed.
**/
// Parser reference
var self = this
// Current row
var data = []

// Define the parser rules
var eol = ['\n','\r\n']
var sep = options.separator || ','
// Handlers are used instead of events
atok
    // Ignore comments
    .ignore(true) // On rule match, do not do anything, skip the token
        .addRule('#', eol, 'comment')
    .ignore() // Turn the ignore property off
    // Anything else is data
    // Rule definition:
    // first argument: always an exact match. To match anything, use ''
    // next arguments: array of possible matches (first match wins)
    .addRule('', { firstOf: [ sep ].concat(eol) }, function (token, idx) {
        // token=the matched data
        // idx=when using array of patterns, the index of the matched pattern
        if (token === 'error') {
            // Build the error message using positioning
            var err = self.trackError(new Error('Dummy message'), token)
            self.emit('error', err)
            return
        }
        // Add the data
        data.push(token)
        // EOL reached, the parser emits the row
        if (idx > 0) {
            self.emit('data', data)
            data = []
        }
    })<|endoftext|>