<commit_before>import React, { PureComponent } from 'react'
import warning from 'warning'
import PropTypes from 'prop-types'
import Rail from '../Rail'
import Ticks from '../Ticks'
import Tracks from '../Tracks'
import Handles from '../Handles'
import { mode1, mode2, mode3 } from './modes'
import {
  isNotValidTouch,
  getTouchPosition,
  getUpdatedHandles,
  getSliderDomain,
  getHandles,
  prfx,
} from './utils'
import LinearScale from './LinearScale'
import DiscreteScale from './DiscreteScale'

const isBrowser =
  typeof window !== 'undefined' && typeof document !== 'undefined'

const noop = () => {}

const compare = b => (m, d, i) => m && b[i] === d

const equal = (a, b) => {
  return a === b || (a.length === b.length && a.reduce(compare(b), true))
}

const getNextValue = (curr, step, domain, reversed) => {
  const newVal = reversed ? curr - step : curr + step
  return reversed ? Math.max(domain[0], newVal) : Math.min(domain[1], newVal)
}

const getPrevValue = (curr, step, domain, reversed) => {
  const newVal = reversed ? curr + step : curr - step
  return reversed ? Math.min(domain[1], newVal) : Math.max(domain[0], newVal)
}

class Slider extends PureComponent {
  state = {
    step: null,
    values: null,
    domain: null,
    handles: null,
    reversed: null,
    activeHandleID: null,
    valueToPerc: null,
    valueToStep: null,
    pixelToStep: null,
  }

  slider = React.createRef()

  static getDerivedStateFromProps(nextProps, prevState) {
    const {
      step,
      values,
      domain,
      reversed,
      onUpdate,
      onChange,
      // maxSteps,
      // rangeWarnings,
      warnOnChanges,
    } = nextProps

    let valueToPerc = prevState.valueToPerc
    let valueToStep = prevState.valueToStep
    let pixelToStep = prevState.pixelToStep

    const nextState = {}

    if (!valueToPerc || !valueToStep || !pixelToStep) {
      valueToPerc = new LinearScale()
      valueToStep = new DiscreteScale()
      pixelToStep = new DiscreteScale()

      nextState.valueToPerc = valueToPerc
      nextState.valueToStep = valueToStep
      nextState.pixelToStep = pixelToStep
    }

    if (
      prevState.step === null ||
      prevState.domain === null ||
      prevState.reversed === null ||
      step !== prevState.step ||
      domain[0] !== prevState.domain[0] ||
      domain[1] !== prevState.domain[1] ||
      reversed !== prevState.reversed
    ) {
      const [min, max] = domain

      valueToStep
        .setStep(step)
        .setRange([min, max])
        .setDomain([min, max])

      if (reversed === true) {
        valueToPerc.setDomain([min, max]).setRange([100, 0])
        pixelToStep.setStep(step).setRange([max, min])
      } else {
        valueToPerc.setDomain([min, max]).setRange([0, 100])
        pixelToStep.setStep(step).setRange([min, max])
      }

      warning(
        max > min,
        `${prfx} Max must be greater than min (even if reversed). Max is ${max}. Min is ${min}.`,
      )

      const { handles, changes } = getHandles(
        values || prevState.values,
        reversed,
        valueToStep,
        warnOnChanges,
      )

      if (changes || values === undefined || values === prevState.values) {
        onUpdate(handles.map(d => d.val))
        onChange(handles.map(d => d.val))
      }

      nextState.step = step
      nextState.values = values
      nextState.domain = domain
      nextState.handles = handles
      nextState.reversed = reversed
    } else if (!equal(values, prevState.values)) {
      const { handles, changes } = getHandles(
        values,
        reversed,
        valueToStep,
        warnOnChanges,
      )

      if (changes) {
        onUpdate(handles.map(d => d.val))
        onChange(handles.map(d => d.val))
      }

      nextState.values = values
      nextState.handles = handles
    }

    if (Object.keys(nextState).length) {
      return nextState
    }

    return null
  }

  componentDidMount() {
    const { pixelToStep } = this.state
    const { vertical } = this.props

    pixelToStep.setDomain(getSliderDomain(this.slider.current, vertical))
  }

  componentWillUnmount() {
    this.removeListeners()
  }

  removeListeners() {
    if (isBrowser) {
      document.removeEventListener('mousemove', this.onMouseMove)
      document.removeEventListener('mouseup', this.onMouseUp)
      document.removeEventListener('touchmove', this.onTouchMove)
      document.removeEventListener('touchend', this.onTouchEnd)
    }
  }

  onKeyDown = (e, handleID) => {
    let validUpKeys = ['ArrowRight', 'ArrowUp']
    let validDownKeys = ['ArrowDown', 'ArrowLeft']
    const {
      state: { handles },
      props: { step, reversed, vertical, domain },
    } = this
    const key = e.key || e.keyCode

    if (!validUpKeys.concat(validDownKeys).includes(key)) {
      return
    }

    if (vertical) {
      [validUpKeys, validDownKeys] = [validDownKeys, validUpKeys]
    }

    e.stopPropagation && e.stopPropagation()
    e.preventDefault && e.preventDefault()

    const found = handles.find(value => {
      return value.key === handleID
    })
    if (!found) {
      return
    }

    const currVal = found.val
    let newVal = currVal

    if (validUpKeys.includes(key)) {
      newVal = getNextValue(currVal, step, domain, reversed)
    } else if (validDownKeys.includes(key)) {
      newVal = getPrevValue(currVal, step, domain, reversed)
    }
    const nextHandles = handles.map(v =>
      v.key === handleID ? { key: v.key, val: newVal } : v,
    )

    this.submitUpdate(nextHandles, true)
  }

  onMouseDown = (e, handleID) => {
    this.onStart(e, handleID, false)
  }

  onTouchStart = (e, handleID) => {
    if (isNotValidTouch(e)) {
      return
    }

    this.onStart(e, handleID, true)
  }

  onStart(e, handleID, isTouch) {
    const {
      state: { handles },
      props: { onSlideStart },
    } = this

    e.stopPropagation && e.stopPropagation()
    e.preventDefault && e.preventDefault()

    const found = handles.find(value => {
      return value.key === handleID
    })

    if (found) {
      this.setState({ activeHandleID: handleID })
      onSlideStart(handles.map(d => d.val), { activeHandleID: handleID })
      isTouch ? this.addTouchEvents() : this.addMouseEvents()
    } else {
      this.setState({ activeHandleID: null })
      this.handleRailAndTrackClicks(e, isTouch)
    }
  }

  handleRailAndTrackClicks(e, isTouch) {
    const {
      state: { handles: curr, pixelToStep },
      props: { vertical, reversed },
    } = this
    const { slider } = this

    // double check the dimensions of the slider
    pixelToStep.setDomain(getSliderDomain(slider.current, vertical))

    // find the closest value (aka step) to the event location
    let updateValue

    if (isTouch) {
      updateValue = pixelToStep.getValue(getTouchPosition(vertical, e))
    } else {
      updateValue = pixelToStep.getValue(vertical ? e.clientY : e.pageX)
    }

    // find the closest handle key
    let updateKey = null
    let minDiff = Infinity

    for (let i = 0; i < curr.length; i++) {
      const { key, val } = curr[i]
      const diff = Math.abs(val - updateValue)

      if (diff < minDiff) {
        updateKey = key
        minDiff = diff
      }
    }

    // generate a "candidate" set of values - a suggestion of what to do
    const nextHandles = getUpdatedHandles(
      curr,
      updateKey,
      updateValue,
      reversed,
    )

    // submit the candidate values
    this.submitUpdate(nextHandles, true)
  }

  getEventData = (e, isTouch) => {
    const {
      state: { pixelToStep, valueToPerc },
      props: { vertical },
    } = this

    let value

    if (isTouch) {
      value = pixelToStep.getValue(getTouchPosition(vertical, e))
    } else {
      value = pixelToStep.getValue(vertical ? e.clientY : e.pageX)
    }

    return {
      value,
      percent: valueToPerc.getValue(value),
    }
  }

  addMouseEvents() {
    if (isBrowser) {
      document.addEventListener('mousemove', this.onMouseMove)
      document.addEventListener('mouseup', this.onMouseUp)
    }
  }

  addTouchEvents() {
    if (isBrowser) {
      document.addEventListener('touchmove', this.onTouchMove)
      document.addEventListener('touchend', this.onTouchEnd)
    }
  }

  onMouseMove = e => {
    const {
      state: { handles: curr, pixelToStep, activeHandleID },
      props: { vertical, reversed },
    } = this
    const { slider } = this

    // double check the dimensions of the slider
    pixelToStep.setDomain(getSliderDomain(slider.current, vertical))

    // find the closest value (aka step) to the event location
    const updateValue = pixelToStep.getValue(vertical ? e.clientY : e.pageX)

    // generate a "candidate" set of values - a suggestion of what to do
    const nextHandles = getUpdatedHandles(
      curr,
      activeHandleID,
      updateValue,
      reversed,
    )

    // submit the candidate values
    this.submitUpdate(nextHandles)
  }

  onTouchMove = e => {
    const {
      state: { handles: curr, pixelToStep, activeHandleID },
      props: { vertical, reversed },
    } = this
    const { slider } = this

    if (isNotValidTouch(e)) {
      return
    }

    // double check the dimensions of the slider
    pixelToStep.setDomain(getSliderDomain(slider.current, vertical))

    // find the closest value (aka step) to the event location
    const updateValue = pixelToStep.getValue(getTouchPosition(vertical, e))

    // generate a "candidate" set of values - a suggestion of what to do
    const nextHandles = getUpdatedHandles(
      curr,
      activeHandleID,
      updateValue,
      reversed,
    )

    // submit the candidate values
    this.submitUpdate(nextHandles)
  }

  submitUpdate(next, callOnChange) {
    const { mode, step, onUpdate, onChange, reversed } = this.props
    const { getValue } = this.state.valueToStep

    this.setState(({ handles: curr }) => {
      let handles

      // given the current handles and a candidate set, decide what to do
      if (typeof mode === 'function') {
        handles = mode(curr, next, step, reversed, getValue)
        warning(
          Array.isArray(handles),
          'Custom mode function did not return an array.',
        )
      } else {
        switch (mode) {
          case 1:
            handles = mode1(curr, next)
            break
          case 2:
            handles = mode2(curr, next)
            break
          case 3:
            handles = mode3(curr, next, step, reversed, getValue)
            break
          default:
            handles = next
            warning(false, `${prfx} Invalid mode value.`)
        }
      }

      onUpdate(handles.map(d => d.val))

      if (callOnChange) {
        onChange(handles.map(d => d.val))
      }

      return { handles }
    })
  }

  onMouseUp = () => {
    const {
      state: { handles, activeHandleID },
      props: { onChange, onSlideEnd },
    } = this

    onChange(handles.map(d => d.val))
    onSlideEnd(handles.map(d => d.val), { activeHandleID })

    this.setState({ activeHandleID: null })

    if (isBrowser) {
      document.removeEventListener('mousemove', this.onMouseMove)
      document.removeEventListener('mouseup', this.onMouseUp)
    }
  }

  onTouchEnd = () => {
    const {
      state: { handles, activeHandleID },
      props: { onChange, onSlideEnd },
    } = this

    onChange(handles.map(d => d.val))
    onSlideEnd(handles.map(d => d.val), { activeHandleID })

    this.setState({ activeHandleID: null })

    if (isBrowser) {
      document.removeEventListener('touchmove', this.onTouchMove)
      document.removeEventListener('touchend', this.onTouchEnd)
    }
  }

  render() {
    const {
      state: { handles, valueToPerc, activeHandleID },
      props: { className, rootStyle, disabled },
    } = this

    const mappedHandles = handles.map(({ key, val }) => {
      return { id: key, value: val, percent: valueToPerc.getValue(val) }
    })

    const children = React.Children.map(this.props.children, child => {
      if (
        child.type.name === Rail.name ||
        child.type.name === Ticks.name ||
        child.type.name === Tracks.name ||
        child.type.name === Handles.name
      ) {
        return React.cloneElement(child, {
          scale: valueToPerc,
          handles: mappedHandles,
          activeHandleID,
          getEventData: this.getEventData,
          emitKeyboard: disabled ? noop : this.onKeyDown,
          emitMouse: disabled ? noop : this.onMouseDown,
          emitTouch: disabled ? noop : this.onTouchStart,
        })
      }

      return child
    })

    return (
      <div style={rootStyle || {}} className={className} ref={this.slider}>
        {children}
      </div>
    )
  }
}

Slider.propTypes = {
  /**
   * CSS class name applied to the root div of the slider.
   */
  className: PropTypes.string,
  /**
   * An object with any inline styles you want applied to the root div.
   */
  rootStyle: PropTypes.object,
  /**
   * Two element array of numbers providing the min and max values for the slider [min, max] e.g. [0, 100].
   * It does not matter if the slider is reversed on the screen, domain is always [min, max] with min < max.
   */
  domain: PropTypes.array,
  /**
   * An array of numbers. You can supply one for a value slider, two for a range slider or more to create n-handled sliders.
   * The values should correspond to valid step values in the domain.
   * The numbers will be forced into the domain if they are two small or large.
   */
  values: PropTypes.array,
  /**
   * The step value for the slider.
   */
  step: PropTypes.number,
  /**
   * The interaction mode. Value of 1 will allow handles to cross each other.
   * Value of 2 will keep the sliders from crossing and separated by a step.
   * Value of 3 will make the handles pushable and keep them a step apart.
   * ADVANCED: You can also supply a function that will be passed the current values and the incoming update.
   * Your function should return what the state should be set as.
   */
  mode: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
  /**
   * Set to true if the slider is displayed vertically to tell the slider to use the height to calculate positions.
   */
  vertical: PropTypes.bool,
  /**
   * Reverse the display of slider values.
   */
  reversed: PropTypes.bool,
  /**
   * Function triggered when the value of the slider has changed. This will recieve changes at the end of a slide as well as changes from clicks on rails and tracks. Receives values.
   */
  onChange: PropTypes.func,
  /**
   * Function called with the values at each update (caution: high-volume updates when dragging). Receives values.
   */
  onUpdate: PropTypes.func,
  /**
   * Function triggered with ontouchstart or onmousedown on a handle. Receives values.
   */
  onSlideStart: PropTypes.func,
  /**
   * Function triggered on ontouchend or onmouseup on a handle. Receives values.
   */
  onSlideEnd: PropTypes.func,
  /**
   * Ignore all mouse, touch and keyboard events
   */
  disabled: PropTypes.bool,
  /**
   * When true, the slider will warn if values are changed to fit domain and step values.  Defaults to false.
   */
  warnOnChanges: PropTypes.bool,
  /**
   * Component children to render
   */
  children: PropTypes.any,
}

Slider.defaultProps = {
  mode: 1,
  step: 0.1,
  domain: [0, 100],
  vertical: false,
  reversed: false,
  onChange: noop,
  onUpdate: noop,
  onSlideStart: noop,
  onSlideEnd: noop,
  disabled: false,
  maxSteps: 500000,
}

export default Slider
<commit_msg>update props<commit_after>import React, { PureComponent } from 'react'
import warning from 'warning'
import PropTypes from 'prop-types'
import Rail from '../Rail'
import Ticks from '../Ticks'
import Tracks from '../Tracks'
import Handles from '../Handles'
import { mode1, mode2, mode3 } from './modes'
import {
  isNotValidTouch,
  getTouchPosition,
  getUpdatedHandles,
  getSliderDomain,
  getHandles,
  prfx,
} from './utils'
import LinearScale from './LinearScale'
import DiscreteScale from './DiscreteScale'

const isBrowser =
  typeof window !== 'undefined' && typeof document !== 'undefined'

const noop = () => {}

const compare = b => (m, d, i) => m && b[i] === d

const equal = (a, b) => {
  return a === b || (a.length === b.length && a.reduce(compare(b), true))
}

const getNextValue = (curr, step, domain, reversed) => {
  const newVal = reversed ? curr - step : curr + step
  return reversed ? Math.max(domain[0], newVal) : Math.min(domain[1], newVal)
}

const getPrevValue = (curr, step, domain, reversed) => {
  const newVal = reversed ? curr + step : curr - step
  return reversed ? Math.min(domain[1], newVal) : Math.max(domain[0], newVal)
}

class Slider extends PureComponent {
  state = {
    step: null,
    values: null,
    domain: null,
    handles: null,
    reversed: null,
    activeHandleID: null,
    valueToPerc: null,
    valueToStep: null,
    pixelToStep: null,
  }

  slider = React.createRef()

  static getDerivedStateFromProps(nextProps, prevState) {
    const {
      step,
      values,
      domain,
      reversed,
      onUpdate,
      onChange,
      // maxSteps,
      // rangeWarnings,
      warnOnChanges,
    } = nextProps

    let valueToPerc = prevState.valueToPerc
    let valueToStep = prevState.valueToStep
    let pixelToStep = prevState.pixelToStep

    const nextState = {}

    if (!valueToPerc || !valueToStep || !pixelToStep) {
      valueToPerc = new LinearScale()
      valueToStep = new DiscreteScale()
      pixelToStep = new DiscreteScale()

      nextState.valueToPerc = valueToPerc
      nextState.valueToStep = valueToStep
      nextState.pixelToStep = pixelToStep
    }

    if (
      prevState.step === null ||
      prevState.domain === null ||
      prevState.reversed === null ||
      step !== prevState.step ||
      domain[0] !== prevState.domain[0] ||
      domain[1] !== prevState.domain[1] ||
      reversed !== prevState.reversed
    ) {
      const [min, max] = domain

      valueToStep
        .setStep(step)
        .setRange([min, max])
        .setDomain([min, max])

      if (reversed === true) {
        valueToPerc.setDomain([min, max]).setRange([100, 0])
        pixelToStep.setStep(step).setRange([max, min])
      } else {
        valueToPerc.setDomain([min, max]).setRange([0, 100])
        pixelToStep.setStep(step).setRange([min, max])
      }

      warning(
        max > min,
        `${prfx} Max must be greater than min (even if reversed). Max is ${max}. Min is ${min}.`,
      )

      const { handles, changes } = getHandles(
        values || prevState.values,
        reversed,
        valueToStep,
        warnOnChanges,
      )

      if (changes || values === undefined || values === prevState.values) {
        onUpdate(handles.map(d => d.val))
        onChange(handles.map(d => d.val))
      }

      nextState.step = step
      nextState.values = values
      nextState.domain = domain
      nextState.handles = handles
      nextState.reversed = reversed
    } else if (!equal(values, prevState.values)) {
      const { handles, changes } = getHandles(
        values,
        reversed,
        valueToStep,
        warnOnChanges,
      )

      if (changes) {
        onUpdate(handles.map(d => d.val))
        onChange(handles.map(d => d.val))
      }

      nextState.values = values
      nextState.handles = handles
    }

    if (Object.keys(nextState).length) {
      return nextState
    }

    return null
  }

  componentDidMount() {
    const { pixelToStep } = this.state
    const { vertical } = this.props

    pixelToStep.setDomain(getSliderDomain(this.slider.current, vertical))
  }

  componentWillUnmount() {
    this.removeListeners()
  }

  removeListeners() {
    if (isBrowser) {
      document.removeEventListener('mousemove', this.onMouseMove)
      document.removeEventListener('mouseup', this.onMouseUp)
      document.removeEventListener('touchmove', this.onTouchMove)
      document.removeEventListener('touchend', this.onTouchEnd)
    }
  }

  onKeyDown = (e, handleID) => {
    let validUpKeys = ['ArrowRight', 'ArrowUp']
    let validDownKeys = ['ArrowDown', 'ArrowLeft']
    const {
      state: { handles },
      props: { step, reversed, vertical, domain },
    } = this
    const key = e.key || e.keyCode

    if (!validUpKeys.concat(validDownKeys).includes(key)) {
      return
    }

    if (vertical) {
      [validUpKeys, validDownKeys] = [validDownKeys, validUpKeys]
    }

    e.stopPropagation && e.stopPropagation()
    e.preventDefault && e.preventDefault()

    const found = handles.find(value => {
      return value.key === handleID
    })
    if (!found) {
      return
    }

    const currVal = found.val
    let newVal = currVal

    if (validUpKeys.includes(key)) {
      newVal = getNextValue(currVal, step, domain, reversed)
    } else if (validDownKeys.includes(key)) {
      newVal = getPrevValue(currVal, step, domain, reversed)
    }
    const nextHandles = handles.map(v =>
      v.key === handleID ? { key: v.key, val: newVal } : v,
    )

    this.submitUpdate(nextHandles, true)
  }

  onMouseDown = (e, handleID) => {
    this.onStart(e, handleID, false)
  }

  onTouchStart = (e, handleID) => {
    if (isNotValidTouch(e)) {
      return
    }

    this.onStart(e, handleID, true)
  }

  onStart(e, handleID, isTouch) {
    const {
      state: { handles },
      props: { onSlideStart },
    } = this

    e.stopPropagation && e.stopPropagation()
    e.preventDefault && e.preventDefault()

    const found = handles.find(value => {
      return value.key === handleID
    })

    if (found) {
      this.setState({ activeHandleID: handleID })
      onSlideStart(handles.map(d => d.val), { activeHandleID: handleID })
      isTouch ? this.addTouchEvents() : this.addMouseEvents()
    } else {
      this.setState({ activeHandleID: null })
      this.handleRailAndTrackClicks(e, isTouch)
    }
  }

  handleRailAndTrackClicks(e, isTouch) {
    const {
      state: { handles: curr, pixelToStep },
      props: { vertical, reversed },
    } = this
    const { slider } = this

    // double check the dimensions of the slider
    pixelToStep.setDomain(getSliderDomain(slider.current, vertical))

    // find the closest value (aka step) to the event location
    let updateValue

    if (isTouch) {
      updateValue = pixelToStep.getValue(getTouchPosition(vertical, e))
    } else {
      updateValue = pixelToStep.getValue(vertical ? e.clientY : e.pageX)
    }

    // find the closest handle key
    let updateKey = null
    let minDiff = Infinity

    for (let i = 0; i < curr.length; i++) {
      const { key, val } = curr[i]
      const diff = Math.abs(val - updateValue)

      if (diff < minDiff) {
        updateKey = key
        minDiff = diff
      }
    }

    // generate a "candidate" set of values - a suggestion of what to do
    const nextHandles = getUpdatedHandles(
      curr,
      updateKey,
      updateValue,
      reversed,
    )

    // submit the candidate values
    this.submitUpdate(nextHandles, true)
  }

  getEventData = (e, isTouch) => {
    const {
      state: { pixelToStep, valueToPerc },
      props: { vertical },
    } = this

    let value

    if (isTouch) {
      value = pixelToStep.getValue(getTouchPosition(vertical, e))
    } else {
      value = pixelToStep.getValue(vertical ? e.clientY : e.pageX)
    }

    return {
      value,
      percent: valueToPerc.getValue(value),
    }
  }

  addMouseEvents() {
    if (isBrowser) {
      document.addEventListener('mousemove', this.onMouseMove)
      document.addEventListener('mouseup', this.onMouseUp)
    }
  }

  addTouchEvents() {
    if (isBrowser) {
      document.addEventListener('touchmove', this.onTouchMove)
      document.addEventListener('touchend', this.onTouchEnd)
    }
  }

  onMouseMove = e => {
    const {
      state: { handles: curr, pixelToStep, activeHandleID },
      props: { vertical, reversed },
    } = this
    const { slider } = this

    // double check the dimensions of the slider
    pixelToStep.setDomain(getSliderDomain(slider.current, vertical))

    // find the closest value (aka step) to the event location
    const updateValue = pixelToStep.getValue(vertical ? e.clientY : e.pageX)

    // generate a "candidate" set of values - a suggestion of what to do
    const nextHandles = getUpdatedHandles(
      curr,
      activeHandleID,
      updateValue,
      reversed,
    )

    // submit the candidate values
    this.submitUpdate(nextHandles)
  }

  onTouchMove = e => {
    const {
      state: { handles: curr, pixelToStep, activeHandleID },
      props: { vertical, reversed },
    } = this
    const { slider } = this

    if (isNotValidTouch(e)) {
      return
    }

    // double check the dimensions of the slider
    pixelToStep.setDomain(getSliderDomain(slider.current, vertical))

    // find the closest value (aka step) to the event location
    const updateValue = pixelToStep.getValue(getTouchPosition(vertical, e))

    // generate a "candidate" set of values - a suggestion of what to do
    const nextHandles = getUpdatedHandles(
      curr,
      activeHandleID,
      updateValue,
      reversed,
    )

    // submit the candidate values
    this.submitUpdate(nextHandles)
  }

  submitUpdate(next, callOnChange) {
    const { mode, step, onUpdate, onChange, reversed } = this.props
    const { getValue } = this.state.valueToStep

    this.setState(({ handles: curr }) => {
      let handles

      // given the current handles and a candidate set, decide what to do
      if (typeof mode === 'function') {
        handles = mode(curr, next, step, reversed, getValue)
        warning(
          Array.isArray(handles),
          'Custom mode function did not return an array.',
        )
      } else {
        switch (mode) {
          case 1:
            handles = mode1(curr, next)
            break
          case 2:
            handles = mode2(curr, next)
            break
          case 3:
            handles = mode3(curr, next, step, reversed, getValue)
            break
          default:
            handles = next
            warning(false, `${prfx} Invalid mode value.`)
        }
      }

      onUpdate(handles.map(d => d.val))

      if (callOnChange) {
        onChange(handles.map(d => d.val))
      }

      return { handles }
    })
  }

  onMouseUp = () => {
    const {
      state: { handles, activeHandleID },
      props: { onChange, onSlideEnd },
    } = this

    onChange(handles.map(d => d.val))
    onSlideEnd(handles.map(d => d.val), { activeHandleID })

    this.setState({ activeHandleID: null })

    if (isBrowser) {
      document.removeEventListener('mousemove', this.onMouseMove)
      document.removeEventListener('mouseup', this.onMouseUp)
    }
  }

  onTouchEnd = () => {
    const {
      state: { handles, activeHandleID },
      props: { onChange, onSlideEnd },
    } = this

    onChange(handles.map(d => d.val))
    onSlideEnd(handles.map(d => d.val), { activeHandleID })

    this.setState({ activeHandleID: null })

    if (isBrowser) {
      document.removeEventListener('touchmove', this.onTouchMove)
      document.removeEventListener('touchend', this.onTouchEnd)
    }
  }

  render() {
    const {
      state: { handles, valueToPerc, activeHandleID },
      props: { className, rootStyle, disabled },
    } = this

    const mappedHandles = handles.map(({ key, val }) => {
      return { id: key, value: val, percent: valueToPerc.getValue(val) }
    })

    const children = React.Children.map(this.props.children, child => {
      if (
        child.type.name === Rail.name ||
        child.type.name === Ticks.name ||
        child.type.name === Tracks.name ||
        child.type.name === Handles.name
      ) {
        return React.cloneElement(child, {
          scale: valueToPerc,
          handles: mappedHandles,
          activeHandleID,
          getEventData: this.getEventData,
          emitKeyboard: disabled ? noop : this.onKeyDown,
          emitMouse: disabled ? noop : this.onMouseDown,
          emitTouch: disabled ? noop : this.onTouchStart,
        })
      }

      return child
    })

    return (
      <div style={rootStyle || {}} className={className} ref={this.slider}>
        {children}
      </div>
    )
  }
}

Slider.propTypes = {
  /**
   * CSS class name applied to the root div of the slider.
   */
  className: PropTypes.string,
  /**
   * An object with any inline styles you want applied to the root div.
   */
  rootStyle: PropTypes.object,
  /**
   * Two element array of numbers providing the min and max values for the slider [min, max] e.g. [0, 100].
   * It does not matter if the slider is reversed on the screen, domain is always [min, max] with min < max.
   */
  domain: PropTypes.array,
  /**
   * An array of numbers. You can supply one for a value slider, two for a range slider or more to create n-handled sliders.
   * The values should correspond to valid step values in the domain.
   * The numbers will be forced into the domain if they are two small or large.
   */
  values: PropTypes.array,
  /**
   * The step value for the slider.
   */
  step: PropTypes.number,
  /**
   * The interaction mode. Value of 1 will allow handles to cross each other.
   * Value of 2 will keep the sliders from crossing and separated by a step.
   * Value of 3 will make the handles pushable and keep them a step apart.
   * ADVANCED: You can also supply a function that will be passed the current values and the incoming update.
   * Your function should return what the state should be set as.
   */
  mode: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
  /**
   * Set to true if the slider is displayed vertically to tell the slider to use the height to calculate positions.
   */
  vertical: PropTypes.bool,
  /**
   * Reverse the display of slider values.
   */
  reversed: PropTypes.bool,
  /**
   * Function triggered when the value of the slider has changed. This will recieve changes at the end of a slide as well as changes from clicks on rails and tracks. Receives values.
   */
  onChange: PropTypes.func,
  /**
   * Function called with the values at each update (caution: high-volume updates when dragging). Receives values.
   */
  onUpdate: PropTypes.func,
  /**
   * Function triggered with ontouchstart or onmousedown on a handle. Receives values.
   */
  onSlideStart: PropTypes.func,
  /**
   * Function triggered on ontouchend or onmouseup on a handle. Receives values.
   */
  onSlideEnd: PropTypes.func,
  /**
   * Ignore all mouse, touch and keyboard events
   */
  disabled: PropTypes.bool,
  /**
   * When true, the slider will warn if values are changed to fit domain and step values.  Defaults to false.
   */
  warnOnChanges: PropTypes.bool,
  /**
   * Component children to render
   */
  children: PropTypes.any,
}

Slider.defaultProps = {
  mode: 1,
  step: 0.1,
  domain: [0, 100],
  vertical: false,
  reversed: false,
  onChange: noop,
  onUpdate: noop,
  onSlideStart: noop,
  onSlideEnd: noop,
  disabled: false,
  warnOnChanges: false,
}

export default Slider
<|endoftext|>


<commit_before>/**
 * WorkersCmd
 * ==========
 *
 * A command sent to a worker. The idea is that you send the worker an object,
 * and a method to perform on that object, and the arguments to that method. It
 * will send back a result, which is a WorkersResult object.
 */
'use strict'
let dependencies = {
  BW: require('./bw'),
  Struct: require('./struct')
}

let inject = function (deps) {
  let BW = deps.BW
  let Struct = deps.Struct

  function WorkersCmd (objbuf, classname, methodname, args, id) {
    if (!(this instanceof WorkersCmd)) {
      return new WorkersCmd(objbuf, classname, methodname, args, id)
    }
    this.fromObject({objbuf, classname, methodname, args, id})
  }

  WorkersCmd.prototype = Object.create(Struct.prototype)
  WorkersCmd.prototype.constructor = WorkersCmd

  /**
   * The arguments to a workers command can be normal javascript objects,
   * buffers, or fullnode objects.
   */
  WorkersCmd.argsToBW = function (bw, args) {
    bw.writeVarintNum(args.length)
    for (let index in args) {
      let arg = args[index]
      if (Buffer.isBuffer(arg)) {
        // argument is Buffer
        bw.writeUInt8(0) // header byte
        bw.writeVarintNum(arg.length)
        bw.write(arg)
      } else if (arg.toFastBuffer) {
        // argument is fullnode object
        bw.writeUInt8(1) // header byte
        let classname = arg.constructor.name
        bw.writeVarintNum(classname.length)
        bw.write(new Buffer(classname))
        let buf = arg.toFastBuffer()
        bw.writeVarintNum(buf.length)
        bw.write(buf)
      } else {
        // assume basic javascript type
        bw.writeUInt8(2) // header byte
        let buf = new Buffer(JSON.stringify(arg))
        bw.writeVarintNum(buf.length)
        bw.write(buf)
      }
    }
    return bw
  }

  /**
   * The arguments to a workers command can be normal javascript objects,
   * buffers, or fullnode objects.
   */
  WorkersCmd.argsFromBR = function (br, classes) {
    let argslen = br.readVarintNum()
    let args = []
    for (let i = 0; i < argslen; i++) {
      let header = br.readUInt8()
      if (header === 0) {
        // argument is Buffer
        let len = br.readVarintNum()
        let buf = br.read(len)
        args.push(buf)
      } else if (header === 1) {
        // argument is fullnode object
        let classnamelen = br.readVarintNum()
        let classname = br.read(classnamelen).toString()
        let buflen = br.readVarintNum()
        let buf = br.read(buflen)
        let obj = classes[classname]().fromFastBuffer(buf)
        args.push(obj)
      } else if (header === 2) {
        // argument is basic javascript type
        let len = br.readVarintNum()
        let buf = br.read(len)
        let obj = JSON.parse(buf.toString())
        args.push(obj)
      } else {
        throw new Error('invalid header byte for argument')
      }
    }
    return args
  }

  WorkersCmd.prototype.toBW = function (bw) {
    if (!bw) {
      bw = new BW()
    }
    let classnamebuf = new Buffer(this.classname)
    bw.writeUInt8(Number(this.isobj))
    bw.writeVarintNum(classnamebuf.length)
    bw.write(classnamebuf)
    let methodnamebuf = new Buffer(this.methodname)
    bw.writeVarintNum(methodnamebuf.length)
    bw.write(methodnamebuf)
    bw.writeVarintNum(this.objbuf.length)
    bw.write(this.objbuf)
    WorkersCmd.argsToBW(bw, this.args)
    bw.writeVarintNum(this.id)
    return bw
  }

  WorkersCmd.prototype.fromBR = function (br, classes) {
    this.isobj = Boolean(br.readUInt8())
    let classnamelen = br.readVarintNum()
    this.classname = br.read(classnamelen).toString()
    let methodnamelen = br.readVarintNum()
    this.methodname = br.read(methodnamelen).toString()
    let objbuflen = br.readVarintNum()
    this.objbuf = br.read(objbuflen)
    this.args = WorkersCmd.argsFromBR(br, classes)
    this.id = br.readVarintNum()
    return this
  }

  WorkersCmd.prototype.fromObjectMethod = function (obj, methodname, args, id) {
    this.isobj = true
    this.objbuf = obj.toFastBuffer()
    this.classname = obj.constructor.name
    this.methodname = methodname
    this.args = args
    this.id = id
    return this
  }

  WorkersCmd.prototype.fromClassMethod = function (classname, methodname, args, id) {
    this.isobj = false
    this.objbuf = new Buffer(0)
    this.classname = classname
    this.methodname = methodname
    this.args = args
    this.id = id
    return this
  }

  return WorkersCmd
}

inject = require('./injector')(inject, dependencies)
let WorkersCmd = inject()
module.exports = WorkersCmd
<commit_msg>a better way to check if an obj is a fullnode obj<commit_after>/**
 * WorkersCmd
 * ==========
 *
 * A command sent to a worker. The idea is that you send the worker an object,
 * and a method to perform on that object, and the arguments to that method. It
 * will send back a result, which is a WorkersResult object.
 */
'use strict'
let dependencies = {
  BW: require('./bw'),
  Struct: require('./struct')
}

let inject = function (deps) {
  let BW = deps.BW
  let Struct = deps.Struct

  function WorkersCmd (objbuf, classname, methodname, args, id) {
    if (!(this instanceof WorkersCmd)) {
      return new WorkersCmd(objbuf, classname, methodname, args, id)
    }
    this.fromObject({objbuf, classname, methodname, args, id})
  }

  WorkersCmd.prototype = Object.create(Struct.prototype)
  WorkersCmd.prototype.constructor = WorkersCmd

  /**
   * The arguments to a workers command can be normal javascript objects,
   * buffers, or fullnode objects.
   */
  WorkersCmd.argsToBW = function (bw, args) {
    bw.writeVarintNum(args.length)
    for (let index in args) {
      let arg = args[index]
      if (Buffer.isBuffer(arg)) {
        // argument is Buffer
        bw.writeUInt8(0) // header byte
        bw.writeVarintNum(arg.length)
        bw.write(arg)
      } else if (arg instanceof Struct) {
        // argument is fullnode object
        bw.writeUInt8(1) // header byte
        let classname = arg.constructor.name
        bw.writeVarintNum(classname.length)
        bw.write(new Buffer(classname))
        let buf = arg.toFastBuffer()
        bw.writeVarintNum(buf.length)
        bw.write(buf)
      } else {
        // assume basic javascript type
        bw.writeUInt8(2) // header byte
        let buf = new Buffer(JSON.stringify(arg))
        bw.writeVarintNum(buf.length)
        bw.write(buf)
      }
    }
    return bw
  }

  /**
   * The arguments to a workers command can be normal javascript objects,
   * buffers, or fullnode objects.
   */
  WorkersCmd.argsFromBR = function (br, classes) {
    let argslen = br.readVarintNum()
    let args = []
    for (let i = 0; i < argslen; i++) {
      let header = br.readUInt8()
      if (header === 0) {
        // argument is Buffer
        let len = br.readVarintNum()
        let buf = br.read(len)
        args.push(buf)
      } else if (header === 1) {
        // argument is fullnode object
        let classnamelen = br.readVarintNum()
        let classname = br.read(classnamelen).toString()
        let buflen = br.readVarintNum()
        let buf = br.read(buflen)
        let obj = classes[classname]().fromFastBuffer(buf)
        args.push(obj)
      } else if (header === 2) {
        // argument is basic javascript type
        let len = br.readVarintNum()
        let buf = br.read(len)
        let obj = JSON.parse(buf.toString())
        args.push(obj)
      } else {
        throw new Error('invalid header byte for argument')
      }
    }
    return args
  }

  WorkersCmd.prototype.toBW = function (bw) {
    if (!bw) {
      bw = new BW()
    }
    let classnamebuf = new Buffer(this.classname)
    bw.writeUInt8(Number(this.isobj))
    bw.writeVarintNum(classnamebuf.length)
    bw.write(classnamebuf)
    let methodnamebuf = new Buffer(this.methodname)
    bw.writeVarintNum(methodnamebuf.length)
    bw.write(methodnamebuf)
    bw.writeVarintNum(this.objbuf.length)
    bw.write(this.objbuf)
    WorkersCmd.argsToBW(bw, this.args)
    bw.writeVarintNum(this.id)
    return bw
  }

  WorkersCmd.prototype.fromBR = function (br, classes) {
    this.isobj = Boolean(br.readUInt8())
    let classnamelen = br.readVarintNum()
    this.classname = br.read(classnamelen).toString()
    let methodnamelen = br.readVarintNum()
    this.methodname = br.read(methodnamelen).toString()
    let objbuflen = br.readVarintNum()
    this.objbuf = br.read(objbuflen)
    this.args = WorkersCmd.argsFromBR(br, classes)
    this.id = br.readVarintNum()
    return this
  }

  WorkersCmd.prototype.fromObjectMethod = function (obj, methodname, args, id) {
    this.isobj = true
    this.objbuf = obj.toFastBuffer()
    this.classname = obj.constructor.name
    this.methodname = methodname
    this.args = args
    this.id = id
    return this
  }

  WorkersCmd.prototype.fromClassMethod = function (classname, methodname, args, id) {
    this.isobj = false
    this.objbuf = new Buffer(0)
    this.classname = classname
    this.methodname = methodname
    this.args = args
    this.id = id
    return this
  }

  return WorkersCmd
}

inject = require('./injector')(inject, dependencies)
let WorkersCmd = inject()
module.exports = WorkersCmd
<|endoftext|>


<commit_before>var XMLWriter = require('xml-writer');
var default_tag_name = 'row';

exports.dump = function(obj) {

	if (typeof obj != "object") return obj;
	//    if (typeof xw != XMLWriter)
	var	xw = new XMLWriter();

	var getype = function(o) {
		if (!o)
			return 'undefined';
		else if (Array.isArray(o))
			return 'array';
		else
			return typeof o;
	}

	var getval = function(o) {
		return ''+o; // !TODO
	}

	var getname = function(n) {
		return n.replace('$', ':');
	}

	var parse = function(o) {
		if (getype(o) != 'object') return;
		for(var key in o) {
			if (o.hasOwnProperty(key) && o[key] != null){
				var type1 = getype(o[key]);
				if (type1 == 'object') {
					xw.startElement(getname(key));
					parse(o[key]);
					xw.endElement();
				}
				else if (type1 == 'array' && o[key].length <= 0) {
					xw.startElement(getname(key)).endElement();
				}
				else if (type1 == 'array' && o[key].length > 0) {
					var type2 = getype(o[key][0]);
					if (type2 == 'object') {
						o[key].forEach(function(item, index) {
								if (key == '$element' || key == '$e' || key == '#element' || key == '_element' || key == '_e') {
									parse(item);
								}
								else {
									xw.startElement(getname(key));
									parse(item);
									xw.endElement();
								}
						});
					}
					else {
						o[key].forEach(function(item, index) {
								if (key == '$element' || key == '$e' || key == '#element' || key == '_element' || key == '_e') {
									xw.startCData().text(getval(item)).endCData();
								}
								else {
									xw.startElement(getname(key));
									xw.startCData().text(getval(item)).endCData();
									xw.endElement();
								}
						});
					}
				}
				else {
					if (key == '$text' || key == '$t' || key == '#text' || key == '_text' || key == '_t') {
						xw.text(getval(o[key]));
					}
					else if (key == '$comment' || key == '$c' || key == '#comment' || key == '_comment' || key == '_c') {
						xw.startComment().text(getval(o[key])).endComment();
					}
					else if (key == '$cdata' || key == '$cd' || key == '#cdata' || key == '_cdata' || key == '_cd') {
						xw.startCData().text(getval(o[key])).endCData();
					}
					else  {
						xw.startAttribute(getname(key)).text(getval(o[key])).endAttribute();
					}
				}
			}
		}
	}
	var o= {};
	if (Object.keys(obj).length == 1) {
		o = obj;
	}
	else {
		o[default_tag_name] = obj;
	}
	parse(o);

	return xw.toString();

};
exports.load = function(str) {
	if (typeof str != "string") return str;

	var parser = require("sax").parser(true, {trim:true, xmlns:false});
	var result = {},
		stack = [],
		cdata = '';

	function cvalue(n, v) {
		n = n.replace(':', '$');
		var o = stack[stack.length-1];
		if (o == undefined) {
			o = {};
			o[n] = v;
			return o[n];
		}
		else if (o[n] == undefined) {
			o[n] = v;
			return o[n];
		}
		else if (!Array.isArray(o[n])) {
			var x = o[n];
			o[n] = new Array(x, v);
			return o[n][1];
		}
		else {
			var i = o[n].push(v);
			return o[n][i-1];
		}
	}
	function cattr(o) {
		var r = {};
		for(var key in o) {
			if (o.hasOwnProperty(key) && o[key]) {
				r[key.replace(':', '$')] = o[key];
			}
		}
		return r;
	}

	parser.onprocessinginstruction = function (pi) {
	};
	parser.ontext = function (v) {
		cvalue('$t', v);
	};
	parser.oncomment = function (v) {
		cvalue('$c', v);
	};
	parser.oncdata = function (v) {
		cdata += v;
	};
	parser.onopencdata = function () {
		cdata = '';
	};
	parser.onclosecdata = function () {
		cvalue('$cd', cdata);
		cdata = '';
	};
	parser.onopentag = function (node) {
//        console.log(stack);
		if (stack.length == 1 && node.name == default_tag_name) {
			result = cattr(node.attributes);
			stack.push(result);
		}
		else {
			stack.push(cvalue(node.name, cattr(node.attributes)));
		}
	};
	parser.onclosetag = function () {
		stack.pop();
	};
	parser.onready = function () {
	};
	parser.onend = function () {
	};

	stack.push(result);
	try {
		parser.write(str).close();
	}
	catch(e) {
		return str;
	}
	stack.pop();
	return result;
}

exports.toxml = exports.dump;
exports.tojson = exports.load;
<commit_msg>added check for truncated input<commit_after>var XMLWriter = require('xml-writer');
var default_tag_name = 'row';

exports.dump = function(obj) {

	if (typeof obj != "object") return obj;
	//    if (typeof xw != XMLWriter)
	var	xw = new XMLWriter();

	var getype = function(o) {
		if (!o)
			return 'undefined';
		else if (Array.isArray(o))
			return 'array';
		else
			return typeof o;
	}

	var getval = function(o) {
		return ''+o; // !TODO
	}

	var getname = function(n) {
		return n.replace('$', ':');
	}

	var parse = function(o) {
		if (getype(o) != 'object') return;
		for(var key in o) {
			if (o.hasOwnProperty(key) && o[key] != null){
				var type1 = getype(o[key]);
				if (type1 == 'object') {
					xw.startElement(getname(key));
					parse(o[key]);
					xw.endElement();
				}
				else if (type1 == 'array' && o[key].length <= 0) {
					xw.startElement(getname(key)).endElement();
				}
				else if (type1 == 'array' && o[key].length > 0) {
					var type2 = getype(o[key][0]);
					if (type2 == 'object') {
						o[key].forEach(function(item, index) {
								if (key == '$element' || key == '$e' || key == '#element' || key == '_element' || key == '_e') {
									parse(item);
								}
								else {
									xw.startElement(getname(key));
									parse(item);
									xw.endElement();
								}
						});
					}
					else {
						o[key].forEach(function(item, index) {
								if (key == '$element' || key == '$e' || key == '#element' || key == '_element' || key == '_e') {
									xw.startCData().text(getval(item)).endCData();
								}
								else {
									xw.startElement(getname(key));
									xw.startCData().text(getval(item)).endCData();
									xw.endElement();
								}
						});
					}
				}
				else {
					if (key == '$text' || key == '$t' || key == '#text' || key == '_text' || key == '_t') {
						xw.text(getval(o[key]));
					}
					else if (key == '$comment' || key == '$c' || key == '#comment' || key == '_comment' || key == '_c') {
						xw.startComment().text(getval(o[key])).endComment();
					}
					else if (key == '$cdata' || key == '$cd' || key == '#cdata' || key == '_cdata' || key == '_cd') {
						xw.startCData().text(getval(o[key])).endCData();
					}
					else  {
						xw.startAttribute(getname(key)).text(getval(o[key])).endAttribute();
					}
				}
			}
		}
	}
	var o= {};
	if (Object.keys(obj).length == 1) {
		o = obj;
	}
	else {
		o[default_tag_name] = obj;
	}
	parse(o);

	return xw.toString();

};
exports.load = function(str) {
	if (typeof str != "string") return str;

	var parser = require("sax").parser(true, {trim:true, xmlns:false});
	var result = {},
		stack = [],
		cdata = '';

	function cvalue(n, v) {
		n = n.replace(':', '$');
		var o = stack[stack.length-1];
		if (o == undefined) {
			o = {};
			o[n] = v;
			return o[n];
		}
		else if (o[n] == undefined) {
			o[n] = v;
			return o[n];
		}
		else if (!Array.isArray(o[n])) {
			var x = o[n];
			o[n] = new Array(x, v);
			return o[n][1];
		}
		else {
			var i = o[n].push(v);
			return o[n][i-1];
		}
	}
	function cattr(o) {
		var r = {};
		for(var key in o) {
			if (o.hasOwnProperty(key) && o[key]) {
				r[key.replace(':', '$')] = o[key];
			}
		}
		return r;
	}

	parser.onprocessinginstruction = function (pi) {
	};
	parser.ontext = function (v) {
		cvalue('$t', v);
	};
	parser.oncomment = function (v) {
		cvalue('$c', v);
	};
	parser.oncdata = function (v) {
		cdata += v;
	};
	parser.onopencdata = function () {
		cdata = '';
	};
	parser.onclosecdata = function () {
		cvalue('$cd', cdata);
		cdata = '';
	};
	parser.onopentag = function (node) {
//        console.log(stack);
		if (stack.length == 1 && node.name == default_tag_name) {
			result = cattr(node.attributes);
			stack.push(result);
		}
		else {
			stack.push(cvalue(node.name, cattr(node.attributes)));
		}
	};
	parser.onclosetag = function () {
		stack.pop();
	};
	parser.onready = function () {
	};
	parser.onend = function () {
	};

	stack.push(result);
	parser.write(str);
	var line = parser.line;
	var column = parser.column;
	parser.close();
	stack.pop();

	if (stack.length !== 0) {
		var er = "Unexpected end of input";
		er += "\nLine: "+line+
			"\nColumn: "+column+
			"\nChar: null";
		throw new Error(er);
	}

	return result;
}

exports.toxml = exports.dump;
exports.tojson = exports.load;
<|endoftext|>


<commit_before>var net = require('net');
var Server = require('./server');
var JID = require('./jid');
var StreamShaper = require('./../stream_shaper');
var StringPrep = require('node-stringprep').StringPrep;
var nameprep = new StringPrep('nameprep');

dbgStream = function(tag, stream) {
    stream.on('data', function(data) {
	console.log(tag + ' in: ' + data);
    });
    stream.on('error', function(e) {
	console.log(tag + ' error: ' + e.stack ? e.stack : e);
    });
    stream.on('close', function() {
	console.log(tag + ' close');
    });
    var oldSend = stream.send;
    stream.send = function(data) {
	console.log(tag + ' out: ' + data);
	oldSend.call(stream, data);
    };
};

/**
 * Represents a domain we host with connections to federated servers
 */
function DomainContext(router, domain) {
    this.router = router;
    this.domain = domain;
    this.s2sIn = {};
    this.s2sOut = {};
}

/**
 * Buffers until stream has been verified via Dialback
 */
DomainContext.prototype.send = function(stanza) {
    if (stanza.root)
	stanza = stanza.root();

    // TODO: return on empty to
    destDomain = new JID.JID(stanza.attrs.to).domain;
    var outStream = this.getOutStream(destDomain);

    if (outStream.isVerified)
	outStream.send(stanza);
    else {
	outStream.queue = outStream.queue || [];
	outStream.queue.push(stanza);
    }
};

/**
 * Does only buffer until stream is established, used for Dialback
 * communication itself.
 *
 * returns the stream
 */
DomainContext.prototype.sendRaw = function(stanza, destDomain) {
    if (stanza.root)
	stanza = stanza.root();

    var outStream = this.getOutStream(destDomain);
    var send = function() {
	outStream.send(stanza);
    };

    if (outStream.isOnline)
	send();
    else
	outStream.addListener('online', send);

    return outStream;
};

DomainContext.prototype.getOutStream = function(domain) {
    var self = this;

    // unfortunately we cannot use the incoming streams

    if (this.s2sOut.hasOwnProperty(domain)) {
	// There's one already
	return this.s2sOut[domain];
    } else {
	console.log("OUTGOING to " + domain);
	// Setup a new outgoing connection
	var outStream = Server.makeOutgoingServer(domain);
	this.s2sOut[domain] = outStream;
	dbgStream('outgoing', outStream);

	this.router.setupStream(outStream);
	this.setupStream(domain, outStream);

	outStream.addListener('close', function() {
	    // purge queue
	    if (outStream.queue) {
		outStream.queue.forEach(function(stanza) {
					    // do not provoke ping-pong effects
					    if (stanza.attrs.type === 'error')
						return;

					    var dest = stanza.attrs.to;
					    stanza.attrs.to = stanza.attrs.from;
					    stanza.attrs.from = dest;
					    stanza.attrs.type = 'error';
					    // TODO: add <error/> child if any programs wants to use that
					    self.receive(stanza);
					});
	    }
	    delete outStream.queue;

	    // remove from DomainContext
	    delete self.s2sOut[domain];
	});

	// Prepare dialback
	outStream.addListener('online', function() {
	    outStream.isOnline = true;
	    outStream.dbKey = generateKey();
	    outStream.send(Server.dialbackKey(self.domain, domain, outStream.dbKey));
	});
	outStream.addListener('dialbackResult', function(from, to, isValid) {
	    if (isValid) {
		outStream.isVerified = true;
		if (outStream.queue) {
		    outStream.queue.forEach(function(stanza) {
			outStream.send(stanza);
		    });
		    delete outStream.queue;
		}
	    } else {
		// we cannot do anything else with this stream that
		// failed dialback
		outStream.end();
	    }
	});

	return outStream;
    }
};

/**
 * Called by router when verification is done
 */
DomainContext.prototype.addInStream = function(domain, stream) {
    var self = this;

    if (this.s2sIn.hasOwnProperty(domain)) {
	// Replace old
	var oldStream = this.s2sIn[domain];
	oldStream.error('conflict', 'Connection replaced');
    }

    this.setupStream(domain, stream);
    stream.isOnline = true;
    stream.isVerified = true;
    stream.addListener('close', function() {
	if (self.s2sIn[domain] == stream)
	    delete self.s2sIn[domain];
    });
    this.s2sIn[domain] = stream;
};

DomainContext.prototype.setupStream = function(domain, stream) {
    var self = this;

    stream.addListener('stanza', function(stanza) {
	// Before verified they can send whatever they want
	if (!stream.isVerified)
	    return;

	if (!(typeof stanza.attrs.from === 'string' &&
	      typeof stanza.attrs.to === 'string')) {
	    stream.error('improper-addressing');
	    return;
	}

	// Only accept 'from' attribute JIDs that have the same domain
	// that we validated the stream for
	var fromDomain = (new JID.JID(stanza.attrs.from)).domain;
	if (fromDomain !== domain) {
	    stream.error('invalid-from');
	    return;
	}

	// Only accept 'to' attribute JIDs to this DomainContext
	var toDomain = (new JID.JID(stanza.attrs.to)).domain;
	if (toDomain !== self.domain) {
	    stream.error('improper-addressing');
	    return;
	}

	self.receive(stanza);
    });
};

DomainContext.prototype.verifyDialback = function(domain, id, key) {
    var outStream;
    if (this.s2sOut.hasOwnProperty(domain) &&
	(outStream = this.s2sOut[domain])) {

	var isValid = outStream.streamAttrs.id === id &&
	    outStream.dbKey === key;

	return isValid;
    } else
	return false;
};

DomainContext.prototype.receive = function(stanza) {
    if (this.stanzaListener)
	this.stanzaListener(stanza);
};

/**
 * TODO:
 * * TLS
 */
function Router(s2sPort) {
    var self = this;
    this.ctxs = {};

    net.createServer(function(inStream) {
	console.log('INCOMING from ' + inStream.remoteAddress);
	self.acceptConnection(inStream);
    }).listen(s2sPort || 5269);
}
exports.Router = Router;

Router.prototype.rateLimit = 1E6;  // 100 KB/s, it's S2S after all
Router.prototype.maxStanzaSize = 65536;  // 64 KB, by convention
Router.prototype.keepAlive = 30 * 1000;  // 30s
Router.prototype.streamTimeout = 5 * 60 * 1000;  // 5min

Router.prototype.acceptConnection = function(inStream) {
    var self = this;

    dbgStream('incoming', inStream);
    this.setupStream(inStream);
    Server.makeIncomingServer(inStream);

    // incoming server wants to verify an outgoing connection of ours
    inStream.addListener('dialbackVerify', function(from, to, id, key) {
	from = nameprep.prepare(from);
	to = nameprep.prepare(to);
	var isValid = self.verifyDialback(from, to, id, key);
	inStream.send(Server.dialbackVerified(to, from, id, isValid));
    });
    // incoming connection wants to get verified
    inStream.addListener('dialbackKey', function(from, to, key) {
	from = nameprep.prepare(from);
	to = nameprep.prepare(to);
	var destDomain = to;
	if (self.hasContext(to)) {
	    var ctx = self.getContext(to);
	    var outStream = ctx.sendRaw(Server.dialbackVerify(to, from, inStream.streamId, key),
					from);

	    // TODO: hook inStream close
	    // these are needed before for removeListener()
	    var onVerified, onClose;
	    onVerified = function(from, to, id, isValid) {
		from = nameprep.prepare(from);
		to = nameprep.prepare(to);
		if (to !== destDomain ||
		    id != inStream.streamId)  // not for us
		    return;

		inStream.send(Server.dialbackResult(to, from, isValid));

		if (isValid && self.hasContext(to)) {
		    self.getContext(to).addInStream(from, inStream);
		} else {
		    // the connection isn't used for another domain, so
		    // closing is safe
		    inStream.send('</stream:stream>');
		    inStream.end();
		}

		outStream.removeListener('dialbackVerified', onVerified);
		outStream.removeListener('close', onClose);
	    };
	    onClose = function() {
		// outgoing connection didn't work out, tell the incoming
		// connection
		inStream.send(Server.dialbackResult(to, from, false));
	    };
	    outStream.addListener('dialbackVerified', onVerified);
	    outStream.addListener('close', onClose);
	} else {
	    inStream.error('host-unknown', to + ' is not served here');
	}
    });
};

Router.prototype.setupStream = function(stream) {
    stream.maxStanzaSize = this.maxStanzaSize;
    StreamShaper.attach(stream, this.rateLimit);
    stream.setKeepAlive(true, this.keepAlive);
    stream.setTimeout(this.streamTimeout);
    stream.addListener('timeout', function() {
			   stream.error('connection-timeout');
		       });
};

/**
 * Create domain context & register a stanza listener callback
 */
Router.prototype.register = function(domain, listener) {
    domain = nameprep.prepare(domain);
    this.getContext(domain).stanzaListener = listener;
};

// TODO: unregister w/ connections teardown

Router.prototype.send = function(stanza) {
    if (stanza.root)
	stanza = stanza.root();

    // TODO: inner routing
    if (stanza.attrs && stanza.attrs.from) {
	var domain = (new JID.JID(stanza.attrs.from)).domain;
	this.getContext(domain).send(stanza);
    } else
	throw 'Sending stanza without destination';
};

Router.prototype.hasContext = function(domain) {
    return this.ctxs.hasOwnProperty(domain);
};

Router.prototype.getContext = function(domain) {
    if (this.ctxs.hasOwnProperty(domain))
	return this.ctxs[domain];
    else
	return (this.ctxs[domain] = new DomainContext(this, domain));
};

Router.prototype.verifyDialback = function(from, to, id, key) {
    return this.hasContext(to) &&
	this.getContext(to).verifyDialback(from, id, key);
};

function generateKey() {
    var r = new Buffer(16);
    for(var i = 0; i < r.length; i++) {
	r[i] = 48 + Math.floor(Math.random() * 10);  // '0'..'9'
    }
    return r.toString();
}
<commit_msg>router: asynchronous verifyDialback()<commit_after>var net = require('net');
var Server = require('./server');
var JID = require('./jid');
var StreamShaper = require('./../stream_shaper');
var StringPrep = require('node-stringprep').StringPrep;
var nameprep = new StringPrep('nameprep');

dbgStream = function(tag, stream) {
    stream.on('data', function(data) {
	console.log(tag + ' in: ' + data);
    });
    stream.on('error', function(e) {
	console.log(tag + ' error: ' + e.stack ? e.stack : e);
    });
    stream.on('close', function() {
	console.log(tag + ' close');
    });
    var oldSend = stream.send;
    stream.send = function(data) {
	console.log(tag + ' out: ' + data);
	oldSend.call(stream, data);
    };
};

/**
 * Represents a domain we host with connections to federated servers
 */
function DomainContext(router, domain) {
    this.router = router;
    this.domain = domain;
    this.s2sIn = {};
    this.s2sOut = {};
}

/**
 * Buffers until stream has been verified via Dialback
 */
DomainContext.prototype.send = function(stanza) {
    if (stanza.root)
	stanza = stanza.root();

    // TODO: return on empty to
    destDomain = new JID.JID(stanza.attrs.to).domain;
    var outStream = this.getOutStream(destDomain);

    if (outStream.isVerified)
	outStream.send(stanza);
    else {
	outStream.queue = outStream.queue || [];
	outStream.queue.push(stanza);
    }
};

/**
 * Does only buffer until stream is established, used for Dialback
 * communication itself.
 *
 * returns the stream
 */
DomainContext.prototype.sendRaw = function(stanza, destDomain) {
    if (stanza.root)
	stanza = stanza.root();

    var outStream = this.getOutStream(destDomain);
    var send = function() {
	outStream.send(stanza);
    };

    if (outStream.isOnline)
	send();
    else
	outStream.addListener('online', send);

    return outStream;
};

DomainContext.prototype.getOutStream = function(domain) {
    var self = this;

    // unfortunately we cannot use the incoming streams

    if (this.s2sOut.hasOwnProperty(domain)) {
	// There's one already
	return this.s2sOut[domain];
    } else {
	console.log("OUTGOING to " + domain);
	// Setup a new outgoing connection
	var outStream = Server.makeOutgoingServer(domain);
	this.s2sOut[domain] = outStream;
	dbgStream('outgoing', outStream);

	this.router.setupStream(outStream);
	this.setupStream(domain, outStream);

	outStream.addListener('close', function() {
	    // purge queue
	    if (outStream.queue) {
		outStream.queue.forEach(function(stanza) {
					    // do not provoke ping-pong effects
					    if (stanza.attrs.type === 'error')
						return;

					    var dest = stanza.attrs.to;
					    stanza.attrs.to = stanza.attrs.from;
					    stanza.attrs.from = dest;
					    stanza.attrs.type = 'error';
					    // TODO: add <error/> child if any programs wants to use that
					    self.receive(stanza);
					});
	    }
	    delete outStream.queue;

	    // remove from DomainContext
	    delete self.s2sOut[domain];
	});

	// Prepare dialback
	outStream.addListener('online', function() {
	    outStream.isOnline = true;
	    outStream.dbKey = generateKey();
	    outStream.send(Server.dialbackKey(self.domain, domain, outStream.dbKey));
	});
	outStream.addListener('dialbackResult', function(from, to, isValid) {
	    if (isValid) {
		outStream.isVerified = true;
		if (outStream.queue) {
		    outStream.queue.forEach(function(stanza) {
			outStream.send(stanza);
		    });
		    delete outStream.queue;
		}
	    } else {
		// we cannot do anything else with this stream that
		// failed dialback
		outStream.end();
	    }
	});

	return outStream;
    }
};

/**
 * Called by router when verification is done
 */
DomainContext.prototype.addInStream = function(domain, stream) {
    var self = this;

    if (this.s2sIn.hasOwnProperty(domain)) {
	// Replace old
	var oldStream = this.s2sIn[domain];
	oldStream.error('conflict', 'Connection replaced');
    }

    this.setupStream(domain, stream);
    stream.isOnline = true;
    stream.isVerified = true;
    stream.addListener('close', function() {
	if (self.s2sIn[domain] == stream)
	    delete self.s2sIn[domain];
    });
    this.s2sIn[domain] = stream;
};

DomainContext.prototype.setupStream = function(domain, stream) {
    var self = this;

    stream.addListener('stanza', function(stanza) {
	// Before verified they can send whatever they want
	if (!stream.isVerified)
	    return;

	if (!(typeof stanza.attrs.from === 'string' &&
	      typeof stanza.attrs.to === 'string')) {
	    stream.error('improper-addressing');
	    return;
	}

	// Only accept 'from' attribute JIDs that have the same domain
	// that we validated the stream for
	var fromDomain = (new JID.JID(stanza.attrs.from)).domain;
	if (fromDomain !== domain) {
	    stream.error('invalid-from');
	    return;
	}

	// Only accept 'to' attribute JIDs to this DomainContext
	var toDomain = (new JID.JID(stanza.attrs.to)).domain;
	if (toDomain !== self.domain) {
	    stream.error('improper-addressing');
	    return;
	}

	self.receive(stanza);
    });
};

DomainContext.prototype.verifyDialback = function(domain, id, key, cb) {
    var self = this;
    var outStream;
    if (this.s2sOut.hasOwnProperty(domain) &&
	(outStream = this.s2sOut[domain])) {

	if (outStream.isOnline) {
	    var isValid = outStream.streamAttrs.id === id &&
			      outStream.dbKey === key;
	    cb(isValid);
	} else {
	    // Not online, wait for outStream.streamAttrs
	    outStream.addListener('online', function() {
				      // recurse
				      self.verifyDialback(domain, id, key, cb);
				  });
	    outStream.addListener('close', function() {
				      cb(false);
				  });
	}
    } else
	cb(false);
};

DomainContext.prototype.receive = function(stanza) {
    if (this.stanzaListener)
	this.stanzaListener(stanza);
};

/**
 * TODO:
 * * TLS
 */
function Router(s2sPort) {
    var self = this;
    this.ctxs = {};

    net.createServer(function(inStream) {
	console.log('INCOMING from ' + inStream.remoteAddress);
	self.acceptConnection(inStream);
    }).listen(s2sPort || 5269);
}
exports.Router = Router;

Router.prototype.rateLimit = 1E6;  // 100 KB/s, it's S2S after all
Router.prototype.maxStanzaSize = 65536;  // 64 KB, by convention
Router.prototype.keepAlive = 30 * 1000;  // 30s
Router.prototype.streamTimeout = 5 * 60 * 1000;  // 5min

Router.prototype.acceptConnection = function(inStream) {
    var self = this;

    dbgStream('incoming', inStream);
    this.setupStream(inStream);
    Server.makeIncomingServer(inStream);

    // incoming server wants to verify an outgoing connection of ours
    inStream.addListener('dialbackVerify', function(from, to, id, key) {
	from = nameprep.prepare(from);
	to = nameprep.prepare(to);
	self.verifyDialback(from, to, id, key, function(isValid) {
	    inStream.send(Server.dialbackVerified(to, from, id, isValid));
	});
    });
    // incoming connection wants to get verified
    inStream.addListener('dialbackKey', function(from, to, key) {
	from = nameprep.prepare(from);
	to = nameprep.prepare(to);
	var destDomain = to;
	if (self.hasContext(to)) {
	    var ctx = self.getContext(to);
	    var outStream = ctx.sendRaw(Server.dialbackVerify(to, from, inStream.streamId, key),
					from);

	    // TODO: hook inStream close
	    // these are needed before for removeListener()
	    var onVerified, onClose;
	    onVerified = function(from, to, id, isValid) {
		from = nameprep.prepare(from);
		to = nameprep.prepare(to);
		if (to !== destDomain ||
		    id != inStream.streamId)  // not for us
		    return;

		inStream.send(Server.dialbackResult(to, from, isValid));

		if (isValid && self.hasContext(to)) {
		    self.getContext(to).addInStream(from, inStream);
		} else {
		    // the connection isn't used for another domain, so
		    // closing is safe
		    inStream.send('</stream:stream>');
		    inStream.end();
		}

		outStream.removeListener('dialbackVerified', onVerified);
		outStream.removeListener('close', onClose);
	    };
	    onClose = function() {
		// outgoing connection didn't work out, tell the incoming
		// connection
		inStream.send(Server.dialbackResult(to, from, false));
	    };
	    outStream.addListener('dialbackVerified', onVerified);
	    outStream.addListener('close', onClose);
	} else {
	    inStream.error('host-unknown', to + ' is not served here');
	}
    });
};

Router.prototype.setupStream = function(stream) {
    stream.maxStanzaSize = this.maxStanzaSize;
    StreamShaper.attach(stream, this.rateLimit);
    stream.setKeepAlive(true, this.keepAlive);
    stream.setTimeout(this.streamTimeout);
    stream.addListener('timeout', function() {
			   stream.error('connection-timeout');
		       });
};

/**
 * Create domain context & register a stanza listener callback
 */
Router.prototype.register = function(domain, listener) {
    domain = nameprep.prepare(domain);
    this.getContext(domain).stanzaListener = listener;
};

// TODO: unregister w/ connections teardown

Router.prototype.send = function(stanza) {
    if (stanza.root)
	stanza = stanza.root();

    // TODO: inner routing
    if (stanza.attrs && stanza.attrs.from) {
	var domain = (new JID.JID(stanza.attrs.from)).domain;
	this.getContext(domain).send(stanza);
    } else
	throw 'Sending stanza without destination';
};

Router.prototype.hasContext = function(domain) {
    return this.ctxs.hasOwnProperty(domain);
};

Router.prototype.getContext = function(domain) {
    if (this.ctxs.hasOwnProperty(domain))
	return this.ctxs[domain];
    else
	return (this.ctxs[domain] = new DomainContext(this, domain));
};

Router.prototype.verifyDialback = function(from, to, id, key, cb) {
    if (this.hasContext(to))
	this.getContext(to).verifyDialback(from, id, key, cb);
    else
	cb(false);
};

function generateKey() {
    var r = new Buffer(16);
    for(var i = 0; i < r.length; i++) {
	r[i] = 48 + Math.floor(Math.random() * 10);  // '0'..'9'
    }
    return r.toString();
}
<|endoftext|>


<commit_before>describe('Trees', function () {
  // json response to http://data.gramene.org/taxonomy/select?rows=-1
  // converted into a commonJS module by prepending json doc with
  // `module.exports = `
  var taxonomy = require('../support/taxonomy.js');
  var trees = require('../../src/trees');
  var tax_tree = trees.parseTaxa(taxonomy.data.response);
  var stree = trees.speciesTree(tax_tree);

  it('lca should find the lowest common ancestor', function () {
    // when
    var ath = trees.lca(tax_tree, [3702]);
    var arabidopsis = trees.lca(tax_tree, [3702,81972]);
    var rosids = trees.lca(tax_tree, [3702,29760]);
    var oryza = trees.lca(tax_tree, [39947,4528,4538]);
    var poaceae = trees.lca(tax_tree, [39947,4528,4538,4577]);

    expect(ath.id).toEqual(3702);
    expect(arabidopsis.id).toEqual(3701);
    expect(rosids.id).toEqual(71275);
    expect(oryza.id).toEqual(4527);
    expect(poaceae.id).toEqual(4479);
  });

});<commit_msg>added a test to trigger travis<commit_after>describe('Trees', function () {
  // json response to http://data.gramene.org/taxonomy/select?rows=-1
  // converted into a commonJS module by prepending json doc with
  // `module.exports = `
  var taxonomy = require('../support/taxonomy.js');
  var trees = require('../../src/trees');
  var tax_tree = trees.parseTaxa(taxonomy.data.response);
  var stree = trees.speciesTree(tax_tree);

  it('parseTaxa should return a rooted tree object', function() {
    expect(tax_tree['1'].name).toEqual('root');
    // TODO: add a test for tree-ness
  });

  it('lca should find the lowest common ancestor', function () {
    // when
    var ath = trees.lca(tax_tree, [3702]);
    var arabidopsis = trees.lca(tax_tree, [3702,81972]);
    var rosids = trees.lca(tax_tree, [3702,29760]);
    var oryza = trees.lca(tax_tree, [39947,4528,4538]);
    var poaceae = trees.lca(tax_tree, [39947,4528,4538,4577]);

    expect(ath.id).toEqual(3702);
    expect(arabidopsis.id).toEqual(3701);
    expect(rosids.id).toEqual(71275);
    expect(oryza.id).toEqual(4527);
    expect(poaceae.id).toEqual(4479);
  });

});<|endoftext|>


<commit_before>export { Params } from "./Block/Block_Params.js";
export { Base } from "./Block/Block_Base.js";
<commit_msg>Update Block.js<commit_after>export { Params } from "./Block/Block_Params.js";
export { ParamsPool } from "./Block/Block_Params.js";
export { Base } from "./Block/Block_Base.js";
export { BasePool } from "./Block/Block_Base.js";
<|endoftext|>


<commit_before>export const PLANT_GROWTH_PHASES = [
  'Seedling',
  'Vegetative',
  'Budding',
  'Flowering',
  'Mature',
  'Over-Mature'
];

export const STRAINS = [
  {
    id: 0,
    tier: 0,
    name: `Schmendrick's Schwag`,
    description: `The schwiggitiest of schwag. Really not dank at all. Kinda gross, actually.`
  },
  {
    id: 1,
    tier: 1,
    name: `Dutchmen's Purps`,
    description: `An indica-heavy strain favored by sailors and lighthouse keepers.`
  },
  {
    id: 2,
    tier: 1,
    name: `Avalon Canna Cat`,
    description: `The purrfect strain for pretending cardboard boxes are castles and sleeping 18 hours a day.`
  },
  {
    id: 3,
    tier: 1,
    name: `Oregon Sorbet`,
    description: `Light and refreshing. The spritzer of weeds.`
  },
  {
    id: 4,
    tier: 1,
    name: `Goat Moonshine`,
    description: `As the name implies, this strain was first discovered in a field of stoned goats. Beware the munchies; you might try to eat anything.`
  },
  {
    id: 5,
    tier: 1,
    name: `Bob Mist`,
    description: `Some weed gets you in touch with Jah. This weed gets you in touch with Bob.`
  }
];

export const TIERS = [
  {
    id: 0,
    prices: {
      g: 5,
      eighth: 15,
      quarter: 27,
      half: 50,
      oz: 95,
      lb: 1450,
      seed: 0.25
    },
    growthRate: 1,
    seedDropMod: 1,
    harvestAmount: 1,
    highness: 1,
    uom: 'oz'
  },
  {
    id: 1,
    prices: {
      g: 7,
      eighth: 20,
      quarter: 37,
      half: 69,
      oz: 125,
      lb: 1900,
      seed: 0.5
    },
    growthRate: 1,
    seedDropMod: 0.7,
    harvestAmount: 1,
    highness: 1.2,
    uom: 'oz'
  }
];<commit_msg>Schwag drops more seeds.<commit_after>export const PLANT_GROWTH_PHASES = [
  'Seedling',
  'Vegetative',
  'Budding',
  'Flowering',
  'Mature',
  'Over-Mature'
];

export const STRAINS = [
  {
    id: 0,
    tier: 0,
    name: `Schmendrick's Schwag`,
    description: `The schwiggitiest of schwag. Really not dank at all. Kinda gross, actually.`
  },
  {
    id: 1,
    tier: 1,
    name: `Dutchmen's Purps`,
    description: `An indica-heavy strain favored by sailors and lighthouse keepers.`
  },
  {
    id: 2,
    tier: 1,
    name: `Avalon Canna Cat`,
    description: `The purrfect strain for pretending cardboard boxes are castles and sleeping 18 hours a day.`
  },
  {
    id: 3,
    tier: 1,
    name: `Oregon Sorbet`,
    description: `Light and refreshing. The spritzer of weeds.`
  },
  {
    id: 4,
    tier: 1,
    name: `Goat Moonshine`,
    description: `As the name implies, this strain was first discovered in a field of stoned goats. Beware the munchies; you might try to eat anything.`
  },
  {
    id: 5,
    tier: 1,
    name: `Bob Mist`,
    description: `Some weed gets you in touch with Jah. This weed gets you in touch with Bob.`
  }
];

export const TIERS = [
  {
    id: 0,
    prices: {
      g: 5,
      eighth: 15,
      quarter: 27,
      half: 50,
      oz: 95,
      lb: 1450,
      seed: 0.25
    },
    growthRate: 1,
    seedDropMod: 3,
    harvestAmount: 1,
    highness: 1,
    uom: 'oz'
  },
  {
    id: 1,
    prices: {
      g: 7,
      eighth: 20,
      quarter: 37,
      half: 69,
      oz: 125,
      lb: 1900,
      seed: 0.5
    },
    growthRate: 1,
    seedDropMod: 0.7,
    harvestAmount: 1,
    highness: 1.2,
    uom: 'oz'
  }
];<|endoftext|>