<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 modalDialogCustom = require('./app/ui/modal-dialog-custom')
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
  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) {
    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(`transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'} pending ... `)
          } else {
            self._api.logMessage(`call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`)
          }
        }
        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) {
                modalDialogCustom.alert(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 {
            modalDialogCustom.alert(error)
          }
        })
      } else {
        modalDialogCustom.alert(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.to, args.data, false, 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>set transactionContextAPI only if not null<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 modalDialogCustom = require('./app/ui/modal-dialog-custom')
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) {
    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(`transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'} pending ... `)
          } else {
            self._api.logMessage(`call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`)
          }
        }
        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) {
                modalDialogCustom.alert(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 {
            modalDialogCustom.alert(error)
          }
        })
      } else {
        modalDialogCustom.alert(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.to, args.data, false, 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>'use strict';

import React from 'react';
import Upvote from 'react-upvote';

// fake User/Posts APIs
import UserAPI from './UserAPI';
import PostsAPI from './PostsAPI';

const App = React.createClass({
    getInitialState() {
        PostsAPI.listen(this.updatePosts);
        UserAPI.listen(this.updateUser);

        return {
            user: UserAPI.getUser(),
            posts: PostsAPI.getPosts(),
            errorMessage: ''
        };
    },

    updatePosts(postsData) {
        this.setState({
            posts: postsData
        });
    },

    updateUser(userData) {
        this.setState({
            user: userData,
            errorMessage: ''
        });
    },

    errorMessage(message) {
        this.setState({
            errorMessage: message
        });
    },

    toggleLogin(e) {
        e.preventDefault();
        return this.state.user.isLoggedIn
            ? UserAPI.logout()
            : UserAPI.login();
    },

    upvotePost(id) {
        PostsAPI.upvote(id);
        UserAPI.upvote(id);
    },

    downvotePost(id) {
        PostsAPI.downvote(id);
        UserAPI.downvote(id);
    },

    removeVote(id) {
        let currentVoteStatus = this.state.user.votes[id];

        UserAPI.removeVote(id);

        if (currentVoteStatus === 1) {
            // remove upvote
            PostsAPI.downvote(id);
        } else {
            // remove downvote
            PostsAPI.upvote(id);
        }
    },

    render() {
        let user = this.state.user;
        let isLoggedIn = user.isLoggedIn;

        let posts = this.state.posts.map((postData, i) => (

                <Upvote
                    voteStatus={ user.votes[postData.id] || 0 }
                    upvoteContent={  }
                    downvoteContent={  }
                    afterContent={ { postData.upvotes } }
                    shouldAllow={ () => isLoggedIn }
                    onDisallowed={ () => this.errorMessage('You have to log in!') }
                    onUpvote={ () => this.upvotePost(postData.id) }
                    onDownvote={ () => this.downvotePost(postData.id) }
                    onRemoveVote={ () => this.removeVote(postData.id) }
                />

{ postData.title }

        ));

        return (

                    You are currently { isLoggedIn ? '' : 'NOT' } logged in.

                        { isLoggedIn ? 'Logout' : 'Login' }

                { posts }

{ this.state.errorMessage }

        );
    }
});

React.render(<App />, document.getElementById('app'));
<commit_msg>add fake api callbacks in example.js<commit_after>'use strict';

import React from 'react';
import Upvote from 'react-upvote';

// fake User/Posts APIs
import UserAPI from './UserAPI';
import PostsAPI from './PostsAPI';

const App = React.createClass({
    getInitialState() {
        PostsAPI.listen(this.updatePosts);
        UserAPI.listen(this.updateUser);

        return {
            user: UserAPI.getUser(),
            posts: PostsAPI.getPosts(),
            errorMessage: ''
        };
    },

    updatePosts(postsData) {
        this.setState({
            posts: postsData
        });
    },

    updateUser(userData) {
        this.setState({
            user: userData,
            errorMessage: ''
        });
    },

    errorMessage(message) {
        this.setState({
            errorMessage: message
        });
    },

    toggleLogin(e) {
        e.preventDefault();
        return this.state.user.isLoggedIn
            ? UserAPI.logout()
            : UserAPI.login();
    },

    upvotePost(id) {
        PostsAPI.upvote(id, () => {
            UserAPI.upvote(id);
        });
    },

    downvotePost(id) {
        PostsAPI.downvote(id, () => {
            UserAPI.downvote(id);
        });
    },

    removeVote(id) {
        let currentVoteStatus = this.state.user.votes[id];

        UserAPI.removeVote(id, () => {
            if (currentVoteStatus === 1) {
                // remove upvote
                PostsAPI.downvote(id);
            } else {
                // remove downvote
                PostsAPI.upvote(id);
            }
        });
    },

    render() {
        let user = this.state.user;
        let isLoggedIn = user.isLoggedIn;

        let posts = this.state.posts.map((postData, i) => (

                <Upvote
                    voteStatus={ user.votes[postData.id] || 0 }
                    upvoteContent={  }
                    downvoteContent={  }
                    afterContent={ { postData.upvotes } }
                    shouldAllow={ () => isLoggedIn }
                    onDisallowed={ () => this.errorMessage('You have to log in!') }
                    onUpvote={ () => this.upvotePost(postData.id) }
                    onDownvote={ () => this.downvotePost(postData.id) }
                    onRemoveVote={ () => this.removeVote(postData.id) }
                />

{ postData.title }

        ));

        return (

                    You are currently { isLoggedIn ? '' : 'NOT' } logged in.

                        { isLoggedIn ? 'Logout' : 'Login' }

                { posts }

{ this.state.errorMessage }

        );
    }
});

React.render(<App />, document.getElementById('app'));
<|endoftext|>


<commit_before>
module.exports = function () { // wrapper in case we're in module_context mode

"use strict";

var args       = require('minimist')(process.argv.slice(2));
var cache      = new (require('inmemfilecache'));
var Feed       = require('feed');
var fs         = require('fs');
var glob       = require('glob');
var Handlebars = require('handlebars');
var hanson     = require('hanson');
var marked     = require('marked');
var path       = require('path');
var Promise    = require('promise');
var sitemap    = require('sitemap');
var utils      = require('./utils');

//process.title = "build";

var executeP = Promise.denodeify(utils.execute);

marked.setOptions({
  rawHtml: true,
  //pedantic: true,
});

function applyObject(src, dst) {
  Object.keys(src).forEach(function(key) {
    dst[key] = src[key];
  });
  return dst;
}

function mergeObjects() {
  var merged = {};
  Array.prototype.slice.call(arguments).forEach(function(src) {
    applyObject(src, merged);
  });
  return merged;
}

function readFile(fileName) {
  return cache.readFileSync(fileName, "utf-8");
}

function writeFileIfChanged(fileName, content) {
  if (fs.existsSync(fileName)) {
    var old = readFile(fileName);
    if (content == old) {
      return;
    }
  }
  fs.writeFileSync(fileName, content);
  console.log("Wrote: " + fileName);
};

function copyFile(src, dst) {
  writeFileIfChanged(dst, readFile(src));
}

function replaceParams(str, params) {
  var template = Handlebars.compile(str);
  if (Array.isArray(params)) {
    params = mergeObjects.apply(null, params.slice().reverse());
  }

  return template(params);
}

function TemplateManager() {
  var templates = {};

  this.apply = function(filename, params) {
    var template = templates[filename];
    if (!template) {
      var template = Handlebars.compile(readFile(filename));
      templates[filename] = template;
    }

    if (Array.isArray(params)) {
      params = mergeObjects.apply(null, params.slice().reverse());
    }

    return template(params);
  };
}

var templateManager = new TemplateManager();

Handlebars.registerHelper('include', function(filename, options) {
  var context;
  if (options && options.hash && options.hash.filename) {
    var varName = options.hash.filename;
    filename = options.data.root[varName];
    context = options.hash;
  } else {
    context = options.data.root;
  }
  return templateManager.apply(filename, context);
});

Handlebars.registerHelper('example', function(options) {
  options.hash.width   = options.hash.width || "400";
  options.hash.height  = options.hash.height || "300";
  options.hash.caption = options.hash.caption || options.data.root.defaultExampleCaption;
  options.hash.examplePath = options.data.root.examplePath;

  return templateManager.apply("build/templates/example.template", options.hash);
});

Handlebars.registerHelper('diagram', function(options) {

  options.hash.width  = options.hash.width || "400";
  options.hash.height = options.hash.height || "300";
  options.hash.examplePath = options.data.root.examplePath;
  options.hash.className = options.hash.className || "";

  return templateManager.apply("build/templates/diagram.template", options.hash);
});

var Builder = function() {

  var g_articlesByLang = {};
  var g_articles = [];
  var g_langInfo;
  var g_langs;
  var g_langDB = {};

  var extractHeader = (function() {
    var headerRE = /([A-Z0-9_-]+): (.*?)$/i;

    return function(content) {
      var metaData = { };
      var lines = content.split("\n");
      while (true) {
        var line = lines[0].trim();
        var m = headerRE.exec(line);
        if (!m) {
          break;
        }
        metaData[m[1].toLowerCase()] = m[2];
        lines.shift();
      }
      return {
        content: lines.join("\n"),
        headers: metaData,
      };
    };
  }());

  var loadMD = function(contentFileName) {
    var content = cache.readFileSync(contentFileName, "utf-8");
    return extractHeader(content);
  };

  function extractHandlebars(content) {
    var tripleRE = /\{\{\{.*?\}\}\}/g;
    var doubleRE = /\{\{\{.*?\}\}\}/g;

    var numExtractions = 0;
    var extractions = {
    };

    function saveHandlebar(match) {
      var id = "==HANDLEBARS_ID_" + (++numExtractions) + "==";
      extractions[id] = match;
      return id;
    }

    content = content.replace(tripleRE, saveHandlebar);
    content = content.replace(doubleRE, saveHandlebar);

    return {
      content: content,
      extractions: extractions,
    };
  }

  function insertHandlebars(info, content) {
    var handlebarRE = /==HANDLEBARS_ID_\d+==/g;

    function restoreHandlebar(match) {
      var value = info.extractions[match];
      if (value === undefined) {
        throw new Error("no match restoring handlebar for: " + match);
      }
      return value;
    }

    content = content.replace(handlebarRE, restoreHandlebar);

    return content;
  }

  var applyTemplateToFile = function(templatePath, contentFileName, outFileName, opt_extra) {
    console.log("processing: ", contentFileName);
    opt_extra = opt_extra || {};
    var data = loadMD(contentFileName);
    // Call prep's Content which parses the HTML. This helps us find missing tags
    // should probably call something else.
    //Convert(md_content)
    var metaData = data.headers;
    var content = data.content;
    //console.log(JSON.stringify(metaData, undefined, "  "));
    var info = extractHandlebars(content);
    var html = marked(info.content);
    html = insertHandlebars(info, html);
    html = replaceParams(html, [opt_extra, g_langInfo]);
    metaData['content'] = html;
    metaData['langs'] = g_langs;
    metaData['src_file_name'] = contentFileName;
    metaData['dst_file_name'] = outFileName;
    metaData['basedir'] = "";
    metaData['toc'] = opt_extra.toc;
    metaData['templateOptions'] = opt_extra.templateOptions;
    metaData['langInfo'] = g_langInfo;
    metaData['url'] = "http://webglfundamentals.org/" + outFileName;
    metaData['screenshot'] = "http://webglfundamentals.org/webgl/lessons/resources/webglfundamentals.jpg";
    var basename = path.basename(contentFileName, ".md");
    [".jpg", ".png"].forEach(function(ext) {
      var filename = path.join("webgl", "lessons", "screenshots", basename + ext);
      if (fs.existsSync(filename)) {
        metaData['screenshot'] = "http://webglfundamentals.org/webgl/lessons/screenshots/" + basename + ext;
      }
    });

    var output = templateManager.apply(templatePath, metaData);
    writeFileIfChanged(outFileName, output)
    g_articles.push(metaData);
  };

  var applyTemplateToFiles = function(templatePath, filesSpec, extra) {
    var files = glob.sync(filesSpec);
    files.forEach(function(fileName) {
      var ext = path.extname(fileName);
      var baseName = fileName.substr(0, fileName.length - ext.length);
      var outFileName = baseName + ".html";
      applyTemplateToFile(templatePath, fileName, outFileName, extra);
    });

  };

  var addArticleByLang = function(article, lang) {
    var filename = path.basename(article.dst_file_name);
    var articleInfo = g_articlesByLang[filename];
    var url = "http://webglfundamentals.org/" + article.dst_file_name;
    if (!articleInfo) {
      articleInfo = {
        url: url,
        changefreq: 'monthly',
        links: [],
      };
      g_articlesByLang[filename] = articleInfo;
    }
    articleInfo.links.push({
      url: url,
      lang: lang,
    });
  };

  var getLanguageSelection = function(lang) {
    var lessons = lang.lessons || ("webgl/lessons/" + lang.lang);
    var langInfo = hanson.parse(fs.readFileSync(path.join(lessons, "langinfo.hanson"), {encoding: "utf8"}));
    g_langDB[lang.lang] = {
      lang: lang.lang,
      language: langInfo.language,
    };
    return templateManager.apply("build/templates/lang-select.template", [lang, langInfo]);
  };

  this.preProcess = function(langs) {
     var langs = langs.map(getLanguageSelection).join("\n");
     g_langs = templateManager.apply("build/templates/languages.template", {languages: langs});
  };

  this.process = function(options) {
    console.log("Processing Lang: " + options.lang);
    options.lessons     = options.lessons     || ("webgl/lessons/" + options.lang);
    options.toc         = options.toc         || ("webgl/lessons/" + options.lang + "/toc.html");
    options.template    = options.template    || "build/templates/lesson.template";
    options.examplePath = options.examplePath === undefined ? "../" : options.examplePath;

    g_articles = [];
    g_langInfo = hanson.parse(fs.readFileSync(path.join(options.lessons, "langinfo.hanson"), {encoding: "utf8"}));

    applyTemplateToFiles(options.template, path.join(options.lessons, "webgl*.md"), options);

    var toc = [];
    g_articles.forEach(function(article) {
      toc.push('
' + article.title + '
');
    });

    var tasks = g_articles.map(function(article, ndx) {
      return function() {
        return executeP('git', [
          'log',
          '--format="%ci"',
          '--name-only',
          '--diff-filter=A',
          article.src_file_name,
        ]).then(function(result) {
          var dateStr = result.stdout.split("\n")[0];
          article.date = new Date(Date.parse(dateStr));
        });
      };
    });

    return tasks.reduce(function(cur, next){
        return cur.then(next);
    }, Promise.resolve()).then(function() {
      var articles = g_articles.filter(function(article) {
        return article.date != undefined;
      });
      articles = articles.sort(function(a, b) {
        return a.date > b.date ? -1 : (a.date < b.date ? 1 : 0);
      });

      var feed = new Feed({
        title:          g_langInfo.title,
        description:    g_langInfo.description,
        link:           g_langInfo.link,
        image:          'http://webglfundamentals.org/webgl/lessons/resources/webglfundamentals.jpg',
        updated:        articles[0].date,
        author: {
          name:       'WebGLFundamenals Contributors',
          link:       'http://webglfundamentals.org/contributors.html',
        },
      });

      articles.forEach(function(article, ndx) {
        feed.addItem({
          title:          article.title,
          link:           "http://webglfundamentals.org/" + article.dst_file_name,
          description:    "",
          author: [
            {
              name:       'WebGLFundamenals Contributors',
              link:       'http://webglfundamentals.org/contributors.html',
            },
          ],
          // contributor: [
          // ],
          date:           article.date,
          // image:          posts[key].image
        });

        addArticleByLang(article, options.lang);
      });

      try {
        writeFileIfChanged(path.join(options.lessons, "atom.xml"), feed.render('atom-1.0'));
      } catch (err) {
        return Promise.reject(err);
      }
      return Promise.resolve();
    }).then(function() {
      applyTemplateToFile("build/templates/index.template", path.join(options.lessons, "index.md"), path.join(options.lessons, "index.html"), {
        table_of_contents: "
" + toc.join("\n") + "
",
        templateOptions: g_langInfo,
      });
      return Promise.resolve();
    }, function(err) {
      console.error("ERROR!:");
      console.error(err);
      if (err.stack) {
        console.error(err.stack);
      }
    });
  }

  this.writeGlobalFiles = function() {
    var sm = sitemap.createSitemap ({
      hostname: 'http://webglfundamentals.org',
      cacheTime: 600000,
    });
    var articleLangs = { };
    Object.keys(g_articlesByLang).forEach(function(filename) {
      var article = g_articlesByLang[filename];
      var langs = {};
      article.links.forEach(function(link) {
        langs[link.lang] = true;
      });
      articleLangs[filename] = langs;
      sm.add(article);
    });
    var langInfo = {
      articles: articleLangs,
      langs: g_langDB,
    };
    var langJS = "window.langDB = " + JSON.stringify(langInfo, null, 2);
    writeFileIfChanged("langdb.js", langJS);
    writeFileIfChanged("sitemap.xml", sm.toString());
    copyFile("webgl/lessons/atom.xml",  "atom.xml");
    copyFile("webgl/lessons/index.html",  "index.html");

    applyTemplateToFile("build/templates/index.template", "contributors.md", "contributors.html", {
      table_of_contents: "",
      templateOptions: "",
    });
  };

};

var b = new Builder();

var readdirs = function(dirpath) {
  var dirsOnly = function(filename) {
    var stat = fs.statSync(filename);
    return stat.isDirectory();
  };

  var addPath = function(filename) {
    return path.join(dirpath, filename);
  };

  return fs.readdirSync("webgl/lessons")
      .map(addPath)
      .filter(dirsOnly);
};

var isLangFolder = function(dirname) {
  var filename = path.join(dirname, "langinfo.hanson");
  return fs.existsSync(filename);
};

var pathToLang = function(filename) {
  return {
    lang: path.basename(filename),
  };
};

var langs = [
  // English is special (sorry it's where I started)
  {
    template: "build/templates/lesson.template",
    lessons: "webgl/lessons",
    lang: 'en',
    toc: 'webgl/lessons/toc.html',
    examplePath: '',
  },
];

langs = langs.concat(readdirs("webgl/lessons")
    .filter(isLangFolder)
    .map(pathToLang));

b.preProcess(langs);

var tasks = langs.map(function(lang) {
  return function() {
    return b.process(lang);
  };
});

return tasks.reduce(function(cur, next) {
  return cur.then(next);
}, Promise.resolve()).then(function() {
  b.writeGlobalFiles();
  cache.clear();
  return Promise.resolve();
});

};

<commit_msg>\ to /<commit_after>
module.exports = function () { // wrapper in case we're in module_context mode

"use strict";

var args       = require('minimist')(process.argv.slice(2));
var cache      = new (require('inmemfilecache'));
var Feed       = require('feed');
var fs         = require('fs');
var glob       = require('glob');
var Handlebars = require('handlebars');
var hanson     = require('hanson');
var marked     = require('marked');
var path       = require('path');
var Promise    = require('promise');
var sitemap    = require('sitemap');
var utils      = require('./utils');

//process.title = "build";

var executeP = Promise.denodeify(utils.execute);

marked.setOptions({
  rawHtml: true,
  //pedantic: true,
});

function applyObject(src, dst) {
  Object.keys(src).forEach(function(key) {
    dst[key] = src[key];
  });
  return dst;
}

function mergeObjects() {
  var merged = {};
  Array.prototype.slice.call(arguments).forEach(function(src) {
    applyObject(src, merged);
  });
  return merged;
}

function readFile(fileName) {
  return cache.readFileSync(fileName, "utf-8");
}

function writeFileIfChanged(fileName, content) {
  if (fs.existsSync(fileName)) {
    var old = readFile(fileName);
    if (content == old) {
      return;
    }
  }
  fs.writeFileSync(fileName, content);
  console.log("Wrote: " + fileName);
};

function copyFile(src, dst) {
  writeFileIfChanged(dst, readFile(src));
}

function replaceParams(str, params) {
  var template = Handlebars.compile(str);
  if (Array.isArray(params)) {
    params = mergeObjects.apply(null, params.slice().reverse());
  }

  return template(params);
}

function TemplateManager() {
  var templates = {};

  this.apply = function(filename, params) {
    var template = templates[filename];
    if (!template) {
      var template = Handlebars.compile(readFile(filename));
      templates[filename] = template;
    }

    if (Array.isArray(params)) {
      params = mergeObjects.apply(null, params.slice().reverse());
    }

    return template(params);
  };
}

var templateManager = new TemplateManager();

Handlebars.registerHelper('include', function(filename, options) {
  var context;
  if (options && options.hash && options.hash.filename) {
    var varName = options.hash.filename;
    filename = options.data.root[varName];
    context = options.hash;
  } else {
    context = options.data.root;
  }
  return templateManager.apply(filename, context);
});

Handlebars.registerHelper('example', function(options) {
  options.hash.width   = options.hash.width || "400";
  options.hash.height  = options.hash.height || "300";
  options.hash.caption = options.hash.caption || options.data.root.defaultExampleCaption;
  options.hash.examplePath = options.data.root.examplePath;

  return templateManager.apply("build/templates/example.template", options.hash);
});

Handlebars.registerHelper('diagram', function(options) {

  options.hash.width  = options.hash.width || "400";
  options.hash.height = options.hash.height || "300";
  options.hash.examplePath = options.data.root.examplePath;
  options.hash.className = options.hash.className || "";

  return templateManager.apply("build/templates/diagram.template", options.hash);
});

var Builder = function() {

  var g_articlesByLang = {};
  var g_articles = [];
  var g_langInfo;
  var g_langs;
  var g_langDB = {};

  var extractHeader = (function() {
    var headerRE = /([A-Z0-9_-]+): (.*?)$/i;

    return function(content) {
      var metaData = { };
      var lines = content.split("\n");
      while (true) {
        var line = lines[0].trim();
        var m = headerRE.exec(line);
        if (!m) {
          break;
        }
        metaData[m[1].toLowerCase()] = m[2];
        lines.shift();
      }
      return {
        content: lines.join("\n"),
        headers: metaData,
      };
    };
  }());

  var loadMD = function(contentFileName) {
    var content = cache.readFileSync(contentFileName, "utf-8");
    return extractHeader(content);
  };

  function extractHandlebars(content) {
    var tripleRE = /\{\{\{.*?\}\}\}/g;
    var doubleRE = /\{\{\{.*?\}\}\}/g;

    var numExtractions = 0;
    var extractions = {
    };

    function saveHandlebar(match) {
      var id = "==HANDLEBARS_ID_" + (++numExtractions) + "==";
      extractions[id] = match;
      return id;
    }

    content = content.replace(tripleRE, saveHandlebar);
    content = content.replace(doubleRE, saveHandlebar);

    return {
      content: content,
      extractions: extractions,
    };
  }

  function insertHandlebars(info, content) {
    var handlebarRE = /==HANDLEBARS_ID_\d+==/g;

    function restoreHandlebar(match) {
      var value = info.extractions[match];
      if (value === undefined) {
        throw new Error("no match restoring handlebar for: " + match);
      }
      return value;
    }

    content = content.replace(handlebarRE, restoreHandlebar);

    return content;
  }

  var applyTemplateToFile = function(templatePath, contentFileName, outFileName, opt_extra) {
    console.log("processing: ", contentFileName);
    opt_extra = opt_extra || {};
    var data = loadMD(contentFileName);
    // Call prep's Content which parses the HTML. This helps us find missing tags
    // should probably call something else.
    //Convert(md_content)
    var metaData = data.headers;
    var content = data.content;
    //console.log(JSON.stringify(metaData, undefined, "  "));
    var info = extractHandlebars(content);
    var html = marked(info.content);
    html = insertHandlebars(info, html);
    html = replaceParams(html, [opt_extra, g_langInfo]);
    metaData['content'] = html;
    metaData['langs'] = g_langs;
    metaData['src_file_name'] = contentFileName.replace(/\\/g, '/');
    metaData['dst_file_name'] = outFileName.replace(/\\/g, '/');
    metaData['basedir'] = "";
    metaData['toc'] = opt_extra.toc;
    metaData['templateOptions'] = opt_extra.templateOptions;
    metaData['langInfo'] = g_langInfo;
    metaData['url'] = "http://webglfundamentals.org/" + outFileName.replace(/\\/g, '/');
    metaData['screenshot'] = "http://webglfundamentals.org/webgl/lessons/resources/webglfundamentals.jpg";
    var basename = path.basename(contentFileName, ".md");
    [".jpg", ".png"].forEach(function(ext) {
      var filename = path.join("webgl", "lessons", "screenshots", basename + ext);
      if (fs.existsSync(filename)) {
        metaData['screenshot'] = "http://webglfundamentals.org/webgl/lessons/screenshots/" + basename + ext;
      }
    });

    var output = templateManager.apply(templatePath, metaData);
    writeFileIfChanged(outFileName, output)
    g_articles.push(metaData);
  };

  var applyTemplateToFiles = function(templatePath, filesSpec, extra) {
    var files = glob.sync(filesSpec);
    files.forEach(function(fileName) {
      var ext = path.extname(fileName);
      var baseName = fileName.substr(0, fileName.length - ext.length);
      var outFileName = baseName + ".html";
      applyTemplateToFile(templatePath, fileName, outFileName, extra);
    });

  };

  var addArticleByLang = function(article, lang) {
    var filename = path.basename(article.dst_file_name);
    var articleInfo = g_articlesByLang[filename];
    var url = "http://webglfundamentals.org/" + article.dst_file_name;
    if (!articleInfo) {
      articleInfo = {
        url: url,
        changefreq: 'monthly',
        links: [],
      };
      g_articlesByLang[filename] = articleInfo;
    }
    articleInfo.links.push({
      url: url,
      lang: lang,
    });
  };

  var getLanguageSelection = function(lang) {
    var lessons = lang.lessons || ("webgl/lessons/" + lang.lang);
    var langInfo = hanson.parse(fs.readFileSync(path.join(lessons, "langinfo.hanson"), {encoding: "utf8"}));
    g_langDB[lang.lang] = {
      lang: lang.lang,
      language: langInfo.language,
    };
    return templateManager.apply("build/templates/lang-select.template", [lang, langInfo]);
  };

  this.preProcess = function(langs) {
     var langs = langs.map(getLanguageSelection).join("\n");
     g_langs = templateManager.apply("build/templates/languages.template", {languages: langs});
  };

  this.process = function(options) {
    console.log("Processing Lang: " + options.lang);
    options.lessons     = options.lessons     || ("webgl/lessons/" + options.lang);
    options.toc         = options.toc         || ("webgl/lessons/" + options.lang + "/toc.html");
    options.template    = options.template    || "build/templates/lesson.template";
    options.examplePath = options.examplePath === undefined ? "../" : options.examplePath;

    g_articles = [];
    g_langInfo = hanson.parse(fs.readFileSync(path.join(options.lessons, "langinfo.hanson"), {encoding: "utf8"}));

    applyTemplateToFiles(options.template, path.join(options.lessons, "webgl*.md"), options);

    var toc = [];
    g_articles.forEach(function(article) {
      toc.push('
' + article.title + '
');
    });

    var tasks = g_articles.map(function(article, ndx) {
      return function() {
        return executeP('git', [
          'log',
          '--format="%ci"',
          '--name-only',
          '--diff-filter=A',
          article.src_file_name,
        ]).then(function(result) {
          var dateStr = result.stdout.split("\n")[0];
          article.date = new Date(Date.parse(dateStr));
        });
      };
    });

    return tasks.reduce(function(cur, next){
        return cur.then(next);
    }, Promise.resolve()).then(function() {
      var articles = g_articles.filter(function(article) {
        return article.date != undefined;
      });
      articles = articles.sort(function(a, b) {
        return a.date > b.date ? -1 : (a.date < b.date ? 1 : 0);
      });

      var feed = new Feed({
        title:          g_langInfo.title,
        description:    g_langInfo.description,
        link:           g_langInfo.link,
        image:          'http://webglfundamentals.org/webgl/lessons/resources/webglfundamentals.jpg',
        updated:        articles[0].date,
        author: {
          name:       'WebGLFundamenals Contributors',
          link:       'http://webglfundamentals.org/contributors.html',
        },
      });

      articles.forEach(function(article, ndx) {
        feed.addItem({
          title:          article.title,
          link:           "http://webglfundamentals.org/" + article.dst_file_name,
          description:    "",
          author: [
            {
              name:       'WebGLFundamenals Contributors',
              link:       'http://webglfundamentals.org/contributors.html',
            },
          ],
          // contributor: [
          // ],
          date:           article.date,
          // image:          posts[key].image
        });

        addArticleByLang(article, options.lang);
      });

      try {
        writeFileIfChanged(path.join(options.lessons, "atom.xml"), feed.render('atom-1.0'));
      } catch (err) {
        return Promise.reject(err);
      }
      return Promise.resolve();
    }).then(function() {
      applyTemplateToFile("build/templates/index.template", path.join(options.lessons, "index.md"), path.join(options.lessons, "index.html"), {
        table_of_contents: "
" + toc.join("\n") + "
",
        templateOptions: g_langInfo,
      });
      return Promise.resolve();
    }, function(err) {
      console.error("ERROR!:");
      console.error(err);
      if (err.stack) {
        console.error(err.stack);
      }
    });
  }

  this.writeGlobalFiles = function() {
    var sm = sitemap.createSitemap ({
      hostname: 'http://webglfundamentals.org',
      cacheTime: 600000,
    });
    var articleLangs = { };
    Object.keys(g_articlesByLang).forEach(function(filename) {
      var article = g_articlesByLang[filename];
      var langs = {};
      article.links.forEach(function(link) {
        langs[link.lang] = true;
      });
      articleLangs[filename] = langs;
      sm.add(article);
    });
    var langInfo = {
      articles: articleLangs,
      langs: g_langDB,
    };
    var langJS = "window.langDB = " + JSON.stringify(langInfo, null, 2);
    writeFileIfChanged("langdb.js", langJS);
    writeFileIfChanged("sitemap.xml", sm.toString());
    copyFile("webgl/lessons/atom.xml",  "atom.xml");
    copyFile("webgl/lessons/index.html",  "index.html");

    applyTemplateToFile("build/templates/index.template", "contributors.md", "contributors.html", {
      table_of_contents: "",
      templateOptions: "",
    });
  };

};

var b = new Builder();

var readdirs = function(dirpath) {
  var dirsOnly = function(filename) {
    var stat = fs.statSync(filename);
    return stat.isDirectory();
  };

  var addPath = function(filename) {
    return path.join(dirpath, filename);
  };

  return fs.readdirSync("webgl/lessons")
      .map(addPath)
      .filter(dirsOnly);
};

var isLangFolder = function(dirname) {
  var filename = path.join(dirname, "langinfo.hanson");
  return fs.existsSync(filename);
};

var pathToLang = function(filename) {
  return {
    lang: path.basename(filename),
  };
};

var langs = [
  // English is special (sorry it's where I started)
  {
    template: "build/templates/lesson.template",
    lessons: "webgl/lessons",
    lang: 'en',
    toc: 'webgl/lessons/toc.html',
    examplePath: '',
  },
];

langs = langs.concat(readdirs("webgl/lessons")
    .filter(isLangFolder)
    .map(pathToLang));

b.preProcess(langs);

var tasks = langs.map(function(lang) {
  return function() {
    return b.process(lang);
  };
});

return tasks.reduce(function(cur, next) {
  return cur.then(next);
}, Promise.resolve()).then(function() {
  b.writeGlobalFiles();
  cache.clear();
  return Promise.resolve();
});

};

<|endoftext|>


<commit_before>'use strict';

/**
 *
 */
var Player = function(settings) {
    this.init(settings);
};

Player.prototype = {
    player: null,
    playerIndex: null,
    playerSelector: '#social-music-player',
    songsList: [],
    el: null,

    /**
     *
     */
    init: function(settings) {
      this.playerIndex = settings.playerIndex || 0;
      this.songsList = settings.songsList || [];
    },

    /**
     *
     */
    render: function() {
        let playerEl = _.template(this._getPlayerTemplate());

        this.el = $(playerEl({
            playerIndex: this.playerIndex,
            elID: this.getPlayerID().substr(1)
          }));

        this._subscribePlayerEvents();
        this._appendSongsList();
        this._simpleButtonsBind();
        this._modifierButtonsBind();
        this._getterButtonsBind();
        this._consoleButtonsBind();

        this.log('Rendering PlayerID: '+ this.playerIndex);

        return this.el;
    },

    /**
     *
     */
    log: function(message) {
      let output = this.el.find('.console .output');

      output.html(message + '\n' + output.html());

      return this;
    },

    /**
     *
     */
    clearLog: function() {
      let output = this.el.find('.console .output');

      output.html('');

      return this;
    },

    /**
     *
     */
    _subscribePlayerEvents: function() {
        var player = this.getPlayer(),
            self = this;

        player.on(this.player.EVENT.PLAYER_INITIALIZED, function() {
          self.log('PLAYER_INITIALIZED');
        });

        player.on(this.player.EVENT.PLAY, function() {
          self.log('PLAY');
        });

        player.on(this.player.EVENT.PAUSE, function() {
          self.log('PAUSE');
        });

        player.on(this.player.EVENT.VOLUME, function() {
          self.log('VOLUME');
        });

        player.on(this.player.EVENT.MUTE, function() {
          self.log('MUTE');
        });

        player.on(this.player.EVENT.UNMUTE, function() {
          self.log('UNMUTE');
        });

        player.on(this.player.EVENT.SEEK_TO_SECOND, function() {
          self.log('SEEK_TO_SECOND');
        });

        player.on(this.player.EVENT.SEEK_TO_PERCENT, function() {
          self.log('SEEK_TO_PERCENT');
        });

        player.on(this.player.EVENT.STATE_CHANGED, function() {
          self.log('STATE_CHANGED');
        });

        player.on(this.player.EVENT.ON_PROGRESS, function() {
          self.log('ON_PROGRESS');
        });

        player.on(this.player.EVENT.NEW_SONG_PLAYING, function() {
          self.log('NEW_SONG_PLAYING');
        });

        return this;
    },

    /**
     *
     */
    _simpleButtonsBind: function() {
        var player = this.getPlayer();

        this.el.find('.simple-buttons .play').on('click', function() {
          player.play();
        });

        this.el.find('.simple-buttons .pause').on('click', function() {
          player.pause();
        });

        return this;
    },

    /**
     *
     */
    _modifierButtonsBind: function() {
        var player = this.getPlayer();

        this.el.find('.modifier-buttons .seek').on('click', function() {
          var seconds = this.querySelector('input').value;
          player.seek(seconds);
        });

        this.el.find('.modifier-buttons .volume').on('click', function() {
          var volume = this.querySelector('input').value;
          player.setVolume(volume);
        });

        this.el.find('.modifier-buttons .mute').on('click', function() {
          player.mute();
        });

        this.el.find('.modifier-buttons .unmute').on('click', function() {
          player.unmute();
        });

        this.el.find('.modifier-buttons .url').on('click', function() {
          var url = this.querySelector('input').value;
          player.play(url);
        });

        return this;
    },

    /**
     *
     */
    _getterButtonsBind: function() {
        var player = this.getPlayer();

        this.el.find('.getter-buttons .current-second').on('click', function() {
          console.log('Current Second');
        });

        this.el.find('.getter-buttons .duration').on('click', function() {
          console.log('Duration');
        });

        this.el.find('.getter-buttons .volume').on('click', function() {
          console.log('Volume');
        });

        this.el.find('.getter-buttons .is-playing').on('click', function() {
          console.log('isPlaying');
        });

        this.el.find('.getter-buttons .is-paused').on('click', function() {
          console.log('isPaused');
        });

        this.el.find('.getter-buttons .url').on('click', function() {
          console.log('URL');
        });

        this.el.find('.getter-buttons .video-width').on('click', function() {
          console.log('Video Width');
        });

        this.el.find('.getter-buttons .video-height').on('click', function() {
          console.log('Video Height');
        });

        return this;
    },

    /**
     *
     */
    _consoleButtonsBind: function() {
        var self = this;

        this.el.find('.console .clear').on('click', function() {
          self.clearLog();
        });

        return this;
    },

    /**
     *
     */
    _appendSongsList: function() {
      var songBitTpl = _.template('
<%- title %>
'),
          player = this.getPlayer();

      for (var songIdx in this.songsList) {
        var songHTML = $(songBitTpl({
          title: this.songsList[songIdx].title
        }));

        songHTML.find('a').on('click', function(songModel) {
          return function() {
            player.play(songModel);
          }
        }(this.songsList[songIdx]));

        this.el.find('ul.songs-list').append(songHTML);
      }

      return this;
    },

    /**
     *
     */
    getPlayer: function() {
      if (null !== this.player) {
        return this.player;
      }

      this.player = window.SocialMusicPlayer.create({
        elID: this.getPlayerID(),
        width: '100%',
        height: '100%'
      });

      return this.player;
    },

    getPlayerID: function() {
      return this.playerSelector + this.playerIndex;
    },

    /**
     *
     */
    _getPlayerTemplate: function () {
        return `

SMP Player #<%- playerIndex %>

                    <div id="<%- elID %>">

                        Demo songs list
                        (Those can be played one by one from each supported provider)

                        Simple Controls
                        (Buttons will call API functions on SMP)

                        <button class="play">Play</button>
                        <button class="pause">Pause</button>

                        Modifier Controls
                        (buttons to interact with the player during runtime)

<button class="seek">Seek <input type="text" size="3" maxlength="3" value="60" /></button>

<button class="volume">Volume <input type="text" size="3" maxlength="3" value="0.8" /></button>

<button class="mute">Mute</button>

<button class="unmute">Unmute</button>

<button class="url">Vimeo <input type="text" size="30" maxlength="80" value="https://vimeo.com/126864213" /></button>

<button class="url">Youtube <input type="text" size="50" maxlength="80" value="https://www.youtube.com/watch?v=w319Ew1quF0" /></button>

                        Getters
                        (getters for the values of diffrent player states)

<button class="current-second">Current Second</button>

<button class="duration">Duration</button>

<button class="volume">Volume</button>

<button class="is-playing">isPlaying</button>

<button class="is-paused">isPaused</button>

<button class="url">URL</button>

<button class="video-width">Video Width</button>

<button class="video-height">Video Height</button>

                        Event Listeners

<input class="new-song-playing" type="checkbox" checked="checked" /> newSongPlaying

<input class="play-progress" type="checkbox" checked="checked" /> playProgress

<input class="play" type="checkbox" checked="checked" /> play

<input class="pause" type="checkbox" checked="checked" /> pause

<input class="finish" type="checkbox" checked="checked" /> finish

<input class="seek-second" type="checkbox" checked="checked" /> seekSecond

<input class="seek-percent" type="checkbox" checked="checked" /> seekPercent

<input class="mute" type="checkbox" checked="checked" /> mute

<input class="unmute" type="checkbox" checked="checked" /> unmute

<input class="volume" type="checkbox" checked="checked" /> volume

Console

                        <textarea class="output" readonly="readonly"></textarea>
                        <button class="clear">Clear</button>

        `;
    }
};<commit_msg>added input:checked to prevent not needed events<commit_after>'use strict';

/**
 *
 */
var Player = function(settings) {
    this.init(settings);
};

Player.prototype = {
    player: null,
    playerIndex: null,
    playerSelector: '#social-music-player',
    songsList: [],
    el: null,

    /**
     *
     */
    init: function(settings) {
      this.playerIndex = settings.playerIndex || 0;
      this.songsList = settings.songsList || [];
    },

    /**
     *
     */
    render: function() {
        let playerEl = _.template(this._getPlayerTemplate());

        this.el = $(playerEl({
            playerIndex: this.playerIndex,
            elID: this.getPlayerID().substr(1)
          }));

        this._subscribePlayerEvents();
        this._appendSongsList();
        this._simpleButtonsBind();
        this._modifierButtonsBind();
        this._getterButtonsBind();
        this._consoleButtonsBind();

        this.log('Rendering PlayerID: '+ this.playerIndex);

        return this.el;
    },

    /**
     *
     */
    log: function(message) {
      let output = this.el.find('.console .output');

      output.html(message + '\n' + output.html());

      return this;
    },

    /**
     *
     */
    clearLog: function() {
      let output = this.el.find('.console .output');

      output.html('');

      return this;
    },

    isChecked: function(selector) {
      return this.el.find(selector).is(':checked');
    },

    /**
     *
     */
    _subscribePlayerEvents: function() {
        var player = this.getPlayer(),
            self = this;

        player.on(this.player.EVENT.PLAYER_INITIALIZED, function() {
          self.log('PLAYER_INITIALIZED');
        });

        player.on(this.player.EVENT.PLAY, function() {
          if (!self.isChecked('.event-buttons input.play')) return;

          self.log('PLAY');
        });

        player.on(this.player.EVENT.PAUSE, function() {
          if (!self.isChecked('.event-buttons input.pause')) return;

          self.log('PAUSE');
        });

        player.on(this.player.EVENT.VOLUME, function() {
          if (!self.isChecked('.event-buttons input.volume')) return;

          self.log('VOLUME');
        });

        player.on(this.player.EVENT.MUTE, function() {
          if (!self.isChecked('.event-buttons input.mute')) return;

          self.log('MUTE');
        });

        player.on(this.player.EVENT.UNMUTE, function() {
          if (!self.isChecked('.event-buttons input.unmute')) return;

          self.log('UNMUTE');
        });

        player.on(this.player.EVENT.SEEK_TO_SECOND, function() {
          if (!self.isChecked('.event-buttons input.seek-second')) return;

          self.log('SEEK_TO_SECOND');
        });

        player.on(this.player.EVENT.SEEK_TO_PERCENT, function() {
          if (!self.isChecked('.event-buttons input.seek-percent')) return;

          self.log('SEEK_TO_PERCENT');
        });

        player.on(this.player.EVENT.STATE_CHANGED, function() {
          if (!self.isChecked('.event-buttons input.state-changed')) return;

          self.log('STATE_CHANGED');
        });

        player.on(this.player.EVENT.PLAY_PROGRESS, function() {
          if (!self.isChecked('.event-buttons input.play-progress')) return;

          self.log('PLAY_PROGRESS');
        });

        player.on(this.player.EVENT.NEW_SONG_PLAYING, function() {
          if (!self.isChecked('.event-buttons input.new-song-playing')) return;

          self.log('NEW_SONG_PLAYING');
        });

        return this;
    },

    /**
     *
     */
    _simpleButtonsBind: function() {
        var player = this.getPlayer();

        this.el.find('.simple-buttons .play').on('click', function() {
          player.play();
        });

        this.el.find('.simple-buttons .pause').on('click', function() {
          player.pause();
        });

        return this;
    },

    /**
     *
     */
    _modifierButtonsBind: function() {
        var player = this.getPlayer();

        this.el.find('.modifier-buttons .seek').on('click', function() {
          var seconds = this.querySelector('input').value;
          player.seek(seconds);
        });

        this.el.find('.modifier-buttons .volume').on('click', function() {
          var volume = this.querySelector('input').value;
          player.setVolume(volume);
        });

        this.el.find('.modifier-buttons .mute').on('click', function() {
          player.mute();
        });

        this.el.find('.modifier-buttons .unmute').on('click', function() {
          player.unmute();
        });

        this.el.find('.modifier-buttons .url').on('click', function() {
          var url = this.querySelector('input').value;
          player.play(url);
        });

        return this;
    },

    /**
     *
     */
    _getterButtonsBind: function() {
        var player = this.getPlayer();

        this.el.find('.getter-buttons .current-second').on('click', function() {
          console.log('Current Second');
        });

        this.el.find('.getter-buttons .duration').on('click', function() {
          console.log('Duration');
        });

        this.el.find('.getter-buttons .volume').on('click', function() {
          console.log('Volume');
        });

        this.el.find('.getter-buttons .is-playing').on('click', function() {
          console.log('isPlaying');
        });

        this.el.find('.getter-buttons .is-paused').on('click', function() {
          console.log('isPaused');
        });

        this.el.find('.getter-buttons .url').on('click', function() {
          console.log('URL');
        });

        this.el.find('.getter-buttons .video-width').on('click', function() {
          console.log('Video Width');
        });

        this.el.find('.getter-buttons .video-height').on('click', function() {
          console.log('Video Height');
        });

        return this;
    },

    /**
     *
     */
    _consoleButtonsBind: function() {
        var self = this;

        this.el.find('.console .clear').on('click', function() {
          self.clearLog();
        });

        return this;
    },

    /**
     *
     */
    _appendSongsList: function() {
      var songBitTpl = _.template('
<%- title %>
'),
          player = this.getPlayer();

      for (var songIdx in this.songsList) {
        var songHTML = $(songBitTpl({
          title: this.songsList[songIdx].title
        }));

        songHTML.find('a').on('click', function(songModel) {
          return function() {
            player.play(songModel);
          }
        }(this.songsList[songIdx]));

        this.el.find('ul.songs-list').append(songHTML);
      }

      return this;
    },

    /**
     *
     */
    getPlayer: function() {
      if (null !== this.player) {
        return this.player;
      }

      this.player = window.SocialMusicPlayer.create({
        elID: this.getPlayerID(),
        width: '100%',
        height: '100%'
      });

      return this.player;
    },

    getPlayerID: function() {
      return this.playerSelector + this.playerIndex;
    },

    /**
     *
     */
    _getPlayerTemplate: function () {
        return `

SMP Player #<%- playerIndex %>

                    <div id="<%- elID %>">

                        Demo songs list
                        (Those can be played one by one from each supported provider)

                        Simple Controls
                        (Buttons will call API functions on SMP)

                        <button class="play">Play</button>
                        <button class="pause">Pause</button>

                        Modifier Controls
                        (buttons to interact with the player during runtime)

<button class="seek">Seek <input type="text" size="3" maxlength="3" value="60" /></button>

<button class="volume">Volume <input type="text" size="3" maxlength="3" value="0.8" /></button>

<button class="mute">Mute</button>

<button class="unmute">Unmute</button>

<button class="url">Vimeo <input type="text" size="30" maxlength="80" value="https://vimeo.com/126864213" /></button>

<button class="url">Youtube <input type="text" size="50" maxlength="80" value="https://www.youtube.com/watch?v=w319Ew1quF0" /></button>

                        Getters
                        (getters for the values of diffrent player states)

<button class="current-second">Current Second</button>

<button class="duration">Duration</button>

<button class="volume">Volume</button>

<button class="is-playing">isPlaying</button>

<button class="is-paused">isPaused</button>

<button class="url">URL</button>

<button class="video-width">Video Width</button>

<button class="video-height">Video Height</button>

                        Event Listeners

<input class="new-song-playing" type="checkbox" checked="checked" /> newSongPlaying

<input class="play-progress" type="checkbox" checked="checked" /> playProgress

<input class="play" type="checkbox" checked="checked" /> play

<input class="pause" type="checkbox" checked="checked" /> pause

<input class="finish" type="checkbox" checked="checked" /> finish

<input class="state-changed" type="checkbox" checked="checked" /> stateChanged

<input class="seek-second" type="checkbox" checked="checked" /> seekSecond

<input class="seek-percent" type="checkbox" checked="checked" /> seekPercent

<input class="mute" type="checkbox" checked="checked" /> mute

<input class="unmute" type="checkbox" checked="checked" /> unmute

<input class="volume" type="checkbox" checked="checked" /> volume

Console

                        <textarea class="output" readonly="readonly"></textarea>
                        <button class="clear">Clear</button>

        `;
    }
};<|endoftext|>


<commit_before>// identity functor
const Box = x => (

  // container object wrapping x
  {
    // Now x is available in the closure instead of 'this'

    // functor map, sends f:a->b into map(f):Box(a)->Box(b),
    // the same as f(x) but wrapped into the Box container,
    // so we can keep chaining
    map: f => Box(f(x)),

    // fold functor map, applies f and returns the raw unwrapped value,
    // sends f:a->b into fold(f):Box(a)->b,
    // does not return any Box container, so can't be chained with map
    fold: f => f(x),

    // custom getter function -- called by console.log
    inspect: () => `Box(${x})`
  }
)

const moneyToFloat =  str =>

  // unnesting expressions!
  Box(str)
  .map(s => s.replace(/\$/g, ''))
  .map(r => parseFloat(r))
  // .fold(r => parseFloat(r))

const percentToFloat = str =>
  Box(str.replace(/\%/g, ''))
  .map(replaced => parseFloat(replaced))
  .map(number => number * 0.01)
  // .fold(number => number * 0.01)

// function of 2 arguments!
// need to .fold twice to get back the value
const applyDiscount = (price, discount) =>

  // already in container Box,
  // otherwise we would need to wrap it as Box(moneyToFloat(price))
  moneyToFloat(price)

  // no further chaining, so get the value
  .fold(cost =>

    // pick the 2nd argument
    percentToFloat(discount)

    // also here get the value
    .fold(savings =>

      // cost is available in the closure
      // as result of previous calculations
      cost - cost * savings
    )
  )

console.log(moneyToFloat(' $33 ')) //=> Box(33)
console.log(percentToFloat(' 1.23% ')) //=> Box(0.0123)

const result = applyDiscount('$55', '20%')

console.log(result) //=> 44

<commit_msg>Reformat<commit_after>// identity functor
const Box = x => (

  // container object wrapping x
  {
    // Now x is available in the closure instead of 'this'

    // functor map, sends f:a->b into map(f):Box(a)->Box(b),
    // the same as f(x) but wrapped into the Box container,
    // so we can keep chaining
    map: f => Box(f(x)),

    // fold functor map, applies f and returns the raw unwrapped value,
    // sends f:a->b into fold(f):Box(a)->b,
    // does not return any Box container, so can't be chained with map
    fold: f => f(x),

    // custom getter function -- called by console.log
    inspect: () => `Box(${x})`
  }
)

const moneyToFloat =  str =>
  Box(str)
    .map(s => s.replace(/\$/g, ''))
    .map(r => parseFloat(r))
    // .fold(r => parseFloat(r))

const percentToFloat = str =>
  Box(str.replace(/\%/g, ''))
    .map(replaced => parseFloat(replaced))
    .map(number => number * 0.01)
    // .fold(number => number * 0.01)

// function of 2 arguments!
// need to .fold twice to get back the value
const applyDiscount = (price, discount) =>

  // already in container Box,
  // otherwise we would need to wrap it as Box(moneyToFloat(price))
  moneyToFloat(price)

    // no further chaining, so get the value
    .fold(cost =>

      // pick the 2nd argument
      percentToFloat(discount)

        // also here get the value
        .fold(savings =>

          // cost is available in the closure
          // as result of previous calculations
          cost - cost * savings
        )
    )

console.log(moneyToFloat(' $33 ')) //=> Box(33)
console.log(percentToFloat(' 1.23% ')) //=> Box(0.0123)

const result = applyDiscount('$55', '20%')

console.log(result) //=> 44

<|endoftext|>


<commit_before>var MathUtils = new function() {
	var maxDepth = 53; // MANT_DIGITS: 64 bit: 53 digits, 32 bit: 24 digits

	// Gauss-Legendre Numerical Integration, a Gauss.as port from Singularity:
	//
	// Copyright (c) 2006-2007, Jim Armstrong (www.algorithmist.net)
	// All Rights Reserved.
	//
	// Simplified and further optimised by Juerg Lehni.

	var abscissa = [
		-0.5773502692, 0.5773502692,
		-0.7745966692, 0.7745966692, 0,
		-0.8611363116, 0.8611363116, -0.3399810436, 0.3399810436,
		-0.9061798459, 0.9061798459, -0.5384693101, 0.5384693101, 0.0000000000,
		-0.9324695142, 0.9324695142, -0.6612093865, 0.6612093865, -0.2386191861, 0.2386191861,
		-0.9491079123, 0.9491079123, -0.7415311856, 0.7415311856, -0.4058451514, 0.4058451514, 0.0000000000,
		-0.9602898565, 0.9602898565, -0.7966664774, 0.7966664774, -0.5255324099, 0.5255324099, -0.1834346425, 0.1834346425
	];

	var weight = [
		1, 1,
		0.5555555556, 0.5555555556, 0.8888888888,
		0.3478548451, 0.3478548451, 0.6521451549, 0.6521451549,
		0.2369268851, 0.2369268851, 0.4786286705, 0.4786286705, 0.5688888888,
		0.1713244924, 0.1713244924, 0.3607615730, 0.3607615730, 0.4679139346, 0.4679139346,
		0.1294849662, 0.1294849662, 0.2797053915, 0.2797053915, 0.3818300505, 0.3818300505, 0.4179591837,
		0.1012285363, 0.1012285363, 0.2223810345, 0.2223810345, 0.3137066459, 0.3137066459, 0.3626837834, 0.3626837834
	];

	return {
		EPSILON: Math.pow(2, -maxDepth + 1),

		gauss: function(f, a, b, n) {
			n = Math.min(Math.max(n, 2), 8);

			var l = n == 2 ? 0 : n * (n - 1) / 2 - 1,
				sum = 0,
				mul = 0.5 * (b - a),
				ab2 = mul + a;
			for(var i = 0; i < n; i++)
				sum += f(ab2 + mul * abscissa[l + i]) * weight[l + i];

			return mul * sum;
		}
	}
};
<commit_msg>Add Van Wijngaarden–Dekker–Brent method for root finding, ported from Numerical Recipes in C.<commit_after>var MathUtils = new function() {
	var maxDepth = 53; // MANT_DIGITS: 64 bit: 53 digits, 32 bit: 24 digits

	var abscissa = [
		-0.5773502692, 0.5773502692,
		-0.7745966692, 0.7745966692, 0,
		-0.8611363116, 0.8611363116, -0.3399810436, 0.3399810436,
		-0.9061798459, 0.9061798459, -0.5384693101, 0.5384693101, 0.0000000000,
		-0.9324695142, 0.9324695142, -0.6612093865, 0.6612093865, -0.2386191861, 0.2386191861,
		-0.9491079123, 0.9491079123, -0.7415311856, 0.7415311856, -0.4058451514, 0.4058451514, 0.0000000000,
		-0.9602898565, 0.9602898565, -0.7966664774, 0.7966664774, -0.5255324099, 0.5255324099, -0.1834346425, 0.1834346425
	];

	var weight = [
		1, 1,
		0.5555555556, 0.5555555556, 0.8888888888,
		0.3478548451, 0.3478548451, 0.6521451549, 0.6521451549,
		0.2369268851, 0.2369268851, 0.4786286705, 0.4786286705, 0.5688888888,
		0.1713244924, 0.1713244924, 0.3607615730, 0.3607615730, 0.4679139346, 0.4679139346,
		0.1294849662, 0.1294849662, 0.2797053915, 0.2797053915, 0.3818300505, 0.3818300505, 0.4179591837,
		0.1012285363, 0.1012285363, 0.2223810345, 0.2223810345, 0.3137066459, 0.3137066459, 0.3626837834, 0.3626837834
	];

	return {
		EPSILON: Math.pow(2, -maxDepth + 1),

		// Gauss-Legendre Numerical Integration, a Gauss.as port from Singularity:
		//
		// Copyright (c) 2006-2007, Jim Armstrong (www.algorithmist.net)
		// All Rights Reserved.

		gauss: function(f, a, b, n) {
			n = Math.min(Math.max(n, 2), 8);

			var l = n == 2 ? 0 : n * (n - 1) / 2 - 1,
				sum = 0,
				mul = 0.5 * (b - a),
				ab2 = mul + a;
			for(var i = 0; i < n; i++)
				sum += f(ab2 + mul * abscissa[l + i]) * weight[l + i];

			return mul * sum;
		},

		// Van Wijngaarden–Dekker–Brent method for root finding
		brent: function(f, a, b, tol) {
			var c = b, d = 0, e = 0,
				fa = f(a),
				fb = f(b),
				fc = fb;

			for (var i = 1; i <= 16; i++) {
				if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) {
					c = a;
					fc = fa;
					e = d = b - a;
				}
				if (Math.abs(fc) < Math.abs(fb)) {
					a = b;
					b = c;
					c = a;
					fa = fb;
					fb = fc;
					fc = fa;
				}
				var tol1 = 2 * MathUtils.EPSILON * Math.abs(b) + 0.5 * tol,
					xm = 0.5 * (c - b);
				if (Math.abs(xm) <= tol1 || fb == 0.0) {
					return b;
				}
				if (Math.abs(e) >= tol1 && Math.abs(fa) > Math.abs(fb)) {
					var p, q, r,
						s = fb / fa;
					if (a == c) {
						p = 2.0 * xm * s;
						q = 1.0 - s;
					} else {
						q = fa / fc;
						r = fb / fc;
						p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0));
						q = (q - 1.0) * (r - 1.0) * (s - 1.0);
					}
					if (p > 0.0)
						q = -q;
					p = Math.abs(p);
					var min1 = 3.0 * xm * q - Math.abs(tol1 * q),
					 	min2 = Math.abs(e * q);
					if (2.0 * p < (min1 < min2 ? min1 : min2)) {
						e = d;
						d = p / q;
					} else {
						d = xm;
						e = d;
					}
				} else {
					d = xm;
					e = d;
				}
				a = b;
				fa = fb;
				if (Math.abs(d) > tol1)
					b += d;
				else
					b += xm >= 0.0 ? Math.abs(tol1) : -Math.abs(tol1);
				fb = f(b);
			}
			return b;
		}
	}
};
<|endoftext|>


<commit_before>/* eslint  @typescript-eslint/no-var-requires: off */

const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  output: {
    publicPath: '/app/'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: '!!handlebars-loader!html/index.html',
      templateParameters: {
        jsConfig: '{ auth: { google: true, yahoo: true } }'
      },
      historyApiFallback: true
    })
  ],
  devServer: {
    host: 'localhost',
    open: true,
    openPage: 'app',
    historyApiFallback: {
      index: '/app',
      verbose: true
    },
    overlay: true,
    dev: {
      publicPath: '/app'
    },
    proxy: {
      '/socket.io': {
        target: 'http://localhost:3200',
        ws: true
      },
      '^/(auth|login|api|register|forgot-password|reset-password|confirm-email|files|about|home|tos|pricing|support|website-assets)': {
        target: 'http://localhost:3200'
      }
    }
  }
});
<commit_msg>Fix dev server routing<commit_after>/* eslint  @typescript-eslint/no-var-requires: off */

const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  output: {
    publicPath: '/app/'
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: '!!handlebars-loader!html/index.html',
      templateParameters: {
        jsConfig: '{ auth: { google: true, yahoo: true } }'
      },
      historyApiFallback: true
    })
  ],
  devServer: {
    host: 'localhost',
    open: true,
    openPage: 'app',
    historyApiFallback: {
      rewrites: [{ from: /app.*/, to: '/app/index.html' }],
      verbose: true
    },
    overlay: true,
    dev: {
      publicPath: '/app'
    },
    proxy: {
      '/socket.io': {
        target: 'http://localhost:3200',
        ws: true
      },
      '/': {
        target: 'http://localhost:3200'
      }
    }
  }
});
<|endoftext|>


<commit_before>/** This controller is simply here to launch the login dialog when the route is explicitly changed to /login */
angular.module('umbraco').controller("Umbraco.LoginController", function (eventsService, $scope, userService, $location, $rootScope, $window) {

    userService._showLoginDialog();

    var evtOn = eventsService.on("app.ready", function(evt, data){
        $scope.avatar = "assets/img/application/logo.png";

        var path = "/";

        //check if there's a returnPath query string, if so redirect to it
        var locationObj = $location.search();
        if (locationObj.returnPath) {
            path = decodeURIComponent(locationObj.returnPath);
        }

        // Ensure path is not absolute
        path = path.replace(/^.*\/\/[^\/]+/, '')

        window.location.href = path;
    });

    $scope.$on('$destroy', function () {
        eventsService.unsubscribe(evtOn);
    });

});
<commit_msg>Fix redirect after logout bug https://github.com/umbraco/Umbraco-CMS/issues/12592<commit_after>/** This controller is simply here to launch the login dialog when the route is explicitly changed to /login */
angular.module('umbraco').controller("Umbraco.LoginController", function (eventsService, $scope, userService, $location, $rootScope, $window) {

    userService._showLoginDialog();

    var evtOn = eventsService.on("app.ready", function(evt, data){
        $scope.avatar = "assets/img/application/logo.png";

        var path = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath;

      //check if there's a returnPath query string, if so redirect to it
        var locationObj = $location.search();
        if (locationObj.returnPath) {
            path = decodeURIComponent(locationObj.returnPath);
        }

        // Ensure path is not absolute
        path = path.replace(/^.*\/\/[^\/]+/, '')

        window.location.href = path;
    });

    $scope.$on('$destroy', function () {
        eventsService.unsubscribe(evtOn);
    });

});
<|endoftext|>