<commit_before>/* global */
'use strict'

var $ = require('jquery')
var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var EventManager = require('ethereum-remix').lib.EventManager
var crypto = require('crypto')
var async = require('async')
var TxRunner = require('./app/execution/txRunner')
var yo = require('yo-yo')
var txFormat = require('./app/execution/txFormat')
var txHelper = require('./app/execution/txHelper')
var txExecution = require('./app/execution/txExecution')
var helper = require('./lib/helper')
var executionContext = require('./execution-context')

// copy to copyToClipboard
const copy = require('clipboard-copy')

// -------------- styling ----------------------
var csjs = require('csjs-inject')

var remix = require('ethereum-remix')
var styleGuide = remix.ui.styleGuide
var styles = styleGuide()

var css = csjs`
  .instanceTitleContainer {
    display: flex;
    align-items: center;
  }
  .title {
    ${styles.dropdown}
    width: 400px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    overflow: hidden;
    word-break: break-word;
    line-height: initial;
    background-color: ${styles.colors.white};
  }
  .titleText {
    margin-right: 1em;
    word-break: break-word;
    min-width: 230px;
  }
  .instance {
    ${styles.displayBox}
    margin-bottom: 2px;
    padding: 10px 15px 6px 15px;
  }
  .instance .title:before {
    content: "\\25BE";
    margin-right: 5%;
  }
  .instance.hidesub .title:before {
    content: "\\25B8";
    margin-right: 5%;
  }
  .instance.hidesub > * {
      display: none;
  }
  .instance.hidesub .title {
      display: flex;
  }
  .copy  {
    font-size: 13px;
    cursor: pointer;
    opacity: 0.8;
    margin-left: 3%;
    color: ${styles.colors.blue};
  }
  .copy:hover{
    color: ${styles.colors.grey};
  }
  .buttonsContainer {
    margin-top: 2%;
  }
  .contractActions {
    display: flex;
  }
  .instanceButton {}
  .closeIcon {
    font-size: 10px;
    position: relative;
    top: -5px;
    right: -2px;
  }
  .udappClose {
    margin-left: 3%;
    align-self: center;
  }
`

/*
  trigger debugRequested
*/
function UniversalDApp (opts = {}) {
  this.event = new EventManager()
  var self = this

  self._api = opts.api
  self.removable = opts.opt.removable
  self.removable_instance = opts.opt.removable_instance
  self.el = yo`<div class="udapp"></div>`
  self.personalMode = opts.opt.personalMode || false
  self.contracts
  self.transactionContextAPI
  executionContext.event.register('contextChanged', this, function (context) {
    self.reset(self.contracts)
  })
  self.txRunner = new TxRunner({}, {
    queueTxs: true,
    personalMode: this.personalMode
  })
}

UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) {
  this.el.innerHTML = ''
  this.contracts = contracts
  if (transactionContextAPI) {
  this.transactionContextAPI = transactionContextAPI
  }
  this.accounts = {}
  if (executionContext.isVM()) {
    this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000')
    this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000')
    this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000')
    this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000')
    this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000')
    executionContext.vm().stateManager.cache.flush(function () {})
  }
  this.txRunner = new TxRunner(this.accounts, {
    queueTxs: true,
    personalMode: this.personalMode
  })
}

UniversalDApp.prototype.newAccount = function (password, cb) {
  if (!executionContext.isVM()) {
    if (!this.personalMode) {
      return cb('Not running in personal mode')
    }
    executionContext.web3().personal.newAccount(password, cb)
  } else {
    var privateKey
    do {
      privateKey = crypto.randomBytes(32)
    } while (!ethJSUtil.isValidPrivate(privateKey))
    this._addAccount(privateKey)
    cb(null, '0x' + ethJSUtil.privateToAddress(privateKey))
  }
}

UniversalDApp.prototype._addAccount = function (privateKey, balance) {
  var self = this

  if (!executionContext.isVM()) {
    throw new Error('_addAccount() cannot be called in non-VM mode')
  }

  if (self.accounts) {
    privateKey = new Buffer(privateKey, 'hex')
    var address = ethJSUtil.privateToAddress(privateKey)

    // FIXME: we don't care about the callback, but we should still make this proper
    executionContext.vm().stateManager.putAccountBalance(address, balance || '0xf00000000000000001', function cb () {})
    self.accounts['0x' + address.toString('hex')] = { privateKey: privateKey, nonce: 0 }
  }
}

UniversalDApp.prototype.getAccounts = function (cb) {
  var self = this

  if (!executionContext.isVM()) {
    // Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async
    // See: https://github.com/ethereum/web3.js/issues/442
    if (self.personalMode) {
      executionContext.web3().personal.getListAccounts(cb)
    } else {
      executionContext.web3().eth.getAccounts(cb)
    }
  } else {
    if (!self.accounts) {
      return cb('No accounts?')
    }

    cb(null, Object.keys(self.accounts))
  }
}

UniversalDApp.prototype.getBalance = function (address, cb) {
  var self = this

  address = ethJSUtil.stripHexPrefix(address)

  if (!executionContext.isVM()) {
    executionContext.web3().eth.getBalance(address, function (err, res) {
      if (err) {
        cb(err)
      } else {
        cb(null, res.toString(10))
      }
    })
  } else {
    if (!self.accounts) {
      return cb('No accounts?')
    }

    executionContext.vm().stateManager.getAccountBalance(new Buffer(address, 'hex'), function (err, res) {
      if (err) {
        cb('Account not found')
      } else {
        cb(null, new BN(res).toString(10))
      }
    })
  }
}

// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
UniversalDApp.prototype.renderInstance = function (contract, address, contractName) {
  var self = this

  function remove () { $instance.remove() }
  var $instance = $(`<div class="instance ${css.instance}"/>`)
  var context = executionContext.isVM() ? 'memory' : 'blockchain'

  address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
  var shortAddress = helper.shortenAddress(address)
  var title = yo`

 ${contractName} at ${shortAddress} (${context})

`
  if (self.removable_instances) {
    var close = yo`<div class="${css.udappClose}" onclick=${remove}><i class="${css.closeIcon} fa fa-close" aria-hidden="true"></i></div>`
    title.appendChild(close)
  }

  function toggleClass () {
    $instance.toggleClass(`${css.hidesub}`)
  }

  function copyToClipboard (event) {
    event.stopPropagation()
    copy(address)
  }

  var abi = txHelper.sortAbiFunction(contract)

  $instance.get(0).appendChild(title)

  // Add the fallback function
  var fallback = txHelper.getFallbackInterface(abi)
  if (fallback) {
    $instance.append(this.getCallButton({
      funABI: fallback,
      address: address,
      contractAbi: abi,
      contractName: contractName
    }))
  }

  $.each(abi, (i, funABI) => {
    if (funABI.type !== 'function') {
      return
    }
    // @todo getData cannot be used with overloaded functions
    $instance.append(this.getCallButton({
      funABI: funABI,
      address: address,
      contractAbi: abi,
      contractName: contractName
    }))
  })

  return $instance.get(0)
}

// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
UniversalDApp.prototype.getCallButton = function (args) {
  var self = this
  // args.funABI, args.address [fun only]
  // args.contractName [constr only]
  var lookupOnly = args.funABI.constant

  var inputs = ''
  if (args.funABI.inputs) {
    inputs = txHelper.inputParametersDeclarationToString(args.funABI.inputs)
  }
  var inputField = $('<input/>').attr('placeholder', inputs).attr('title', inputs)
  var $outputOverride = $('
')

  var title
  if (args.funABI.name) {
    title = args.funABI.name
  } else {
    title = '(fallback)'
  }

  var button = $(`<button class="${css.instanceButton}"/>`)
    .addClass('call')
    .attr('title', title)
    .text(title)
    .click(() => {
      call(true)
    })

  function call (isUserAction) {
    var logMsg
    if (isUserAction) {
      if (!args.funABI.constant) {
        logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
      } else {
        logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
      }
    }
    txFormat.buildData(args.contractAbi, self.contracts, false, args.funABI, inputField.val(), self, (error, data) => {
      if (!error) {
        if (isUserAction) {
          if (!args.funABI.constant) {
            self._api.logMessage(`${logMsg} pending ... `)
          } else {
            self._api.logMessage(`${logMsg}`)
          }
        }
        txExecution.callFunction(args.address, data, args.funABI, self, (error, txResult) => {
          if (!error) {
            var isVM = executionContext.isVM()
            if (isVM) {
              var vmError = txExecution.checkVMError(txResult)
              if (vmError.error) {
                self._api.logMessage(`${logMsg} errored: ${vmError.message} `)
                return
              }
            }
            if (lookupOnly) {
              var decoded = txFormat.decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI)
              $outputOverride.html(decoded)
            }
          } else {
            self._api.logMessage(`${logMsg} errored: ${error} `)
          }
        })
      } else {
        self._api.logMessage(`${logMsg} errored: ${error} `)
      }
    })
  }

  var $contractProperty = $(`<div class="contractProperty ${css.buttonsContainer}"></div>`)
  var $contractActions = $(`<div class="${css.contractActions}" ></div>`)
  $contractProperty.append($contractActions)
  $contractActions.append(button)
  if (inputs.length) {
    $contractActions.append(inputField)
  }
  if (lookupOnly) {
    $contractProperty.append($outputOverride)
  }

  if (lookupOnly) {
    $contractProperty.addClass('constant')
    button.attr('title', (title + ' - call'))
    call(false)
  }

  if (args.funABI.inputs && args.funABI.inputs.length > 0) {
    $contractProperty.addClass('hasArgs')
  }

  if (args.funABI.payable === true) {
    $contractProperty.addClass('payable')
    button.attr('title', (title + ' - transact (payable)'))
  }

  if (!lookupOnly && args.funABI.payable === false) {
    button.attr('title', (title + ' - transact (not payable)'))
  }

  return $contractProperty
}

UniversalDApp.prototype.pendingTransactions = function () {
  return this.txRunner.pendingTxs
}

UniversalDApp.prototype.runTx = function (args, cb) {
  var self = this
  var tx = {
    to: args.to,
    data: args.data,
    useCall: args.useCall
  }
  async.waterfall([
    // query gas limit
    function (callback) {
      tx.gasLimit = 3000000

      if (self.transactionContextAPI.getGasLimit) {
        self.transactionContextAPI.getGasLimit(function (err, ret) {
          if (err) {
            return callback(err)
          }

          tx.gasLimit = ret
          callback()
        })
      } else {
        callback()
      }
    },
    // query value
    function (callback) {
      tx.value = 0
      if (tx.useCall) return callback()
      if (self.transactionContextAPI.getValue) {
        self.transactionContextAPI.getValue(function (err, ret) {
          if (err) {
            return callback(err)
          }

          tx.value = ret
          callback()
        })
      } else {
        callback()
      }
    },
    // query address
    function (callback) {
      if (self.transactionContextAPI.getAddress) {
        self.transactionContextAPI.getAddress(function (err, ret) {
          if (err) {
            return callback(err)
          }

          tx.from = ret

          callback()
        })
      } else {
        self.getAccounts(function (err, ret) {
          if (err) {
            return callback(err)
          }

          if (ret.length === 0) {
            return callback('No accounts available')
          }

          if (executionContext.isVM() && !self.accounts[ret[0]]) {
            return callback('Invalid account selected')
          }

          tx.from = ret[0]

          callback()
        })
      }
    },
    // run transaction
    function (callback) {
      self.txRunner.rawRun(tx, function (error, result) {
        if (!args.useCall) {
          self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result])
        } else {
          self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result])
        }
        if (error) {
          if (typeof (error) !== 'string') {
            if (error.message) {
              error = error.message
            } else {
              try {
                error = 'error: ' + JSON.stringify(error)
              } catch (e) {}
            }
          }
        }
        callback(error, result)
      })
    }
  ], cb)
}

module.exports = UniversalDApp
<commit_msg>fix standard<commit_after>/* global */
'use strict'

var $ = require('jquery')
var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var EventManager = require('ethereum-remix').lib.EventManager
var crypto = require('crypto')
var async = require('async')
var TxRunner = require('./app/execution/txRunner')
var yo = require('yo-yo')
var txFormat = require('./app/execution/txFormat')
var txHelper = require('./app/execution/txHelper')
var txExecution = require('./app/execution/txExecution')
var helper = require('./lib/helper')
var executionContext = require('./execution-context')

// copy to copyToClipboard
const copy = require('clipboard-copy')

// -------------- styling ----------------------
var csjs = require('csjs-inject')

var remix = require('ethereum-remix')
var styleGuide = remix.ui.styleGuide
var styles = styleGuide()

var css = csjs`
  .instanceTitleContainer {
    display: flex;
    align-items: center;
  }
  .title {
    ${styles.dropdown}
    width: 400px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    overflow: hidden;
    word-break: break-word;
    line-height: initial;
    background-color: ${styles.colors.white};
  }
  .titleText {
    margin-right: 1em;
    word-break: break-word;
    min-width: 230px;
  }
  .instance {
    ${styles.displayBox}
    margin-bottom: 2px;
    padding: 10px 15px 6px 15px;
  }
  .instance .title:before {
    content: "\\25BE";
    margin-right: 5%;
  }
  .instance.hidesub .title:before {
    content: "\\25B8";
    margin-right: 5%;
  }
  .instance.hidesub > * {
      display: none;
  }
  .instance.hidesub .title {
      display: flex;
  }
  .copy  {
    font-size: 13px;
    cursor: pointer;
    opacity: 0.8;
    margin-left: 3%;
    color: ${styles.colors.blue};
  }
  .copy:hover{
    color: ${styles.colors.grey};
  }
  .buttonsContainer {
    margin-top: 2%;
  }
  .contractActions {
    display: flex;
  }
  .instanceButton {}
  .closeIcon {
    font-size: 10px;
    position: relative;
    top: -5px;
    right: -2px;
  }
  .udappClose {
    margin-left: 3%;
    align-self: center;
  }
`

/*
  trigger debugRequested
*/
function UniversalDApp (opts = {}) {
  this.event = new EventManager()
  var self = this

  self._api = opts.api
  self.removable = opts.opt.removable
  self.removable_instance = opts.opt.removable_instance
  self.el = yo`<div class="udapp"></div>`
  self.personalMode = opts.opt.personalMode || false
  self.contracts
  self.transactionContextAPI
  executionContext.event.register('contextChanged', this, function (context) {
    self.reset(self.contracts)
  })
  self.txRunner = new TxRunner({}, {
    queueTxs: true,
    personalMode: this.personalMode
  })
}

UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) {
  this.el.innerHTML = ''
  this.contracts = contracts
  if (transactionContextAPI) {
    this.transactionContextAPI = transactionContextAPI
  }
  this.accounts = {}
  if (executionContext.isVM()) {
    this._addAccount('3cd7232cd6f3fc66a57a6bedc1a8ed6c228fff0a327e169c2bcc5e869ed49511', '0x56BC75E2D63100000')
    this._addAccount('2ac6c190b09897cd8987869cc7b918cfea07ee82038d492abce033c75c1b1d0c', '0x56BC75E2D63100000')
    this._addAccount('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', '0x56BC75E2D63100000')
    this._addAccount('d74aa6d18aa79a05f3473dd030a97d3305737cbc8337d940344345c1f6b72eea', '0x56BC75E2D63100000')
    this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000')
    executionContext.vm().stateManager.cache.flush(function () {})
  }
  this.txRunner = new TxRunner(this.accounts, {
    queueTxs: true,
    personalMode: this.personalMode
  })
}

UniversalDApp.prototype.newAccount = function (password, cb) {
  if (!executionContext.isVM()) {
    if (!this.personalMode) {
      return cb('Not running in personal mode')
    }
    executionContext.web3().personal.newAccount(password, cb)
  } else {
    var privateKey
    do {
      privateKey = crypto.randomBytes(32)
    } while (!ethJSUtil.isValidPrivate(privateKey))
    this._addAccount(privateKey)
    cb(null, '0x' + ethJSUtil.privateToAddress(privateKey))
  }
}

UniversalDApp.prototype._addAccount = function (privateKey, balance) {
  var self = this

  if (!executionContext.isVM()) {
    throw new Error('_addAccount() cannot be called in non-VM mode')
  }

  if (self.accounts) {
    privateKey = new Buffer(privateKey, 'hex')
    var address = ethJSUtil.privateToAddress(privateKey)

    // FIXME: we don't care about the callback, but we should still make this proper
    executionContext.vm().stateManager.putAccountBalance(address, balance || '0xf00000000000000001', function cb () {})
    self.accounts['0x' + address.toString('hex')] = { privateKey: privateKey, nonce: 0 }
  }
}

UniversalDApp.prototype.getAccounts = function (cb) {
  var self = this

  if (!executionContext.isVM()) {
    // Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async
    // See: https://github.com/ethereum/web3.js/issues/442
    if (self.personalMode) {
      executionContext.web3().personal.getListAccounts(cb)
    } else {
      executionContext.web3().eth.getAccounts(cb)
    }
  } else {
    if (!self.accounts) {
      return cb('No accounts?')
    }

    cb(null, Object.keys(self.accounts))
  }
}

UniversalDApp.prototype.getBalance = function (address, cb) {
  var self = this

  address = ethJSUtil.stripHexPrefix(address)

  if (!executionContext.isVM()) {
    executionContext.web3().eth.getBalance(address, function (err, res) {
      if (err) {
        cb(err)
      } else {
        cb(null, res.toString(10))
      }
    })
  } else {
    if (!self.accounts) {
      return cb('No accounts?')
    }

    executionContext.vm().stateManager.getAccountBalance(new Buffer(address, 'hex'), function (err, res) {
      if (err) {
        cb('Account not found')
      } else {
        cb(null, new BN(res).toString(10))
      }
    })
  }
}

// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
UniversalDApp.prototype.renderInstance = function (contract, address, contractName) {
  var self = this

  function remove () { $instance.remove() }
  var $instance = $(`<div class="instance ${css.instance}"/>`)
  var context = executionContext.isVM() ? 'memory' : 'blockchain'

  address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
  var shortAddress = helper.shortenAddress(address)
  var title = yo`

 ${contractName} at ${shortAddress} (${context})

`
  if (self.removable_instances) {
    var close = yo`<div class="${css.udappClose}" onclick=${remove}><i class="${css.closeIcon} fa fa-close" aria-hidden="true"></i></div>`
    title.appendChild(close)
  }

  function toggleClass () {
    $instance.toggleClass(`${css.hidesub}`)
  }

  function copyToClipboard (event) {
    event.stopPropagation()
    copy(address)
  }

  var abi = txHelper.sortAbiFunction(contract)

  $instance.get(0).appendChild(title)

  // Add the fallback function
  var fallback = txHelper.getFallbackInterface(abi)
  if (fallback) {
    $instance.append(this.getCallButton({
      funABI: fallback,
      address: address,
      contractAbi: abi,
      contractName: contractName
    }))
  }

  $.each(abi, (i, funABI) => {
    if (funABI.type !== 'function') {
      return
    }
    // @todo getData cannot be used with overloaded functions
    $instance.append(this.getCallButton({
      funABI: funABI,
      address: address,
      contractAbi: abi,
      contractName: contractName
    }))
  })

  return $instance.get(0)
}

// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
UniversalDApp.prototype.getCallButton = function (args) {
  var self = this
  // args.funABI, args.address [fun only]
  // args.contractName [constr only]
  var lookupOnly = args.funABI.constant

  var inputs = ''
  if (args.funABI.inputs) {
    inputs = txHelper.inputParametersDeclarationToString(args.funABI.inputs)
  }
  var inputField = $('<input/>').attr('placeholder', inputs).attr('title', inputs)
  var $outputOverride = $('
')

  var title
  if (args.funABI.name) {
    title = args.funABI.name
  } else {
    title = '(fallback)'
  }

  var button = $(`<button class="${css.instanceButton}"/>`)
    .addClass('call')
    .attr('title', title)
    .text(title)
    .click(() => {
      call(true)
    })

  function call (isUserAction) {
    var logMsg
    if (isUserAction) {
      if (!args.funABI.constant) {
        logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
      } else {
        logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
      }
    }
    txFormat.buildData(args.contractAbi, self.contracts, false, args.funABI, inputField.val(), self, (error, data) => {
      if (!error) {
        if (isUserAction) {
          if (!args.funABI.constant) {
            self._api.logMessage(`${logMsg} pending ... `)
          } else {
            self._api.logMessage(`${logMsg}`)
          }
        }
        txExecution.callFunction(args.address, data, args.funABI, self, (error, txResult) => {
          if (!error) {
            var isVM = executionContext.isVM()
            if (isVM) {
              var vmError = txExecution.checkVMError(txResult)
              if (vmError.error) {
                self._api.logMessage(`${logMsg} errored: ${vmError.message} `)
                return
              }
            }
            if (lookupOnly) {
              var decoded = txFormat.decodeResponseToTreeView(executionContext.isVM() ? txResult.result.vm.return : ethJSUtil.toBuffer(txResult.result), args.funABI)
              $outputOverride.html(decoded)
            }
          } else {
            self._api.logMessage(`${logMsg} errored: ${error} `)
          }
        })
      } else {
        self._api.logMessage(`${logMsg} errored: ${error} `)
      }
    })
  }

  var $contractProperty = $(`<div class="contractProperty ${css.buttonsContainer}"></div>`)
  var $contractActions = $(`<div class="${css.contractActions}" ></div>`)
  $contractProperty.append($contractActions)
  $contractActions.append(button)
  if (inputs.length) {
    $contractActions.append(inputField)
  }
  if (lookupOnly) {
    $contractProperty.append($outputOverride)
  }

  if (lookupOnly) {
    $contractProperty.addClass('constant')
    button.attr('title', (title + ' - call'))
    call(false)
  }

  if (args.funABI.inputs && args.funABI.inputs.length > 0) {
    $contractProperty.addClass('hasArgs')
  }

  if (args.funABI.payable === true) {
    $contractProperty.addClass('payable')
    button.attr('title', (title + ' - transact (payable)'))
  }

  if (!lookupOnly && args.funABI.payable === false) {
    button.attr('title', (title + ' - transact (not payable)'))
  }

  return $contractProperty
}

UniversalDApp.prototype.pendingTransactions = function () {
  return this.txRunner.pendingTxs
}

UniversalDApp.prototype.runTx = function (args, cb) {
  var self = this
  var tx = {
    to: args.to,
    data: args.data,
    useCall: args.useCall
  }
  async.waterfall([
    // query gas limit
    function (callback) {
      tx.gasLimit = 3000000

      if (self.transactionContextAPI.getGasLimit) {
        self.transactionContextAPI.getGasLimit(function (err, ret) {
          if (err) {
            return callback(err)
          }

          tx.gasLimit = ret
          callback()
        })
      } else {
        callback()
      }
    },
    // query value
    function (callback) {
      tx.value = 0
      if (tx.useCall) return callback()
      if (self.transactionContextAPI.getValue) {
        self.transactionContextAPI.getValue(function (err, ret) {
          if (err) {
            return callback(err)
          }

          tx.value = ret
          callback()
        })
      } else {
        callback()
      }
    },
    // query address
    function (callback) {
      if (self.transactionContextAPI.getAddress) {
        self.transactionContextAPI.getAddress(function (err, ret) {
          if (err) {
            return callback(err)
          }

          tx.from = ret

          callback()
        })
      } else {
        self.getAccounts(function (err, ret) {
          if (err) {
            return callback(err)
          }

          if (ret.length === 0) {
            return callback('No accounts available')
          }

          if (executionContext.isVM() && !self.accounts[ret[0]]) {
            return callback('Invalid account selected')
          }

          tx.from = ret[0]

          callback()
        })
      }
    },
    // run transaction
    function (callback) {
      self.txRunner.rawRun(tx, function (error, result) {
        if (!args.useCall) {
          self.event.trigger('transactionExecuted', [error, args.from, args.to, args.data, false, result])
        } else {
          self.event.trigger('callExecuted', [error, args.from, args.to, args.data, true, result])
        }
        if (error) {
          if (typeof (error) !== 'string') {
            if (error.message) {
              error = error.message
            } else {
              try {
                error = 'error: ' + JSON.stringify(error)
              } catch (e) {}
            }
          }
        }
        callback(error, result)
      })
    }
  ], cb)
}

module.exports = UniversalDApp
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import SPELLS from 'common/SPELLS';

const PRE_INTELLECT_POTION_BUFF = 900;

class RestorationShamanSpreadsheet extends React.Component {
  static propTypes = {
    parser: PropTypes.object.isRequired,
  };

  render() {
    const { parser } = this.props;

    const styles = {
      table: { borderBottom: '1px solid #dddddd', borderTop: '1px solid #dddddd', align: 'right', padding: '20px', float: 'left', margin: '5px', borderCollapse: 'separate', borderSpacing: '10px 0' },
    };

    const getAbility = spellId => parser._modules.abilityTracker.getAbility(spellId);
    const casts = spellId => getAbility(spellId).casts;
    const cpm = (spellId, tw = false) => (tw ? getAbility(spellId).healingTwHits : casts(spellId)) / (parser.fightDuration / 1000 / 60) >= 0 ? ((tw ? getAbility(spellId).healingTwHits : casts(spellId)) / (parser.fightDuration / 1000 / 60)).toFixed(2) : '0';
    const prePotion = parser._modules.prePotion.usedPrePotion ? PRE_INTELLECT_POTION_BUFF : 0;

    return (

Please use the below table to populate the Restoration Shaman Spreadsheet. Link to the sheet

{parser.selectedCombatant._combatantInfo.intellect - prePotion}

{parser.selectedCombatant._combatantInfo.critSpell}

{parser.selectedCombatant._combatantInfo.hasteSpell}

{parser.selectedCombatant._combatantInfo.mastery}

{parser.selectedCombatant._combatantInfo.versatilityHealingDone}

{Math.floor(parser.fightDuration / 1000)}

{parser._modules.statValues.hpsPerIntellect.toFixed(2)}

{parser._modules.statValues.hpsPerCriticalStrike.toFixed(2)}

{parser._modules.statValues.hpsPerHaste.toFixed(2)}

{parser._modules.statValues.hpsPerMastery.toFixed(2)}

{parser._modules.statValues.hpsPerVersatility.toFixed(2)}

{cpm(SPELLS.RIPTIDE.id)}

{cpm(SPELLS.HEALING_RAIN_CAST.id)}

{cpm(SPELLS.HEALING_TIDE_TOTEM_CAST.id)}

{cpm(SPELLS.SPIRIT_LINK_TOTEM.id)}

{parser.selectedCombatant.hasTalent(SPELLS.CLOUDBURST_TOTEM_TALENT.id) ? cpm(SPELLS.CLOUDBURST_TOTEM_TALENT.id) : cpm(SPELLS.HEALING_STREAM_TOTEM_CAST.id)}

{cpm(SPELLS.CHAIN_HEAL.id)}

{parser._modules.spreadsheet.surgingTideProcsPerMinute}

{(parser._modules.spreadsheet.spoutingSpiritsHits / casts(SPELLS.SPIRIT_LINK_TOTEM.id) || 0).toFixed(2)}

{(parser._modules.spreadsheet.overflowingShoresHits / casts(SPELLS.HEALING_RAIN_CAST.id) || 0).toFixed(2)}

{parser._modules.spreadsheet.ebbAndFlowEffectiveness.toFixed(2)}

{parser._modules.combatants.playerCount}

{cpm(SPELLS.ASTRAL_SHIFT.id)}

{(parser.selectedCombatant.getBuffUptime(SPELLS.GHOST_WOLF.id) / 1000).toFixed(2)}

{parser._modules.masteryEffectiveness.masteryEffectivenessPercent.toFixed(2)}

{parser.selectedCombatant.race ? parser.selectedCombatant.race.name : 'Unknown'}

{cpm(SPELLS.HEALING_WAVE.id,true)}

{cpm(SPELLS.HEALING_SURGE_RESTORATION.id,true)}

    );
  }
}

export default RestorationShamanSpreadsheet;
<commit_msg>add a standalone tw function<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import SPELLS from 'common/SPELLS';

const PRE_INTELLECT_POTION_BUFF = 900;

class RestorationShamanSpreadsheet extends React.Component {
  static propTypes = {
    parser: PropTypes.object.isRequired,
  };

  render() {
    const { parser } = this.props;

    const styles = {
      table: { borderBottom: '1px solid #dddddd', borderTop: '1px solid #dddddd', align: 'right', padding: '20px', float: 'left', margin: '5px', borderCollapse: 'separate', borderSpacing: '10px 0' },
    };

    const minutes = (parser.fightDuration / 1000 / 60);
    const getAbility = spellId => parser._modules.abilityTracker.getAbility(spellId);
    const casts = spellId => getAbility(spellId).casts;
    const cpm = spellId => casts(spellId) / minutes >= 0 ? (casts(spellId) / minutes).toFixed(2) : '0';
    const twcpm = spellId => getAbility(spellId).healingTwHits / minutes >= 0 ? (getAbility(spellId).healingTwHits / minutes).toFixed(2) : '0';
    const prePotion = parser._modules.prePotion.usedPrePotion ? PRE_INTELLECT_POTION_BUFF : 0;

    return (

Please use the below table to populate the Restoration Shaman Spreadsheet. Link to the sheet

{parser.selectedCombatant._combatantInfo.intellect - prePotion}

{parser.selectedCombatant._combatantInfo.critSpell}

{parser.selectedCombatant._combatantInfo.hasteSpell}

{parser.selectedCombatant._combatantInfo.mastery}

{parser.selectedCombatant._combatantInfo.versatilityHealingDone}

{Math.floor(parser.fightDuration / 1000)}

{parser._modules.statValues.hpsPerIntellect.toFixed(2)}

{parser._modules.statValues.hpsPerCriticalStrike.toFixed(2)}

{parser._modules.statValues.hpsPerHaste.toFixed(2)}

{parser._modules.statValues.hpsPerMastery.toFixed(2)}

{parser._modules.statValues.hpsPerVersatility.toFixed(2)}

{cpm(SPELLS.RIPTIDE.id)}

{cpm(SPELLS.HEALING_RAIN_CAST.id)}

{cpm(SPELLS.HEALING_TIDE_TOTEM_CAST.id)}

{cpm(SPELLS.SPIRIT_LINK_TOTEM.id)}

{parser.selectedCombatant.hasTalent(SPELLS.CLOUDBURST_TOTEM_TALENT.id) ? cpm(SPELLS.CLOUDBURST_TOTEM_TALENT.id) : cpm(SPELLS.HEALING_STREAM_TOTEM_CAST.id)}

{cpm(SPELLS.CHAIN_HEAL.id)}

{parser._modules.spreadsheet.surgingTideProcsPerMinute}

{(parser._modules.spreadsheet.spoutingSpiritsHits / casts(SPELLS.SPIRIT_LINK_TOTEM.id) || 0).toFixed(2)}

{(parser._modules.spreadsheet.overflowingShoresHits / casts(SPELLS.HEALING_RAIN_CAST.id) || 0).toFixed(2)}

{parser._modules.spreadsheet.ebbAndFlowEffectiveness.toFixed(2)}

{parser._modules.combatants.playerCount}

{cpm(SPELLS.ASTRAL_SHIFT.id)}

{(parser.selectedCombatant.getBuffUptime(SPELLS.GHOST_WOLF.id) / 1000).toFixed(2)}

{parser._modules.masteryEffectiveness.masteryEffectivenessPercent.toFixed(2)}

{parser.selectedCombatant.race ? parser.selectedCombatant.race.name : 'Unknown'}

{twcpm(SPELLS.HEALING_WAVE.id,true)}

{twcpm(SPELLS.HEALING_SURGE_RESTORATION.id,true)}

    );
  }
}

export default RestorationShamanSpreadsheet;
<|endoftext|>


<commit_before>/**
 * Allows you to animate any Drawable node
 * @class Animation
 * @fires FF.Animation#start
 * @fires FF.Animation#step
 * @fires FF.Animation#end
 * @example
 * TODO
 */
FF.Animation = function(item, duration, options){
	FF.EventManager.call(this);

	var that = this;

	this.node = item;

	this.timer = new FF.Timer({ timeout : duration, interval : options.step || 100, autostart : options.autostart || false  });
	this.timer.addEventListener("start", function(){ that.dispatchEvent({ type : "start" }); });
	this.timer.addEventListener("tick", function(e){ that.animate(e); that.dispatchEvent({ type : "step" }); });
	this.timer.addEventListener("timeout", function(){ that.dispatchEvent({ type : "end" }); });

	this.options = options || {};

	this.originalX = this.node.rect().x;
	this.originalY = this.node.rect().y;
	this.originalWidth = this.node.rect().width;
	this.originalHeight = this.node.rect().height;
	this.originalScale = this.node.scale;
	this.originalAlpla = this.node.alpha;
};

FF.Animation.prototype.update = function(){
	this.timer.update();
};

FF.Animation.prototype.animate = function(tickEvent){
	var percent = (this.timer.time / this.timer.timeout);

	if(this.options.left) this.node.x = this.originalX - (this.options.left * percent);
	if(this.options.right) this.node.x = this.originalX + (this.options.right * percent);
	if(this.options.top) this.node.y = this.originalY - (this.options.top * percent);
	if(this.options.bottom) this.node.y = this.originalY + (this.options.bottom * percent);

	if(this.options.width) this.node.width = this.originalWidth + ((this.options.width - this.originalWidth) * percent);
	if(this.options.height) this.node.height = this.originalHeight + ((this.options.height - this.originalHeight) * percent);
	if(this.options.scale) this.node.scale = this.originalScale + ((this.options.scale - this.originalScale) * percent);
	if(this.options.opacity) this.node.alpha = this.originalAlpha + ((this.options.opacity - this.originalAlpha) * percent);
};

FF.Animation.prototype.start = function(){
	this.timer.restart();
};
<commit_msg>Working fix for issue #1<commit_after>/**
 * Allows you to animate any Drawable node
 * @class Animation
 * @fires FF.Animation#start
 * @fires FF.Animation#step
 * @fires FF.Animation#end
 * @example
 * TODO
 */
FF.Animation = function(item, duration, options){
	FF.EventManager.call(this);

	var that = this;

	this.node = item;

	this.timer = new FF.Timer({ timeout : duration, interval : options.step || 10, autostart : options.autostart || false  });
	this.timer.addEventListener("start", function(e){ that.resetValues(); that.dispatchEvent({ type : "start" }); });
	this.timer.addEventListener("tick", function(e){ that.animate(e); that.dispatchEvent({ type : "step" }); });
	this.timer.addEventListener("timeout", function(){ that.dispatchEvent({ type : "end" }); });

	this.options = options;
};

FF.Animation.prototype.update = function(){
	this.timer.update();
};

FF.Animation.prototype.animate = function(tickEvent){
	var percent = parseFloat(tickEvent.completion);

	if(this.options.left) this.node.x = this.originalX - (this.options.left * percent);
	if(this.options.right) this.node.x = this.originalX + (this.options.right * percent);
	if(this.options.top) this.node.y = this.originalY - (this.options.top * percent);
	if(this.options.bottom) this.node.y = this.originalY + (this.options.bottom * percent);

	if(this.options.width || this.options.width == 0) this.node.width = this.originalWidth + ((this.options.width - this.originalWidth) * percent);
	if(this.options.height || this.options.height == 0) this.node.height = this.originalHeight + ((this.options.height - this.originalHeight) * percent);
	if(this.options.scale || this.options.scale == 0) this.node.scale = this.originalScale + ((this.options.scale - this.originalScale) * percent);
	if(this.options.opacity || this.options.opacity == 0) this.node.alpha = this.originalAlpha + ((this.options.opacity - this.originalAlpha) * percent);
};

FF.Animation.prototype.start = function(){
	this.timer.restart();
};

FF.Animation.prototype.resetValues = function(){
	this.originalX = this.node.rect().x;
	this.originalY = this.node.rect().y;
	this.originalWidth = this.node.rect().width;
	this.originalHeight = this.node.rect().height;
	this.originalScale = this.node.scale;
	this.originalAlpha = this.node.alpha;
};
<|endoftext|>


<commit_before>/****************************************
 *
 *  script2svg-html5
 *
 *  Reads an HTML5 file from stdin that contains TeX in script tags
 *  and writes a new HTML5 document to stdout that
 *  contains SVG versions of the math instead.
 *
 */

var typeset = require("./lib/mj-single.js").typeset;
var fs = require('fs');
var jsdom = require('jsdom').jsdom;

//
//  Produce a Usage message, if needed
//
if (process.argv.length !== 5) {
    console.error("Usage:" + process.argv[1] + " --output input.html  output.html");
    process.exit(1);
}
// Collect the CLI parameters
var inputFile = process.argv[3];
//console.log(inputFile);
var outputFile = process.argv[4];
var outputChoice = process.argv[2].substr(2);
if (!outputChoice==="mathml" && !outputChoice==="svg"){
        console.log("Please use '--svg' or '--mathml'");
        process.exit(1);
    }

// function for moving paths to a global element
function svgCleaning (localElement,globalElement) {
    var thePaths = localElement.querySelectorAll("path");
    for (var i = 0; i < thePaths.length; i++) {
        var currentPath = thePaths[i];
        var currentPathID = currentPath.getAttribute("id");
        if ( globalElement.querySelector("#"+currentPathID) === null){
            globalElement.firstChild.appendChild(currentPath);
        }
        else {
            currentPath.parentNode.removeChild(currentPath);
//            console.log(currentPath);
        }
    }
//    localElement.removeChild(localElement.querySelector("defs");
}

//
//  Process an HTML file:
//    Find all math elements,
//    Loop through them, and make typeset calls for each math tag,
//      If the MathML processes correctly,
//        replace the math tag by the svg result.
//      If this is the last one,
//        do the callback with the complete page.
//
function processHTML(html, callback) {
    var document = jsdom(html);
    var math = document.querySelectorAll('[type="math/tex"], [type="math/tex; mode=display"]');
//    Creating a global SVG object collecting up all paths TODO:
    var globalSVG = document.createElement("svg");
    globalSVG.setAttribute("style","visibility: hidden; overflow: hidden; position: absolute; top: 0px; height: 1px; width: auto; padding: 0px; border: 0px; margin: 0px; text-align: left; text-indent: 0px; text-transform: none; line-height: normal; letter-spacing: normal; word-spacing: normal;");
    globalSVG.innerHTML = "<defs></defs>";
    var data = {
    math: "",
//    format: "inline-TeX",
//    useGlobalCache: true,
    mml:true,
    svg: true,
//    state: {}
    };

    for (var i = 0, m = math.length; i < m; i++) {
        data.math = math[i].text;
//        console.log("getAttribute: "+math[i].getAttribute("type"));
        if (math[i].getAttribute("type")==="math/tex; mode=display") {
            data.format = "TeX";
        }
        else { data.format = "inline-TeX";}
//        console.log("data.format: " + data.format);
        typeset(data, (function (node, last) {
            return function (result) {
                if (outputChoice==="svg"){
                    if (result.svg) {
//                        console.log("in the svg: " + data.format); // why is this always returning inline-TeX? Async problem?
                        if (node.getAttribute("type") === 'math/tex; mode=display') { // would like to use data.format but see above
                            var div = document.createElement("div");
                            div.innerHTML = result.svg;
                            var thisSVG = div.firstChild;
                            div.setAttribute("style", "text-align: center;");
                            thisSVG.removeAttribute("style"); // the absolute positioning led to some problems?
                            node.parentNode.replaceChild(div, node);
    //                    var newDefs = document.createElement("def");
    //                    newDefs.innerHTML = (data.state.defs.innerHTML);
    //                    globalSVG.appendChild(newDefs);
                            svgCleaning(div,globalSVG);
                        }
                        else{
                            var span = document.createElement("span");
                            span.innerHTML = result.svg;
                            svgCleaning(span,globalSVG);
                            node.parentNode.replaceChild(span.firstChild, node);
                            }

                    }
                }
                else {
                     if (result.mml) {
                         var span = document.createElement("span");
                         span.innerHTML = result.mml;
                         var thisMML = span.firstChild;
                         node.parentNode.replaceChild(span.firstChild, node);
                    }
                }
                if (last) {
//                    console.log(data.state.defs);
//                    globalSVG.appendChild(data.state.defs);
                    document.body.appendChild(globalSVG);
                    callback(document.outerHTML);
                }
            };
        })(math[i], i == m - 1));
    }
}

//
//  Read the input file, process the HTML, write output file.
//

var html = fs.readFileSync(inputFile, "utf8");

processHTML(html, function (html) {
    fs.writeFile(outputFile, html, function (err) {
        if (err) throw err;
        console.log('It\'s saved!');
    });
});<commit_msg>some clean up<commit_after>/****************************************
 *
 *  wrapper.js
 *
 *  Reads an HTML5 file from the filesystem that contains TeX in script tags
 *  and writes a new HTML5 document to the filesystem that
 *  contains MathML or SVG versions of the math instead.
 *
 */

var typeset = require("./lib/mj-single.js").typeset;
var fs = require("fs");
var jsdom = require("jsdom").jsdom;

//
//  Produce a Usage message, if needed
//
if (process.argv.length !== 5) {
    console.error("Usage:" + process.argv[1] + " --output input.html  output.html");
    process.exit(1);
}
// Collect the CLI parameters
var inputFile = process.argv[3];
var outputFile = process.argv[4];
var outputChoice = process.argv[2].substr(2);
if (!outputChoice==="mathml" && !outputChoice==="svg"){
        console.log("Please use '--svg' or '--mathml'");
        process.exit(1);
    }

// function for moving paths to a global element
function svgCleaning (localElement,globalElement) {
    var thePaths = localElement.querySelectorAll("path");
    for (var i = 0; i < thePaths.length; i++) {
        var currentPath = thePaths[i];
        var currentPathID = currentPath.getAttribute("id");
        if ( globalElement.querySelector("#"+currentPathID) === null){
            globalElement.firstChild.appendChild(currentPath);
        }
        else {
            currentPath.parentNode.removeChild(currentPath);
        }
    }
}

//
//  Process an HTML file:
//    Find all math elements,
//    Loop through them, and make typeset calls for each math tag,
//      If the TeX processes correctly,
//        replace the math tag by the result.
//      If this is the last one,
//        do the callback with the complete page.
//
function processHTML(html, callback) {
    var document = jsdom(html);
    var math = document.querySelectorAll("[type='math/tex'], [type='math/tex; mode=display']");
//    Creating a global SVG object collecting up all paths
    var globalSVG = document.createElement("svg");
    globalSVG.setAttribute("style","visibility: hidden; overflow: hidden; position: absolute; top: 0px; height: 1px; width: auto; padding: 0px; border: 0px; margin: 0px; text-align: left; text-indent: 0px; text-transform: none; line-height: normal; letter-spacing: normal; word-spacing: normal;");
    globalSVG.innerHTML = "<defs></defs>";
    var data = {
    math: "",
//    useGlobalCache: true, //this should be the right way to gather a globalSVG but doesn't work for me
    mml:true,
    svg: true,
//    state: {} //see useGlobalCache
    };

    for (var i = 0, m = math.length; i < m; i++) {
        data.math = math[i].text;
        if (math[i].getAttribute("type")==="math/tex; mode=display") {
            data.format = "TeX";
        }
        else { data.format = "inline-TeX";}
        typeset(data, (function (node, last) {
            return function (result) {
                if (outputChoice==="svg"){
                    if (result.svg) {
                        if (node.getAttribute("type") === "math/tex; mode=display") { // FIX: use data.format
                            var div = document.createElement("div");
                            div.innerHTML = result.svg;
                            var thisSVG = div.firstChild;
                            div.setAttribute("style", "text-align: center;");
                            thisSVG.removeAttribute("style"); // FIX: the absolute positioning led to some problems?
                            node.parentNode.replaceChild(div, node);
                            svgCleaning(div,globalSVG);
                        }
                        else{
                            var span = document.createElement("span");
                            span.innerHTML = result.svg;
                            svgCleaning(span,globalSVG);
                            node.parentNode.replaceChild(span.firstChild, node);
                            }
                    }
                }
                else {
                     if (result.mml) {
                         var span = document.createElement("span");
                         span.innerHTML = result.mml;
                         var thisMML = span.firstChild;
                         node.parentNode.replaceChild(span.firstChild, node);
                    }
                }
                if (last) {
                    if (outputChoice==="svg"){
                        document.body.appendChild(globalSVG);
                    }
                    callback(document.outerHTML);
                }
            };
        })(math[i], i == m - 1));
    }
}

//
//  Read the input file, process the HTML, write output file.
//

var html = fs.readFileSync(inputFile, "utf8");

processHTML(html, function (html) {
    fs.writeFile(outputFile, html, function (err) {
        if (err) throw err;
        console.log("It\'s saved!");
    });
});<|endoftext|>


<commit_before>var Slack = require('slack-client'),
    fs = require('fs'),
    models = require('../../models'),
    moment = require('moment');
    // sheet = require('./sheet');

var committees = ['C00004036',
    'C00298000',
    'C00388934',
    'C00406553',
    'C00430819',
    'C00448373',
    'C00458844',
    'C00487363',
    'C00487470',
    'C00490045',
    'C00490409',
    'C00493924',
    'C00495861',
    'C00499335',
    'C00499525',
    'C00499731',
    'C00500025',
    'C00500587',
    'C00501098',
    'C00503417',
    'C00504241',
    'C00505081',
    'C00506055',
    'C00507525',
    'C00507707',
    'C00508002',
    'C00508317',
    'C00508721',
    'C00525220',
    'C00525899',
    'C00528307',
    'C00532572',
    'C00536540',
    'C00538827',
    'C00540898',
    'C00540997',
    'C00541292',
    'C00543157',
    'C00544569',
    'C00548420',
    'C00554725',
    'C00555615',
    'C00559237',
    'C00559765',
    'C00564534',
    'C00566497',
    'C00567685',
    'C00568840',
    'C00569905',
    'C00570325',
    'C00570739',
    'C00571356',
    'C00571372',
    'C00571380',
    'C00571588',
    'C00571711',
    'C00571778',
    'C00571950',
    'C00572537',
    'C00572867',
    'C00573055',
    'C00573154',
    'C00573519',
    'C00573634',
    'C00573733',
    'C00573816',
    'C00573923',
    'C00574277',
    'C00574418',
    'C00574533',
    'C00574624',
    'C00575373',
    'C00575415',
    'C00575423',
    'C00575431',
    'C00575449',
    'C00575795',
    'C00576108',
    'C00577130',
    'C00577312',
    'C00577916',
    'C00577981',
    'C00578245',
    'C00578492',
    'C00578658',
    'C00578724',
    'C00578757',
    'C00578997',
    'C00579458',
    'C00579706',
    'C00580092',
    'C00580100',
    'C00580159',
    'C00580324',
    'C00580373',
    'C00580399',
    'C00580480',
    'C00580969',
    'C00581215',
    'C00581868',
    'C00581876',
    'C00582197',
    'C00582668',
    'C00582973',
    'C00583146',
    'C00586826',
    'C00587022',
    'C00591214',
    'C00592337',
    'C00603621',
    'C00608984',
    'C00609511',
    'C90011909',
    'C90015181',
    'C90015439',
    'C90015587'];

slack = new Slack(process.env.SLACK_TOKEN, true, true);

var filing_lookup = {};

function checkForFilings (channel,type,first_run) {
    console.log('checking ' + (first_run ? 'first run' : 'not first run'));
    models['fec_' + type + '_summary'].findAll({
        attributes: ['filing_id','committee_name','filer_committee_id_number','form_type'],
        logging: console.log,
        where: {
            created_date: {
                $gte: moment().subtract(60,'minutes').toDate()
            }
        }
    })
    .then(function (filings) {
        console.log('found ' + filings.length);

        filings.forEach(function (filing) {
            filing = filing.toJSON();

            if (!(filing.filing_id in filing_lookup) && !first_run && committees.indexOf(filing.filer_committee_id_number) !== -1) {
                // sheet(filing.filing_id);

                console.log(filing.committee_name + ' (' + filing.filer_committee_id_number + ') filed a ' + filing.form_type);
                channel.send(filing.committee_name + ' (' + filing.filer_committee_id_number + ') filed a ' + filing.form_type + ' for the period ending ' + filing.coverage_through_date  + ' http://docquery.fec.gov/cgi-bin/forms/' + filing.filer_committee_id_number + '/' + filing.filing_id + '/');
            }

            filing_lookup[filing.filing_id] = true;
        });
    });
}

slack.on('open', function() {
    var channel = slack.getChannelGroupOrDMByID('C0C8S4TQC');

    checkForFilings(channel,'pac',true);
    checkForFilings(channel,'presidential',true);

    setInterval(function () {
        checkForFilings(channel,'pac',false);
        checkForFilings(channel,'presidential',false);
    },1000*10);
});

slack.login();
<commit_msg>Remove coverage_through_date<commit_after>var Slack = require('slack-client'),
    fs = require('fs'),
    models = require('../../models'),
    moment = require('moment');
    // sheet = require('./sheet');

var committees = ['C00004036',
    'C00298000',
    'C00388934',
    'C00406553',
    'C00430819',
    'C00448373',
    'C00458844',
    'C00487363',
    'C00487470',
    'C00490045',
    'C00490409',
    'C00493924',
    'C00495861',
    'C00499335',
    'C00499525',
    'C00499731',
    'C00500025',
    'C00500587',
    'C00501098',
    'C00503417',
    'C00504241',
    'C00505081',
    'C00506055',
    'C00507525',
    'C00507707',
    'C00508002',
    'C00508317',
    'C00508721',
    'C00525220',
    'C00525899',
    'C00528307',
    'C00532572',
    'C00536540',
    'C00538827',
    'C00540898',
    'C00540997',
    'C00541292',
    'C00543157',
    'C00544569',
    'C00548420',
    'C00554725',
    'C00555615',
    'C00559237',
    'C00559765',
    'C00564534',
    'C00566497',
    'C00567685',
    'C00568840',
    'C00569905',
    'C00570325',
    'C00570739',
    'C00571356',
    'C00571372',
    'C00571380',
    'C00571588',
    'C00571711',
    'C00571778',
    'C00571950',
    'C00572537',
    'C00572867',
    'C00573055',
    'C00573154',
    'C00573519',
    'C00573634',
    'C00573733',
    'C00573816',
    'C00573923',
    'C00574277',
    'C00574418',
    'C00574533',
    'C00574624',
    'C00575373',
    'C00575415',
    'C00575423',
    'C00575431',
    'C00575449',
    'C00575795',
    'C00576108',
    'C00577130',
    'C00577312',
    'C00577916',
    'C00577981',
    'C00578245',
    'C00578492',
    'C00578658',
    'C00578724',
    'C00578757',
    'C00578997',
    'C00579458',
    'C00579706',
    'C00580092',
    'C00580100',
    'C00580159',
    'C00580324',
    'C00580373',
    'C00580399',
    'C00580480',
    'C00580969',
    'C00581215',
    'C00581868',
    'C00581876',
    'C00582197',
    'C00582668',
    'C00582973',
    'C00583146',
    'C00586826',
    'C00587022',
    'C00591214',
    'C00592337',
    'C00603621',
    'C00608984',
    'C00609511',
    'C90011909',
    'C90015181',
    'C90015439',
    'C90015587'];

slack = new Slack(process.env.SLACK_TOKEN, true, true);

var filing_lookup = {};

function checkForFilings (channel,type,first_run) {
    console.log('checking ' + (first_run ? 'first run' : 'not first run'));
    models['fec_' + type + '_summary'].findAll({
        attributes: ['filing_id','committee_name','filer_committee_id_number','form_type'],
        logging: console.log,
        where: {
            created_date: {
                $gte: moment().subtract(60,'minutes').toDate()
            }
        }
    })
    .then(function (filings) {
        console.log('found ' + filings.length);

        filings.forEach(function (filing) {
            filing = filing.toJSON();

            if (!(filing.filing_id in filing_lookup) && !first_run && committees.indexOf(filing.filer_committee_id_number) !== -1) {
                // sheet(filing.filing_id);

                console.log(filing.committee_name + ' (' + filing.filer_committee_id_number + ') filed a ' + filing.form_type);
                channel.send(filing.committee_name + ' (' + filing.filer_committee_id_number + ') filed a ' + filing.form_type + ' http://docquery.fec.gov/cgi-bin/forms/' + filing.filer_committee_id_number + '/' + filing.filing_id + '/');
                // for the period ending ' + moment(filing.coverage_through_date).format('MM/DD/YYYY')  + '
            }

            filing_lookup[filing.filing_id] = true;
        });
    });
}

slack.on('open', function() {
    var channel = slack.getChannelGroupOrDMByID('C0C8S4TQC');

    checkForFilings(channel,'pac',true);
    checkForFilings(channel,'presidential',true);

    setInterval(function () {
        checkForFilings(channel,'pac',false);
        checkForFilings(channel,'presidential',false);
    },1000*10);
});

slack.login();
<|endoftext|>


<commit_before>// @flow
// @jsx createElement

import '../../../../css/popup/bookmark-tree.css'

import {PureComponent, createElement} from 'react'
import List from 'react-virtualized/dist/es/List'

import * as CST from '../../constants'
import type {BookmarkInfo, BookmarkTree as BookmarkTreeType} from '../../types'
import Mask from '../Mask'
import BookmarkRow from './BookmarkRow'
import TreeHeader from './TreeHeader'

type Props = {|
  focusId: string,
  iconSize: number,
  isShowCover: boolean,
  isShowHeader: boolean,
  listItemWidth: number,
  onCloseButtonClick: () => void,
  onCoverClick: () => void,
  onRowAuxClick: (string) => (MouseEvent) => void,
  onRowClick: (string) => (SyntheticMouseEvent<HTMLElement>) => void,
  onRowMouseEnter: (BookmarkInfo) => () => void,
  onRowMouseLeave: () => void,
  rowHeight: number,
  scrollToIndex: number,
  treeInfo: BookmarkTreeType
|}
type State = {|
  listHeight: number
|}
class BookmarkTree extends PureComponent<Props, State> {
  state = {
    listHeight: 0
  }

  componentDidMount() {
    this.setHeight()
  }

  componentDidUpdate(prevProps: Props) {
    if (prevProps.treeInfo !== this.props.treeInfo) {
      // force recalculate all row heights as it doesn't recalculate
      this.props.treeInfo.children.forEach((x, index) => {
        if (this.list) this.list.recomputeRowHeights(index)
      })

      this.setHeight()
    }
  }

  getRowHeight = (payload: {| index: number |}) => {
    let rowHeight = this.props.rowHeight

    const bookmarkInfo = this.props.treeInfo.children[payload.index]
    if (bookmarkInfo.type === CST.TYPE_SEPARATOR) {
      rowHeight /= 2
    }

    // for the indicator show the end of folder
    if (payload.index === this.props.treeInfo.children.length - 1) {
      rowHeight += CST.GOLDEN_GAP * 2
    }

    return rowHeight
  }

  setHeight = () => {
    const grid = this.list ? this.list.Grid : null

    // search-box and tree-header-box height
    const listContainerElOffsetTop = grid ?
      grid._scrollingContainer.getBoundingClientRect().top :
      0

    const totalRowHeight = this.props.treeInfo.children.reduce(
      (acc, x, index) => acc + this.getRowHeight({index}),
      0
    )

    const maxListHeight = CST.MAX_HEIGHT - listContainerElOffsetTop

    this.setState({
      listHeight: Math.min(maxListHeight, totalRowHeight)
    })
  }

  list: ?Object
  render = () => (

      {this.props.isShowHeader && (
        <TreeHeader
          title={this.props.treeInfo.parent.title}
          onClose={this.props.onCloseButtonClick}
        />
      )}

      <List
        ref={(ref) => {
          this.list = ref
        }}
        height={this.state.listHeight}
        // onScroll={this.props.onScroll}
        rowCount={this.props.treeInfo.children.length}
        rowHeight={this.getRowHeight}
        rowRenderer={(rendererProps: {| index: number, key: string, style: Object |}) => {
          const bookmarkInfo = this.props.treeInfo.children[rendererProps.index]
          return (

              <BookmarkRow
                key={bookmarkInfo.id}
                bookmarkInfo={bookmarkInfo}
                iconSize={this.props.iconSize}
                isFocused={this.props.focusId === bookmarkInfo.id}
                onAuxClick={this.props.onRowAuxClick}
                onClick={this.props.onRowClick}
                onMouseEnter={this.props.onRowMouseEnter}
                onMouseLeave={this.props.onRowMouseLeave}
              />

          )
        }}
        scrollToIndex={this.props.scrollToIndex >= 0 ? this.props.scrollToIndex : undefined}
        // scrollTop={this.props.lastScrollTop}
        tabIndex={-1}
        width={this.props.listItemWidth}
      />

      {this.props.isShowCover && (
        <Mask backgroundColor='#000' opacity={0.16} onClick={this.props.onCoverClick} />
      )}

  )
}

export default BookmarkTree
<commit_msg>style: easier to spot error in BookmarkTree.setHeight<commit_after>// @flow
// @jsx createElement

import '../../../../css/popup/bookmark-tree.css'

import {PureComponent, createElement} from 'react'
import List from 'react-virtualized/dist/es/List'

import * as CST from '../../constants'
import type {BookmarkInfo, BookmarkTree as BookmarkTreeType} from '../../types'
import Mask from '../Mask'
import BookmarkRow from './BookmarkRow'
import TreeHeader from './TreeHeader'

type Props = {|
  focusId: string,
  iconSize: number,
  isShowCover: boolean,
  isShowHeader: boolean,
  listItemWidth: number,
  onCloseButtonClick: () => void,
  onCoverClick: () => void,
  onRowAuxClick: (string) => (MouseEvent) => void,
  onRowClick: (string) => (SyntheticMouseEvent<HTMLElement>) => void,
  onRowMouseEnter: (BookmarkInfo) => () => void,
  onRowMouseLeave: () => void,
  rowHeight: number,
  scrollToIndex: number,
  treeInfo: BookmarkTreeType
|}
type State = {|
  listHeight: number
|}
class BookmarkTree extends PureComponent<Props, State> {
  state = {
    listHeight: 0
  }

  componentDidMount() {
    this.setHeight()
  }

  componentDidUpdate(prevProps: Props) {
    if (prevProps.treeInfo !== this.props.treeInfo) {
      // force recalculate all row heights as it doesn't recalculate
      this.props.treeInfo.children.forEach((x, index) => {
        if (this.list) this.list.recomputeRowHeights(index)
      })

      this.setHeight()
    }
  }

  getRowHeight = (payload: {| index: number |}) => {
    let rowHeight = this.props.rowHeight

    const bookmarkInfo = this.props.treeInfo.children[payload.index]
    if (bookmarkInfo.type === CST.TYPE_SEPARATOR) {
      rowHeight /= 2
    }

    // for the indicator show the end of folder
    if (payload.index === this.props.treeInfo.children.length - 1) {
      rowHeight += CST.GOLDEN_GAP * 2
    }

    return rowHeight
  }

  setHeight = () => {
    if (!this.list) return

    // search-box and tree-header-box height
    const listContainerElOffsetTop = this.list.Grid._scrollingContainer.getBoundingClientRect().top

    const totalRowHeight = this.props.treeInfo.children.reduce(
      (acc, x, index) => acc + this.getRowHeight({index}),
      0
    )

    const maxListHeight = CST.MAX_HEIGHT - listContainerElOffsetTop

    this.setState({
      listHeight: Math.min(maxListHeight, totalRowHeight)
    })
  }

  list: ?Object
  render = () => (

      {this.props.isShowHeader && (
        <TreeHeader
          title={this.props.treeInfo.parent.title}
          onClose={this.props.onCloseButtonClick}
        />
      )}

      <List
        ref={(ref) => {
          this.list = ref
        }}
        height={this.state.listHeight}
        // onScroll={this.props.onScroll}
        rowCount={this.props.treeInfo.children.length}
        rowHeight={this.getRowHeight}
        rowRenderer={(rendererProps: {| index: number, key: string, style: Object |}) => {
          const bookmarkInfo = this.props.treeInfo.children[rendererProps.index]
          return (

              <BookmarkRow
                key={bookmarkInfo.id}
                bookmarkInfo={bookmarkInfo}
                iconSize={this.props.iconSize}
                isFocused={this.props.focusId === bookmarkInfo.id}
                onAuxClick={this.props.onRowAuxClick}
                onClick={this.props.onRowClick}
                onMouseEnter={this.props.onRowMouseEnter}
                onMouseLeave={this.props.onRowMouseLeave}
              />

          )
        }}
        scrollToIndex={this.props.scrollToIndex >= 0 ? this.props.scrollToIndex : undefined}
        // scrollTop={this.props.lastScrollTop}
        tabIndex={-1}
        width={this.props.listItemWidth}
      />

      {this.props.isShowCover && (
        <Mask backgroundColor='#000' opacity={0.16} onClick={this.props.onCoverClick} />
      )}

  )
}

export default BookmarkTree
<|endoftext|>


<commit_before>'use strict';

/**
 * Application object.
 *
 * @author Michael Liao
 */

const
    isProduction = (process.env.NODE_ENV === 'production'),
    HOSTNAME = require('os').hostname(),
    _ = require('lodash'),
    fs = require('mz/fs'),
    Koa = require('koa'),
    Cookies = require('cookies'),
    bodyParser = require('koa-bodyparser'),
    templating = require('./middlewares/templating'),
    controller = require('./middlewares/controller'),
    authenticate = require('./middlewares/authenticate'),
    restify = require('./middlewares/restify'),
    logger = require('./logger.js'),
    api = require('./api'),
    auth = require('./auth'),
    config = require('./config'),
    db = require('./db'),
    cache = require('./cache'),
    constants = require('./constants'),
    i18n = require('./i18n'),
    i18nTranslators = i18n.loadI18NTranslators('./views/i18n'),
    static_prefix = config.cdn.static_prefix,
    ANTI_SPIDER = config.spider.antiSpider,
    SPIDER_WHITELIST = config.spider.whiteList,
    ACTIVE_THEME = config.theme;

function loadVersion() {
    try {
        let ver = require('./version');
        return ver;
    } catch (e) {
        logger.warn('failed to load version.');
        return Date.now();
    }
}

logger.info(`init app: production mode = ${isProduction}`);

// global app:
let app = new Koa();

app.proxy = config.proxy;

process.isProduction = isProduction;
process.appVersion = loadVersion();

function getRequestIp(ctx) {
    let ipAddr = ctx.headers['x-real-ip'];
    if (ipAddr) {
        return ipAddr;
    }
    ipAddr = ctx.headers['x-forwarded-for']
    if (ipAddr) {
        let n = ipAddr.indexOf(',');
        if (n > 0) {
            return ipAddr.substring(0, n);
        }
        return ipAddr;
    }
    return ctx.ip;
}

function isBot(ua, headers) {
    for (let bot of SPIDER_WHITELIST) {
        if (ua.indexOf(bot) >= 0) {
            logger.info(`detect bot: ${ua} headers: ${JSON.stringify(headers)}`);
            return true;
        }
    }
    return false;
}

function serviceUnavailable(ctx) {
    ctx.response.status = 503;
    ctx.response.body = '
503 Service Unavailable
';
}

// log request URL:
app.use(async (ctx, next) => {
    let
        start = Date.now(),
        ipAddr = getRequestIp(ctx);
    if (ANTI_SPIDER > 0) {
        let
            path = ctx.request.path,
            ua = (ctx.request.headers['user-agent'] || '').toLowerCase();
        if (path.startsWith('/blog/')) {
            logger.warn(`deny bad bot: ${ipAddr}: ${ua}`);
            await cache.set(ipAddr, 9999);
            return serviceUnavailable(ctx);
        }
        if (path === '/' || path.startsWith('/wiki') || path.startsWith('/article') || path.startsWith('/discuss') || path.startsWith('/category') || path.startsWith('/webpage')) {
            if (! isBot(ua, ctx.request.headers)) {
                if (ua.indexOf('mozilla') === (-1)) {
                    logger.warn(`deny bot without mozilla: ${ipAddr}: ${ua}`);
                    return serviceUnavailable(ctx);
                }
                let atsp = ctx.cookies.get('atsp');
                if (atsp) {
                    let sp = parseInt(atsp);
                    if (isNaN(sp) || (sp < (start - 600000)) || (sp > (start + 60000))) {
                        return serviceUnavailable(ctx);
                    }
                } else {
                    let n = await cache.incr(ipAddr);
                    if (n > 1) {
                        logger.warn(`potential bot: n=${n}: ${ipAddr} ${ua}`);
                        if (n > ANTI_SPIDER) {
                            logger.warn(`deny bot: ${n} times: ${ipAddr} ${ua}`);
                            return serviceUnavailable(ctx);
                        }
                    }
                }
            }
        }
    }
    logger.info(`will process request: ${ipAddr}, ${JSON.stringify(ctx.headers)} ${ctx.request.method} ${ctx.request.url}...`);
    try {
        await next();
    } catch (e) {
        logger.error('error process request.', e);
    }
    logger.info(`Response: ${ctx.response.status}`);
    let execTime = Date.now() - start;
    ctx.response.set('X-Response-Time', `${execTime}ms`);
    ctx.response.set('X-Host', HOSTNAME);
});

// static file support:
if (! isProduction) {
    let staticFiles = require('./middlewares/static-files');
    app.use(staticFiles('/static/', __dirname + '/static'));
}

// parse request body:
app.use(bodyParser());

// set filter:
let filters = {
    json: (input) => {
        return JSON.stringify(input);
    },
    min: function (input) {
        if (input <= 60) {
            return input + ' minutes';
        }
        let
            h = parseInt(input / 60),
            m = input % 60;
        return h + ' hours ' + m + ' minutes';
    }
};

// add nunjucks as view:
app.use(templating('views', {
    noCache: !isProduction,
    watch: !isProduction,
    filters: filters
}));

// parse user and bind to ctx.state.__user__:
app.use(authenticate);

// rest support:
app.use(restify());

// add global state for MVC:
app.use(async (ctx, next) => {
    let
        request = ctx.request,
        response = ctx.response,
        method = request.method,
        path = request.path,
        isApi = path.substring(0, 5) === '/api/';
    // check if login required for management:
    if (path.substring(0, 8) === '/manage/' && path !== '/manage/signin') {
        if (!ctx.state.__user__ || ctx.state.__user__.role > constants.role.CONTRIBUTOR) {
            response.redirect('/manage/signin');
            return;
        }
    }
    if (! isApi) {
        ctx.state._ = i18n.createI18N(request.get('Accept-Language') || 'en', i18nTranslators);
        ctx.state.__static_prefix__ = static_prefix;
        ctx.state.__time__ = Date.now();
        ctx.state.__theme__ = ACTIVE_THEME;
        ctx.state.__request__ = request;
    }
    await next();
    if (ctx.status === 404) {
        response.redirect('/404');
    }
});

// add controller:
app.use(controller());

module.exports = app;
<commit_msg>add log<commit_after>'use strict';

/**
 * Application object.
 *
 * @author Michael Liao
 */

const
    isProduction = (process.env.NODE_ENV === 'production'),
    HOSTNAME = require('os').hostname(),
    _ = require('lodash'),
    fs = require('mz/fs'),
    Koa = require('koa'),
    Cookies = require('cookies'),
    bodyParser = require('koa-bodyparser'),
    templating = require('./middlewares/templating'),
    controller = require('./middlewares/controller'),
    authenticate = require('./middlewares/authenticate'),
    restify = require('./middlewares/restify'),
    logger = require('./logger.js'),
    api = require('./api'),
    auth = require('./auth'),
    config = require('./config'),
    db = require('./db'),
    cache = require('./cache'),
    constants = require('./constants'),
    i18n = require('./i18n'),
    i18nTranslators = i18n.loadI18NTranslators('./views/i18n'),
    static_prefix = config.cdn.static_prefix,
    ANTI_SPIDER = config.spider.antiSpider,
    SPIDER_WHITELIST = config.spider.whiteList,
    ACTIVE_THEME = config.theme;

function loadVersion() {
    try {
        let ver = require('./version');
        return ver;
    } catch (e) {
        logger.warn('failed to load version.');
        return Date.now();
    }
}

logger.info(`init app: production mode = ${isProduction}`);

// global app:
let app = new Koa();

app.proxy = config.proxy;

process.isProduction = isProduction;
process.appVersion = loadVersion();

function getRequestIp(ctx) {
    let ipAddr = ctx.headers['x-real-ip'];
    if (ipAddr) {
        return ipAddr;
    }
    ipAddr = ctx.headers['x-forwarded-for']
    if (ipAddr) {
        let n = ipAddr.indexOf(',');
        if (n > 0) {
            return ipAddr.substring(0, n);
        }
        return ipAddr;
    }
    return ctx.ip;
}

function isBot(ua, headers) {
    for (let bot of SPIDER_WHITELIST) {
        if (ua.indexOf(bot) >= 0) {
            logger.info(`detect bot: ${ua} headers: ${JSON.stringify(headers)}`);
            return true;
        }
    }
    return false;
}

function serviceUnavailable(ctx) {
    ctx.response.status = 503;
    ctx.response.body = '
503 Service Unavailable
';
}

// log request URL:
app.use(async (ctx, next) => {
    let
        start = Date.now(),
        ipAddr = getRequestIp(ctx);
    if (ANTI_SPIDER > 0) {
        let
            path = ctx.request.path,
            ua = (ctx.request.headers['user-agent'] || '').toLowerCase();
        if (path.startsWith('/blog/')) {
            logger.warn(`deny bad bot: ${ipAddr}: ${ua}`);
            await cache.set(ipAddr, 9999);
            return serviceUnavailable(ctx);
        }
        if (path === '/' || path.startsWith('/wiki') || path.startsWith('/article') || path.startsWith('/discuss') || path.startsWith('/category') || path.startsWith('/webpage')) {
            if (! isBot(ua, ctx.request.headers)) {
                if (ua.indexOf('mozilla') === (-1)) {
                    logger.warn(`deny bot without mozilla: ${ipAddr}: ${ua}`);
                    return serviceUnavailable(ctx);
                }
                let atsp = ctx.cookies.get('atsp');
                if (atsp) {
                    let sp = parseInt(atsp);
                    if (isNaN(sp) || (sp < (start - 800000)) || (sp > (start + 60000))) {
                        logger.warn(`deny bot with bad atsp: now=${start}, atsp=${atsp}: ${ipAddr}: ${ua}`);
                        return serviceUnavailable(ctx);
                    }
                } else {
                    let n = await cache.incr(ipAddr);
                    if (n > 2) {
                        logger.warn(`potential bot: n=${n}: ${ipAddr} ${ua}`);
                        if (n > ANTI_SPIDER) {
                            logger.warn(`deny bot: ${n} times: ${ipAddr} ${ua}`);
                            return serviceUnavailable(ctx);
                        }
                    }
                }
            }
        }
    }
    logger.info(`will process request: ${ipAddr}, ${JSON.stringify(ctx.headers)} ${ctx.request.method} ${ctx.request.url}...`);
    try {
        await next();
    } catch (e) {
        logger.error('error process request.', e);
    }
    logger.info(`Response: ${ctx.response.status}`);
    let execTime = Date.now() - start;
    ctx.response.set('X-Response-Time', `${execTime}ms`);
    ctx.response.set('X-Host', HOSTNAME);
});

// static file support:
if (! isProduction) {
    let staticFiles = require('./middlewares/static-files');
    app.use(staticFiles('/static/', __dirname + '/static'));
}

// parse request body:
app.use(bodyParser());

// set filter:
let filters = {
    json: (input) => {
        return JSON.stringify(input);
    },
    min: function (input) {
        if (input <= 60) {
            return input + ' minutes';
        }
        let
            h = parseInt(input / 60),
            m = input % 60;
        return h + ' hours ' + m + ' minutes';
    }
};

// add nunjucks as view:
app.use(templating('views', {
    noCache: !isProduction,
    watch: !isProduction,
    filters: filters
}));

// parse user and bind to ctx.state.__user__:
app.use(authenticate);

// rest support:
app.use(restify());

// add global state for MVC:
app.use(async (ctx, next) => {
    let
        request = ctx.request,
        response = ctx.response,
        method = request.method,
        path = request.path,
        isApi = path.substring(0, 5) === '/api/';
    // check if login required for management:
    if (path.substring(0, 8) === '/manage/' && path !== '/manage/signin') {
        if (!ctx.state.__user__ || ctx.state.__user__.role > constants.role.CONTRIBUTOR) {
            response.redirect('/manage/signin');
            return;
        }
    }
    if (! isApi) {
        ctx.state._ = i18n.createI18N(request.get('Accept-Language') || 'en', i18nTranslators);
        ctx.state.__static_prefix__ = static_prefix;
        ctx.state.__time__ = Date.now();
        ctx.state.__theme__ = ACTIVE_THEME;
        ctx.state.__request__ = request;
    }
    await next();
    if (ctx.status === 404) {
        response.redirect('/404');
    }
});

// add controller:
app.use(controller());

module.exports = app;
<|endoftext|>


<commit_before>// LINK LIMITS
var GOOD_LINK_RSSI      = -70;
var MEDIUM_LINK_RSSI    = -80;
var GOOD_LINK_PDR       = 80;
var MEDIUM_LINK_PDR     = 50;

// LINK COLOR
var GOOD_LINK_COLOR     = "#00ff00"     // green
var MEDIUM_LINK_COLOR   = "#ffff00"     // orange
var BAD_LINK_COLOR      = "#ffff00"     // red
var UNKNOWN_LINK_COLOR  = "#ffffff"     // white

// Global variables
var map;
var MOTES = [];         // a list of [mac, Marker]
var LINKS = [];         // a list of [Polyline, rssi]
var timeout;
var infoWindow;
var SITE_TIME_OFFSET = -3

//------------------ Init functions ------------------------------------------//

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: {lat: -33.114974, lng: -68.481041},
        zoom: 18
    });
    map.setMapTypeId(google.maps.MapTypeId.SATELLITE);

    // set date
    $( "#datepicker" ).datepicker();
    $( "#datepicker" ).datepicker('setDate', 'today');

    // set time
    defaultDate = new Date();
    $('#timepicker').timepicker({
        showNowButton: true,
        showDeselectButton: true,
        showCloseButton: true,
    });
    $('#timepicker').timepicker('setTime', defaultDate.toLocaleTimeString(
                                'es-AR', { hour: "numeric",
                                minute: "numeric"}));
    load_data();
}

function load_data(loop){
    clearLinks();

    // set default time to current time minux 5 mins
    var date    = $("#datepicker").datepicker('getDate');
    var time    = $("#timepicker").timepicker('getTime').split(':');
    date.setHours(parseInt(time[0]) + SITE_TIME_OFFSET, time[1]);
    isoTime = date.toISOString()

    // MOTE CREATE
    var solType     = "SOL_TYPE_DUST_EVENTMOTECREATE";
    var encType     = encodeURIComponent(solType);
    var encTime     = encodeURIComponent(isoTime);
    $.getJSON("jsonp/ARG_junin/" + encType + "/time/" + encTime, create_mote);

    // LINKS CREATE
    solType         = "SOL_TYPE_DUST_NOTIF_HRNEIGHBORS";
    encType         = encodeURIComponent(solType);
    $.getJSON("jsonp/ARG_junin/" + encType + "/time/" + encTime, create_links);

    if (loop != 0)
        timeout = setTimeout(load_data, 30000);
    else
        clearTimeout(timeout)
}

//------------------ Main functions ------------------------------------------//

function create_mote(data){
    for (var i=0; i < data.length; i++) {
        // populate table
        MOTES[data[i].value.moteId] = [data[i].value.macAddress, null]
    }
}

function create_links(data){
    for (var i=0; i < data.length; i++) {
        udpateMote(data[i].mac, data[i].value.latitude,data[i].value.longitude);
    }
    for (var i=0; i < data.length; i++) {
        for (var j=0; j<data[i].value.neighbors.length; j++){
            var neighbor = data[i].value.neighbors[j];
            if (neighbor.neighborId in MOTES){
                var crd1 = getLocationFromMac(data[i].mac);
                var crd2 = getLocationFromId(neighbor.neighborId)
                if (crd1 != null && crd2 != null){
                    var lineCoordinates = [
                        crd1,
                        crd2
                    ];
                    var link    = getLink(crd1, crd2);

                    // get metric
                    var pdr     = ((neighbor.numTxPackets- neighbor.numTxFailures)
                                    /neighbor.numTxPackets
                                  ) * 100;
                    var rssi    = neighbor.rssi;

                    // set line parameters
                    var content =   "RSSI: " + rssi + "dB
" +
                                    "PDR: " + pdr + "%"
                    var color   = getLinkColor(rssi, pdr);

                    // update link if already exists and new rssi is worst
                    if (link != null){
                        if (neighbor.rssi < link[1]){
                            link[0].setMap(null);
                            link[0] = createPolyline(lineCoordinates, content, color);
                            link[1] = [rssi, pdr];
                        }
                    } // create link if it does not already exists
                    else {
                        var l = createPolyline(lineCoordinates, content, color);
                        LINKS.push([l, [rssi, pdr]]);
                    }
                }
            }
        }
    }
}

//----------------Interface Listeners ---------------------------------------//

$(document).ready(function() {
    $("#next_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // add 5 minutes
        var newDate = new Date(selectedTime.getTime() + 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0);
    });
});

$(document).ready(function() {
    $("#prev_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // remove 5 minutes
        var newDate = new Date(selectedTime.getTime() - 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0)
    });
});

//------------------ Helpers ------------------------------------------------//

function clearLinks(){
    // remove links
    for (var i=0; i<LINKS.length; i++) {
        if (i in LINKS){
            LINKS[i][0].setMap(null);
        }
    }
    LINKS = [];
    LINKS.length = 0;
}

function getLinkColor(rssi, pdr){
    var metric = $("#metric").val()

    if (metric == "rssi"){
        if (rssi > GOOD_LINK_RSSI)
            return GOOD_LINK_COLOR;
        else if (rssi >= MEDIUM_LINK_RSSI && rssi <= GOOD_LINK_RSSI)
            return MEDIUM_LINK_COLOR;
        else
            return BAD_LINK_COLOR;
    }
    else if (metric == "pdr"){
        if (pdr >= GOOD_LINK_PDR)
            return GOOD_LINK_COLOR;
        else if (pdr > MEDIUM_LINK_COLOR && pdr < GOOD_LINK_COLOR)
            return MEDIUM_LINK_COLOR;
        else if (pdr < 0)
            return BAD_LINK_COLOR;
        else
            return UNKNOWN_LINK_COLOR;
    }
}

function udpateMote(mac, lat, lng){
    var moteId = null;
    for (var i=0; i<MOTES.length; i++) {
        if (i in MOTES){
            if (MOTES[i][0] == mac){
                if (MOTES[i][1] == null){
                    MOTES[i][1] = createMarker(lat, lng, mac);
                } else if (MOTES[i][1].position.lat() != lat ||
                            MOTES[i][1].position.lng() != lng) {
                    MOTES[i][1].setMap(null);
                    MOTES[i][1] = createMarker(lat, lng, mac);
                }
            }
        }
    }
}

function createMarker(lat, lng, content){
    // create Markers
    var myLatLng = new google.maps.LatLng(lat, lng)
    var marker = new google.maps.Marker({
        position: myLatLng,
        map: map,
    });
    // add popup listener
    infoBox(map, marker, content);
    return marker;
}

function createPolyline(lineCoordinates, content, color){
    var opacity = 1.0;
    if (color == UNKNOWN_LINK_COLOR)
      opacity = 0.0001;
    var line = new google.maps.Polyline({
          path: lineCoordinates,
          geodesic: true,
          strokeColor: color,
          strokeOpacity: opacity,
          strokeWeight: 2
    });
    line.setMap(map);
    infoBox(map, line, content);
    return line;
}

function getLink(LatLng1, LatLng2){
    for (i=0; i<LINKS.length; i++) {
        link = LINKS[i][0];
        var coord1 = "("+link.getPath().getAt(0).lat()+", "+link.getPath().getAt(0).lng()+")"
        var coord2 = "("+link.getPath().getAt(1).lat()+", "+link.getPath().getAt(1).lng()+")"
        if ( ((LatLng1.toString() == coord1) && (LatLng2.toString() == coord2)) ||
             ((LatLng2.toString() == coord1) && (LatLng1.toString() == coord2))
           ){
            return LINKS[i]
        }
    }
    return null
}

function markerExists(LatLng){
    for (i in MOTES) {
        if (MOTES[i][0].getPosition().toString() == LatLng.toString())
          return 1;
    }
    return 0;
}

function getLocationFromId(moteId){
    if (MOTES[moteId][1] != null)
        return MOTES[moteId][1].position;
    else
        return null
}

function getLocationFromMac(mac){
    for( i=0; i<MOTES.length; i++ ) {
        if (i in MOTES && MOTES[i][0] == mac){
            return MOTES[i][1].position;
        }
    }
    console.log("MAC not found:" + mac)
}

/*Function that shows a window with the content or info
 about the marker (sensor location)*/

function infoBox(map, marker, content) {
    infoWindow = new google.maps.InfoWindow();
    // Attaching a click event to the current marker
    google.maps.event.addListener(marker, "click", function(e) {
        infoWindow.setPosition(e.latLng);
        infoWindow.setContent(content);
        infoWindow.open(map, marker);
    });
}

function pad2(s_num) {
    i_num = s_num.parseInt()
    p_num = (i_num < 10 ? '0' : '') + i_num
    return p_num.toString()
}

<commit_msg>#32 cleaning map code and correcting time offset<commit_after>// LINK LIMITS
var GOOD_LINK_RSSI      = -70;
var MEDIUM_LINK_RSSI    = -80;
var GOOD_LINK_PDR       = 80;
var MEDIUM_LINK_PDR     = 50;

// LINK COLOR
var GOOD_LINK_COLOR     = "#00ff00"     // green
var MEDIUM_LINK_COLOR   = "#ffff00"     // orange
var BAD_LINK_COLOR      = "#ffff00"     // red
var UNKNOWN_LINK_COLOR  = "#ffffff"     // white

// Global variables
var map;
var MOTES = [];         // a list of [mac, Marker]
var LINKS = [];         // a list of [Polyline, rssi]
var timeout;
var infoWindow;
var SITE_TIME_OFFSET = -3

//------------------ Init functions ------------------------------------------//

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: {lat: -33.114974, lng: -68.481041},
        zoom: 18
    });
    map.setMapTypeId(google.maps.MapTypeId.SATELLITE);

    // set date
    $( "#datepicker" ).datepicker();
    $( "#datepicker" ).datepicker('setDate', 'today');

    // set time
    defaultDate = new Date();
    $('#timepicker').timepicker({
        showNowButton: true,
        showDeselectButton: true,
        showCloseButton: true,
    });
    $('#timepicker').timepicker('setTime', defaultDate.toLocaleTimeString(
                                'es-AR', { hour: "numeric",
                                minute: "numeric"}));
    load_data();
}

function load_data(loop){
    clearLinks();

    // set default time to current time minux 5 mins
    var date        = $("#datepicker").datepicker('getDate');
    var time        = $("#timepicker").timepicker('getTime').split(':');
    var localOffset = new Date().getTimezoneOffset()/60;
    date.setHours(parseInt(time[0]) - localOffset, time[1]);
    isoTime = date.toISOString()

    // MOTE CREATE
    var solType     = "SOL_TYPE_DUST_EVENTMOTECREATE";
    var encType     = encodeURIComponent(solType);
    var encTime     = encodeURIComponent(isoTime);
    $.getJSON("jsonp/ARG_junin/" + encType + "/time/" + encTime, create_mote);

    // LINKS CREATE
    solType         = "SOL_TYPE_DUST_NOTIF_HRNEIGHBORS";
    encType         = encodeURIComponent(solType);
    $.getJSON("jsonp/ARG_junin/" + encType + "/time/" + encTime, create_links);

    if (loop != 0)
        timeout = setTimeout(load_data, 30000);
    else
        clearTimeout(timeout)
}

//------------------ Main functions ------------------------------------------//

function create_mote(data){
    for (var i=0; i < data.length; i++) {
        // populate table
        MOTES[data[i].value.moteId] = [data[i].value.macAddress, null]
    }
}

function create_links(data){
    for (var i=0; i < data.length; i++) {
        udpateMote(data[i].mac, data[i].value.latitude,data[i].value.longitude);
    }
    for (var i=0; i < data.length; i++) {
        for (var j=0; j<data[i].value.neighbors.length; j++){
            var neighbor = data[i].value.neighbors[j];
            if (neighbor.neighborId in MOTES){
                var crd1 = getLocationFromMac(data[i].mac);
                var crd2 = getLocationFromId(neighbor.neighborId)
                if (crd1 != null && crd2 != null){
                    var lineCoordinates = [
                        crd1,
                        crd2
                    ];
                    var link    = getLink(crd1, crd2);

                    // get metric
                    var pdr     = ((neighbor.numTxPackets- neighbor.numTxFailures)
                                    /neighbor.numTxPackets
                                  ) * 100;
                    var rssi    = neighbor.rssi;

                    // set line parameters
                    var content =   "RSSI: " + rssi + "dBm
" +
                                    "PDR: " + pdr + "%"
                    var color   = getLinkColor(rssi, pdr);

                    // update link if already exists and new rssi is worst
                    if (link != null){
                        if (neighbor.rssi < link[1]){
                            link[0].setMap(null);
                            link[0] = createPolyline(lineCoordinates, content, color);
                            link[1] = [rssi, pdr];
                        }
                    } // create link if it does not already exists
                    else {
                        var l = createPolyline(lineCoordinates, content, color);
                        LINKS.push([l, [rssi, pdr]]);
                    }
                }
            }
        }
    }
}

//----------------Interface Listeners ---------------------------------------//

$(document).ready(function() {
    $("#next_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // add 5 minutes
        var newDate = new Date(selectedTime.getTime() + 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0);
    });
});

$(document).ready(function() {
    $("#prev_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // remove 5 minutes
        var newDate = new Date(selectedTime.getTime() - 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0)
    });
});

//------------------ Helpers ------------------------------------------------//

function clearLinks(){
    // remove links
    for (var i=0; i<LINKS.length; i++) {
        if (i in LINKS){
            LINKS[i][0].setMap(null);
        }
    }
    LINKS = [];
    LINKS.length = 0;
}

function getLinkColor(rssi, pdr){
    var metric = $("#metric").val()

    if (metric == "rssi"){
        if (rssi > GOOD_LINK_RSSI)
            return GOOD_LINK_COLOR;
        else if (rssi >= MEDIUM_LINK_RSSI && rssi <= GOOD_LINK_RSSI)
            return MEDIUM_LINK_COLOR;
        else
            return BAD_LINK_COLOR;
    }
    else if (metric == "pdr"){
        if (pdr >= GOOD_LINK_PDR)
            return GOOD_LINK_COLOR;
        else if (pdr > MEDIUM_LINK_COLOR && pdr < GOOD_LINK_COLOR)
            return MEDIUM_LINK_COLOR;
        else if (pdr < 0)
            return BAD_LINK_COLOR;
        else
            return UNKNOWN_LINK_COLOR;
    }
}

function udpateMote(mac, lat, lng){
    var moteId = null;
    for (var i=0; i<MOTES.length; i++) {
        if (i in MOTES){
            if (MOTES[i][0] == mac){
                if (MOTES[i][1] == null){
                    MOTES[i][1] = createMarker(lat, lng, mac);
                } else if (MOTES[i][1].position.lat() != lat ||
                            MOTES[i][1].position.lng() != lng) {
                    MOTES[i][1].setMap(null);
                    MOTES[i][1] = createMarker(lat, lng, mac);
                }
            }
        }
    }
}

function createMarker(lat, lng, content){
    // create Markers
    var myLatLng = new google.maps.LatLng(lat, lng)
    var marker = new google.maps.Marker({
        position: myLatLng,
        map: map,
    });
    // add popup listener
    infoBox(map, marker, content);
    return marker;
}

function createPolyline(lineCoordinates, content, color){
    var opacity = 1.0;
    if (color == UNKNOWN_LINK_COLOR)
      opacity = 0.0001;
    var line = new google.maps.Polyline({
          path: lineCoordinates,
          geodesic: true,
          strokeColor: color,
          strokeOpacity: opacity,
          strokeWeight: 2
    });
    line.setMap(map);
    infoBox(map, line, content);
    return line;
}

function getLink(LatLng1, LatLng2){
    for (i=0; i<LINKS.length; i++) {
        link = LINKS[i][0];
        var coord1 = "("+link.getPath().getAt(0).lat()+", "+link.getPath().getAt(0).lng()+")"
        var coord2 = "("+link.getPath().getAt(1).lat()+", "+link.getPath().getAt(1).lng()+")"
        if ( ((LatLng1.toString() == coord1) && (LatLng2.toString() == coord2)) ||
             ((LatLng2.toString() == coord1) && (LatLng1.toString() == coord2))
           ){
            return LINKS[i]
        }
    }
    return null
}

function getLocationFromId(moteId){
    if (MOTES[moteId][1] != null)
        return MOTES[moteId][1].position;
    else
        return null
}

function getLocationFromMac(mac){
    for( i=0; i<MOTES.length; i++ ) {
        if (i in MOTES && MOTES[i][0] == mac){
            return MOTES[i][1].position;
        }
    }
    console.log("MAC not found:" + mac)
}

/*Function that shows a window with the content or info
 about the marker (sensor location)*/

function infoBox(map, marker, content) {
    infoWindow = new google.maps.InfoWindow();
    // Attaching a click event to the current marker
    google.maps.event.addListener(marker, "click", function(e) {
        infoWindow.setPosition(e.latLng);
        infoWindow.setContent(content);
        infoWindow.open(map, marker);
    });
}
<|endoftext|>


<commit_before>/*globals define: true*/
define(['jquery', './item'], function ($, ValidateItem) {
    'use strict';
    var Validate = function ($form) {
        this.validateItems = $('[data-validate]', $form).map(function () {
            return new ValidateItem($(this), $(this).data('validate'));
        });
    };

    Validate.prototype.validate = function (passed, failed) {
        $.each(this.validateItems, function (i, items) {
            items.validate();
        });
    };
    Validate.prototype.addValidItem = function ($item) {
    };
    Validate.prototype.validateItem = function ($item, passed, failed) {

    };
    return Validate;
});
<commit_msg>happy new year<commit_after>/*globals define: true*/
define(['jquery', './item'], function ($, ValidateItem) {
    'use strict';
    var Validate = function ($form) {
        this.validateItems = $('[data-validate]', $form).map(function () {
            return new ValidateItem($(this), $(this).data('validate'));
        });
    };
    Validate.prototype.validate = function (passed, failed) {
        $.each(this.validateItems, function (i, items) {
            items.validate();
        });
    };
    Validate.prototype.addValidItem = function ($item) {
    };
    Validate.prototype.validateItem = function ($item, passed, failed) {

    };
    return Validate;
});
<|endoftext|>


<commit_before>/*
 * validator plugin
 *
 * Copyright (c) 2013 Filament Group, Inc.
 * Licensed under MIT
 */

/* global Validator:true */
/* global jQuery:true */
(function( $, window, undefined ) {

	var pluginName = "validator",
		dataKey = pluginName,
		formSubmitErrorEventName = 'error.validator',
		initSelector = "[required],[data-validate]";

	$.fn[ pluginName ] = function(){
		return this.each(function(){
			var $el = $( this );

			if( $el.data( dataKey ) ) {
				return;
			}

			var validator = new Validator( this, {
				applyElement: $el.parents( "label, .btn" ).last()
			});

			$el.data( dataKey, validator );

			$el.bind( "blur", function() {
				validator.validate();
				//if( this.checkValidity ) {
				//	this.checkValidity();
				//}
			});

			//$el.bind( "invalid", function() {
			//	if( !( this.validity && this.validity.patternMismatch ) ) {
			//		validator.invalidate();
			//	}
			//});

			$el.closest( "form" ).bind( "submit", function( e ){
				if( !validator.validate() ){
					if( !e.isDefaultPrevented() ) {
						$( this ).trigger( formSubmitErrorEventName );
					}
					e.preventDefault();
				}
			});
		});
	};

	// auto-init on enhance (which is called on domready)
	$( document ).bind( "enhance", function( e ){
		$( initSelector, e.target )[ pluginName ]();
	});

}( jQuery, this ));
<commit_msg>Changes default applyElement function to work better with formcore<commit_after>/*
 * validator plugin
 *
 * Copyright (c) 2013 Filament Group, Inc.
 * Licensed under MIT
 */

/* global Validator:true */
/* global jQuery:true */
(function( $, window, undefined ) {

	var pluginName = "validator",
		dataKey = pluginName,
		formSubmitErrorEventName = 'error.validator',
		initSelector = "[required],[data-validate]";

	$.fn[ pluginName ] = function(){
		return this.each(function(){
			var $el = $( this );

			if( $el.data( dataKey ) ) {
				return;
			}

			var validator = new Validator( this, {
				applyElement: $el.parents().filter( "input, textarea, label, .custom-select, [data-validator-anchor]" ).last()
			});

			$el.data( dataKey, validator );

			$el.bind( "blur", function() {
				validator.validate();
				//if( this.checkValidity ) {
				//	this.checkValidity();
				//}
			});

			//$el.bind( "invalid", function() {
			//	if( !( this.validity && this.validity.patternMismatch ) ) {
			//		validator.invalidate();
			//	}
			//});

			$el.closest( "form" ).bind( "submit", function( e ){
				if( !validator.validate() ){
					if( !e.isDefaultPrevented() ) {
						$( this ).trigger( formSubmitErrorEventName );
					}
					e.preventDefault();
				}
			});
		});
	};

	// auto-init on enhance (which is called on domready)
	$( document ).bind( "enhance", function( e ){
		$( initSelector, e.target )[ pluginName ]();
	});

}( jQuery, this ));
<|endoftext|>