<commit_before>const React = require('react')
const { Component } = React
const PropTypes = require('prop-types')
const { getDeviceId, getFacingModePattern } = require('./getDeviceId')
const havePropsChanged = require('./havePropsChanged')
const createBlob = require('./createBlob')

// Require adapter to support older browser implementations
require('webrtc-adapter')

// Inline worker.js as a string value of workerBlob.
// eslint-disable-next-line
let workerBlob = createBlob([__inline('../lib/worker.js')], {
  type: 'application/javascript',
})

// Props that are allowed to change dynamicly
const propsKeys = ['delay', 'legacyMode', 'facingMode']

module.exports = class Reader extends Component {
  static propTypes = {
    onScan: PropTypes.func.isRequired,
    onError: PropTypes.func.isRequired,
    onLoad: PropTypes.func,
    onImageLoad: PropTypes.func,
    delay: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
    facingMode: PropTypes.oneOf(['user', 'environment']),
    legacyMode: PropTypes.bool,
    resolution: PropTypes.number,
    showViewFinder: PropTypes.bool,
    style: PropTypes.any,
    className: PropTypes.string,
  };
  static defaultProps = {
    delay: 500,
    resolution: 600,
    facingMode: 'environment',
    showViewFinder: true,
  };

  els = {};

  constructor(props) {
    super(props)

    this.state = {
      mirrorVideo: false,
    }

    // Bind function to the class
    this.initiate = this.initiate.bind(this)
    this.initiateLegacyMode = this.initiateLegacyMode.bind(this)
    this.check = this.check.bind(this)
    this.handleVideo = this.handleVideo.bind(this)
    this.handleLoadStart = this.handleLoadStart.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)
    this.clearComponent = this.clearComponent.bind(this)
    this.handleReaderLoad = this.handleReaderLoad.bind(this)
    this.openImageDialog = this.openImageDialog.bind(this)
    this.handleWorkerMessage = this.handleWorkerMessage.bind(this)
    this.setRefFactory = this.setRefFactory.bind(this)
  }
  componentDidMount() {
    // Initiate web worker execute handler according to mode.
    this.worker = new Worker(URL.createObjectURL(workerBlob))
    this.worker.onmessage = this.handleWorkerMessage

    if (!this.props.legacyMode) {
      this.initiate()
    } else {
      this.initiateLegacyMode()
    }
  }
  componentWillReceiveProps(nextProps) {
    // React according to change in props
    const changedProps = havePropsChanged(this.props, nextProps, propsKeys)

    for (const prop of changedProps) {
      if (prop == 'facingMode') {
        this.clearComponent()
        this.initiate(nextProps)
        break
      } else if (prop == 'delay') {
        if (this.props.delay == false && !nextProps.legacyMode) {
          this.timeout = setTimeout(this.check, nextProps.delay)
        }
        if (nextProps.delay == false) {
          clearTimeout(this.timeout)
        }
      } else if (prop == 'legacyMode') {
        if (this.props.legacyMode && !nextProps.legacyMode) {
          this.clearComponent()
          this.initiate(nextProps)
        } else {
          this.clearComponent()
          this.componentDidUpdate = this.initiateLegacyMode
        }
        break
      }
    }
  }
  shouldComponentUpdate(nextProps, nextState) {
    if(nextState !== this.state){
      return true
    }

    // Only render when the `propsKeys` have changed.
    const changedProps = havePropsChanged(this.props, nextProps, propsKeys)
    return changedProps.length > 0
  }
  componentWillUnmount() {
    // Stop web-worker and clear the component
    if (this.worker) {
      this.worker.terminate()
      this.worker = undefined
    }
    this.clearComponent()
  }
  clearComponent() {
    // Remove all event listeners and variables
    if (this.timeout) {
      clearTimeout(this.timeout)
      this.timeout = undefined
    }
    if (this.stopCamera) {
      this.stopCamera()
    }
    if (this.reader) {
      this.reader.removeEventListener('load', this.handleReaderLoad)
    }
    if (this.els.img) {
      this.els.img.removeEventListener('load', this.check)
    }
  }
  initiate(props = this.props) {
    const { onError, facingMode } = props

    // Check browser facingMode constraint support
    // Firefox ignores facingMode or deviceId constraints
    const isFirefox = /firefox/i.test(navigator.userAgent)
    const supported = navigator.mediaDevices !== undefined
      ? navigator.mediaDevices.getSupportedConstraints()
      : {}
    const constraints = {}

    if(supported.facingMode) {
      constraints.facingMode = { ideal: facingMode }
    }
    if(supported.aspectRatio) {
      constraints.aspectRatio = 1
    }
    if(supported.frameRate) {
      constraints.frameRate = {ideal: 25, min: 10}
    }
    const vConstraintsPromise = (supported.facingMode || isFirefox)
      ? Promise.resolve(constraints)
      : getDeviceId(facingMode).then(deviceId => ({ deviceId }))

    vConstraintsPromise
      .then(video => navigator.mediaDevices.getUserMedia({ video }))
      .then(this.handleVideo)
      .catch(onError)
  }
  handleVideo(stream) {
    const { preview } = this.els

    // Preview element hasn't been rendered so wait for it.
    if (!preview) {
      setTimeout(this.handleVideo, 200, stream)
    }

    // Handle different browser implementations of MediaStreams as src
    if(preview.srcObject !== undefined){
      preview.srcObject = stream
    } else if (preview.mozSrcObject !== undefined) {
      preview.mozSrcObject = stream
    } else if (window.URL.createObjectURL) {
      preview.src = window.URL.createObjectURL(stream)
    } else if (window.webkitURL) {
      preview.src = window.webkitURL.createObjectURL(stream)
    } else {
      preview.src = stream
    }

    // IOS play in fullscreen
    preview.playsInline = true

    const streamTrack = stream.getTracks()[0]
    // Assign `stopCamera` so the track can be stopped once component is cleared
    this.stopCamera = streamTrack.stop.bind(streamTrack)

    preview.addEventListener('loadstart', this.handleLoadStart)

    const facingUserPattern = getFacingModePattern('user')
    const isUserFacing = facingUserPattern.test(streamTrack.label)
    this.setState({ mirrorVideo: isUserFacing })
  }
  handleLoadStart() {
    const { delay, onLoad } = this.props
    const preview = this.els.preview
    preview.play()

    if(typeof onLoad == 'function') {
      onLoad()
    }

    if (typeof delay == 'number') {
      this.timeout = setTimeout(this.check, delay)
    }

    // Some browsers call loadstart continuously
    preview.removeEventListener('loadstart', this.handleLoadStart)
  }
  check() {
    const { legacyMode, resolution, delay } = this.props
    const { preview, canvas, img } = this.els

    // Get image/video dimensions
    let width = Math.floor(legacyMode ? img.naturalWidth : preview.videoWidth)
    let height = Math.floor(legacyMode ? img.naturalHeight : preview.videoHeight)

    // Canvas draw offsets
    let hozOffset = 0
    let vertOffset = 0

    // Scale image to correct resolution
    if(legacyMode){
      // Keep image aspect ratio
      const greatestSize = width > height ? width : height
      const ratio = resolution / greatestSize

      height = ratio * height
      width = ratio * width

      canvas.width = width
      canvas.height = height
    }else{
      // Crop image to fit 1:1 aspect ratio
      const smallestSize = width < height ? width : height
      const ratio = resolution / smallestSize

      height = ratio * height
      width = ratio * width

      vertOffset = (height - resolution) / 2 * -1
      hozOffset = (width - resolution) / 2 * -1

      canvas.width = resolution
      canvas.height = resolution
    }

    const previewIsPlaying = preview &&
      preview.readyState === preview.HAVE_ENOUGH_DATA

    if (legacyMode || previewIsPlaying) {
      const ctx = canvas.getContext('2d')

      ctx.drawImage(legacyMode ? img : preview, hozOffset, vertOffset, width, height)

      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
      // Send data to web-worker
      this.worker.postMessage(imageData)
    } else {
      // Preview not ready -> check later
      this.timeout = setTimeout(this.check, delay)
    }
  }
  handleWorkerMessage(e) {
    const { onScan, legacyMode, delay } = this.props
    const decoded = e.data
    onScan(decoded || null)

    if (!legacyMode && typeof delay == 'number' && this.worker) {
      this.timeout = setTimeout(this.check, delay)
    }
  }
  initiateLegacyMode() {
    this.reader = new FileReader()
    this.reader.addEventListener('load', this.handleReaderLoad)
    this.els.img.addEventListener('load', this.check, false)

    // Reset componentDidUpdate
    this.componentDidUpdate = undefined

    if(typeof this.props.onLoad == 'function') {
      this.props.onLoad()
    }
  }
  handleInputChange(e) {
    const selectedImg = e.target.files[0]
    this.reader.readAsDataURL(selectedImg)
  }
  handleReaderLoad(e) {
    // Set selected image blob as img source
    this.els.img.src = e.target.result
  }
  openImageDialog() {
    // Function to be executed by parent in user action context to trigger img file uploader
    this.els.input.click()
  }
  setRefFactory(key) {
    return element => {
      this.els[key] = element
    }
  }
  render() {
    const {
      style,
      className,
      onImageLoad,
      legacyMode,
      showViewFinder,
      facingMode
    } = this.props

    const containerStyle = {
      position: 'relative',
      width: '100%',
      paddingTop: '100%',
    }
    const hiddenStyle = { display: 'none' }
    const previewStyle = {
      top: 0,
      left: 0,
      display: 'block',
      position: 'absolute',
      overflow: 'hidden',
      width: '100%',
      height: '100%',
    }
    const videoPreviewStyle = {
      ...previewStyle,
      objectFit: 'cover',
      transform: this.state.mirrorVideo ? 'scaleX(-1)' : undefined,
    }
    const imgPreviewStyle = {
      ...previewStyle,
      objectFit: 'scale-down',
    }

    const viewFinderStyle = {
      top: 0,
      left: 0,
      zIndex: 1,
      boxSizing: 'border-box',
      border: '50px solid rgba(0, 0, 0, 0.3)',
      boxShadow: 'inset 0 0 0 5px rgba(255, 0, 0, 0.5)',
      position: 'absolute',
      width: '100%',
      height: '100%',
    }

    return (

          {
            (!legacyMode && showViewFinder)
            ?

            : null
          }
          {
            legacyMode
              ? <input
                style={hiddenStyle}
                type="file"
                accept="image/*"
                ref={this.setRefFactory('input')}
                onChange={this.handleInputChange}
              />
              : null
          }
          {
            legacyMode
              ?
              : <video style={videoPreviewStyle} ref={this.setRefFactory('preview')} />
          }

          <canvas style={hiddenStyle} ref={this.setRefFactory('canvas')} />

    )
  }
}
<commit_msg>fix #80<commit_after>const React = require('react')
const { Component } = React
const PropTypes = require('prop-types')
const { getDeviceId, getFacingModePattern } = require('./getDeviceId')
const havePropsChanged = require('./havePropsChanged')
const createBlob = require('./createBlob')

// Require adapter to support older browser implementations
require('webrtc-adapter')

// Inline worker.js as a string value of workerBlob.
// eslint-disable-next-line
let workerBlob = createBlob([__inline('../lib/worker.js')], {
  type: 'application/javascript',
})

// Props that are allowed to change dynamicly
const propsKeys = ['delay', 'legacyMode', 'facingMode']

module.exports = class Reader extends Component {
  static propTypes = {
    onScan: PropTypes.func.isRequired,
    onError: PropTypes.func.isRequired,
    onLoad: PropTypes.func,
    onImageLoad: PropTypes.func,
    delay: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
    facingMode: PropTypes.oneOf(['user', 'environment']),
    legacyMode: PropTypes.bool,
    resolution: PropTypes.number,
    showViewFinder: PropTypes.bool,
    style: PropTypes.any,
    className: PropTypes.string,
  };
  static defaultProps = {
    delay: 500,
    resolution: 600,
    facingMode: 'environment',
    showViewFinder: true,
  };

  els = {};

  constructor(props) {
    super(props)

    this.state = {
      mirrorVideo: false,
    }

    // Bind function to the class
    this.initiate = this.initiate.bind(this)
    this.initiateLegacyMode = this.initiateLegacyMode.bind(this)
    this.check = this.check.bind(this)
    this.handleVideo = this.handleVideo.bind(this)
    this.handleLoadStart = this.handleLoadStart.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)
    this.clearComponent = this.clearComponent.bind(this)
    this.handleReaderLoad = this.handleReaderLoad.bind(this)
    this.openImageDialog = this.openImageDialog.bind(this)
    this.handleWorkerMessage = this.handleWorkerMessage.bind(this)
    this.setRefFactory = this.setRefFactory.bind(this)
  }
  componentDidMount() {
    // Initiate web worker execute handler according to mode.
    this.worker = new Worker(URL.createObjectURL(workerBlob))
    this.worker.onmessage = this.handleWorkerMessage

    if (!this.props.legacyMode) {
      this.initiate()
    } else {
      this.initiateLegacyMode()
    }
  }
  componentWillReceiveProps(nextProps) {
    // React according to change in props
    const changedProps = havePropsChanged(this.props, nextProps, propsKeys)

    for (const prop of changedProps) {
      if (prop == 'facingMode') {
        this.clearComponent()
        this.initiate(nextProps)
        break
      } else if (prop == 'delay') {
        if (this.props.delay == false && !nextProps.legacyMode) {
          this.timeout = setTimeout(this.check, nextProps.delay)
        }
        if (nextProps.delay == false) {
          clearTimeout(this.timeout)
        }
      } else if (prop == 'legacyMode') {
        if (this.props.legacyMode && !nextProps.legacyMode) {
          this.clearComponent()
          this.initiate(nextProps)
        } else {
          this.clearComponent()
          this.componentDidUpdate = this.initiateLegacyMode
        }
        break
      }
    }
  }
  shouldComponentUpdate(nextProps, nextState) {
    if(nextState !== this.state){
      return true
    }

    // Only render when the `propsKeys` have changed.
    const changedProps = havePropsChanged(this.props, nextProps, propsKeys)
    return changedProps.length > 0
  }
  componentWillUnmount() {
    // Stop web-worker and clear the component
    if (this.worker) {
      this.worker.terminate()
      this.worker = undefined
    }
    this.clearComponent()
  }
  clearComponent() {
    // Remove all event listeners and variables
    if (this.timeout) {
      clearTimeout(this.timeout)
      this.timeout = undefined
    }
    if (this.stopCamera) {
      this.stopCamera()
    }
    if (this.reader) {
      this.reader.removeEventListener('load', this.handleReaderLoad)
    }
    if (this.els.img) {
      this.els.img.removeEventListener('load', this.check)
    }
  }
  initiate(props = this.props) {
    const { onError, facingMode } = props

    // Check browser facingMode constraint support
    // Firefox ignores facingMode or deviceId constraints
    const isFirefox = /firefox/i.test(navigator.userAgent)
    const supported = navigator.mediaDevices !== undefined
      ? navigator.mediaDevices.getSupportedConstraints()
      : {}
    const constraints = {}

    if(supported.facingMode) {
      constraints.facingMode = { ideal: facingMode }
    }
    if(supported.frameRate) {
      constraints.frameRate = {ideal: 25, min: 10}
    }
    const vConstraintsPromise = (supported.facingMode || isFirefox)
      ? Promise.resolve(constraints)
      : getDeviceId(facingMode).then(deviceId => ({ deviceId }))

    vConstraintsPromise
      .then(video => navigator.mediaDevices.getUserMedia({ video }))
      .then(this.handleVideo)
      .catch(onError)
  }
  handleVideo(stream) {
    const { preview } = this.els

    // Preview element hasn't been rendered so wait for it.
    if (!preview) {
      setTimeout(this.handleVideo, 200, stream)
    }

    // Handle different browser implementations of MediaStreams as src
    if(preview.srcObject !== undefined){
      preview.srcObject = stream
    } else if (preview.mozSrcObject !== undefined) {
      preview.mozSrcObject = stream
    } else if (window.URL.createObjectURL) {
      preview.src = window.URL.createObjectURL(stream)
    } else if (window.webkitURL) {
      preview.src = window.webkitURL.createObjectURL(stream)
    } else {
      preview.src = stream
    }

    // IOS play in fullscreen
    preview.playsInline = true

    const streamTrack = stream.getTracks()[0]
    // Assign `stopCamera` so the track can be stopped once component is cleared
    this.stopCamera = streamTrack.stop.bind(streamTrack)

    preview.addEventListener('loadstart', this.handleLoadStart)

    const facingUserPattern = getFacingModePattern('user')
    const isUserFacing = facingUserPattern.test(streamTrack.label)
    this.setState({ mirrorVideo: isUserFacing })
  }
  handleLoadStart() {
    const { delay, onLoad } = this.props
    const preview = this.els.preview
    preview.play()

    if(typeof onLoad == 'function') {
      onLoad()
    }

    if (typeof delay == 'number') {
      this.timeout = setTimeout(this.check, delay)
    }

    // Some browsers call loadstart continuously
    preview.removeEventListener('loadstart', this.handleLoadStart)
  }
  check() {
    const { legacyMode, resolution, delay } = this.props
    const { preview, canvas, img } = this.els

    // Get image/video dimensions
    let width = Math.floor(legacyMode ? img.naturalWidth : preview.videoWidth)
    let height = Math.floor(legacyMode ? img.naturalHeight : preview.videoHeight)

    // Canvas draw offsets
    let hozOffset = 0
    let vertOffset = 0

    // Scale image to correct resolution
    if(legacyMode){
      // Keep image aspect ratio
      const greatestSize = width > height ? width : height
      const ratio = resolution / greatestSize

      height = ratio * height
      width = ratio * width

      canvas.width = width
      canvas.height = height
    }else{
      // Crop image to fit 1:1 aspect ratio
      const smallestSize = width < height ? width : height
      const ratio = resolution / smallestSize

      height = ratio * height
      width = ratio * width

      vertOffset = (height - resolution) / 2 * -1
      hozOffset = (width - resolution) / 2 * -1

      canvas.width = resolution
      canvas.height = resolution
    }

    const previewIsPlaying = preview &&
      preview.readyState === preview.HAVE_ENOUGH_DATA

    if (legacyMode || previewIsPlaying) {
      const ctx = canvas.getContext('2d')

      ctx.drawImage(legacyMode ? img : preview, hozOffset, vertOffset, width, height)

      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
      // Send data to web-worker
      this.worker.postMessage(imageData)
    } else {
      // Preview not ready -> check later
      this.timeout = setTimeout(this.check, delay)
    }
  }
  handleWorkerMessage(e) {
    const { onScan, legacyMode, delay } = this.props
    const decoded = e.data
    onScan(decoded || null)

    if (!legacyMode && typeof delay == 'number' && this.worker) {
      this.timeout = setTimeout(this.check, delay)
    }
  }
  initiateLegacyMode() {
    this.reader = new FileReader()
    this.reader.addEventListener('load', this.handleReaderLoad)
    this.els.img.addEventListener('load', this.check, false)

    // Reset componentDidUpdate
    this.componentDidUpdate = undefined

    if(typeof this.props.onLoad == 'function') {
      this.props.onLoad()
    }
  }
  handleInputChange(e) {
    const selectedImg = e.target.files[0]
    this.reader.readAsDataURL(selectedImg)
  }
  handleReaderLoad(e) {
    // Set selected image blob as img source
    this.els.img.src = e.target.result
  }
  openImageDialog() {
    // Function to be executed by parent in user action context to trigger img file uploader
    this.els.input.click()
  }
  setRefFactory(key) {
    return element => {
      this.els[key] = element
    }
  }
  render() {
    const {
      style,
      className,
      onImageLoad,
      legacyMode,
      showViewFinder,
      facingMode
    } = this.props

    const containerStyle = {
      position: 'relative',
      width: '100%',
      paddingTop: '100%',
    }
    const hiddenStyle = { display: 'none' }
    const previewStyle = {
      top: 0,
      left: 0,
      display: 'block',
      position: 'absolute',
      overflow: 'hidden',
      width: '100%',
      height: '100%',
    }
    const videoPreviewStyle = {
      ...previewStyle,
      objectFit: 'cover',
      transform: this.state.mirrorVideo ? 'scaleX(-1)' : undefined,
    }
    const imgPreviewStyle = {
      ...previewStyle,
      objectFit: 'scale-down',
    }

    const viewFinderStyle = {
      top: 0,
      left: 0,
      zIndex: 1,
      boxSizing: 'border-box',
      border: '50px solid rgba(0, 0, 0, 0.3)',
      boxShadow: 'inset 0 0 0 5px rgba(255, 0, 0, 0.5)',
      position: 'absolute',
      width: '100%',
      height: '100%',
    }

    return (

          {
            (!legacyMode && showViewFinder)
            ?

            : null
          }
          {
            legacyMode
              ? <input
                style={hiddenStyle}
                type="file"
                accept="image/*"
                ref={this.setRefFactory('input')}
                onChange={this.handleInputChange}
              />
              : null
          }
          {
            legacyMode
              ?
              : <video style={videoPreviewStyle} ref={this.setRefFactory('preview')} />
          }

          <canvas style={hiddenStyle} ref={this.setRefFactory('canvas')} />

    )
  }
}
<|endoftext|>


<commit_before>// @flow

type Cancel = () => void
type Handler<-T> = (x: T) => void
type Computation<+S, +F> = (handleSucc: (s: S) => void, handleFail: (f: F) => void) => ?Cancel

const defaultFailureHandler = failure => {
  throw new Error(failure)
}

const noop = () => {}

export default class Task<+S, +F> {

  constructor() {
    if (this.constructor === Task) {
      throw new Error('Don\'t call `new Task()`, call `Task.create()` instead')
    }
  }

  // Creates a task with an arbitrary computation
  static create<S, F>(computation: Computation<S, F>): Task<S, F> {
    return new FromComputation(computation)
  }

  // Creates a task that resolves with a given value
  static of<S, F>(value: S): Task<S, F> {
    return new Of(value)
  }
  // instance alias for Fantasy Land
  of<S, F>(value: S): Task<S, F> {
    return Task.of(value)
  }

  // Creates a task that rejects with a given error
  static rejected<S, F>(error: F): Task<S, F> {
    return new Rejected(error)
  }

  // Creates a task that never completes
  static empty(): Task<any, any> {
    return new Empty()
  }
  // instance alias for Fantasy Land
  empty(): Task<any, any> {
    return Task.empty()
  }

  // Given array of tasks creates a task of array
  static all<S, F>(tasks: Array<Task<S, F>>): Task<S[], F> {
    return new All(tasks)
  }

  // Given array of tasks creates a task that completes with the earliest value or error
  static race<S, F>(task: Array<Task<S, F>>): Task<S, F> {
    return new Race(task)
  }

  // Transforms a task by applying `fn` to the successful value
  map<S1>(fn: (x: S) => S1): Task<S1, F> {
    return new Map(this, fn)
  }

  // Transforms a task by applying `fn` to the failure value
  mapRejected<F1>(fn: (x: F) => F1): Task<S, F1> {
    return new MapRejected(this, fn)
  }

  // Transforms a task by applying `fn` to the successful value (where `fn` returns a Task)
  chain<S1, F1>(fn: (x: S) => Task<S1, F1>): Task<S1, F | F1> {
    return new Chain(this, fn)
  }

  // Transforms a task by applying `fn` to the failure value (where `fn` returns a Task)
  orElse<S1, F1>(fn: (x: F) => Task<S1, F1>): Task<S | S1, F1> {
    // todo
    return (null: any)
  }

  // Applies the successful value of task `withF` to to the successful value of task `withX`
  ap<F1>(otherTask: Task<any, F1>): Task<any, F | F1> {
    return Task.all([(this: Task<any, F>), otherTask]).map(([f, x]) => f(x))
  }

  // Selects the earlier of the two tasks
  concat<S1, F1>(otherTask: Task<S1, F1>): Task<S | S1, F | F1> {
    return Task.race([this, otherTask])
  }

  run(handleSucc: Handler<S>, handleFail?: Handler<F>): Cancel {
    throw new Error('Method run() is not implemented in basic Task class.')
  }

  runAndCatch(handleSucc: Handler<S>, handleFail?: Handler<F>, handleThrown?: Handler<any>): Cancel {
    // todo: not implemented on concrete classes
    throw new Error('Method runAndCatch() is not implemented in basic Task class.')
  }

  runAndPrintResult(): void {
    this.run(
      x => console.log('Success:', x),
      x => console.log('Failure:', x)
    )
  }

}

class FromComputation<S, F> extends Task<S, F> {

  _computation: Computation<S, F>;

  constructor(computation: Computation<S, F>) {
    super()
    this._computation = computation
  }

  run(handleSucc: Handler<S>, handleFail: Handler<F> = defaultFailureHandler): Cancel {
    let succ = handleSucc
    let fail = handleFail
    let cancel = noop
    let closed = false
    let close = () => {
      // The idea here (and in various places below) is to kill links to all stuff
      // that we expose from the run() method closure. We expose via the return value
      // (cancelation function) and by passing callbacks to the computation.
      // We reason from an assumption that outer code may keep links to values that we exposed forever.
      // So we look at all thing that referenced in the exposed callback and kill them.
      succ = noop
      fail = noop
      cancel = noop
      close = noop
      closed = true
    }
    cancel = this._computation(x => { succ(x); close() }, x => { fail(x); close() }) || noop
    if (closed) {
      cancel = noop
    }
    return () => { cancel(); close() }
  }

}

class Of<S, F> extends Task<S, F> {

  _value: S;

  constructor(value: S) {
    super()
    this._value = value
  }

  run(handleSucc: Handler<S>): Cancel {
    handleSucc(this._value)
    return noop
  }

}

class Rejected<S, F> extends Task<S, F> {

  _error: F;

  constructor(error: F) {
    super()
    this._error = error
  }

  run(handleSucc: Handler<S>, handleFail: Handler<F> = defaultFailureHandler): Cancel {
    handleFail(this._error)
    return noop
  }

}

class Empty<S, F> extends Task<S, F> {

  run(): Cancel {
    return noop
  }

}

class All<S, F> extends Task<S[], F> {

  _tasks: Array<Task<S, F>>;

  constructor(tasks: Array<Task<S, F>>) {
    super()
    this._tasks = tasks
  }

  run(handleSucc: Handler<S[]>, handleFail: Handler<F> = defaultFailureHandler): Cancel {
    const length = this._tasks.length
    const values: Array<?S> = Array(length)
    let completedCount = 0
    let succ = handleSucc
    let fail = handleFail
    let cancel = noop
    let closed = false
    let cancelRest = noop
    let close = () => {
      cancelRest()
      succ = noop
      fail = noop
      close = noop
      closed = true
    }
    const onSucc = () => { if (completedCount === length) { succ((values: any)); close() } }
    const onFail = x => { fail(x); close() }
    const cancels = this._tasks.map(
      (task, index) => task.run(x => { values[index] = x; completedCount++; onSucc() }, onFail)
    )
    cancelRest = () => cancels.forEach(cancel => cancel())
    if (closed) {
      cancelRest()
    }
    return () => { close() }
  }

}

class Race<S, F> extends Task<S, F> {

  _tasks: Array<Task<S, F>>;

  constructor(tasks: Array<Task<S, F>>) {
    super()
    this._tasks = tasks
  }

  run(handleSucc: Handler<S>, handleFail: Handler<F> = defaultFailureHandler): Cancel {
    let succ = handleSucc
    let fail = handleFail
    let cancel = noop
    let closed = false
    let cancelRest = noop
    let close = () => {
      cancelRest()
      succ = noop
      fail = noop
      close = noop
      closed = true
    }
    const onSucc = x => { succ(x); close() }
    const onFail = x => { fail(x); close() }
    const cancels = this._tasks.map(task => task.run(onSucc, onFail))
    cancelRest = () => cancels.forEach(cancel => cancel())
    if (closed) {
      cancelRest()
    }
    return () => { close() }
  }

}

class Map<SIn, SOut, F> extends Task<SOut, F> {

  _task: Task<SIn, F>;
  _fn: (x: SIn) => SOut;

  constructor(task: Task<SIn, F>, fn: (x: SIn) => SOut) {
    super()
    this._task = task
    this._fn = fn
  }

  run(handleSucc: Handler<SOut>, handleFail?: Handler<F>): Cancel {
    const {_fn} = this
    return this._task.run(x => { handleSucc(_fn(x)) }, handleFail)
  }
}

class MapRejected<S, FIn, FOut> extends Task<S, FOut> {

  _task: Task<S, FIn>;
  _fn: (x: FIn) => FOut;

  constructor(task: Task<S, FIn>, fn: (x: FIn) => FOut) {
    super()
    this._task = task
    this._fn = fn
  }

  run(handleSucc: Handler<S>, handleFail: Handler<FOut> = defaultFailureHandler): Cancel {
    const {_fn} = this
    return this._task.run(handleSucc, x => { handleFail(_fn(x)) })
  }
}

class Chain<SIn, SOut, F, F1> extends Task<SOut, F1 | F> {

  _task: Task<SIn, F>;
  _fn: (x: SIn) => Task<SOut, F1>;

  constructor(task: Task<SIn, F>, fn: (x: SIn) => Task<SOut, F1>) {
    super()
    this._task = task
    this._fn = fn
  }

  run(handleSucc: Handler<SOut>, handleFail: Handler<F | F1> = defaultFailureHandler): Cancel {
    const {_fn} = this
    let succ = handleSucc
    let fail = handleFail
    let cancel1 = noop
    let cancel2 = noop
    let closed = false
    let close = () => {
      succ = noop
      fail = noop
      cancel1 = noop
      cancel2 = noop
      close = noop
      closed = true
    }
    const onFail = x => { fail(x); close() }
    cancel1 = this._task.run(x => {
      cancel2 = _fn(x).run(x => { succ(x); close() }, onFail)
      if (closed) {
        cancel2 = noop
      }
    }, onFail)
    if (closed) {
      cancel1 = noop
    }
    return () => { cancel1(); cancel2(); close() }
  }
}
<commit_msg>refactoring<commit_after>// @flow

type Cancel = () => void
type Handler<-T> = (x: T) => void
type Computation<+S, +F> = (handleSucc: (s: S) => void, handleFail: (f: F) => void) => ?Cancel

const defaultFailureHandler = failure => {
  throw new Error(failure)
}

const noop = () => {}

const safeRun = <S, F>(
  computation: (succ: (s: S) => void, fail: (f: F) => void) => ?(Cancel | {cancel?: Cancel, onClose: Cancel}),
  handleSucc: Handler<S>,
  handleFail: Handler<F> = defaultFailureHandler
): Cancel => {
  let succ = handleSucc
  let fail = handleFail
  let cancel = noop
  let onClose = noop
  let closed = false
  let close = () => {
    onClose()
    closed = true
    // The idea here is to kill links to all stuff that we exposed from safeRun closure.
    // We expose via the return value (cancelation function) and by passing callbacks to the computation.
    // We reason from an assumption that outer code may keep links to values that we exposed forever.
    // So we look at all thing that referenced in the exposed callback and kill them.
    succ = noop
    fail = noop
    cancel = noop
    close = noop
  }
  const computationReturn = computation(
    x => { succ(x); close() },
    x => { fail(x); close() }
  )
  if (computationReturn) {
    if (typeof computationReturn === 'function') {
      cancel = computationReturn
    } else {
      // this is called only when user cancels
      cancel = computationReturn.cancel || noop
      // this is called when user cancels plus when succ/fail are called
      onClose = computationReturn.onClose
    }
  }
  if (closed) {
    cancel = noop
    onClose()
  }
  return () => { cancel(); close() }
}

export default class Task<+S, +F> {

  constructor() {
    if (this.constructor === Task) {
      throw new Error('Don\'t call `new Task()`, call `Task.create()` instead')
    }
  }

  // Creates a task with an arbitrary computation
  static create<S, F>(computation: Computation<S, F>): Task<S, F> {
    return new FromComputation(computation)
  }

  // Creates a task that resolves with a given value
  static of<S, F>(value: S): Task<S, F> {
    return new Of(value)
  }
  // instance alias for Fantasy Land
  of<S, F>(value: S): Task<S, F> {
    return Task.of(value)
  }

  // Creates a task that rejects with a given error
  static rejected<S, F>(error: F): Task<S, F> {
    return new Rejected(error)
  }

  // Creates a task that never completes
  static empty(): Task<any, any> {
    return new Empty()
  }
  // instance alias for Fantasy Land
  empty(): Task<any, any> {
    return Task.empty()
  }

  // Given array of tasks creates a task of array
  static all<S, F>(tasks: Array<Task<S, F>>): Task<S[], F> {
    return new All(tasks)
  }

  // Given array of tasks creates a task that completes with the earliest value or error
  static race<S, F>(task: Array<Task<S, F>>): Task<S, F> {
    return new Race(task)
  }

  // Transforms a task by applying `fn` to the successful value
  map<S1>(fn: (x: S) => S1): Task<S1, F> {
    return new Map(this, fn)
  }

  // Transforms a task by applying `fn` to the failure value
  mapRejected<F1>(fn: (x: F) => F1): Task<S, F1> {
    return new MapRejected(this, fn)
  }

  // Transforms a task by applying `fn` to the successful value (where `fn` returns a Task)
  chain<S1, F1>(fn: (x: S) => Task<S1, F1>): Task<S1, F | F1> {
    return new Chain(this, fn)
  }

  // Transforms a task by applying `fn` to the failure value (where `fn` returns a Task)
  orElse<S1, F1>(fn: (x: F) => Task<S1, F1>): Task<S | S1, F1> {
    // todo
    return (null: any)
  }

  // Applies the successful value of task `withF` to to the successful value of task `withX`
  ap<F1>(otherTask: Task<any, F1>): Task<any, F | F1> {
    return Task.all([(this: Task<any, F>), otherTask]).map(([f, x]) => f(x))
  }

  // Selects the earlier of the two tasks
  concat<S1, F1>(otherTask: Task<S1, F1>): Task<S | S1, F | F1> {
    return Task.race([this, otherTask])
  }

  run(handleSucc: Handler<S>, handleFail?: Handler<F>): Cancel {
    throw new Error('Method run() is not implemented in basic Task class.')
  }

  runAndCatch(handleSucc: Handler<S>, handleFail?: Handler<F>, handleThrown?: Handler<any>): Cancel {
    // todo: not implemented on concrete classes
    throw new Error('Method runAndCatch() is not implemented in basic Task class.')
  }

  runAndPrintResult(): void {
    this.run(
      x => console.log('Success:', x),
      x => console.log('Failure:', x)
    )
  }

}

class FromComputation<S, F> extends Task<S, F> {

  _computation: Computation<S, F>;

  constructor(computation: Computation<S, F>) {
    super()
    this._computation = computation
  }

  run(handleSucc: Handler<S>, handleFail?: Handler<F>): Cancel {
    return safeRun(this._computation, handleSucc, handleFail)
  }

}

class Of<S, F> extends Task<S, F> {

  _value: S;

  constructor(value: S) {
    super()
    this._value = value
  }

  run(handleSucc: Handler<S>): Cancel {
    handleSucc(this._value)
    return noop
  }

}

class Rejected<S, F> extends Task<S, F> {

  _error: F;

  constructor(error: F) {
    super()
    this._error = error
  }

  run(handleSucc: Handler<S>, handleFail: Handler<F> = defaultFailureHandler): Cancel {
    handleFail(this._error)
    return noop
  }

}

class Empty<S, F> extends Task<S, F> {

  run(): Cancel {
    return noop
  }

}

class All<S, F> extends Task<S[], F> {

  _tasks: Array<Task<S, F>>;

  constructor(tasks: Array<Task<S, F>>) {
    super()
    this._tasks = tasks
  }

  run(handleSucc: Handler<S[]>, handleFail?: Handler<F>): Cancel {
    return safeRun((succ, fail) => {
      const length = this._tasks.length
      const values: Array<?S> = Array(length)
      let completedCount = 0
      const cancels = this._tasks.map(
        (task, index) => task.run(x => {
          values[index] = x
          completedCount++
          if (completedCount === length) {
            succ((values: any))
          }
        }, fail)
      )
      return {onClose() { cancels.forEach(cancel => cancel()) }}
    }, handleSucc, handleFail)
  }

}

class Race<S, F> extends Task<S, F> {

  _tasks: Array<Task<S, F>>;

  constructor(tasks: Array<Task<S, F>>) {
    super()
    this._tasks = tasks
  }

  run(handleSucc: Handler<S>, handleFail?: Handler<F>): Cancel {
    return safeRun((succ, fail) => {
      const cancels = this._tasks.map(task => task.run(succ, fail))
      return {onClose() { cancels.forEach(cancel => cancel()) }}
    }, handleSucc, handleFail)
  }

}

class Map<SIn, SOut, F> extends Task<SOut, F> {

  _task: Task<SIn, F>;
  _fn: (x: SIn) => SOut;

  constructor(task: Task<SIn, F>, fn: (x: SIn) => SOut) {
    super()
    this._task = task
    this._fn = fn
  }

  run(handleSucc: Handler<SOut>, handleFail?: Handler<F>): Cancel {
    const {_fn} = this
    return this._task.run(x => { handleSucc(_fn(x)) }, handleFail)
  }
}

class MapRejected<S, FIn, FOut> extends Task<S, FOut> {

  _task: Task<S, FIn>;
  _fn: (x: FIn) => FOut;

  constructor(task: Task<S, FIn>, fn: (x: FIn) => FOut) {
    super()
    this._task = task
    this._fn = fn
  }

  run(handleSucc: Handler<S>, handleFail: Handler<FOut> = defaultFailureHandler): Cancel {
    const {_fn} = this
    return this._task.run(handleSucc, x => { handleFail(_fn(x)) })
  }
}

class Chain<SIn, SOut, F, F1> extends Task<SOut, F1 | F> {

  _task: Task<SIn, F>;
  _fn: (x: SIn) => Task<SOut, F1>;

  constructor(task: Task<SIn, F>, fn: (x: SIn) => Task<SOut, F1>) {
    super()
    this._task = task
    this._fn = fn
  }

  run(handleSucc: Handler<SOut>, handleFail?: Handler<F | F1>): Cancel {
    const {_fn} = this
    return safeRun((succ, fail) => {
      let cancel2 = noop
      const cancel1 = this._task.run(x => { cancel2 = _fn(x).run(succ, fail) }, fail)
      return () => { cancel1(); cancel2() }
    }, handleSucc, handleFail)
  }
}
<|endoftext|>


<commit_before>(function() {
    function mrFactory(mobservable, React) {
        var reactComponentId = 1;
        var isTracking = false;
        var observeUntilInvalid = mobservable.observeUntilInvalid;

        // WeakMap<Node, Object>;
        var componentByNodeRegistery = typeof WeakMap !== "undefined" ? new WeakMap() : undefined;
        var renderReporter = new mobservable._.SimpleEventEmitter();

        function reportRendering(component) {
            // TODO: Fix in 0.14: React.findDOMNode is deprecated. Please use ReactDOM.findDOMNode from require('react-dom') instead.
            var node = React.findDOMNode(component);
            if (node)
                componentByNodeRegistery.set(node, component);

            renderReporter.emit({
                event: 'render',
                renderTime: component.__renderEnd - component.__renderStart,
                totalTime: Date.now() - component.__renderStart,
                component: component,
                node: node
            });
        }

        var reactiveMixin = {
            componentWillMount: function() {
                var name = (this.displayName || this.constructor.name || "ReactiveComponent") + reactComponentId++;
                var baseRender = this.render;

                this.render = function() {
                    if (isTracking)
                        this.__renderStart = Date.now();

                    if (this.__watchDisposer)
                        this.__watchDisposer();

                    var hasRendered = false;
                    var rendering;
                    this.__watchDisposer = mobservable.sideEffect(function reactiveRender() {
                        if (!hasRendered) {
                            hasRendered = true;
                            rendering = baseRender.call(this);
                        } else {
                            React.Component.prototype.forceUpdate.call(this);
                        }
                    }, this);

                    this.$mobservable = this.__watchDisposer.$mobservable;
                    if (isTracking)
                        this.__renderEnd = Date.now();
                    return rendering;
                }
            },

            componentWillUnmount: function() {
                if (this.__watchDisposer)
                    this.__watchDisposer();
                delete this.$mobservable;
                if (isTracking) {
                    // TODO: Fix in 0.14: React.findDOMNode is deprecated. Please use ReactDOM.findDOMNode from require('react-dom') instead.
                    var node = React.findDOMNode(this);
                    if (node) {
                        componentByNodeRegistery.delete(node);
                        renderReporter.emit({
                            event: 'destroy',
                            component: this,
                            node: node
                        });
                    }
                }
            },

            componentDidMount: function() {
                if (isTracking)
                    reportRendering(this);
            },

            componentDidUpdate: function() {
                if (isTracking)
                    reportRendering(this);
            },

            shouldComponentUpdate: function(nextProps, nextState) {
                // update on any state changes (as is the default)
                if (this.state !== nextState)
                    return true;
                // update if props are shallowly not equal, inspired by PureRenderMixin
                var keys = Object.keys(this.props);
                var key;
                if (keys.length !== Object.keys(nextProps).length)
                    return true;
                for(var i = keys.length -1; i >= 0, key = keys[i]; i--) {
                    var newValue = nextProps[key];
                    if (newValue !== this.props[key]) {
                        return true;
                    } else if (newValue && typeof newValue === "object" && !mobservable.isReactive(newValue)) {
                        /**
                         * If the newValue is still the same object, but that object is not reactive,
                         * fallback to the default React behavior: update, because the object *might* have changed.
                         * If you need the non default behavior, just use the React pure render mixin, as that one
                         * will work fine with mobservable as well, instead of the default implementation of
                         * reactiveComponent.
                         */
                        return true;
                    }
                }
                return false;
            }
        }

        function patch(target, funcName) {
            var base = target[funcName];
            var mixinFunc = reactiveMixin[funcName];
            target[funcName] = function() {
                base && base.apply(this, arguments);
                mixinFunc.apply(this, arguments);
            }
        }

        function reactiveComponent(componentClass) {
            if (!componentClass)
                throw new Error("Please pass a valid component to 'reactiveComponent'");
            var target = componentClass.prototype || componentClass;

            [
                "componentWillMount",
                "componentWillUnmount",
                "componentDidMount",
                "componentDidUpdate"
            ].forEach(function(funcName) {
                patch(target, funcName)
            });

            if (!target.shouldComponentUpdate)
                target.shouldComponentUpdate = reactiveMixin.shouldComponentUpdate;
            return componentClass;
        }

        function trackComponents() {
            if (typeof WeakMap === "undefined")
                throw new Error("tracking components is not supported in this browser");
            if (!isTracking)
                isTracking = true;
        }

        return ({
            reactiveComponent: reactiveComponent,
            renderReporter: renderReporter,
            componentByNodeRegistery: componentByNodeRegistery,
            trackComponents: trackComponents
        });
    }

    // UMD
    if (typeof define === 'function' && define.amd) {
        define('mobservable-react', ['mobservable', 'react'], mrFactory);
    }
    else if (typeof exports === 'object')
        module.exports = mrFactory(require('mobservable'), require('react'));
    else
        this.mobservableReact = mrFactory(this['mobservable'], this['React']);
})();
<commit_msg>removed unused reference<commit_after>(function() {
    function mrFactory(mobservable, React) {
        var reactComponentId = 1;
        var isTracking = false;

        // WeakMap<Node, Object>;
        var componentByNodeRegistery = typeof WeakMap !== "undefined" ? new WeakMap() : undefined;
        var renderReporter = new mobservable._.SimpleEventEmitter();

        function reportRendering(component) {
            // TODO: Fix in 0.14: React.findDOMNode is deprecated. Please use ReactDOM.findDOMNode from require('react-dom') instead.
            var node = React.findDOMNode(component);
            if (node)
                componentByNodeRegistery.set(node, component);

            renderReporter.emit({
                event: 'render',
                renderTime: component.__renderEnd - component.__renderStart,
                totalTime: Date.now() - component.__renderStart,
                component: component,
                node: node
            });
        }

        var reactiveMixin = {
            componentWillMount: function() {
                var name = (this.displayName || this.constructor.name || "ReactiveComponent") + reactComponentId++;
                var baseRender = this.render;

                this.render = function() {
                    if (isTracking)
                        this.__renderStart = Date.now();

                    if (this.__watchDisposer)
                        this.__watchDisposer();

                    var hasRendered = false;
                    var rendering;
                    this.__watchDisposer = mobservable.sideEffect(function reactiveRender() {
                        if (!hasRendered) {
                            hasRendered = true;
                            rendering = baseRender.call(this);
                        } else {
                            React.Component.prototype.forceUpdate.call(this);
                        }
                    }, this);

                    this.$mobservable = this.__watchDisposer.$mobservable;
                    if (isTracking)
                        this.__renderEnd = Date.now();
                    return rendering;
                }
            },

            componentWillUnmount: function() {
                if (this.__watchDisposer)
                    this.__watchDisposer();
                delete this.$mobservable;
                if (isTracking) {
                    // TODO: Fix in 0.14: React.findDOMNode is deprecated. Please use ReactDOM.findDOMNode from require('react-dom') instead.
                    var node = React.findDOMNode(this);
                    if (node) {
                        componentByNodeRegistery.delete(node);
                        renderReporter.emit({
                            event: 'destroy',
                            component: this,
                            node: node
                        });
                    }
                }
            },

            componentDidMount: function() {
                if (isTracking)
                    reportRendering(this);
            },

            componentDidUpdate: function() {
                if (isTracking)
                    reportRendering(this);
            },

            shouldComponentUpdate: function(nextProps, nextState) {
                // update on any state changes (as is the default)
                if (this.state !== nextState)
                    return true;
                // update if props are shallowly not equal, inspired by PureRenderMixin
                var keys = Object.keys(this.props);
                var key;
                if (keys.length !== Object.keys(nextProps).length)
                    return true;
                for(var i = keys.length -1; i >= 0, key = keys[i]; i--) {
                    var newValue = nextProps[key];
                    if (newValue !== this.props[key]) {
                        return true;
                    } else if (newValue && typeof newValue === "object" && !mobservable.isReactive(newValue)) {
                        /**
                         * If the newValue is still the same object, but that object is not reactive,
                         * fallback to the default React behavior: update, because the object *might* have changed.
                         * If you need the non default behavior, just use the React pure render mixin, as that one
                         * will work fine with mobservable as well, instead of the default implementation of
                         * reactiveComponent.
                         */
                        return true;
                    }
                }
                return false;
            }
        }

        function patch(target, funcName) {
            var base = target[funcName];
            var mixinFunc = reactiveMixin[funcName];
            target[funcName] = function() {
                base && base.apply(this, arguments);
                mixinFunc.apply(this, arguments);
            }
        }

        function reactiveComponent(componentClass) {
            if (!componentClass)
                throw new Error("Please pass a valid component to 'reactiveComponent'");
            var target = componentClass.prototype || componentClass;

            [
                "componentWillMount",
                "componentWillUnmount",
                "componentDidMount",
                "componentDidUpdate"
            ].forEach(function(funcName) {
                patch(target, funcName)
            });

            if (!target.shouldComponentUpdate)
                target.shouldComponentUpdate = reactiveMixin.shouldComponentUpdate;
            return componentClass;
        }

        function trackComponents() {
            if (typeof WeakMap === "undefined")
                throw new Error("tracking components is not supported in this browser");
            if (!isTracking)
                isTracking = true;
        }

        return ({
            reactiveComponent: reactiveComponent,
            renderReporter: renderReporter,
            componentByNodeRegistery: componentByNodeRegistery,
            trackComponents: trackComponents
        });
    }

    // UMD
    if (typeof define === 'function' && define.amd) {
        define('mobservable-react', ['mobservable', 'react'], mrFactory);
    }
    else if (typeof exports === 'object')
        module.exports = mrFactory(require('mobservable'), require('react'));
    else
        this.mobservableReact = mrFactory(this['mobservable'], this['React']);
})();
<|endoftext|>


<commit_before>// Native
import path from 'path'
import {spawn} from 'child_process'

// Packages
import parser from 'minimist'
import pkginfo from 'pkginfo'
import loudRejection from 'loud-rejection'
import camelcase from 'camelcase'
import chalk from 'chalk'

class Args {
  constructor() {
    // Will later hold registered options and commands
    this.details = {
      options: [],
      commands: []
    }

    // Configuration defaults
    this.config = {
      help: true,
      version: true,
      usageFilter: null,
      value: null,
      name: null
    }

    // Make unhandled promise rejections fail loudly instead of the default silent fail
    loudRejection()
  }

  options(list) {
    if (list.constructor !== Array) {
      throw new Error('Item passed to .options is not an array')
    }

    for (const item of list) {
      const preset = item.defaultValue || false
      const init = item.init || false

      this.option(item.name, item.description, preset, init)
    }

    return this
  }

  option(name, description, defaultValue, init) {
    let usage = []

    // If name is an array, pick the values
    // Otherwise just use the whole thing
    switch (name.constructor) {
      case String:
        usage[0] = name.charAt(0)
        usage[1] = name

        break
      case Array:
        usage = usage.concat(name)
        break
      default:
        throw new Error('Invalid name for option')
    }

    // Throw error if short option is too long
    if (usage.length > 0 && usage[0].length > 1) {
      throw new Error('Short version of option is longer than 1 char')
    }

    const optionDetails = {
      defaultValue,
      usage,
      description
    }

    let defaultIsWrong

    switch (defaultValue) {
      case false:
        defaultIsWrong = true
        break
      case null:
        defaultIsWrong = true
        break
      case undefined:
        defaultIsWrong = true
        break
      default:
        defaultIsWrong = false
    }

    // Set initializer depending on type of default value
    if (!defaultIsWrong) {
      const initFunction = typeof init === 'function'
      optionDetails.init = initFunction ? init : this.handleType(defaultValue)[1]
    }

    // Register option to global scope
    this.details.options.push(optionDetails)

    // Allow chaining of .option()
    return this
  }

  command(usage, description, init, aliases) {
    if (Array.isArray(init)) {
      aliases = init
      init = undefined
    }
    if (aliases && Array.isArray(aliases)) {
      usage = [].concat([usage], aliases)
    }

    // Register command to global scope
    this.details.commands.push({
      usage,
      description,
      init: typeof init === 'function' ? init : false
    })

    // Allow chaining of .command()
    return this
  }

  handleType(value) {
    let type = value

    if (typeof value !== 'function') {
      type = value.constructor
    }

    // Depending on the type of the default value,
    // select a default initializer function
    switch (type) {
      case String:
        return ['[value]']
      case Array:
        return ['<list>']
      case Number:
      case parseInt:
        return ['<n>', parseInt]
      default:
        return ['']
    }
  }

  readOption(option) {
    let value = false
    const contents = {}

    // If option has been used, get its value
    for (const name of option.usage) {
      const fromArgs = this.raw[name]

      if (fromArgs) {
        value = fromArgs
      }
    }

    // Process the option's value
    for (let name of option.usage) {
      let propVal = value || option.defaultValue
      let condition = true

      if (option.init) {
        // Only use the toString initializer if value is a number
        if (option.init === toString) {
          condition = propVal.constructor === Number
        }

        if (condition) {
          // Pass it through the initializer
          propVal = option.init(propVal)
        }
      }

      // Camelcase option name
      name = camelcase(name)

      // Add option to list if it has a value
      if (propVal) {
        contents[name] = propVal
      }
    }

    return contents
  }

  getOptions() {
    const options = {}
    const args = {}

    // Copy over the arguments
    Object.assign(args, this.raw)
    delete args._

    // Set option defaults
    for (const option of this.details.options) {
      if (!option.defaultValue) {
        continue
      }

      Object.assign(options, this.readOption(option))
    }

    // Override defaults if used in command line
    for (const option in args) {
      if (!{}.hasOwnProperty.call(args, option)) {
        continue
      }

      const related = this.isDefined(option, 'options')

      if (related) {
        const details = this.readOption(related)
        Object.assign(options, details)
      }
    }

    return options
  }

  generateDetails(kind) {
    // Get all properties of kind from global scope
    const items = this.details[kind]
    const parts = []
    const isCmd = kind === 'commands'

    // Sort items alphabetically
    items.sort((a, b) => {
      const first = isCmd ? a.usage : a.usage[1]
      const second = isCmd ? b.usage : b.usage[1]

      switch (true) {
        case (first < second): return -1
        case (first > second): return 1
        default: return 0
      }
    })

    for (const item in items) {
      if (!{}.hasOwnProperty.call(items, item)) {
        continue
      }

      let usage = items[item].usage
      let initial = items[item].defaultValue

      // If usage is an array, show its contents
      if (usage.constructor === Array) {
        if (isCmd) {
          usage = usage.join(', ')
        } else {
          const isVersion = usage.indexOf('v')
          usage = `-${usage[0]}, --${usage[1]}`

          if (!initial) {
            initial = items[item].init
          }

          usage += (initial && isVersion === -1) ? ' ' + this.handleType(initial)[0] : ''
        }
      }

      // Overwrite usage with readable syntax
      items[item].usage = usage
    }

    // Find length of longest option or command
    // Before doing that, make a copy of the original array
    const longest = items.slice().sort((a, b) => {
      return b.usage.length - a.usage.length
    })[0].usage.length

    for (const item of items) {
      let {usage, description} = item
      const difference = longest - usage.length

      // Compensate the difference to longest property with spaces
      usage += ' '.repeat(difference)

      // Add some space around it as well
      if (typeof item.defaultValue !== 'undefined') {
        description += ` (defaults to ${JSON.stringify(item.defaultValue)})`
      }
      parts.push('  ' + chalk.yellow(usage) + '  ' + chalk.dim(description))
    }

    return parts
  }

  runCommand(details, options) {
    // If help is disabled, remove initializer
    if (details.usage === 'help' && !this.config.help) {
      details.init = false
    }

    // If command has initializer, call it
    if (details.init) {
      const sub = [].concat(this.sub)
      sub.shift()

      return details.init.bind(this)(details.usage, sub, options)
    }

    // Generate full name of binary
    const full = this.binary + '-' + (Array.isArray(details.usage) ? details.usage[0] : details.usage)

    const args = process.argv
    let i = 0

    while (i < 3) {
      args.shift()
      i++
    }

    // Run binary of sub command
    this.child = spawn(full, args, {
      stdio: 'inherit'
    })

    // Throw an error if something fails within that binary
    this.child.on('error', err => {
      throw err
    })
  }

  checkVersion() {
    const parent = module.parent

    // Load parent module
    pkginfo(parent)

    // And get its version propery
    const version = parent.exports.version

    if (version) {
      // If it exists, register it as a default option
      this.option('version', 'Output the version number')

      // And immediately output it if used in command line
      if (this.raw.v || this.raw.version) {
        console.log(version)
        process.exit()
      }
    }
  }

  isDefined(name, list) {
    // Get all items of kind
    const children = this.details[list]

    // Check if a child matches the requested name
    for (const child of children) {
      const usage = child.usage
      const type = usage.constructor

      if (type === Array && usage.indexOf(name) > -1) {
        return child
      }

      if (type === String && usage === name) {
        return child
      }
    }

    // If nothing matches, item is not defined
    return false
  }

  parse(argv, options) {
    // Override default option values
    Object.assign(this.config, options)

    if (this.config.help) {
      // Register default options and commands
      this.option('help', 'Output usage information')
      this.command('help', 'Display help', this.showHelp)
    }

    // Parse arguments using minimist
    this.raw = parser(argv.slice(1))
    this.binary = path.basename(this.raw._[0])

    // If default version is allowed, check for it
    if (this.config.version) {
      this.checkVersion()
    }

    const subCommand = this.raw._[1]
    const helpTriggered = this.raw.h || this.raw.help

    const args = {}
    const defined = this.isDefined(subCommand, 'commands')
    const optionList = this.getOptions()

    Object.assign(args, this.raw)
    args._.shift()

    // Export sub arguments of command
    this.sub = args._

    // If sub command is defined, run it
    if (defined) {
      this.runCommand(defined, optionList)
      return {}
    }

    // Show usage information if "help" or "h" option was used
    // And respect the option related to it
    if (this.config.help && helpTriggered) {
      this.showHelp()
    }

    // Hand back list of options
    return optionList
  }

  showHelp() {
    const name = this.config.name || this.binary.replace('-', ' ')
    const firstBig = word => word.charAt(0).toUpperCase() + word.substr(1)

    const parts = []

    const groups = {
      commands: true,
      options: true
    }

    for (const group in groups) {
      if (this.details[group].length > 0) {
        continue
      }

      groups[group] = false
    }

    const optionHandle = groups.options ? ' [options]' : ''
    const cmdHandle = groups.commands ? ' [command]' : ''
    const value = typeof this.config.value === 'string' ? ' ' + this.config.value : ''

    parts.push([
      '',
      'Usage: ' + chalk.yellow(name) + chalk.dim(optionHandle + cmdHandle + value),
      ''
    ])

    for (const group in groups) {
      if (!groups[group]) {
        continue
      }

      parts.push([
        '',
        firstBig(group) + ':',
        '',
        ''
      ])

      parts.push(this.generateDetails(group))
      parts.push(['', ''])
    }

    let output = ''

    // And finally, merge and output them
    for (const part of parts) {
      output += part.join('\n  ')
    }

    if (!groups.commands && !groups.options) {
      output = 'No sub commands or options available'
    }

    const usageFilter = this.config.usageFilter

    // If filter is available, pass usage information through
    if (typeof usageFilter === 'function') {
      output = usageFilter(output) || output
    }

    console.log(output)
    process.exit()
  }
}

export default new Args()
<commit_msg>use enabled/disabled for boolean default values<commit_after>// Native
import path from 'path'
import {spawn} from 'child_process'

// Packages
import parser from 'minimist'
import pkginfo from 'pkginfo'
import loudRejection from 'loud-rejection'
import camelcase from 'camelcase'
import chalk from 'chalk'

class Args {
  constructor() {
    // Will later hold registered options and commands
    this.details = {
      options: [],
      commands: []
    }

    // Configuration defaults
    this.config = {
      help: true,
      version: true,
      usageFilter: null,
      value: null,
      name: null
    }

    // Make unhandled promise rejections fail loudly instead of the default silent fail
    loudRejection()
  }

  options(list) {
    if (list.constructor !== Array) {
      throw new Error('Item passed to .options is not an array')
    }

    for (const item of list) {
      const preset = item.defaultValue || false
      const init = item.init || false

      this.option(item.name, item.description, preset, init)
    }

    return this
  }

  option(name, description, defaultValue, init) {
    let usage = []

    // If name is an array, pick the values
    // Otherwise just use the whole thing
    switch (name.constructor) {
      case String:
        usage[0] = name.charAt(0)
        usage[1] = name

        break
      case Array:
        usage = usage.concat(name)
        break
      default:
        throw new Error('Invalid name for option')
    }

    // Throw error if short option is too long
    if (usage.length > 0 && usage[0].length > 1) {
      throw new Error('Short version of option is longer than 1 char')
    }

    const optionDetails = {
      defaultValue,
      usage,
      description
    }

    let defaultIsWrong

    switch (defaultValue) {
      case false:
        defaultIsWrong = true
        break
      case null:
        defaultIsWrong = true
        break
      case undefined:
        defaultIsWrong = true
        break
      default:
        defaultIsWrong = false
    }

    // Set initializer depending on type of default value
    if (!defaultIsWrong) {
      const initFunction = typeof init === 'function'
      optionDetails.init = initFunction ? init : this.handleType(defaultValue)[1]
    }

    // Register option to global scope
    this.details.options.push(optionDetails)

    // Allow chaining of .option()
    return this
  }

  command(usage, description, init, aliases) {
    if (Array.isArray(init)) {
      aliases = init
      init = undefined
    }
    if (aliases && Array.isArray(aliases)) {
      usage = [].concat([usage], aliases)
    }

    // Register command to global scope
    this.details.commands.push({
      usage,
      description,
      init: typeof init === 'function' ? init : false
    })

    // Allow chaining of .command()
    return this
  }

  handleType(value) {
    let type = value

    if (typeof value !== 'function') {
      type = value.constructor
    }

    // Depending on the type of the default value,
    // select a default initializer function
    switch (type) {
      case String:
        return ['[value]']
      case Array:
        return ['<list>']
      case Number:
      case parseInt:
        return ['<n>', parseInt]
      default:
        return ['']
    }
  }

  readOption(option) {
    let value = false
    const contents = {}

    // If option has been used, get its value
    for (const name of option.usage) {
      const fromArgs = this.raw[name]

      if (fromArgs) {
        value = fromArgs
      }
    }

    // Process the option's value
    for (let name of option.usage) {
      let propVal = value || option.defaultValue
      let condition = true

      if (option.init) {
        // Only use the toString initializer if value is a number
        if (option.init === toString) {
          condition = propVal.constructor === Number
        }

        if (condition) {
          // Pass it through the initializer
          propVal = option.init(propVal)
        }
      }

      // Camelcase option name
      name = camelcase(name)

      // Add option to list if it has a value
      if (propVal) {
        contents[name] = propVal
      }
    }

    return contents
  }

  getOptions() {
    const options = {}
    const args = {}

    // Copy over the arguments
    Object.assign(args, this.raw)
    delete args._

    // Set option defaults
    for (const option of this.details.options) {
      if (typeof option.defaultValue === 'undefined') {
        continue
      }

      Object.assign(options, this.readOption(option))
    }

    // Override defaults if used in command line
    for (const option in args) {
      if (!{}.hasOwnProperty.call(args, option)) {
        continue
      }

      const related = this.isDefined(option, 'options')

      if (related) {
        const details = this.readOption(related)
        Object.assign(options, details)
      }
    }

    return options
  }

  generateDetails(kind) {
    // Get all properties of kind from global scope
    const items = this.details[kind]
    const parts = []
    const isCmd = kind === 'commands'

    // Sort items alphabetically
    items.sort((a, b) => {
      const first = isCmd ? a.usage : a.usage[1]
      const second = isCmd ? b.usage : b.usage[1]

      switch (true) {
        case (first < second): return -1
        case (first > second): return 1
        default: return 0
      }
    })

    for (const item in items) {
      if (!{}.hasOwnProperty.call(items, item)) {
        continue
      }

      let usage = items[item].usage
      let initial = items[item].defaultValue

      // If usage is an array, show its contents
      if (usage.constructor === Array) {
        if (isCmd) {
          usage = usage.join(', ')
        } else {
          const isVersion = usage.indexOf('v')
          usage = `-${usage[0]}, --${usage[1]}`

          if (!initial) {
            initial = items[item].init
          }

          usage += (initial && isVersion === -1) ? ' ' + this.handleType(initial)[0] : ''
        }
      }

      // Overwrite usage with readable syntax
      items[item].usage = usage
    }

    // Find length of longest option or command
    // Before doing that, make a copy of the original array
    const longest = items.slice().sort((a, b) => {
      return b.usage.length - a.usage.length
    })[0].usage.length

    for (const item of items) {
      let {usage, description, defaultValue} = item
      const difference = longest - usage.length

      // Compensate the difference to longest property with spaces
      usage += ' '.repeat(difference)

      // Add some space around it as well
      if (typeof defaultValue !== 'undefined') {
        if (typeof defaultValue === 'boolean') {
          description += ` (${defaultValue ? 'enabled' : 'disabled'} by default)`
        } else {
          description += ` (defaults to ${JSON.stringify(defaultValue)})`
        }
      }
      parts.push('  ' + chalk.yellow(usage) + '  ' + chalk.dim(description))
    }

    return parts
  }

  runCommand(details, options) {
    // If help is disabled, remove initializer
    if (details.usage === 'help' && !this.config.help) {
      details.init = false
    }

    // If command has initializer, call it
    if (details.init) {
      const sub = [].concat(this.sub)
      sub.shift()

      return details.init.bind(this)(details.usage, sub, options)
    }

    // Generate full name of binary
    const full = this.binary + '-' + (Array.isArray(details.usage) ? details.usage[0] : details.usage)

    const args = process.argv
    let i = 0

    while (i < 3) {
      args.shift()
      i++
    }

    // Run binary of sub command
    this.child = spawn(full, args, {
      stdio: 'inherit'
    })

    // Throw an error if something fails within that binary
    this.child.on('error', err => {
      throw err
    })
  }

  checkVersion() {
    const parent = module.parent

    // Load parent module
    pkginfo(parent)

    // And get its version propery
    const version = parent.exports.version

    if (version) {
      // If it exists, register it as a default option
      this.option('version', 'Output the version number')

      // And immediately output it if used in command line
      if (this.raw.v || this.raw.version) {
        console.log(version)
        process.exit()
      }
    }
  }

  isDefined(name, list) {
    // Get all items of kind
    const children = this.details[list]

    // Check if a child matches the requested name
    for (const child of children) {
      const usage = child.usage
      const type = usage.constructor

      if (type === Array && usage.indexOf(name) > -1) {
        return child
      }

      if (type === String && usage === name) {
        return child
      }
    }

    // If nothing matches, item is not defined
    return false
  }

  parse(argv, options) {
    // Override default option values
    Object.assign(this.config, options)

    if (this.config.help) {
      // Register default options and commands
      this.option('help', 'Output usage information')
      this.command('help', 'Display help', this.showHelp)
    }

    // Parse arguments using minimist
    this.raw = parser(argv.slice(1))
    this.binary = path.basename(this.raw._[0])

    // If default version is allowed, check for it
    if (this.config.version) {
      this.checkVersion()
    }

    const subCommand = this.raw._[1]
    const helpTriggered = this.raw.h || this.raw.help

    const args = {}
    const defined = this.isDefined(subCommand, 'commands')
    const optionList = this.getOptions()

    Object.assign(args, this.raw)
    args._.shift()

    // Export sub arguments of command
    this.sub = args._

    // If sub command is defined, run it
    if (defined) {
      this.runCommand(defined, optionList)
      return {}
    }

    // Show usage information if "help" or "h" option was used
    // And respect the option related to it
    if (this.config.help && helpTriggered) {
      this.showHelp()
    }

    // Hand back list of options
    return optionList
  }

  showHelp() {
    const name = this.config.name || this.binary.replace('-', ' ')
    const firstBig = word => word.charAt(0).toUpperCase() + word.substr(1)

    const parts = []

    const groups = {
      commands: true,
      options: true
    }

    for (const group in groups) {
      if (this.details[group].length > 0) {
        continue
      }

      groups[group] = false
    }

    const optionHandle = groups.options ? ' [options]' : ''
    const cmdHandle = groups.commands ? ' [command]' : ''
    const value = typeof this.config.value === 'string' ? ' ' + this.config.value : ''

    parts.push([
      '',
      'Usage: ' + chalk.yellow(name) + chalk.dim(optionHandle + cmdHandle + value),
      ''
    ])

    for (const group in groups) {
      if (!groups[group]) {
        continue
      }

      parts.push([
        '',
        firstBig(group) + ':',
        '',
        ''
      ])

      parts.push(this.generateDetails(group))
      parts.push(['', ''])
    }

    let output = ''

    // And finally, merge and output them
    for (const part of parts) {
      output += part.join('\n  ')
    }

    if (!groups.commands && !groups.options) {
      output = 'No sub commands or options available'
    }

    const usageFilter = this.config.usageFilter

    // If filter is available, pass usage information through
    if (typeof usageFilter === 'function') {
      output = usageFilter(output) || output
    }

    console.log(output)
    process.exit()
  }
}

export default new Args()
<|endoftext|>


<commit_before>import axios from 'axios'
import forceArray from './forceArray'
import timeTransform from './timeTransform'

const CancelToken = axios.CancelToken

const orchestrate = (config, options) => store => next => originalAction => {
  if (!Array.isArray(config)) {
    throw new Error('Orchestrate config must be an array')
  }

  if (!options || !options.validate) {
    next(originalAction)
  }

  function internalNext (action) {
    next(action)
    checkAction(action)
  }

  function checkAction (action) {
    config.forEach(rule => {
      if (typeof rule._debounceTimeoutRefs !== 'object') {
        rule._debounceTimeoutRefs = {}
      }

      const ruleConfig = {}
      Object.keys(rule).forEach(ruleKey => {
        if (typeof rule[ruleKey] === 'function') {
          ruleConfig[ruleKey] = rule[ruleKey](action, store.getState())
        } else {
          ruleConfig[ruleKey] = rule[ruleKey]
        }
      })
      const testCase = forceArray(ruleConfig.case)
      testCase.forEach( c => {
        let dispatchAction = ruleConfig.dispatch

        if (typeof ruleConfig.dispatch === 'string') {
          dispatchAction = {...action, type: ruleConfig.dispatch}
        }

        let requestConfig = ruleConfig.request

        const supportMethods = {
          get: 'get',
          post: 'post',
          put: 'put',
          patch: 'patch',
          del: 'delete',
          head: 'head',
          options: 'options'
        }
        Object.keys(supportMethods).forEach(method => {
          if (ruleConfig[method]) {
            requestConfig = {...ruleConfig[method], method: supportMethods[method]}
          }
        })

        if (
          rule._cancelFn &&
          requestConfig &&
          requestConfig.cancelWhen &&
          requestConfig.cancelWhen.indexOf(action.type) !== -1
        ) {
          rule._cancelFn()
        }

        if (action.type === c) {
          timeTransform(action, ruleConfig, () => {
            if (dispatchAction) {
              internalNext(dispatchAction)
            }

            if (requestConfig) {
              axios({
                ...requestConfig,
                cancelToken: new CancelToken(c => rule._cancelFn = c)
              })
                .then(res => {
                  if (requestConfig.onSuccess) {
                    let onSuccessAction = requestConfig.onSuccess
                    if (typeof requestConfig.onSuccess === 'string') {
                      onSuccessAction = {type: requestConfig.onSuccess}
                    } else if (typeof requestConfig.onSuccess === 'function') {
                      onSuccessAction = requestConfig.onSuccess(res)
                    }
                    internalNext(onSuccessAction)
                  }

                  if (requestConfig.callback) {
                    requestConfig.callback(null, res)
                  }
                })
                .catch(err => {
                  if (
                    requestConfig.onFail &&
                    !(err && err.message && err.message.type === 'CANCEL_EVENT')
                  ) {
                    let onFailAction = requestConfig.onFail
                    if (typeof requestConfig.onFail === 'string') {
                      onFailAction = {type: requestConfig.onFail}
                    } else if (typeof requestConfig.onFail === 'function') {
                      onFailAction = requestConfig.onFail(err)
                    }
                    internalNext(onFailAction)
                  }

                  if (requestConfig.callback) {
                    requestConfig.callback(err)
                  }
                })
            }
          })

        }
      })
    })
  }
  checkAction(originalAction)
}

export default orchestrate
<commit_msg>dispatch array<commit_after>import axios from 'axios'
import forceArray from './forceArray'
import timeTransform from './timeTransform'

const CancelToken = axios.CancelToken

const orchestrate = (config, options) => store => next => originalAction => {
  if (!Array.isArray(config)) {
    throw new Error('Orchestrate config must be an array')
  }

  if (!options || !options.validate) {
    next(originalAction)
  }

  function internalNext (action) {
    next(action)
    checkAction(action)
  }

  function checkAction (action) {
    config.forEach(rule => {
      if (typeof rule._debounceTimeoutRefs !== 'object') {
        rule._debounceTimeoutRefs = {}
      }

      const ruleConfig = {}
      Object.keys(rule).forEach(ruleKey => {
        if (typeof rule[ruleKey] === 'function') {
          ruleConfig[ruleKey] = rule[ruleKey](action, store.getState())
        } else {
          ruleConfig[ruleKey] = rule[ruleKey]
        }
      })
      const testCase = forceArray(ruleConfig.case)
      testCase.forEach( c => {
        let dispatchActions
        if (ruleConfig.dispatch) {
          dispatchActions = ruleConfig.dispatch
          if (!Array.isArray(dispatchActions)) {
            dispatchActions = [dispatchActions]
          }
          dispatchActions = dispatchActions.map(dispatchAction => {
            if (typeof dispatchAction === 'string') {
              return {...action, type: dispatchAction}
            } else {
              return dispatchAction
            }
          })
        }

        let requestConfig = ruleConfig.request

        const supportMethods = {
          get: 'get',
          post: 'post',
          put: 'put',
          patch: 'patch',
          del: 'delete',
          head: 'head',
          options: 'options'
        }
        Object.keys(supportMethods).forEach(method => {
          if (ruleConfig[method]) {
            requestConfig = {...ruleConfig[method], method: supportMethods[method]}
          }
        })

        if (
          rule._cancelFn &&
          requestConfig &&
          requestConfig.cancelWhen &&
          requestConfig.cancelWhen.indexOf(action.type) !== -1
        ) {
          rule._cancelFn()
        }

        if (action.type === c) {
          timeTransform(action, ruleConfig, () => {
            if (dispatchActions) {
              dispatchActions.map(internalNext)
            }

            if (requestConfig) {
              axios({
                ...requestConfig,
                cancelToken: new CancelToken(c => rule._cancelFn = c)
              })
                .then(res => {
                  if (requestConfig.onSuccess) {
                    let onSuccessAction = requestConfig.onSuccess
                    if (typeof requestConfig.onSuccess === 'string') {
                      onSuccessAction = {type: requestConfig.onSuccess}
                    } else if (typeof requestConfig.onSuccess === 'function') {
                      onSuccessAction = requestConfig.onSuccess(res)
                    }
                    internalNext(onSuccessAction)
                  }

                  if (requestConfig.callback) {
                    requestConfig.callback(null, res)
                  }
                })
                .catch(err => {
                  if (
                    requestConfig.onFail &&
                    !(err && err.message && err.message.type === 'CANCEL_EVENT')
                  ) {
                    let onFailAction = requestConfig.onFail
                    if (typeof requestConfig.onFail === 'string') {
                      onFailAction = {type: requestConfig.onFail}
                    } else if (typeof requestConfig.onFail === 'function') {
                      onFailAction = requestConfig.onFail(err)
                    }
                    internalNext(onFailAction)
                  }

                  if (requestConfig.callback) {
                    requestConfig.callback(err)
                  }
                })
            }
          })

        }
      })
    })
  }
  checkAction(originalAction)
}

export default orchestrate
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar';
import SeasonList from './components/season_list';
import SeasonDetail from './components/season_detail';
import axios from 'axios';

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

		let a = 'https://mfwkweb-api.clarovideo.net/services/content/serie?api_version=v5.4&authpn=webclient&authpt=tfg1h3j4k6fd7&format=json&region=mexico&device_id=web&device_category=web&device_model=web&device_type=web&device_manufacturer=generic&HKS=q12fva9pjnhauhd4jg0d02e0t1&group_id=544242';

		this.state = {
			seasons: [],
			selectedSeason: null
		 };

		axios.get(a)
  		.then((response) => {
				if (response.data.status === '0') {
					let r = response.data.response;
					this.performState(r.seasons, 1);
					this.seasonsCache = r.seasons;
				}
		  })
		  .catch(function (error) {
		    console.log(error);
		});

	}

	performState(seasons, selectedSeasonNumber, selectedEpisodeId = undefined){
		let selectedSeason = seasons.find( season => {
			return season.number == selectedSeasonNumber;
		});
		let selectedEpisode = selectedSeason.episodes.find( episode => {
			if (selectedEpisodeId !== undefined) {
				return episode.id == selectedEpisodeId;
			}
			return episode.id == selectedSeason.first_episode;
		});
		this.setState({ seasons, selectedSeason, selectedEpisode });
	}

	selectSeason(ev) {
		this.performState(this.state.seasons, ev.target.value);
	}

	selectEpisode(ev) {
		this.performState(this.state.seasons, this.state.selectedSeason.number, ev.target.id);
	}

	searchTerm(term) {
		console.log(this.props.seasonsCache);
	}

	render() {
		return (

				<SearchBar onSearchTermChange={term => this.searchTerm(term)} />
				<SeasonList
					onSeasonSelect={ev => this.selectSeason(ev)}
					selectedSeason={this.state.selectedSeason}
					seasons={this.state.seasons}
				/>
				<SeasonDetail
					onEpisodeSelect={ev => this.selectEpisode(ev)}
					season={this.state.selectedSeason}
					selectedEpisode={this.state.selectedEpisode}
				/>

		);
	}
}

ReactDOM.render(<App />, document.querySelector('.container'));
<commit_msg>Se completa funcionalidad de busqueda con manejo de errores vacio<commit_after>import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar';
import SeasonList from './components/season_list';
import SeasonDetail from './components/season_detail';
import axios from 'axios';

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

		let a = 'https://mfwkweb-api.clarovideo.net/services/content/serie?api_version=v5.4&authpn=webclient&authpt=tfg1h3j4k6fd7&format=json&region=mexico&device_id=web&device_category=web&device_model=web&device_type=web&device_manufacturer=generic&HKS=q12fva9pjnhauhd4jg0d02e0t1&group_id=544242';

		this.state = {
			seasons: [],
			selectedSeason: null
		 };

		axios.get(a)
  		.then((response) => {
				if (response.data.status === '0') {
					let r = response.data.response;
					this.performState(r.seasons, 1);
					this.seasonsCache = r.seasons;
				}
		  })
		  .catch(function (error) {
		    console.log(error);
		});

	}

	performState(seasons, selectedSeasonNumber, selectedEpisodeId = undefined){
		let selectedSeason = seasons.find( season => {
			return season.number == selectedSeasonNumber;
		});
		let selectedEpisode = selectedSeason.episodes.find( episode => {
			if (selectedEpisodeId !== undefined) {
				return episode.id == selectedEpisodeId;
			}
			return episode.id == selectedSeason.first_episode;
		});
		this.setState({ seasons, selectedSeason, selectedEpisode });
	}

	selectSeason(ev) {
		this.performState(this.state.seasons, ev.target.value);
	}

	selectEpisode(ev) {
		this.performState(this.state.seasons, this.state.selectedSeason.number, ev.target.id);
	}

	searchTerm(search) {
		let busqueda = search.term.toLowerCase();
		let seasons = [];
		if (busqueda == '') {
			seasons = this.seasonsCache;
		} else {
			seasons = this.seasonsCache.filter(season => {
				if (season.title.toLowerCase().indexOf(busqueda) != -1) {
					return season;
				}
			});
		}
		if (seasons.length > 0) {
			this.performState(seasons, seasons[0].number);
		} else {
			console.log('No results');
		}
	}

	render() {
		return (

				<SearchBar onSearchTermChange={term => this.searchTerm(term)} />
				<SeasonList
					onSeasonSelect={ev => this.selectSeason(ev)}
					selectedSeason={this.state.selectedSeason}
					seasons={this.state.seasons}
				/>
				<SeasonDetail
					onEpisodeSelect={ev => this.selectEpisode(ev)}
					season={this.state.selectedSeason}
					selectedEpisode={this.state.selectedEpisode}
				/>

		);
	}
}

ReactDOM.render(<App />, document.querySelector('.container'));
<|endoftext|>


<commit_before>const invariant = require('invariant');

function generateRouteName(components) {
  return `Nested_${
    components.map(component => component.displayName).join('_')
  }`;
}

const CACHED_STATES = {};

export default function generateRootContainer(React, Relay) {
  return class NestedRootContainer extends React.Component {
    constructor(props, context) {
      super(props, context);
      this.state = this._generateContainer(props);
    }

    componentWillReceiveProps(props) {
      this.setState(this._generateContainer(props));
    }

    render() {
      const { Component, route } = this.state;

      return (
        <Relay.RootContainer
          Component={Component}
          route={{ ...route, params: this.props.params }}/>
      );
    }

    _generateContainer(newProps) {
      const { branch, components } = newProps;
      const routeName = generateRouteName(components);

      if (CACHED_STATES[routeName]) {
        return CACHED_STATES[routeName];
      }

      const queries = {};
      const fragments = {};
      let queryIdx = 0;

      const [, ...elems] = components.map((Component, index) => {
        const fragmentResolvers = [];

        if (Relay.isContainer(Component)) {
          const { route } = branch[index];

          invariant(
            route,
            'Routes with Relay.Containers must include a `route` prop.'
          );

          Object.keys(route.queries).forEach(queryName => {
            const newQueryName = `Nested_${route.name}_${queryName}_${++queryIdx}`;
            queries[newQueryName] =
              (_, ...args) => route.queries[queryName](Component, ...args);

            const fragment = Component.getFragment(queryName)._fragmentGetter;
            fragments[newQueryName] = (...args) => fragment(...args);
            fragmentResolvers.push({
              prop: queryName,
              resolve: function getLocalProp() {
                return this.props[newQueryName];
              },
            });
          });
        }

        return function ComponentGenerator(props) {
          fragmentResolvers.forEach(fragment => {
            props[fragment.prop] = fragment.resolve.call(this);
          });

          return <Component {...props}/>;
        };
      });

      class NestedRenderer extends React.Component {
        render() {
          return elems.reduceRight((children, generateComponent) => {
            return generateComponent.call(this, { children: children });
          }, null);
        }
      }

      const NestedRendererContainer = Relay.createContainer(NestedRenderer, {
        fragments,
      });

      const route = {
        name: routeName,
        queries,
      };

      const state = CACHED_STATES[routeName] = {
        Component: NestedRendererContainer,
        route,
      };
      return state;
    }
  };
}
<commit_msg>Don't need the actual fragment getters<commit_after>const invariant = require('invariant');

function generateRouteName(components) {
  return `Nested_${
    components.map(component => component.displayName).join('_')
  }`;
}

const CACHED_STATES = {};

export default function generateRootContainer(React, Relay) {
  return class NestedRootContainer extends React.Component {
    constructor(props, context) {
      super(props, context);
      this.state = this._generateContainer(props);
    }

    componentWillReceiveProps(props) {
      this.setState(this._generateContainer(props));
    }

    render() {
      const { Component, route } = this.state;

      return (
        <Relay.RootContainer
          Component={Component}
          route={{ ...route, params: this.props.params }}/>
      );
    }

    _generateContainer(newProps) {
      const { branch, components } = newProps;
      const routeName = generateRouteName(components);

      if (CACHED_STATES[routeName]) {
        return CACHED_STATES[routeName];
      }

      const queries = {};
      const fragmentNames = [];
      let queryIdx = 0;

      const [, ...elems] = components.map((Component, index) => {
        const fragmentResolvers = [];

        if (Relay.isContainer(Component)) {
          const { route } = branch[index];

          invariant(
            route,
            'Routes with Relay.Containers must include a `route` prop.'
          );

          Object.keys(route.queries).forEach(queryName => {
            const newQueryName = `Nested_${route.name}_${queryName}_${++queryIdx}`;
            queries[newQueryName] =
              (_, ...args) => route.queries[queryName](Component, ...args);

            fragmentNames.push(newQueryName);
            fragmentResolvers.push({
              prop: queryName,
              resolve: function getLocalProp() {
                return this.props[newQueryName];
              },
            });
          });
        }

        return function ComponentGenerator(props) {
          fragmentResolvers.forEach(fragment => {
            props[fragment.prop] = fragment.resolve.call(this);
          });

          return <Component {...props}/>;
        };
      });

      class NestedRenderer extends React.Component {
        static getFragmentNames() {
          return fragmentNames;
        }

        // Hackishly satisfy isRelayContainer.
        static getQuery() {}
        static getQueryNames() {}

        render() {
          return elems.reduceRight((children, generateComponent) => {
            return generateComponent.call(this, { children: children });
          }, null);
        }
      }

      const route = {
        name: routeName,
        queries,
      };

      const state = CACHED_STATES[routeName] = {
        Component: NestedRenderer,
        route,
      };
      return state;
    }
  };
}
<|endoftext|>


<commit_before>import element from 'virtex-element'
import ace from 'brace'

let editor

const defaultProps = {
  name: 'brace-editor',
  mode: 'javascript',
  theme: 'monokai',
  height: '500px',
  width: '500px',
  value: '',
  fontSize: 12,
  showGutter: true,
  onChange: null,
  onPaste: null,
  onLoad: null,
  maxLines: null,
  readOnly: false,
  highlightActiveLine: true,
  showPrintMargin: true,
  tabSize: 2,
  cursorStart: 1,
  editorProps: {},
  wrapEnabled: false
}

function initEditor (props, ref) {
  const {
      name,
      onBeforeLoad,
      mode,
      theme,
      fontSize,
      value,
      cursorStart,
      showGutter,
      wrapEnabled,
      maxLines,
      readOnly,
      highlightActiveLine,
      tabSize,
      showPrintMargin,
      keyboardHandler,
      onLoad
  } = props

  let element = document.getElementById(name)
  editor = ace.edit(name)
  editor.getSession().setMode(`ace/mode/${mode}`)
  editor.setTheme(`ace/theme/${theme}`)
  editor.setFontSize(fontSize)
  editor.setValue(value, cursorStart)
  editor.renderer.setShowGutter(showGutter)
  editor.getSession().setUseWrapMode(wrapEnabled)
  editor.setOption('maxLines', maxLines)
  editor.setOption('readOnly', readOnly)
  editor.setOption('highlightActiveLine', highlightActiveLine)
  editor.setOption('tabSize', tabSize)
  editor.setShowPrintMargin(showPrintMargin)
  editor.on('change', () => element.click())
  editor.on('focus', () => element.focus())
  editor.on('blur', () => element.blur())
  editor.on('copy', text => {
    let evt = new Event('copy', {'bubbles': true, 'cancelable': false})
    return element.dispatchEvent(evt, text)
  })
  editor.on('paste', () => {
    let evt = new Event('paste', {'bubbles': true, 'cancelable': false})
    return element.dispatchEvent(evt)
  })
}

function afterMount ({props, local, ref}) {
  props = {...defaultProps, ...props}
  setTimeout(initEditor.bind(null, props, ref))
}

function render ({props, state, local, ref}) {
  props = {...defaultProps, ...props}
  var divStyle = {
    width: props.width,
    height: props.height
  }
  var className = props.className
  return (
    <div
      id={props.name}
      className={className}
      style={divStyle}
      ref={ref.as('container')}
      onFocus={onFocus}
      onClick={onChange}
      onCopy={onCopy}
      onBlur={onBlur}
      onPaste={onPaste} />
  )

  function onFocus () {
    if (props.onFocus) {
      return props.onFocus()
    }
  }

  function onBlur () {
    if (props.onBlur) {
      return props.onBlur()
    }
  }

  // this.silent
  function onChange () {
    if (props.onChange) {
      var value = editor.getValue()
      return props.onChange(value)
    }
  }

  function onCopy (text) {
    console.log('copy')
    if (props.onCopy) {
      return props.onCopy(text)
    }
  }

  function onPaste (text) {
    console.log('paste')
    if (props.onPaste) {
      props.onPaste(text)
    }
  }
}

export default {
  render,
  afterMount
}
<commit_msg>add highlighting<commit_after>import element from 'virtex-element'
if (window != undefined) {
  var ace = require('brace')
  return
}

let editor

const defaultProps = {
  name: 'brace-editor',
  mode: 'javascript',
  theme: 'monokai',
  height: '500px',
  width: '500px',
  value: '',
  fontSize: 12,
  showGutter: true,
  onChange: null,
  onPaste: null,
  onLoad: null,
  maxLines: null,
  readOnly: false,
  highlightActiveLine: true,
  showPrintMargin: true,
  tabSize: 2,
  cursorStart: 1,
  editorProps: {},
  wrapEnabled: false
}

function initEditor (props, ref) {
  const {
      name,
      onBeforeLoad,
      mode,
      theme,
      fontSize,
      value,
      cursorStart,
      showGutter,
      wrapEnabled,
      maxLines,
      readOnly,
      highlightActiveLine,
      tabSize,
      showPrintMargin,
      keyboardHandler,
      onLoad
  } = props

  let element = document.getElementById(name)
  editor = ace.edit(name)
  editor.getSession().setMode(`ace/mode/${mode}`)
  editor.setTheme(`ace/theme/${theme}`)
  editor.setFontSize(fontSize)
  editor.setValue(value, cursorStart)
  editor.renderer.setShowGutter(showGutter)
  editor.getSession().setUseWrapMode(wrapEnabled)
  editor.setOption('maxLines', maxLines)
  editor.setOption('readOnly', readOnly)
  editor.setOption('highlightActiveLine', highlightActiveLine)
  editor.setOption('tabSize', tabSize)
  editor.setShowPrintMargin(showPrintMargin)
  editor.on('change', () => element.click())
  editor.on('focus', () => element.focus())
  editor.on('blur', () => element.blur())
  editor.on('copy', text => {
    let evt = new Event('copy', {'bubbles': true, 'cancelable': false})
    return element.dispatchEvent(evt, text)
  })
  editor.on('paste', () => {
    let evt = new Event('paste', {'bubbles': true, 'cancelable': false})
    return element.dispatchEvent(evt)
  })
}

function afterMount ({props, local, ref}) {
  props = {...defaultProps, ...props}
  setTimeout(initEditor.bind(null, props, ref))
}

function render ({props, state, local, ref}) {
  const {activeLine} = props
  props = {...defaultProps, ...props}

  var divStyle = {
    width: props.width,
    height: props.height
  }
  var className = props.className
  return (
    <div
      id={props.name}
      className={className}
      style={divStyle}
      ref={ref.as('container')}
      onFocus={onFocus}
      onClick={onChange}
      onCopy={onCopy}
      onBlur={onBlur}
      onPaste={onPaste} />
  )

  editor.getSession().highlightLines(activeLine - 1)

  function onFocus () {
    if (props.onFocus) {
      return props.onFocus()
    }
  }

  function onBlur () {
    if (props.onBlur) {
      return props.onBlur()
    }
  }

  // this.silent
  function onChange () {
    if (props.onChange) {
      var value = editor.getValue()
      return props.onChange(value)
    }
  }

  function onCopy (text) {
    if (props.onCopy) {
      return props.onCopy(text)
    }
  }

  function onPaste (text) {
    if (props.onPaste) {
      props.onPaste(text)
    }
  }
}

export default {
  render,
  afterMount
}
<|endoftext|>