<commit_before>module.exports = function (_Firebase) {
  Firebase = _Firebase
  return Node
}

var debug = require('debug')('fireflower')
var merge = require('merge').recursive
var events = require('events')
var inherits = require('inherits')
var SimplePeer = require('simple-peer')
var Blacklist = require('./blacklist')
var Firebase = null

var CONNECTION_TIMEOUT = 5000

inherits(Node, events.EventEmitter)

function Node (url, opts) {
  if (!(this instanceof Node)) {
    return new Node(url, opts)
  }

  this.url = url
  this.opts = opts || {}
  this.root = this.opts.root
  this.reportInterval = this.opts.reportInterval
  this.state = 'disconnected'
  this.upstream = null
  this.downstream = {}
  this.blacklist = new Blacklist()

  // firebase refs
  this._ref = new Firebase(this.url)
  this._configRef = this._ref.child('configuration')
  this._requestsRef = this._ref.child('requests')
  this._reports = this._ref.child('reports')

  // set a random id if one was not provided
  this.id = this.opts.id || this._requestsRef.push().key()

  // ensure K
  this.K = this.K || 0

  // bind callbacks
  this._onconfig = this._onconfig.bind(this)
  this._doconnect = this._doconnect.bind(this)
  this._onrequest = this._onrequest.bind(this)
  this._onresponse = this._onresponse.bind(this)
  this._onmaskupdate = this._onmaskupdate.bind(this)
  this._onreportNeeded = this._onreportNeeded.bind(this)

  events.EventEmitter.call(this)
}

Object.defineProperty(Node.prototype, 'K', {
  get: function () {
    return this.opts.K
  },
  set: function (value) {
    if (value === this.opts.K) return
    if (isNaN(value) || ~~value !== value || value < 0) {
      throw new Error('K must be 0 or a positive integer')
    }

    debug(this.id + ' set K to ' + value)
    this.opts.K = value

    var n = 0
    for (var i in this.downstream) {
      if (++n > this.opts.K) {
        this.downstream[i].destroy()
      }
    }
    this._reviewRequests()
  }
})

Node.prototype.connect = function () {
  if (this.state !== 'disconnected') {
    throw new Error('invalid state for connect')
  }

  this._preventReconnect = false

  // reporting?
  if (this.reportInterval && !this._reportInterval) {
    this._reportInterval = setInterval(this._onreportNeeded, this.reportInterval)
    this._onreportNeeded()
  }

  // change state -> requesting
  debug(this.id + ' requesting connection')
  this.state = 'requesting'
  this.emit('statechange')

  // watch config
  if (!this._watchingConfig) {
    this._configRef.on('value', this._onconfig)
    this._watchingConfig = true
  }

  this._doconnect()

  return this
}

Node.prototype.disconnect = function () {
  this.state = 'disconnected'
  this._preventReconnect = true
  this._watchingConfig = false

  // remove some listeners
  this.removeListener('configure', this._doconnect)
  this._configRef.off('value', this._onconfig)
  this._requestsRef.off('child_added', this._onrequest)

  // stop reporting
  clearInterval(this._reportInterval)
  delete this._reportInterval

  // remove outstanding request / response listener
  if (this._requestRef) {
    this._requestRef.remove()
    this._responsesRef.off('child_added', this._onresponse)
  }

  // destroy upstream connection
  if (this.upstream) {
    this.upstream.destroy()
    this.upstream = null
  }

  // destroy downstream connections
  for (var i in this.downstream) {
    this.downstream[i].destroy()
  }
  this.downstream = {}

  return this
}

// private api below

Node.prototype._onconfig = function (snapshot) {
  var data = snapshot.val()
  merge(this.opts, data)
  debug(this.id + ' updated configuration')
  this.emit('configure')
}

Node.prototype._doconnect = function () {
  var self = this

  if (this.root) {

    // emit connect but in nextTick
    setTimeout(function () {
      // change state -> connected
      debug(self.id + ' connected as root')
      self.state = 'connected'
      self.emit('statechange')

      // emit connect
      self.emit('connect')

      // start responding to requests
      self._requestsRef.on('child_added', self._onrequest)
    })

    return
  }

  // we are not root so publish a connection request
  this._dorequest()
}

Node.prototype._dorequest = function () {
  var self = this

  this._requestRef = this._requestsRef.push({
    id: this.id,
    removal_flag: {
      removed: false
    }
  })

  // make sure no one removes our request until we're connected
  this._requestRef.child('removal_flag').once('child_removed', function () {
    if (self.state === 'requesting') {
      self._responsesRef.off('child_added', self._onresponse)
      self._dorequest()
    }
  })

  // listen for a response
  this._responsesRef = this._requestRef.child('responses')
  this._responsesRef.once('child_added', this._onresponse)
}

Node.prototype._onrequest = function (snapshot) {
  if (this.state !== 'connected') {
    return // can't respond to requests unless we are connected
  }

  if (Object.keys(this.downstream).length >= this.opts.K) {
    return // can't respond to requests if we've hit K peers
  }

  var self = this
  var requestRef = snapshot.ref()
  var requestId = snapshot.key()
  var request = snapshot.val()
  var peerId = request.id

  // responders may accidentally recreate requests
  // these won't have an id though and can be removed
  if (!peerId) {
    requestRef.remove()
    return
  }

  // prevent circles
  if (peerId === this._mask) {
    return
  }

  // since we review requests after every connect / disconnect
  // it is possible to see a peer we know about
  var knownPeer = this.downstream[peerId]
  if (knownPeer) {
    if (knownPeer.requestId !== requestId) {
      // if request ids don't match, the peer must have disconnected without us noticing
      knownPeer.destroy()
    } else {
      return
    }
  }

  debug(this.id + ' saw request by ' + peerId)

  var responseRef = requestRef.child('responses/' + this.id)

  // initiate peer connection
  // we have to do this before actually writing our response because
  // firebase can trigger events in the same tick which could circumvent
  // the K check at the top of this method
  this._connectToPeer(true, peerId, requestId, responseRef)

  // publish response
  responseRef.update({
    id: this.id
  })

  // watch for request withdrawal
  responseRef.once('child_removed', function () {
    var peer = self.downstream[peerId]
    if (peer && !peer.didConnect) {
      peer.requestWithdrawn = true
      peer.destroy()
    }
  })
}

Node.prototype._onresponse = function (snapshot) {
  if (this.state !== 'requesting') {
    return
  }

  var responseRef = snapshot.ref()
  var response = snapshot.val()
  var peerId = response.id

  if (!peerId || this.blacklist.contains(peerId)) {
    return
  }

  // change state -> connecting (this prevents accepting multiple responses)
  debug(this.id + ' got response from ' + peerId)
  this.state = 'connecting'
  this.emit('statechange')

  // stop watching for request removal
  this._requestRef.child('removal_flag').off()

  // attempt a connection
  this._connectToPeer(false, peerId, null, responseRef)
}

Node.prototype._connectToPeer = function (initiator, peerId, requestId, responseRef) {
  var self = this
  var localSignals = responseRef.child(initiator ? 'responderSignals' : 'requesterSignals')
  var remoteSignals = responseRef.child(initiator ? 'requesterSignals' : 'responderSignals')

  var peer = new SimplePeer({
    initiator: initiator,
    config: this.opts.peerConfig,
    channelConfig: this.opts.channelConfig,
    channelName: 'default'
  })

  peer.id = peerId

  if (initiator) {
    this.downstream[peer.id] = peer
    peer.notifications = peer._pc.createDataChannel('notifications')
    peer.requestId = requestId
  } else {
    var oldondatachannel = peer._pc.ondatachannel
    peer._pc.ondatachannel = function (evt) {
      if (evt.channel.label === 'default') {
        oldondatachannel(evt)
      } else if (evt.channel.label === 'notifications') {
        peer.notifications = evt.channel
        peer.notifications.onmessage = self._onmaskupdate
      }
    }
  }

  peer.on('connect', this._onpeerConnect.bind(this, peer, remoteSignals))
  peer.on('close', this._onpeerDisconnect.bind(this, peer, remoteSignals))

  peer.on('error', function (err) {
    debug(this.id + ' saw peer connection error', err)
  })

  peer.on('signal', function (signal) {
    if (initiator && self.state !== 'connected') return
    signal = JSON.parse(JSON.stringify(signal))
    localSignals.push(signal)
  })

  remoteSignals.on('child_added', function (snapshot) {
    if (initiator && self.state !== 'connected') return
    var signal = snapshot.val()
    peer.signal(signal)
  })

  // timeout connections
  setTimeout(function () {
    if (!peer.didConnect) {
      peer.destroy()
    }
  }, CONNECTION_TIMEOUT)
}

Node.prototype._onpeerConnect = function (peer, remoteSignals) {
  peer.didConnect = true
  peer.removeAllListeners('connect')
  peer.removeAllListeners('signal')
  remoteSignals.off()

  if (this.downstream[peer.id]) {
    this._ondownstreamConnect(peer)
  } else {
    this._onupstreamConnect(peer)
  }
}

Node.prototype._onpeerDisconnect = function (peer, remoteSignals) {
  peer.removeAllListeners()
  remoteSignals.off()

  if (this.downstream[peer.id]) {
    this._ondownstreamDisconnect(peer)
  } else {
    this._onupstreamDisconnect(peer)
  }
}

Node.prototype._onupstreamConnect = function (peer) {
  // remove our request
  this._requestRef.remove()

  // already got connected by someone else
  if (this.state === 'connected') {
    debug(this.id + ' rejected upstream connection by ' + peer.id)
    peer.destroy()
    return
  }

  this.upstream = peer

  // change state -> connected
  debug(this.id + ' established upstream connection to ' + peer.id)
  this.state = 'connected'
  this.emit('statechange')
  this.emit('connect', peer)

  // begin responding to requests
  this._reviewRequests()
}

Node.prototype._onupstreamDisconnect = function (peer) {
  // stop responding to new requests
  this._requestsRef.off('child_added', this._onrequest)

  // remove request
  this._requestRef.remove()

  this.upstream = null

  // change state -> disconnected
  if (this.state !== 'disconnected') {
    debug(this.id + ' lost upstream connection to ' + peer.id)
  }

  this.state = 'disconnected'
  this.emit('statechange')

  // emit disconnect if we were connected
  if (peer.didConnect) {
    this.emit('disconnect', peer)
  }

  // attempt to reconnect if we were not disconnected intentionally
  if (!this._preventReconnect) {

    // mask off our descendants
    this._onmaskupdate({ data: this.id })

    // give our mask update a tiny head start
    var self = this
    setTimeout(function () {
      if (!self._preventReconnect) {
        self.connect()
      }
    }, 100)
  }
}

Node.prototype._ondownstreamConnect = function (peer) {
  // emit peerconnect
  debug(this.id + ' established downstream connection to ' + peer.id)
  this.emit('peerconnect', peer)

  // stop responding to requests if peers > K
  if (Object.keys(this.downstream).length >= this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
  }

  // make sure downstream has the most up to date mask
  if (this._mask && peer.notifications.readyState === 'open') {
    peer.notifications.send(this._mask)
  }
}

Node.prototype._ondownstreamDisconnect = function (peer) {
  // remove from lookup
  delete this.downstream[peer.id]

  // emit events and potentially remove stale requests
  if (peer.didConnect) {
    if (this.state !== 'disconnected') {
      debug(this.id + ' lost downstream connection to ' + peer.id)
    }

    this.emit('peerdisconnect', peer)
  } else {
    if (peer.requestWithdrawn) {
      debug(this.id + ' saw request withdrawn by ' + peer.id)
    } else {
      debug(this.id + ' removing stale request by ' + peer.id)
      this._requestsRef.child(peer.requestId).remove()
    }
  }

  this._reviewRequests()
}

Node.prototype._onmaskupdate = function (evt) {
  this._mask = evt.data

  debug(this.id + ' set mask to ' + this._mask)

  // oops we made a circle, fix that
  if (this.downstream[this._mask]) {
    debug(this.id + ' destroying accidental circle back to ' + this._mask)
    this.downstream[this._mask].destroy()
  }

  for (var i in this.downstream) {
    var notifications = this.downstream[i].notifications
    if (notifications.readyState === 'open') {
      notifications.send(this._mask)
    }
  }
}

Node.prototype._onreportNeeded = function () {
  var report = {
    state: this.state,
    upstream: this.upstream ? this.upstream.id : null,
    timestamp: Firebase.ServerValue.TIMESTAMP
  }

  if (this.root) {
    report.root = true
  }

  this._reports
    .child(this.id)
    .update(report)
}

Node.prototype._reviewRequests = function () {
  if (this.state === 'connected' && Object.keys(this.downstream).length < this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
    this._requestsRef.on('child_added', this._onrequest)
  }
}
<commit_msg>always wrap send in try-catch<commit_after>module.exports = function (_Firebase) {
  Firebase = _Firebase
  return Node
}

var debug = require('debug')('fireflower')
var merge = require('merge').recursive
var events = require('events')
var inherits = require('inherits')
var SimplePeer = require('simple-peer')
var Blacklist = require('./blacklist')
var Firebase = null

var CONNECTION_TIMEOUT = 5000

inherits(Node, events.EventEmitter)

function Node (url, opts) {
  if (!(this instanceof Node)) {
    return new Node(url, opts)
  }

  this.url = url
  this.opts = opts || {}
  this.root = this.opts.root
  this.reportInterval = this.opts.reportInterval
  this.state = 'disconnected'
  this.upstream = null
  this.downstream = {}
  this.blacklist = new Blacklist()

  // firebase refs
  this._ref = new Firebase(this.url)
  this._configRef = this._ref.child('configuration')
  this._requestsRef = this._ref.child('requests')
  this._reports = this._ref.child('reports')

  // set a random id if one was not provided
  this.id = this.opts.id || this._requestsRef.push().key()

  // ensure K
  this.K = this.K || 0

  // bind callbacks
  this._onconfig = this._onconfig.bind(this)
  this._doconnect = this._doconnect.bind(this)
  this._onrequest = this._onrequest.bind(this)
  this._onresponse = this._onresponse.bind(this)
  this._onmaskupdate = this._onmaskupdate.bind(this)
  this._onreportNeeded = this._onreportNeeded.bind(this)

  events.EventEmitter.call(this)
}

Object.defineProperty(Node.prototype, 'K', {
  get: function () {
    return this.opts.K
  },
  set: function (value) {
    if (value === this.opts.K) return
    if (isNaN(value) || ~~value !== value || value < 0) {
      throw new Error('K must be 0 or a positive integer')
    }

    debug(this.id + ' set K to ' + value)
    this.opts.K = value

    var n = 0
    for (var i in this.downstream) {
      if (++n > this.opts.K) {
        this.downstream[i].destroy()
      }
    }
    this._reviewRequests()
  }
})

Node.prototype.connect = function () {
  if (this.state !== 'disconnected') {
    throw new Error('invalid state for connect')
  }

  this._preventReconnect = false

  // reporting?
  if (this.reportInterval && !this._reportInterval) {
    this._reportInterval = setInterval(this._onreportNeeded, this.reportInterval)
    this._onreportNeeded()
  }

  // change state -> requesting
  debug(this.id + ' requesting connection')
  this.state = 'requesting'
  this.emit('statechange')

  // watch config
  if (!this._watchingConfig) {
    this._configRef.on('value', this._onconfig)
    this._watchingConfig = true
  }

  this._doconnect()

  return this
}

Node.prototype.disconnect = function () {
  this.state = 'disconnected'
  this._preventReconnect = true
  this._watchingConfig = false

  // remove some listeners
  this.removeListener('configure', this._doconnect)
  this._configRef.off('value', this._onconfig)
  this._requestsRef.off('child_added', this._onrequest)

  // stop reporting
  clearInterval(this._reportInterval)
  delete this._reportInterval

  // remove outstanding request / response listener
  if (this._requestRef) {
    this._requestRef.remove()
    this._responsesRef.off('child_added', this._onresponse)
  }

  // destroy upstream connection
  if (this.upstream) {
    this.upstream.destroy()
    this.upstream = null
  }

  // destroy downstream connections
  for (var i in this.downstream) {
    this.downstream[i].destroy()
  }
  this.downstream = {}

  return this
}

// private api below

Node.prototype._onconfig = function (snapshot) {
  var data = snapshot.val()
  merge(this.opts, data)
  debug(this.id + ' updated configuration')
  this.emit('configure')
}

Node.prototype._doconnect = function () {
  var self = this

  if (this.root) {

    // emit connect but in nextTick
    setTimeout(function () {
      // change state -> connected
      debug(self.id + ' connected as root')
      self.state = 'connected'
      self.emit('statechange')

      // emit connect
      self.emit('connect')

      // start responding to requests
      self._requestsRef.on('child_added', self._onrequest)
    })

    return
  }

  // we are not root so publish a connection request
  this._dorequest()
}

Node.prototype._dorequest = function () {
  var self = this

  this._requestRef = this._requestsRef.push({
    id: this.id,
    removal_flag: {
      removed: false
    }
  })

  // make sure no one removes our request until we're connected
  this._requestRef.child('removal_flag').once('child_removed', function () {
    if (self.state === 'requesting') {
      self._responsesRef.off('child_added', self._onresponse)
      self._dorequest()
    }
  })

  // listen for a response
  this._responsesRef = this._requestRef.child('responses')
  this._responsesRef.once('child_added', this._onresponse)
}

Node.prototype._onrequest = function (snapshot) {
  if (this.state !== 'connected') {
    return // can't respond to requests unless we are connected
  }

  if (Object.keys(this.downstream).length >= this.opts.K) {
    return // can't respond to requests if we've hit K peers
  }

  var self = this
  var requestRef = snapshot.ref()
  var requestId = snapshot.key()
  var request = snapshot.val()
  var peerId = request.id

  // responders may accidentally recreate requests
  // these won't have an id though and can be removed
  if (!peerId) {
    requestRef.remove()
    return
  }

  // prevent circles
  if (peerId === this._mask) {
    return
  }

  // since we review requests after every connect / disconnect
  // it is possible to see a peer we know about
  var knownPeer = this.downstream[peerId]
  if (knownPeer) {
    if (knownPeer.requestId !== requestId) {
      // if request ids don't match, the peer must have disconnected without us noticing
      knownPeer.destroy()
    } else {
      return
    }
  }

  debug(this.id + ' saw request by ' + peerId)

  var responseRef = requestRef.child('responses/' + this.id)

  // initiate peer connection
  // we have to do this before actually writing our response because
  // firebase can trigger events in the same tick which could circumvent
  // the K check at the top of this method
  this._connectToPeer(true, peerId, requestId, responseRef)

  // publish response
  responseRef.update({
    id: this.id
  })

  // watch for request withdrawal
  responseRef.once('child_removed', function () {
    var peer = self.downstream[peerId]
    if (peer && !peer.didConnect) {
      peer.requestWithdrawn = true
      peer.destroy()
    }
  })
}

Node.prototype._onresponse = function (snapshot) {
  if (this.state !== 'requesting') {
    return
  }

  var responseRef = snapshot.ref()
  var response = snapshot.val()
  var peerId = response.id

  if (!peerId || this.blacklist.contains(peerId)) {
    return
  }

  // change state -> connecting (this prevents accepting multiple responses)
  debug(this.id + ' got response from ' + peerId)
  this.state = 'connecting'
  this.emit('statechange')

  // stop watching for request removal
  this._requestRef.child('removal_flag').off()

  // attempt a connection
  this._connectToPeer(false, peerId, null, responseRef)
}

Node.prototype._connectToPeer = function (initiator, peerId, requestId, responseRef) {
  var self = this
  var localSignals = responseRef.child(initiator ? 'responderSignals' : 'requesterSignals')
  var remoteSignals = responseRef.child(initiator ? 'requesterSignals' : 'responderSignals')

  var peer = new SimplePeer({
    initiator: initiator,
    config: this.opts.peerConfig,
    channelConfig: this.opts.channelConfig,
    channelName: 'default'
  })

  peer.id = peerId

  if (initiator) {
    this.downstream[peer.id] = peer
    peer.notifications = peer._pc.createDataChannel('notifications')
    peer.requestId = requestId
  } else {
    var oldondatachannel = peer._pc.ondatachannel
    peer._pc.ondatachannel = function (evt) {
      if (evt.channel.label === 'default') {
        oldondatachannel(evt)
      } else if (evt.channel.label === 'notifications') {
        peer.notifications = evt.channel
        peer.notifications.onmessage = self._onmaskupdate
      }
    }
  }

  peer.on('connect', this._onpeerConnect.bind(this, peer, remoteSignals))
  peer.on('close', this._onpeerDisconnect.bind(this, peer, remoteSignals))

  peer.on('error', function (err) {
    debug(this.id + ' saw peer connection error', err)
  })

  peer.on('signal', function (signal) {
    if (initiator && self.state !== 'connected') return
    signal = JSON.parse(JSON.stringify(signal))
    localSignals.push(signal)
  })

  remoteSignals.on('child_added', function (snapshot) {
    if (initiator && self.state !== 'connected') return
    var signal = snapshot.val()
    peer.signal(signal)
  })

  // timeout connections
  setTimeout(function () {
    if (!peer.didConnect) {
      peer.destroy()
    }
  }, CONNECTION_TIMEOUT)
}

Node.prototype._onpeerConnect = function (peer, remoteSignals) {
  peer.didConnect = true
  peer.removeAllListeners('connect')
  peer.removeAllListeners('signal')
  remoteSignals.off()

  if (this.downstream[peer.id]) {
    this._ondownstreamConnect(peer)
  } else {
    this._onupstreamConnect(peer)
  }
}

Node.prototype._onpeerDisconnect = function (peer, remoteSignals) {
  peer.removeAllListeners()
  remoteSignals.off()

  if (this.downstream[peer.id]) {
    this._ondownstreamDisconnect(peer)
  } else {
    this._onupstreamDisconnect(peer)
  }
}

Node.prototype._onupstreamConnect = function (peer) {
  // remove our request
  this._requestRef.remove()

  // already got connected by someone else
  if (this.state === 'connected') {
    debug(this.id + ' rejected upstream connection by ' + peer.id)
    peer.destroy()
    return
  }

  this.upstream = peer

  // change state -> connected
  debug(this.id + ' established upstream connection to ' + peer.id)
  this.state = 'connected'
  this.emit('statechange')
  this.emit('connect', peer)

  // begin responding to requests
  this._reviewRequests()
}

Node.prototype._onupstreamDisconnect = function (peer) {
  // stop responding to new requests
  this._requestsRef.off('child_added', this._onrequest)

  // remove request
  this._requestRef.remove()

  this.upstream = null

  // change state -> disconnected
  if (this.state !== 'disconnected') {
    debug(this.id + ' lost upstream connection to ' + peer.id)
  }

  this.state = 'disconnected'
  this.emit('statechange')

  // emit disconnect if we were connected
  if (peer.didConnect) {
    this.emit('disconnect', peer)
  }

  // attempt to reconnect if we were not disconnected intentionally
  if (!this._preventReconnect) {

    // mask off our descendants
    this._onmaskupdate({ data: this.id })

    // give our mask update a tiny head start
    var self = this
    setTimeout(function () {
      if (!self._preventReconnect) {
        self.connect()
      }
    }, 100)
  }
}

Node.prototype._ondownstreamConnect = function (peer) {
  // emit peerconnect
  debug(this.id + ' established downstream connection to ' + peer.id)
  this.emit('peerconnect', peer)

  // stop responding to requests if peers > K
  if (Object.keys(this.downstream).length >= this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
  }

  // make sure downstream has the most up to date mask
  if (this._mask) {
    try {
      peer.notifications.send(this._mask)
    } catch (err) {
      console.warn(err)
    }
  }
}

Node.prototype._ondownstreamDisconnect = function (peer) {
  // remove from lookup
  delete this.downstream[peer.id]

  // emit events and potentially remove stale requests
  if (peer.didConnect) {
    if (this.state !== 'disconnected') {
      debug(this.id + ' lost downstream connection to ' + peer.id)
    }

    this.emit('peerdisconnect', peer)
  } else {
    if (peer.requestWithdrawn) {
      debug(this.id + ' saw request withdrawn by ' + peer.id)
    } else {
      debug(this.id + ' removing stale request by ' + peer.id)
      this._requestsRef.child(peer.requestId).remove()
    }
  }

  this._reviewRequests()
}

Node.prototype._onmaskupdate = function (evt) {
  this._mask = evt.data

  debug(this.id + ' set mask to ' + this._mask)

  // oops we made a circle, fix that
  if (this.downstream[this._mask]) {
    debug(this.id + ' destroying accidental circle back to ' + this._mask)
    this.downstream[this._mask].destroy()
  }

  for (var i in this.downstream) {
    var notifications = this.downstream[i].notifications
    try {
      notifications.send(this._mask)
    } catch (err) {
      console.warn(err)
    }
  }
}

Node.prototype._onreportNeeded = function () {
  var report = {
    state: this.state,
    upstream: this.upstream ? this.upstream.id : null,
    timestamp: Firebase.ServerValue.TIMESTAMP
  }

  if (this.root) {
    report.root = true
  }

  this._reports
    .child(this.id)
    .update(report)
}

Node.prototype._reviewRequests = function () {
  if (this.state === 'connected' && Object.keys(this.downstream).length < this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
    this._requestsRef.on('child_added', this._onrequest)
  }
}
<|endoftext|>


<commit_before>    //
    //  Lux Codemirror module
    //  ============================
    //  Directive allows to add CodeMirror to the textarea elements
    //
    //  Html:
    //
    //      <textarea lux-codemirror="{'mode': 'html'}">

    //
    //
    //  Supported modes:
    //
    //      html, markdown, json
    //
    //
    //  ============================
    //
    angular.module('lux.codemirror', ['lux.services'])
        //
        .constant('luxCodemirrorDefaults', {
            lineWrapping : true,
            lineNumbers: true,
            mode: "markdown",
            theme: lux.context.CODEMIRROR_THEME || "monokai",
            reindentOnLoad: true,
        })
        //
        .directive('luxCodemirror', ['$lux', 'luxCodemirrorDefaults', function ($lux, luxCodemirrorDefaults) {
            //
            // Initialize codemirror, load css.
            function initCodemirror() {
                loadCss('https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/codemirror.css');
                loadCss('https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/theme/' + luxCodemirrorDefaults.theme + '.css');
            }
            //
            // Creates a new instance of the editor
            function newEditor(element, options) {
                var cm;

                if (element[0].tagName === 'TEXTAREA') {
                    cm = window.CodeMirror.fromTextArea(element[0], options);
                } else {
                    element.html('');
                    cm = new window.CodeMirror(function(cm_el) {
                        element.append(cm_el);
                    }, options);
                }

                return cm;
            }
            //
            // Allows play with ng-model
            function ngModelLink(cm, ngModel, scope) {
                if (!ngModel) return;

                // CodeMirror expects a string, so make sure it gets one.
                // This does not change the model.
                ngModel.$formatters.push(function(value) {
                    if (angular.isUndefined(value) || value === null)
                        return '';
                    else if (angular.isObject(value) || angular.isArray(value))
                        throw new Error('codemirror cannot use an object or an array as a model');
                    return value;
                });

                // Override the ngModelController $render method, which is what gets called when the model is updated.
                // This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
                ngModel.$render = function() {
                    var safeViewValue = ngModel.$viewValue || '';
                    cm.setValue(safeViewValue);
                };

                // Keep the ngModel in sync with changes from CodeMirror
                cm.on('change', function(instance) {
                    var newValue = instance.getValue();
                    if (newValue !== ngModel.$viewValue) {
                        scope.$evalAsync(function() {
                            ngModel.$setViewValue(newValue);
                        });
                    }
                });
            }
            //
            // Set specified mode
            function setMode(options) {
                switch (options.mode) {
                    case 'json':
                        options.mode = 'javascript';
                        break;
                    case 'html':
                        options.mode = 'htmlmixed';
                        break;
                    default:
                        options.mode = luxCodemirrorDefaults.mode;
                        break;
                }
                return options;
            }
            //
            return {
                restrict: 'EA',
                require: '?ngModel',
                //
                compile: function () {
                    // Require CodeMirror
                    if (angular.isUndefined(window.CodeMirror)) {
                        throw new Error('lux-codemirror needs CodeMirror to work!');
                    }

                    // Initialize codemirror
                    initCodemirror();

                    return {
                        post: function(scope, element, attrs, ngModel) {

                            var options = angular.extend(
                                { value: element.text() },
                                luxCodemirrorDefaults || {},
                                scope.$eval(attrs.luxCodemirror) || {}
                            );

                            options = setMode(options);

                            var cm = newEditor(element, options);

                            ngModelLink(cm, ngModel, scope);

                            // Allow access to the CodeMirror instance through a broadcasted event
                            // eg: $broadcast('CodeMirror', function(cm){...});
                            scope.$on('CodeMirror', function(event, callback) {
                                if (angular.isFunction(callback))
                                    callback(cm);
                                else
                                    throw new Error('the CodeMirror event requires a callback function');
                            });

                            scope.$on('$viewContentLoaded', function () {
                                cm.refresh();
                            });
                        }
                    };
                }
            };
        }]);
<commit_msg>require added inside codemirror directive<commit_after>    //
    //  Lux Codemirror module
    //  ============================
    //  Directive allows to add CodeMirror to the textarea elements
    //
    //  Html:
    //
    //      <textarea lux-codemirror="{'mode': 'html'}">

    //
    //
    //  Supported modes:
    //
    //      html, markdown, json
    //
    //
    //  ============================
    //
    angular.module('lux.codemirror', ['lux.services'])
        //
        .constant('luxCodemirrorDefaults', {
            lineWrapping : true,
            lineNumbers: true,
            mode: "markdown",
            theme: lux.context.CODEMIRROR_THEME || "monokai",
            reindentOnLoad: true,
            htmlModes: ['javascript', 'xml', 'css', 'htmlmixed'],
        })
        //
        .directive('luxCodemirror', ['$lux', 'luxCodemirrorDefaults', function ($lux, luxCodemirrorDefaults) {
            //
            // Initialize codemirror, load css.
            function loadCSS() {
                loadCss('https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/codemirror.css');
                loadCss('https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/theme/' + luxCodemirrorDefaults.theme + '.css');
            }
            //
            // Creates a new instance of the editor
            function newEditor(element, options) {
                var cm;

                if (element[0].tagName === 'TEXTAREA') {
                    cm = window.CodeMirror.fromTextArea(element[0], options);
                } else {
                    element.html('');
                    cm = new window.CodeMirror(function(cm_el) {
                        element.append(cm_el);
                    }, options);
                }

                return cm;
            }
            //
            // Allows play with ng-model
            function ngModelLink(cm, ngModel, scope) {
                if (!ngModel) return;

                // CodeMirror expects a string, so make sure it gets one.
                // This does not change the model.
                ngModel.$formatters.push(function(value) {
                    if (angular.isUndefined(value) || value === null)
                        return '';
                    else if (angular.isObject(value) || angular.isArray(value))
                        throw new Error('codemirror cannot use an object or an array as a model');
                    return value;
                });

                // Override the ngModelController $render method, which is what gets called when the model is updated.
                // This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
                ngModel.$render = function() {
                    var safeViewValue = ngModel.$viewValue || '';
                    cm.setValue(safeViewValue);
                };

                // Keep the ngModel in sync with changes from CodeMirror
                cm.on('change', function(instance) {
                    var newValue = instance.getValue();
                    if (newValue !== ngModel.$viewValue) {
                        scope.$evalAsync(function() {
                            ngModel.$setViewValue(newValue);
                        });
                    }
                });
            }
            //
            // Set specified mode
            function setMode(options) {
                switch (options.mode) {
                    case 'json':
                        options.mode = 'javascript';
                        break;
                    case 'html':
                        options.mode = 'htmlmixed';
                        break;
                    default:
                        options.mode = luxCodemirrorDefaults.mode;
                        break;
                }
                return options;
            }
            //
            // Returns suffix of the js module name to load depending on the editor mode
            function getJSModuleSuffix(modeName) {
                if (luxCodemirrorDefaults.htmlModes.indexOf(modeName) >= 0)
                    return 'htmlmixed';
                else
                    return 'markdown';
            }
            //
            return {
                restrict: 'EA',
                require: '?ngModel',
                //
                compile: function () {
                    loadCSS();
                    return {
                        post: function(scope, element, attrs, ngModel) {
                            var jsModuleSuffix,
                                options = angular.extend(
                                    { value: element.text() },
                                    luxCodemirrorDefaults || {},
                                    scope.$eval(attrs.luxCodemirror) || {}
                                );

                            options = setMode(options);
                            jsModuleSuffix = getJSModuleSuffix(options.mode);

                            require(['codemirror-' + jsModuleSuffix], function() {
                                // Require CodeMirror
                                if (angular.isUndefined(window.CodeMirror)) {
                                    throw new Error('lux-codemirror needs CodeMirror to work!');
                                }

                                var cm = newEditor(element, options);

                                ngModelLink(cm, ngModel, scope);

                                // Allow access to the CodeMirror instance through a broadcasted event
                                // eg: $broadcast('CodeMirror', function(cm){...});
                                scope.$on('CodeMirror', function(event, callback) {
                                    if (angular.isFunction(callback))
                                        callback(cm);
                                    else
                                        throw new Error('the CodeMirror event requires a callback function');
                                });

                                scope.$on('$viewContentLoaded', function () {
                                    cm.refresh();
                                });
                            });
                        }
                    };
                }
            };
        }]);
<|endoftext|>


<commit_before><commit_msg>Can't hurt<commit_after><|endoftext|>


<commit_before>var s2 = require('s2'),
fc = require('turf-featurecollection'),
geocolor = require('geocolor')
center = require('turf-center')
linestring = require('turf-linestring')

var ll = new s2.S2LatLngRect(new s2.S2LatLng(10, 10), new s2.S2LatLng(11, 11));

var llcover = s2.getCover(ll, {
    max_cells: 30,
    min:12,
    max:2
});

var cells = fc([])
cells.features = llcover.map(function(cover){
	return {
		type: 'Feature',
		geometry: cover.toGeoJSON(),
		properties: {
			id: cover.id().toToken()
		}
	}
})

cells.features.sort(function(a,b){
	return a>b
})

for(var i=0;i<cells.features.length;i++){
	cells.features[i].properties.rank = i+1
}

var coloredCells = geocolor.jenks(cells, 'rank', 100, ['white', 'blue'])

var hilburt = fc([linestring([])])
coloredCells.features.forEach(function(cell){
	hilburt.features[0].geometry.coordinates.push(center(cell).geometry.coordinates)
})

coloredCells.features = coloredCells.features.concat(hilburt.features)

console.log(JSON.stringify(coloredCells))<commit_msg>added comments to code<commit_after>var s2 = require('s2'),
	fc = require('turf-featurecollection'),
	geocolor = require('geocolor')
	center = require('turf-center')
	linestring = require('turf-linestring')

//create a bounding box to be covered
var ll = new s2.S2LatLngRect(new s2.S2LatLng(10, 10), new s2.S2LatLng(11, 11));

//compute cover
var llcover = s2.getCover(ll, {
    max_cells: 30,
    min:12,
    max:2
});

//convert cover to geojson
var cells = fc([])
cells.features = llcover.map(function(cover){
	return {
		type: 'Feature',
		geometry: cover.toGeoJSON(),
		properties: {
			id: cover.id().toToken()
		}
	}
})
//sort cells by alphanumeric token
cells.features.sort(function(a,b){
	return a>b
})
//rank cells
for(var i=0;i<cells.features.length;i++){
	cells.features[i].properties.rank = i+1
}
//color cells
var coloredCells = geocolor.jenks(cells, 'rank', 100, ['white', 'blue'])

//calculate hilbut curve by creating a linestring with vertices from the center of each cell in order
var hilburt = fc([linestring([])])
coloredCells.features.forEach(function(cell){
	hilburt.features[0].geometry.coordinates.push(center(cell).geometry.coordinates)
})

//concat the cells with the curve
coloredCells.features = coloredCells.features.concat(hilburt.features)

console.log(JSON.stringify(coloredCells))<|endoftext|>


<commit_before><commit_msg>Game status check logic<commit_after><|endoftext|>


<commit_before>var _ = require('lodash');
var request = require('request');

var ENDPOINT = 'http://api.captainteemo.com';
var PLATFORMS = [
  {'short': 'na', 'full': 'North_America'},
  {'short': 'br', 'full': 'Brasil'},
  {'short': 'ru', 'full': 'Russia'},
  {'short': 'euw', 'full': 'Europe_West'},
  {'short': 'eun', 'full': 'Europe_East'},
  {'short': 'tr', 'full': 'Turkey'},
  {'short': 'las', 'full': 'Latin_America_South'},
  {'short': 'lan', 'full': 'Latin_America_North'}
];

/**
 * Create an error whose message begins with 'node-resteemo - '.
 *
 * @private
 * @param {String} value The error message.
 * @return {Error}
 */
function brandError(value) {
  return new Error('node-resteemo - ' + value);
}

/**
 * Converts a full platform string to its shorthand equivalent.
 *
 * @private
 * @param {String} platform The platform to shorten.
 * @return {String} The shorthand equivalent of `platform` if one exists, else
 *   `null`.
 */
function normalizePlatform(platform) {
  var matchFromFullPlatform = _.find(PLATFORMS, {'full': platform});
  if (!_.isUndefined(matchFromFullPlatform)) {
    return matchFromFullPlatform['short'];
  }

  var matchFromShortPlatform = _.find(PLATFORMS, {'short': platform});
  if (!_.isUndefined(matchFromShortPlatform)) {
    return platform;
  }

  return null;
}

/**
 * Parse JSON data from a request.
 *
 * @private
 * @param {Object} req The request instance to handle.
 * @param {Function} callback The callback, which is given two arguments:
 *   `(err, response)`, where `response` is a JSON Object from `req`.
 */
function responseHandler(req, callback) {
  req.on('response', function(res) {
    var response = '';
    res.setEncoding('utf8');
    res.on('data', function(chunk) {
      response += chunk;
    });
    res.on('end', function() {
      try {
        response = JSON.parse(response);
      }
      catch(e) {
        var error = brandError('invalid json response');
        return callback(error);
      }

      if (!response.success) {
        var error = brandError('api failed');
        return callback(error);
      }

      var hasSecondSuccess;
      switch (false) {
        case !(_.contains(res.request.uri.path, 'recent_games')):
          hasSecondSuccess = true;
          break;
        case !(_.contains(res.request.uri.path, 'runes')):
          hasSecondSuccess = true;
          break;
        case !(_.contains(res.request.uri.path, 'mastery')):
          hasSecondSuccess = true;
          break;
        case !(_.contains(res.request.uri.path, 'leagues')):
          hasSecondSuccess = true;
          break;
        default:
          hasSecondSuccess = false;
      }

      if (hasSecondSuccess) {
        if (!response.data._success) {
          var error = brandError('api failed at second success check');
          return callback(error);
        }
      }

      callback(null, response);
    });
  });
}

/**
 * Establishes the user agent of all API calls.
 *
 * @param {String} refererString The referer string to use in API calls.
 * @return {Object}
 */
module.exports = function(refererString) {
  if (!_.isString(refererString)) {
    var error = brandError('`refererString` not defined');
    throw error;
  }

  /**
   * Contructs the headers and options for the API request.
   *
   * @private
   * @param {String} method The HTTP verb.
   * @param {String} path The path to query from the API endpoint.
   * @param {Function} callback The callback, which is passed to
   *   `responseHandler`.
   */
  function prepareRequest(method, path, callback) {
    if (!_.isFunction(callback)) {
      var error = brandError('missing callback');
      throw error;
    }

    var headers = {
      'Accept'    : 'application/json',
      'User-Agent': refererString
    };

    var requestOptions = {
      method : method,
      uri    : ENDPOINT + path,
      headers: headers
    };

    var req = request(requestOptions);
    responseHandler(req, callback);
    req.end();
  }

  /**
   * Begins the preparation for a GET request.
   *
   * @private
   * @param {String} path The path to query from the API endpoint.
   * @param {Function} callback The callback, which is passed to
   *   `prepareRequest`.
   */
  function get(path, callback) {
    prepareRequest('GET', path, callback);
  }

  /**
   * Sets up a player request.
   *
   * @private
   * @param {String} platform
   * @param {String} summoner
   * @param {String} path
   * @param {Function} callback
   */
  function playerRequest(platform, summoner, path, callback) {
    var shortPlatform = normalizePlatform(platform);
    if (_.isNull(shortPlatform)) {
      var error = brandError('invalid platform');
      return callback(error);
    }

    if (_.isNull(path)) {
      path = '';
    } else {
      path = '/' + path;
    }

    get('/player/' + shortPlatform + '/' + summoner + path, callback);
  }

  var teemo = {};

  /**
   * Returns primarily ID-based data for String `summoner` on String `platform`.
   * Account and summoner IDs are not unique across multiple platforms.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, profile)` where
   *   `profile` is the API response as an Object.
   */
  teemo.player = function(platform, summoner, callback) {
    playerRequest(platform, summoner, null, callback);
  };

  /**
   * Returns last 10 matches (order is random) for String `summoner` on String
   * `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, games)` where `games`
   *   is the API response as an Object.
   */
  teemo.player.recentGames = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'recent_games', callback);
  };

  /**
   * Returns lifetime influence point gains for String `summoner` on String
   * `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, points)` where `points`
   *   is the API response as an Object.
   */
  teemo.player.influencePoints = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'influence_points', callback);
  };

  /**
   * Returns runepages for String `summoner` on String `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, runes)` where `runes`
   *   is the API response as an Object.
   */
  teemo.player.runes = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'runes', callback);
  };

  /**
   * Returns mastery pages for String `summoner` on String `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, pages)` where `pages`
   *   is the API response as an Object.
   */
  teemo.player.mastery = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'mastery', callback);
  };

  /**
   * Returns Season 3 Leagues info.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, leagues)` where
   *   `leagues` is the API response as an Object.
   */
  teemo.player.leagues = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'leagues', callback);
  };

  /**
   * Returns honor.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, honor)` where `honor`
   *   is the API response as an Object.
   */
  teemo.player.honor = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'honor', callback);
  };

  return teemo;
};<commit_msg>Check if platform is short first<commit_after>var _ = require('lodash');
var request = require('request');

var ENDPOINT = 'http://api.captainteemo.com';
var PLATFORMS = [
  {'short': 'na', 'full': 'North_America'},
  {'short': 'br', 'full': 'Brasil'},
  {'short': 'ru', 'full': 'Russia'},
  {'short': 'euw', 'full': 'Europe_West'},
  {'short': 'eun', 'full': 'Europe_East'},
  {'short': 'tr', 'full': 'Turkey'},
  {'short': 'las', 'full': 'Latin_America_South'},
  {'short': 'lan', 'full': 'Latin_America_North'}
];

/**
 * Create an error whose message begins with 'node-resteemo - '.
 *
 * @private
 * @param {String} value The error message.
 * @return {Error}
 */
function brandError(value) {
  return new Error('node-resteemo - ' + value);
}

/**
 * Converts a full platform string to its shorthand equivalent.
 *
 * @private
 * @param {String} platform The platform to shorten.
 * @return {String} The shorthand equivalent of `platform` if one exists, else
 *   `null`.
 */
function normalizePlatform(platform) {
  var matchFromShortPlatform = _.find(PLATFORMS, {'short': platform});
  if (!_.isUndefined(matchFromShortPlatform)) {
    return platform;
  }

  var matchFromFullPlatform = _.find(PLATFORMS, {'full': platform});
  if (!_.isUndefined(matchFromFullPlatform)) {
    return matchFromFullPlatform['short'];
  }

  return null;
}

/**
 * Parse JSON data from a request.
 *
 * @private
 * @param {Object} req The request instance to handle.
 * @param {Function} callback The callback, which is given two arguments:
 *   `(err, response)`, where `response` is a JSON Object from `req`.
 */
function responseHandler(req, callback) {
  req.on('response', function(res) {
    var response = '';
    res.setEncoding('utf8');
    res.on('data', function(chunk) {
      response += chunk;
    });
    res.on('end', function() {
      try {
        response = JSON.parse(response);
      }
      catch(e) {
        var error = brandError('invalid json response');
        return callback(error);
      }

      if (!response.success) {
        var error = brandError('api failed');
        return callback(error);
      }

      var hasSecondSuccess;
      switch (false) {
        case !(_.contains(res.request.uri.path, 'recent_games')):
          hasSecondSuccess = true;
          break;
        case !(_.contains(res.request.uri.path, 'runes')):
          hasSecondSuccess = true;
          break;
        case !(_.contains(res.request.uri.path, 'mastery')):
          hasSecondSuccess = true;
          break;
        case !(_.contains(res.request.uri.path, 'leagues')):
          hasSecondSuccess = true;
          break;
        default:
          hasSecondSuccess = false;
      }

      if (hasSecondSuccess) {
        if (!response.data._success) {
          var error = brandError('api failed at second success check');
          return callback(error);
        }
      }

      callback(null, response);
    });
  });
}

/**
 * Establishes the user agent of all API calls.
 *
 * @param {String} refererString The referer string to use in API calls.
 * @return {Object}
 */
module.exports = function(refererString) {
  if (!_.isString(refererString)) {
    var error = brandError('`refererString` not defined');
    throw error;
  }

  /**
   * Contructs the headers and options for the API request.
   *
   * @private
   * @param {String} method The HTTP verb.
   * @param {String} path The path to query from the API endpoint.
   * @param {Function} callback The callback, which is passed to
   *   `responseHandler`.
   */
  function prepareRequest(method, path, callback) {
    if (!_.isFunction(callback)) {
      var error = brandError('missing callback');
      throw error;
    }

    var headers = {
      'Accept'    : 'application/json',
      'User-Agent': refererString
    };

    var requestOptions = {
      method : method,
      uri    : ENDPOINT + path,
      headers: headers
    };

    var req = request(requestOptions);
    responseHandler(req, callback);
    req.end();
  }

  /**
   * Begins the preparation for a GET request.
   *
   * @private
   * @param {String} path The path to query from the API endpoint.
   * @param {Function} callback The callback, which is passed to
   *   `prepareRequest`.
   */
  function get(path, callback) {
    prepareRequest('GET', path, callback);
  }

  /**
   * Sets up a player request.
   *
   * @private
   * @param {String} platform
   * @param {String} summoner
   * @param {String} path
   * @param {Function} callback
   */
  function playerRequest(platform, summoner, path, callback) {
    var shortPlatform = normalizePlatform(platform);
    if (_.isNull(shortPlatform)) {
      var error = brandError('invalid platform');
      return callback(error);
    }

    if (_.isNull(path)) {
      path = '';
    } else {
      path = '/' + path;
    }

    get('/player/' + shortPlatform + '/' + summoner + path, callback);
  }

  var teemo = {};

  /**
   * Returns primarily ID-based data for String `summoner` on String `platform`.
   * Account and summoner IDs are not unique across multiple platforms.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, profile)` where
   *   `profile` is the API response as an Object.
   */
  teemo.player = function(platform, summoner, callback) {
    playerRequest(platform, summoner, null, callback);
  };

  /**
   * Returns last 10 matches (order is random) for String `summoner` on String
   * `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, games)` where `games`
   *   is the API response as an Object.
   */
  teemo.player.recentGames = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'recent_games', callback);
  };

  /**
   * Returns lifetime influence point gains for String `summoner` on String
   * `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, points)` where `points`
   *   is the API response as an Object.
   */
  teemo.player.influencePoints = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'influence_points', callback);
  };

  /**
   * Returns runepages for String `summoner` on String `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, runes)` where `runes`
   *   is the API response as an Object.
   */
  teemo.player.runes = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'runes', callback);
  };

  /**
   * Returns mastery pages for String `summoner` on String `platform`.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, pages)` where `pages`
   *   is the API response as an Object.
   */
  teemo.player.mastery = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'mastery', callback);
  };

  /**
   * Returns Season 3 Leagues info.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, leagues)` where
   *   `leagues` is the API response as an Object.
   */
  teemo.player.leagues = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'leagues', callback);
  };

  /**
   * Returns honor.
   *
   * @public
   * @param {String} platform
   * @param {String} summoner
   * @param {Function} callback Used as `callback(error, honor)` where `honor`
   *   is the API response as an Object.
   */
  teemo.player.honor = function(platform, summoner, callback) {
    playerRequest(platform, summoner, 'honor', callback);
  };

  return teemo;
};<|endoftext|>


<commit_before>/*jshint node: true */
'use strict';

var path = require('path');
var mergeTrees = require('broccoli-merge-trees');
var Funnel = require('broccoli-funnel');

module.exports = {
  name: 'liquid-tether',

  init: function() {
    if (this._super.init) {
      this._super.init.apply(this, arguments);
    }

    // Shim this.import for Engines support
    if (!this.import) {
      // Shim from https://github.com/ember-cli/ember-cli/blob/5d64cfbf1276cf1e3eb88761df4546c891b5efa6/lib/models/addon.js#L387
      this._findHost = function findHostShim() {
        var current = this;
        var app;

        // Keep iterating upward until we don't have a grandparent.
        // Has to do this grandparent check because at some point we hit the project.
        do {
          app = current.app || app;
        } while (current.parent.parent && (current = current.parent));

        return app;
      };
      // Shim from https://github.com/ember-cli/ember-cli/blob/5d64cfbf1276cf1e3eb88761df4546c891b5efa6/lib/models/addon.js#L443
      this.import = function importShim(asset, options) {
        var app = this._findHost();
        app.import(asset, options);
      };
    }
  },

  treeForVendor: function(tree) {
    var tetherPath = path.dirname(require.resolve('tether'));
    var tetherTree = new Funnel(this.treeGenerator(tetherPath), {
      srcDir: '/',
      destDir: 'tether'
    });
    return mergeTrees([tree, tetherTree]);
  },

  included: function(app) {
    this._super.included(app);
    this.import('vendor/tether/tether.js');
  }
};
<commit_msg>Update index.js (#80)<commit_after>/*jshint node: true */
'use strict';

var path = require('path');
var mergeTrees = require('broccoli-merge-trees');
var Funnel = require('broccoli-funnel');

module.exports = {
  name: 'liquid-tether',

  init: function() {
    if (this._super.init) {
      this._super.init.apply(this, arguments);
    }

    // Shim this.import for Engines support
    if (!this.import) {
      // Shim from https://github.com/ember-cli/ember-cli/blob/5d64cfbf1276cf1e3eb88761df4546c891b5efa6/lib/models/addon.js#L387
      this._findHost = function findHostShim() {
        var current = this;
        var app;

        // Keep iterating upward until we don't have a grandparent.
        // Has to do this grandparent check because at some point we hit the project.
        do {
          app = current.app || app;
        } while (current.parent.parent && (current = current.parent));

        return app;
      };
      // Shim from https://github.com/ember-cli/ember-cli/blob/5d64cfbf1276cf1e3eb88761df4546c891b5efa6/lib/models/addon.js#L443
      this.import = function importShim(asset, options) {
        var app = this._findHost();
        app.import(asset, options);
      };
    }
  },

  treeForVendor: function(tree) {
    var tetherPath = path.dirname(require.resolve('tether'));
    var tetherTree = new Funnel(this.treeGenerator(tetherPath), {
      srcDir: '/',
      destDir: 'tether'
    });
    return mergeTrees([tree, tetherTree]);
  },

  included: function(app) {
    this._super.included.apply(this, app)
    this.import('vendor/tether/tether.js');
  }
};
<|endoftext|>


<commit_before><commit_msg>feat(chart): Chart limit is now 10 meas/s.<commit_after><|endoftext|>


<commit_before>"use strict";

import React, { Component, PropTypes } from "react";
import {
    Dimensions,
    StyleSheet,
    View,
    ScrollView,
    Text,
    TouchableWithoutFeedback
} from "react-native";

import styles from "./styles";

let { width } = Dimensions.get("window");

export default class Carousel extends Component {

    displayName = "Carousel";

    static propTypes = {
        pageStyle: PropTypes.object,
        pageWidth: PropTypes.number,
        children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), PropTypes.node ]).isRequired,
        initialPage: PropTypes.number,
        noItemsText: PropTypes.string,
        onPageChange: PropTypes.func,
        sneak: PropTypes.number,
        transitionDelay: PropTypes.number
    };

    static defaultProps = {
        initialPage: 0,
        pageStyle: null,
        pageWidth: width - 80,
        sneak: 20,
        noItemsText: "Sorry, there are currently \n no items available",
        transitionDelay: 0
    };

    componentWillMount() {
        this.calculateGap(this.props);
    }

    componentDidMount() {
        if (this.props.children && this.props.initialPage > 0 && this.props.initialPage < this.props.children.length) {
            this.goToPage(this.props.initialPage);
        }
    }

    componentWillReceiveProps(props) {
        this.calculateGap(props);
    }

    calculateGap(props) {
        let { sneak, pageWidth } = props;
        if (pageWidth > width) {
            throw new Error("invalid pageWith");
        }
        /*
         ------------
        |            |
        |-   ----   -|
        | | |    | | |
        | | |    | | |
        | | |    | | |
        |-   ----   -|
        |^-- sneak   |
        |         ^--- gap
         ------------

        */
        let gap = (width - (2 * sneak) - pageWidth) / 2;
        this.setState({gap: gap});
    }

    goToPage(position) {
        let { pageWidth, transitionDelay } = this.props;
        let { gap } = this.state;
        let pagePosition = position * (pageWidth + gap);
        // in android, you can't scroll directly in componentDidMount
        // (http://stackoverflow.com/questions/33208477/react-native-android-scrollview-scrollto-not-working)
        // however this doesn't work in android for some reason:
        // InteractionManager.runAfterInteractions(() => {
        //     this.scrollView.scrollTo({ y: 0, x: pagePosition}, true);
        //     console.log('scrollView.scrollTo x:', pagePosition);
        // });
        // So I was left with an arbitrary timeout.
        setTimeout(()=> {
            this.scrollView.scrollTo({ y: 0, x: pagePosition}, true);
        }, transitionDelay);
        this._onPageChange(position);
    }

    handleScrollEnd = (e) => {
        let { pageWidth } = this.props;
        let { gap } = this.state;
        let pageOffset = pageWidth + gap;
        //select page based on the position of the middle of the screen.
        let currentPosition = e.nativeEvent.contentOffset.x + (pageOffset / 2);
        let currentPage = ~~(currentPosition / pageOffset);

        this.scrollView.scrollTo({ y: 0, x: currentPage * pageOffset });
        this._onPageChange(currentPage);
    };

    _onPageChange(position) {
        if (this.props.onPageChange) {
            let currentElement = this.props.children[position];
            this.props.onPageChange(position, currentElement);
        }
    }

    render() {
        let { sneak, pageWidth } = this.props;
        let { gap } = this.state;
        let computedStyles = StyleSheet.create({
            scrollView: {
                paddingLeft: sneak + gap / 2,
                paddingRight: sneak + gap / 2
            },
            page: {
                width: pageWidth,
                justifyContent: "center",
                marginLeft: gap / 2,
                marginRight: gap / 2
            }
        });

        // if no children render a no items dummy page without callbacks
        let body = null;
        if (!this.props.children) {
            body = (
                <TouchableWithoutFeedback>
                    <View style={ [ styles.page, computedStyles.page, this.props.pageStyle ] }>
                        <Text style={ styles.noItemsText }>
                            { this.props.noItemsText }
                        </Text>
                    </View>
                </TouchableWithoutFeedback>
            );
        }
        else {
            const children = Array.isArray(this.props.children) ? this.props.children : [this.props.children]
            body = children.map((c, index) => {
                return (
                    <TouchableWithoutFeedback
                        key={ index }
                        onPress={ () => this.goToPage(index) }
                    >
                        <View
                            style={ [ styles.page, computedStyles.page, this.props.pageStyle ] }
                        >
                            { c }
                        </View>
                    </TouchableWithoutFeedback>
                );
            });
        }

        return (
            <View style={ styles.container }>
                <ScrollView
                    automaticallyAdjustContentInsets={ false }
                    bounces
                    contentContainerStyle={ [ computedStyles.scrollView ] }
                    decelerationRate={ 0.9 }
                    horizontal
                    onScrollEndDrag={ this.handleScrollEnd }
                    ref={ c => this.scrollView = c }
                    showsHorizontalScrollIndicator={ false }
                >
                    { body }
                </ScrollView>
            </View>
        );
    }
}
<commit_msg>new props to modify container style<commit_after>"use strict";

import React, { Component, PropTypes } from "react";
import {
    Dimensions,
    StyleSheet,
    View,
    ScrollView,
    Text,
    TouchableWithoutFeedback
} from "react-native";

import styles from "./styles";

let { width } = Dimensions.get("window");

export default class Carousel extends Component {

    displayName = "Carousel";

    static propTypes = {
        pageStyle: PropTypes.object,
        pageWidth: PropTypes.number,
        children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), PropTypes.node ]).isRequired,
        initialPage: PropTypes.number,
        containerStyle: PropTypes.object,
        noItemsText: PropTypes.string,
        onPageChange: PropTypes.func,
        sneak: PropTypes.number,
        transitionDelay: PropTypes.number
    };

    static defaultProps = {
        initialPage: 0,
        pageStyle: null,
        containerStyle: null,
        pageWidth: width - 80,
        sneak: 20,
        noItemsText: "Sorry, there are currently \n no items available",
        transitionDelay: 0
    };

    componentWillMount() {
        this.calculateGap(this.props);
    }

    componentDidMount() {
        if (this.props.children && this.props.initialPage > 0 && this.props.initialPage < this.props.children.length) {
            this.goToPage(this.props.initialPage);
        }
    }

    componentWillReceiveProps(props) {
        this.calculateGap(props);
    }

    calculateGap(props) {
        let { sneak, pageWidth } = props;
        if (pageWidth > width) {
            throw new Error("invalid pageWith");
        }
        /*
         ------------
        |            |
        |-   ----   -|
        | | |    | | |
        | | |    | | |
        | | |    | | |
        |-   ----   -|
        |^-- sneak   |
        |         ^--- gap
         ------------

        */
        let gap = (width - (2 * sneak) - pageWidth) / 2;
        this.setState({gap: gap});
    }

    goToPage(position) {
        let { pageWidth, transitionDelay } = this.props;
        let { gap } = this.state;
        let pagePosition = position * (pageWidth + gap);
        // in android, you can't scroll directly in componentDidMount
        // (http://stackoverflow.com/questions/33208477/react-native-android-scrollview-scrollto-not-working)
        // however this doesn't work in android for some reason:
        // InteractionManager.runAfterInteractions(() => {
        //     this.scrollView.scrollTo({ y: 0, x: pagePosition}, true);
        //     console.log('scrollView.scrollTo x:', pagePosition);
        // });
        // So I was left with an arbitrary timeout.
        setTimeout(()=> {
            this.scrollView.scrollTo({ y: 0, x: pagePosition}, true);
        }, transitionDelay);
        this._onPageChange(position);
    }

    handleScrollEnd = (e) => {
        let { pageWidth } = this.props;
        let { gap } = this.state;
        let pageOffset = pageWidth + gap;
        //select page based on the position of the middle of the screen.
        let currentPosition = e.nativeEvent.contentOffset.x + (pageOffset / 2);
        let currentPage = ~~(currentPosition / pageOffset);

        this.scrollView.scrollTo({ y: 0, x: currentPage * pageOffset });
        this._onPageChange(currentPage);
    };

    _onPageChange(position) {
        if (this.props.onPageChange) {
            let currentElement = this.props.children[position];
            this.props.onPageChange(position, currentElement);
        }
    }

    render() {
        let { sneak, pageWidth } = this.props;
        let { gap } = this.state;
        let computedStyles = StyleSheet.create({
            scrollView: {
                paddingLeft: sneak + gap / 2,
                paddingRight: sneak + gap / 2
            },
            page: {
                width: pageWidth,
                justifyContent: "center",
                marginLeft: gap / 2,
                marginRight: gap / 2
            }
        });

        // if no children render a no items dummy page without callbacks
        let body = null;
        if (!this.props.children) {
            body = (
                <TouchableWithoutFeedback>
                    <View style={ [ styles.page, computedStyles.page, this.props.pageStyle ] }>
                        <Text style={ styles.noItemsText }>
                            { this.props.noItemsText }
                        </Text>
                    </View>
                </TouchableWithoutFeedback>
            );
        }
        else {
            const children = Array.isArray(this.props.children) ? this.props.children : [this.props.children]
            body = children.map((c, index) => {
                return (
                    <TouchableWithoutFeedback
                        key={ index }
                        onPress={ () => this.goToPage(index) }
                    >
                        <View
                            style={ [ styles.page, computedStyles.page, this.props.pageStyle ] }
                        >
                            { c }
                        </View>
                    </TouchableWithoutFeedback>
                );
            });
        }

        return (
            <View style={[ styles.container, this.props.containerStyle ]}>
                <ScrollView
                    automaticallyAdjustContentInsets={ false }
                    bounces
                    contentContainerStyle={ [ computedStyles.scrollView ] }
                    decelerationRate={ 0.9 }
                    horizontal
                    onScrollEndDrag={ this.handleScrollEnd }
                    ref={ c => this.scrollView = c }
                    showsHorizontalScrollIndicator={ false }
                >
                    { body }
                </ScrollView>
            </View>
        );
    }
}
<|endoftext|>


<commit_before>"use strict"

var vecs = require("./vecs/index.js")

window.addEventListener("DOMContentLoaded", function () {
    var rect = new vecs.SVGRect(100, 100)
    rect.setStrokeColor("black")
    rect.setStrokeWidth(3)

    var svgRoot = new vecs.SVGRoot(400, 400)
    svgRoot.addAt(rect, 100, 100)
    svgRoot.addToDocumentBodyWithId("logo")
    svgRoot.setPreserveAspectRatio("none")
    svgRoot.setViewBox("0 0 1500 1500")

    svgRoot.observe("click", function (e) {
        // rect.setCoords(e.position.x, e.position.y)
        console.log("click!", e.positionRelativeToViewport(), e.rawEventObj())
    })
})

<commit_msg>Fixed forgetting to pass false to addEventListener call.<commit_after>"use strict"

var vecs = require("./vecs/index.js")

window.addEventListener("DOMContentLoaded", function () {
    var rect = new vecs.SVGRect(100, 100)
    rect.setStrokeColor("black")
    rect.setStrokeWidth(3)

    var svgRoot = new vecs.SVGRoot(400, 400)
    svgRoot.addAt(rect, 100, 100)
    svgRoot.addToDocumentBodyWithId("logo")
    svgRoot.setPreserveAspectRatio("none")
    svgRoot.setViewBox("0 0 1500 1500")

    svgRoot.observe("click", function (e) {
        // rect.setCoords(e.position.x, e.position.y)
        console.log("click!", e.positionRelativeToViewport(), e.rawEventObj())
    })
}, false)

<|endoftext|>


<commit_before>
'use strict';

// import Immutable from 'immutable';

// import Interpreter from 'js-interpreter';

import { TESTS_STARTED, CODE_CHANGED, START_CHALLENGE, TESTS_FINISHED } from '../actions/ArcadeAction';
import UserData from '../model/UserData';
import Challenges from '../../../json/challenges.json';
import TestResults from '../model/TestResults';
import Challenge from '../model/Challenge';

// const initialState = Immutable.Map();
//

export default function arcadeReducer(state, action) {
  if (typeof state === 'undefined') {
    return {
      code: Challenges.challenges[0].challengeSeed.join('\n'),
      interpreterError: false,
      isRunningTests: false,
      userData: new UserData({ username: '' }),
      testResults: new TestResults([]),
      currChallenge: new Challenge(Challenges.challenges[0])
    };
  }

  let nextState = Object.assign({}, state);

  switch (action.type) {
    case TESTS_STARTED: {
      nextState.isRunningTests = true;
      break;
    }
    case TESTS_FINISHED: {
      nextState.isRunningTests = false;
      nextState.testResults = new TestResults(action.testResults);
      break;
    }
    case CODE_CHANGED: {
      nextState.code = action.code;
      break;
    }
    case START_CHALLENGE: {
      nextState.code = state.currChallenge.getSeed().join('\n');
      break;
    }
    default: console.log('Default reached.');
  }

  return nextState;
}
<commit_msg>Added TIMER_FINISHED/UPDATED actions. Added timer into the state.<commit_after>
'use strict';

// import Immutable from 'immutable';

// import Interpreter from 'js-interpreter';

import { TESTS_STARTED, CODE_CHANGED, START_CHALLENGE, TESTS_FINISHED, TIMER_FINISHED } from '../actions/ArcadeAction';
import UserData from '../model/UserData';
import Challenges from '../../../json/challenges.json';
import TestResults from '../model/TestResults';
import Challenge from '../model/Challenge';
import Timer from '../model/Timer';

// const initialState = Immutable.Map();
//

export default function arcadeReducer(state, action) {
  if (typeof state === 'undefined') {
    return {
      code: Challenges.challenges[0].challengeSeed.join('\n'),
      interpreterError: false,
      isRunningTests: false,
      userData: new UserData({ username: '' }),
      testResults: new TestResults([]),
      currChallenge: new Challenge(Challenges.challenges[0]),
      isTimerFinished: false,
      timer: new Timer(1000 * 900)
    };
  }

  let nextState = Object.assign({}, state);

  switch (action.type) {
    case TESTS_STARTED: {
      nextState.isRunningTests = true;
      break;
    }
    case TESTS_FINISHED: {
      nextState.isRunningTests = false;
      nextState.testResults = new TestResults(action.testResults);
      break;
    }
    case CODE_CHANGED: {
      nextState.code = action.code;
      break;
    }
    case START_CHALLENGE: {
      nextState.code = state.currChallenge.getSeed().join('\n');
      nextState.isTimerFinished = false;
      break;
    }
    case TIMER_FINISHED: {
      nextState.isTimerFinished = true;
      break;
    }
    case TIMER_UPDATED: {
      nextState.timeLeft = state.timer.getTimeLeft();
      break;
    }
    default: console.log('Default reached.');
  }

  return nextState;
}
<|endoftext|>


<commit_before>
import React, { PropTypes } from 'react'
import FontAwesome from 'react-fontawesome'

import Icon from '../Icon'
import * as helpers from '../../helpers'

export default class SearchSettingsModal extends React.Component{

	constructor(props){
		super(props)

		this.state = {
			spotify: true,
			uri_schemes: []
		}
	}

	componentDidMount(){
		if (this.props.search_settings){
			this.setState(this.props.search_settings)
		} else {
			this.setState({
				spotify: true,
				uri_schemes: this.props.uri_schemes
			})
		}
	}

	handleSubmit(e){
		this.props.uiActions.set({search_settings: this.state})
		this.props.uiActions.closeModal()
		this.props.uiActions.startSearch(this.props.data.type, this.props.data.query)
	}

	handleToggle(scheme){
		var uri_schemes = this.state.uri_schemes
		var index = uri_schemes.indexOf(scheme)

		if (index > -1){
			uri_schemes.splice(index,1)
		} else {
			uri_schemes.push(scheme)
		}

		this.setState({uri_schemes: uri_schemes})
	}

	render(){
		return (

Search sourced

Customise the providers used when searching. Reduce the number of providers to speed up your searches. Please note that not all backends support searching by artist, album or playlist.

				<form onSubmit={e => this.handleSubmit(e)}>

								<input
									type="checkbox"
									name="enabled"
									checked={ this.state.spotify }
									onChange={ e => this.setState({ spotify: !this.state.spotify })} />
								spotify (http)

						{
							this.props.uri_schemes.map(scheme => {
								return (

											<input
												type="checkbox"
												name={scheme}
												checked={this.state.uri_schemes.indexOf(scheme) > -1}
												onChange={ e => this.handleToggle(scheme)} />
											{scheme.replace(':','')} (mopidy)

								)
							})
						}

						<button type="submit" className="primary wide">Save</button>

				</form>

		)
	}
}<commit_msg>Sources drop off when deselected, fixes #125<commit_after>
import React, { PropTypes } from 'react'
import FontAwesome from 'react-fontawesome'

import Icon from '../Icon'
import * as helpers from '../../helpers'

export default class SearchSettingsModal extends React.Component{

	constructor(props){
		super(props)

		this.state = {
			spotify: true,
			uri_schemes: []
		}
	}

	componentDidMount(){
		if (this.props.search_settings){
			this.setState(Object.assign({},this.props.search_settings))
		} else {
			this.setState({
				spotify: true,
				uri_schemes: Object.assign([],this.props.uri_schemes)
			})
		}
	}

	handleSubmit(e){
		this.props.uiActions.set({search_settings: this.state})
		this.props.uiActions.closeModal()
		this.props.uiActions.startSearch(this.props.data.type, this.props.data.query)
	}

	handleToggle(scheme){
		var uri_schemes = this.state.uri_schemes
		var index = uri_schemes.indexOf(scheme)

		if (index > -1){
			uri_schemes.splice(index,1)
		} else {
			uri_schemes.push(scheme)
		}

		this.setState({uri_schemes: uri_schemes})
	}

	render(){
		return (

Search sourced

Customise the providers used when searching. Reduce the number of providers to speed up your searches. Please note that not all backends support searching by artist, album or playlist.

				<form onSubmit={e => this.handleSubmit(e)}>

								<input
									type="checkbox"
									name="enabled"
									checked={ this.state.spotify }
									onChange={ e => this.setState({ spotify: !this.state.spotify })} />
								spotify (http)

						{
							this.props.uri_schemes.map(scheme => {
								return (

											<input
												type="checkbox"
												name={scheme}
												checked={this.state.uri_schemes.indexOf(scheme) > -1}
												onChange={ e => this.handleToggle(scheme)} />
											{scheme.replace(':','')} (mopidy)

								)
							})
						}

						<button type="submit" className="primary wide">Save</button>

				</form>

		)
	}
}<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var crc = require('buffer-crc32');

var ROOT_KEY = 'dcdr';

function Dcdr() {
  this.features = {};
}

Dcdr.prototype.init = function(config) {
  this.config = config;
  this.config.logger = this.config.logger || console;

  fs.exists(this.config.dcdr.path, function(exists) {
    if (exists) {
      this.loadFeatures(this.config.dcdr.path);
      this.watchConfig();
    } else {
      this.config.logger.error(this.config.dcdr.path + ' not found.');
    }
  }.bind(this));
};

Dcdr.prototype.watchConfig = function() {
  fs.watch(this.config.dcdr.path, { persistent: false }, function() {
    this.config.logger.info('Reloading features from ' + this.config.dcdr.path);
    this.loadFeatures(this.config.dcdr.path);
  }.bind(this));
};

Dcdr.prototype.loadFeatures = function(path) {
  var features = JSON.parse(fs.readFileSync(path, 'utf8'));
  this.setFeatures(features);
};

Dcdr.prototype.setFeatures = function(features) {
  if (features[ROOT_KEY] && features[ROOT_KEY].features && features[ROOT_KEY].features.default) {
    this.features = features[ROOT_KEY].features.default;
  } else {
    this.config.logger.error(ROOT_KEY + '.features.default key not found.');
  }
};

Dcdr.prototype.withinPercentile = function(feature, id, val) {
  var uid = this.crc(feature + id.toString());
  var percent = val * 100.00;

  return uid % 100 <= percent;
};

Dcdr.prototype.crc = function(feature) {
  var buf = new Buffer(feature);
  return crc.unsigned(buf);
};

Dcdr.prototype.isAvailable = function(feature) {
  return this.features && this.features[feature] !== null
    && this.features[feature] === true;
};

Dcdr.prototype.isAvailableForId = function(feature, id) {
  return (this.features && this.features[feature] !== null
    && this.withinPercentile(feature, id, this.features[feature]));
};

Dcdr.prototype.scaleValue = function(feature, min, max) {
  if (!this.features || this.features[feature] === null) return min;
  return min + ((max - min) * this.features[feature]);
};

module.exports = new Dcdr();
<commit_msg>Fix comparison operator<commit_after>'use strict';

var fs = require('fs');
var crc = require('buffer-crc32');

var ROOT_KEY = 'dcdr';

function Dcdr() {
  this.features = {};
}

Dcdr.prototype.init = function(config) {
  this.config = config;
  this.config.logger = this.config.logger || console;

  fs.exists(this.config.dcdr.path, function(exists) {
    if (exists) {
      this.loadFeatures(this.config.dcdr.path);
      this.watchConfig();
    } else {
      this.config.logger.error(this.config.dcdr.path + ' not found.');
    }
  }.bind(this));
};

Dcdr.prototype.watchConfig = function() {
  fs.watch(this.config.dcdr.path, { persistent: false }, function() {
    this.config.logger.info('Reloading features from ' + this.config.dcdr.path);
    this.loadFeatures(this.config.dcdr.path);
  }.bind(this));
};

Dcdr.prototype.loadFeatures = function(path) {
  var features = JSON.parse(fs.readFileSync(path, 'utf8'));
  this.setFeatures(features);
};

Dcdr.prototype.setFeatures = function(features) {
  if (features[ROOT_KEY] && features[ROOT_KEY].features && features[ROOT_KEY].features.default) {
    this.features = features[ROOT_KEY].features.default;
  } else {
    this.config.logger.error(ROOT_KEY + '.features.default key not found.');
  }
};

Dcdr.prototype.withinPercentile = function(feature, id, val) {
  var uid = this.crc(feature + id.toString());
  var percent = val * 100.00;

  return uid % 100 < percent;
};

Dcdr.prototype.crc = function(feature) {
  var buf = new Buffer(feature);
  return crc.unsigned(buf);
};

Dcdr.prototype.isAvailable = function(feature) {
  return this.features && this.features[feature] !== null
    && this.features[feature] === true;
};

Dcdr.prototype.isAvailableForId = function(feature, id) {
  return (this.features && this.features[feature] !== null
    && this.withinPercentile(feature, id, this.features[feature]));
};

Dcdr.prototype.scaleValue = function(feature, min, max) {
  if (!this.features || this.features[feature] === null) return min;
  return min + ((max - min) * this.features[feature]);
};

module.exports = new Dcdr();
<|endoftext|>


<commit_before><commit_msg>start of better queuing<commit_after><|endoftext|>


<commit_before>import site from '../../app';

site.controller('upcomingController', ($scope, $mdMedia, $state, $stateParams, $firebaseObject, $timeout, FirebaseURL, SidebarManagement, EnsureLoggedIn, TournamentInformation) => {
  SidebarManagement.hasSidebar = false;
  EnsureLoggedIn.check(false);

  $scope.$mdMedia = $mdMedia;

  $scope.strings = [];

  $scope.viewBracket = () => $state.go('tournamentInProgress', $stateParams);

  $scope.matchesLeft = () => TournamentInformation.matchesLeft($scope.trn);

  $scope.shouldShow = (match) => !match.m && !_.all(match.p, p => p === 0);

  $scope.getString = (id, idx) => TournamentInformation.getString($scope.strings, id, idx);
  $scope.noRender = TournamentInformation.noRender;
  $scope.getMatchIdString = TournamentInformation.getMatchIdString;
  $scope.getStation = (match) => $scope.ref.stations[TournamentInformation.getMatchStationIdString(match)];

  const hasAnyZeroes = (match) => _.any(match.p, p => p === 0);

  $scope.isReady = (match) => {
    if($scope.noRender(match) || !$scope.shouldShow(match)) return;
    return ~~!!$scope.getStation(match) + ~~!hasAnyZeroes(match);
  };

  $scope.ref = $firebaseObject(new Firebase(`${FirebaseURL}/users/${atob($stateParams.userId)}/players/${$stateParams.setId}/tournaments/${$stateParams.tournamentId}`));

  $scope.getName = (idx) => TournamentInformation.playerName($scope.bucket[idx]);

  $scope.loadTournament = (ref, makeNew = false) => {
    $scope.tournamentName = $scope.ref.name;

    $scope.bucket = ref.players;

    const tournamentProto = TournamentInformation.determineTournament(ref.options);

    $scope.trn = ref.trn && !makeNew ? tournamentProto.restore($scope.bucket.length, ref.options, ref.trn) : new tournamentProto($scope.bucket.length, ref.options);

    TournamentInformation.reset($scope.trn);

    // cache them ids
    _.each($scope.trn.matches, $scope.getMatchIdString);

    if(!_.contains(['groupstage', 'ffa', 'masters'], ref.options.type)) {
      $timeout(() => $scope.strings = TournamentInformation.loadTournamentWinnerStrings($scope.trn), 0);
    }
  };

  $scope.ref.$watch(() => $scope.loadTournament($scope.ref));
});<commit_msg>fix(upcoming): the view will no longer break if there are no stations recorded<commit_after>import site from '../../app';

site.controller('upcomingController', ($scope, $mdMedia, $state, $stateParams, $firebaseObject, $timeout, FirebaseURL, SidebarManagement, EnsureLoggedIn, TournamentInformation) => {
  SidebarManagement.hasSidebar = false;
  EnsureLoggedIn.check(false);

  $scope.$mdMedia = $mdMedia;

  $scope.strings = [];

  $scope.viewBracket = () => $state.go('tournamentInProgress', $stateParams);

  $scope.matchesLeft = () => TournamentInformation.matchesLeft($scope.trn);

  $scope.shouldShow = (match) => !match.m && !_.all(match.p, p => p === 0);

  $scope.getString = (id, idx) => TournamentInformation.getString($scope.strings, id, idx);
  $scope.noRender = TournamentInformation.noRender;
  $scope.getMatchIdString = TournamentInformation.getMatchIdString;
  $scope.getStation = (match) => ($scope.ref.stations || {})[TournamentInformation.getMatchStationIdString(match)];

  const hasAnyZeroes = (match) => _.any(match.p, p => p === 0);

  $scope.isReady = (match) => {
    if($scope.noRender(match) || !$scope.shouldShow(match)) return;
    return ~~!!$scope.getStation(match) + ~~!hasAnyZeroes(match);
  };

  $scope.ref = $firebaseObject(new Firebase(`${FirebaseURL}/users/${atob($stateParams.userId)}/players/${$stateParams.setId}/tournaments/${$stateParams.tournamentId}`));

  $scope.getName = (idx) => TournamentInformation.playerName($scope.bucket[idx]);

  $scope.loadTournament = (ref, makeNew = false) => {
    $scope.tournamentName = $scope.ref.name;

    $scope.bucket = ref.players;

    const tournamentProto = TournamentInformation.determineTournament(ref.options);

    $scope.trn = ref.trn && !makeNew ? tournamentProto.restore($scope.bucket.length, ref.options, ref.trn) : new tournamentProto($scope.bucket.length, ref.options);

    TournamentInformation.reset($scope.trn);

    // cache them ids
    _.each($scope.trn.matches, $scope.getMatchIdString);

    if(!_.contains(['groupstage', 'ffa', 'masters'], ref.options.type)) {
      $timeout(() => $scope.strings = TournamentInformation.loadTournamentWinnerStrings($scope.trn), 0);
    }
  };

  $scope.ref.$watch(() => $scope.loadTournament($scope.ref));
});<|endoftext|>