<commit_before>define([
'angular',
//'lib/viz/viz',
'angularRoute'
],
function( angular, viz ){
  angular.module( 'myApp.view.canvas', [ 'ngRoute' ])
  .config([
    '$routeProvider',
    function( $routeProvider ){
      var std = {
        //template: '
',
        template: [

          '

',
          '

'

        ].join(' '),
        controller: [
          '$scope',
          'iCanvas',
          function(
            $scope,
            iCanvas ){
            $scope.iCanvas = new iCanvas()
          }]
      }
      $routeProvider.when('/canvas', std )
      $routeProvider.when('/canvas/:id', std )
    }
  ])

  // http://progur.com/2017/02/create-mandelbrot-fractal-javascript.html

  .service( 'iCanvasSvc', [
    function(){
      var self = this
      self.list = []
      self.register = function( iCanvas ){
        if ( !_.find( self.list, iCanvas )){
          self.list.push( iCanvas )
        }
      }
    }
  ])
  .factory( 'iCanvas', [
    'iCanvasSvc',
    '$timeout',
    function(
      iCanvasSvc,
      $timeout ){
      var iCanvas = function( config ){
        _.merge( this, {
          id: _.uniqueId(),
          pan: {
            x: 3,
            y: 1.25
          },
          cycle: 10,
          zoom: 175,
          width: 900,
          height: 400,
          palette: [ '#000', '#FF0', '#F0F', '#0FF' ]
        })
        _.merge( this, config )
        iCanvasSvc.register( this )
      }
      iCanvas.prototype.update = function(){
        $timeout( function(){} )
      }
      iCanvas.prototype.transX = function( x ){
        return x / this.zoom - this.pan.x
      }
      iCanvas.prototype.transY = function( y ){
        return y / this.zoom - this.pan.y
      }
      return iCanvas
    }
  ])
  .directive( 'iCanvasCtrl', [
    function(){
      return {
        scope: {
          iCanvasCtrl: '='
        },
        template: [

          'panX<input type="number" ng-model="ctrl.pan.x" step="{{ step( ctrl.pan.x ) }}" />',
          'panY<input type="number" ng-model="ctrl.pan.y" step="{{ step( ctrl.pan.y ) }}" />',
          'zoom<input type="number" ng-model="ctrl.zoom" step="{{ step( ctrl.zoom ) }}" />',
          'cycle<input type="number" ng-model="ctrl.cycle" />'

        ].join(' '),
        link: function( scope ){
          scope.ctrl = scope.iCanvasCtrl
          scope.step = function( i ){
            var str = i.toString()
            if ( str.indexOf('.') == -1 ){
              return Math.pow( 10, str.length-2 )
            }
            return Math.pow( 10, str.split('.')[1].length*-1 )
          }
        }
      }
    }
  ])
  .directive( 'iCanvas', [
    function(){
      return {
        scope: {
          iCanvas: '='
        },
        link: function( scope, elem ){
          var ctrl = scope.iCanvas
          var canvas = document.createElement('canvas')
          canvas.width = ctrl.width
          canvas.height = ctrl.height
          $( elem ).append( canvas )
          var ctx = canvas.getContext( '2d' )

          // is that point "blowing up?"

          function mandelbrot( x, y ){
            var real = x
            var imag = y
            var n = 0
            for ( var i = 0; i < ctrl.cycle; i++ ){
              var tempReal = real*real - imag*imag + x
              var tempImag = 2 * real * imag + y
              real = tempReal
              imag = tempImag
              if ( real*imag > 5 ){
                n=i/ctrl.cycle
              }
            }
            return n
          }

          // redraw if config values change

          function rect( x, y, ci ){
            ctx.fillStyle = ctrl.palette[ Math.floor( ctrl.palette.length * ci ) ] || _.last( ctrl.palette )
            ctx.fillRect( x, y, 1, 1 )
          }
          var draw = _.debounce( function(){
            for ( var x = 0; x < canvas.width; x++ ){
              for ( var y = 0; y < canvas.height; y++ ){
                rect( x, y, mandelbrot( ctrl.transX( x ), ctrl.transY( y )))
              }
            }
          }, 100 )
          scope.$watch( 'iCanvas', draw, true )
        }
      }
    }
  ])
  .directive('canvasCubeTest', [
    function(){
      return {
        replace: true,
        link: function( scope, elem ){
          window.viz = new viz({
            elem: elem.get(0)
          })
        }
      }
    }
  ])
})<commit_msg>angular experiments<commit_after>define([
'angular',
//'lib/viz/viz',
'angularRoute'
],
function( angular, viz ){
  angular.module( 'myApp.view.canvas', [ 'ngRoute' ])
  .config([
    '$routeProvider',
    function( $routeProvider ){
      var std = {
        //template: '
',
        template: [

          '

',
          '

'

        ].join(' '),
        controller: [
          '$scope',
          'iCanvas',
          function(
            $scope,
            iCanvas ){
            $scope.iCanvas = new iCanvas()
          }]
      }
      $routeProvider.when('/canvas', std )
      $routeProvider.when('/canvas/:id', std )
    }
  ])

  // http://progur.com/2017/02/create-mandelbrot-fractal-javascript.html
  // http://math.hws.edu/eck/jsdemo/jsMandelbrot.html

  .service( 'iCanvasSvc', [
    function(){
      var self = this
      self.list = []
      self.register = function( iCanvas ){
        if ( !_.find( self.list, iCanvas )){
          self.list.push( iCanvas )
        }
      }
    }
  ])
  .factory( 'iCanvas', [
    'iCanvasSvc',
    '$timeout',
    function(
      iCanvasSvc,
      $timeout ){
      var iCanvas = function( config ){
        _.merge( this, {
          id: _.uniqueId(),
          pan: {
            x: 3,
            y: 1.25
          },
          cycle: 10,
          zoom: 175,
          width: 900,
          height: 400,
          palette: [ '#000', '#FF0', '#F0F', '#0FF' ]
        })
        _.merge( this, config )
        iCanvasSvc.register( this )
      }
      iCanvas.prototype.update = function(){
        $timeout( function(){} )
      }
      iCanvas.prototype.transX = function( x ){
        return x / this.zoom - this.pan.x
      }
      iCanvas.prototype.transY = function( y ){
        return y / this.zoom - this.pan.y
      }
      return iCanvas
    }
  ])
  .directive( 'iCanvasCtrl', [
    function(){
      return {
        scope: {
          iCanvasCtrl: '='
        },
        template: [

          'panX<input type="number" ng-model="ctrl.pan.x" step="{{ step( ctrl.pan.x ) }}" />',
          'panY<input type="number" ng-model="ctrl.pan.y" step="{{ step( ctrl.pan.y ) }}" />',
          'zoom<input type="number" ng-model="ctrl.zoom" step="{{ step( ctrl.zoom ) }}" />',
          'cycle<input type="number" ng-model="ctrl.cycle" />'

        ].join(' '),
        link: function( scope ){
          scope.ctrl = scope.iCanvasCtrl
          scope.step = function( i ){
            var str = i.toString()

            // calculate optimal step

            return ( str.indexOf('.') == -1 ) ?
              Math.pow( 10, str.length-2 ) :
              Math.pow( 10, str.split('.')[1].length*-1 )
          }
        }
      }
    }
  ])
  .directive( 'iCanvas', [
    function(){
      return {
        scope: {
          iCanvas: '='
        },
        link: function( scope, elem ){
          var ctrl = scope.iCanvas
          var canvas = document.createElement('canvas')
          canvas.width = ctrl.width
          canvas.height = ctrl.height
          $( elem ).append( canvas )
          var ctx = canvas.getContext( '2d' )

          // is that point "blowing up?"

          function mandelbrot( x, y ){
            var real = x
            var imag = y
            var n = 0
            for ( var i = 0; i < ctrl.cycle; i++ ){
              var tempReal = real*real - imag*imag + x
              var tempImag = 2 * real * imag + y
              real = tempReal
              imag = tempImag
              if ( real*imag > 5 ){
                n=i/ctrl.cycle
              }
            }
            return n
          }

          // redraw if config values change

          function rect( x, y, ci ){
            ctx.fillStyle = ctrl.palette[ Math.floor( ctrl.palette.length * ci ) ] || _.last( ctrl.palette )
            ctx.fillRect( x, y, 1, 1 )
          }
          var draw = _.debounce( function(){
            for ( var x = 0; x < canvas.width; x++ ){
              for ( var y = 0; y < canvas.height; y++ ){
                rect( x, y, mandelbrot( ctrl.transX( x ), ctrl.transY( y )))
              }
            }
          }, 100 )
          scope.$watch( 'iCanvas', draw, true )
        }
      }
    }
  ])
  .directive('canvasCubeTest', [
    function(){
      return {
        replace: true,
        link: function( scope, elem ){
          window.viz = new viz({
            elem: elem.get(0)
          })
        }
      }
    }
  ])
})<|endoftext|>


<commit_before>/* eslint no-console: 0 */ // --> OFF

import * as invoicesController from './invoicesController'
import * as channelController from './channelController'
import * as walletController from './walletController'
import * as peersController from './peersController'
import * as paymentsController from './paymentsController'
import * as networkController from './networkController'

// TODO - GetChanInfo
// TODO - GetTransactions
// TODO - GetNodeInfo
// TODO - DescribeGraph
// TODO - GetNetworkInfo
// TODO - QueryRoutes
// TODO - DecodePayReq
// TODO - SendPayment
// TODO - DeleteAllPayments

export default function (lnd, event, msg, data) {
  switch (msg) {
    case 'info':
      networkController.getInfo(lnd)
        .then((infoData) => {
          event.sender.send('receiveInfo', infoData)
          event.sender.send('receiveCryptocurrency', infoData.chains[0])
        })
        .catch(error => console.log('info error: ', error))
      break
    case 'newaddress':
    // Data looks like { address: '' }
      walletController.newAddress(lnd, data.type)
        .then(({ address }) => event.sender.send('receiveAddress', address))
        .catch(error => console.log('newaddress error: ', error))
      break
    case 'peers':
    // Data looks like { peers: [] }
      peersController.listPeers(lnd)
        .then(peersData => event.sender.send('receivePeers', peersData))
        .catch(error => console.log('peers error: ', error))
      break
    case 'channels':
    // Data looks like
    // [ { channels: [] }, { total_limbo_balance: 0, pending_open_channels: [], pending_closing_channels: [], pending_force_closing_channels: [] } ]
      Promise.all([channelController.listChannels, channelController.pendingChannels].map(func => func(lnd)))
        .then(channelsData =>
          event.sender.send('receiveChannels', { channels: channelsData[0].channels, pendingChannels: channelsData[1] })
        )
        .catch(error => console.log('channels error: ', error))
      break
    case 'transactions':
    // Data looks like { transactions: [] }
      walletController.getTransactions(lnd)
        .then(transactionsData => event.sender.send('receiveTransactions', transactionsData))
        .catch(error => console.log('transactions error: ', error))
      break
    case 'payments':
    // Data looks like { payments: [] }
      paymentsController.listPayments(lnd)
        .then(paymentsData => event.sender.send('receivePayments', paymentsData))
        .catch(error => console.log('payments error: ', error))
      break
    case 'invoices':
    // Data looks like { invoices: [] }
      invoicesController.listInvoices(lnd)
        .then(invoicesData => event.sender.send('receiveInvoices', invoicesData))
        .catch(error => console.log('invoices error: ', error))
      break
    case 'invoice':
    // Data looks like { invoices: [] }
      invoicesController.getInvoice(data.payreq)
        .then(invoiceData => event.sender.send('receiveInvoice', invoiceData))
        .catch(error => console.log('invoice error: ', error))
      break
    case 'balance':
    // Balance looks like [ { balance: '129477456' }, { balance: '243914' } ]
      Promise.all([walletController.walletBalance, channelController.channelBalance].map(func => func(lnd)))
        .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance }))
        .catch(error => console.log('balance error: ', error))
      break
    case 'createInvoice':
    // Invoice looks like { r_hash: Buffer, payment_request: '' }
    // { memo, value } = data
      invoicesController.addInvoice(lnd, data)
        .then(newinvoice =>
          event.sender.send(
            'createdInvoice',
            Object.assign(newinvoice, {
              memo: data.memo,
              value: data.value,
              r_hash: new Buffer(newinvoice.r_hash, 'hex').toString('hex'),
              creation_date: Date.now() / 1000
            })
          )
        )
        .catch(error => console.log('addInvoice error: ', error))
      break
    case 'sendPayment':
    // Payment looks like { payment_preimage: Buffer, payment_route: Object }
    // { paymentRequest } = data
      paymentsController.sendPaymentSync(lnd, data)
        .then(({ payment_route }) => event.sender.send('paymentSuccessful', Object.assign(data, { payment_route })))
        .catch(error => console.log('payinvoice error: ', error))
      break
    case 'sendCoins':
    // Transaction looks like { txid: String }
    // { amount, addr } = data
      walletController.sendCoins(lnd, data)
        .then(({ txid }) => event.sender.send('transactionSuccessful', { amount: data.amount, addr: data.addr, txid }))
        .catch(error => event.sender.send('transactionError', { error }))
      break
    case 'openChannel':
    // Response is empty. Streaming updates on channel status and updates
    // { pubkey, localamt, pushamt } = data
      channelController.openChannel(lnd, event, data)
        .then((channel) => {
          console.log('CHANNEL: ', channel)
          event.sender.send('channelSuccessful', { channel })
        })
        .catch(error => console.log('openChannel error: ', error))
      break
    case 'closeChannel':
    // Response is empty. Streaming updates on channel status and updates
    // { channel_point, force } = data
      channelController.closeChannel(lnd, event, data)
        .then((result) => {
          console.log('CLOSE CHANNEL: ', result)
          event.sender.send('closeChannelSuccessful')
        })
        .catch(error => console.log('closeChannel error: ', error))
      break
    case 'connectPeer':
    // Returns a peer_id. Pass the pubkey, host and peer_id so we can add a new peer to the list
    // { pubkey, host } = data
      peersController.connectPeer(lnd, data)
        .then(({ peer_id }) => {
          console.log('peer_id: ', peer_id)
          event.sender.send('connectSuccess', { pub_key: data.pubkey, address: data.host, peer_id })
        })
        .catch(error => console.log('connectPeer error: ', error))
      break
    case 'disconnectPeer':
    // Empty response. Pass back pubkey on success to remove it from the peers list
    // { pubkey } = data
      peersController.disconnectPeer(lnd, data)
        .then(() => {
          console.log('pubkey: ', data.pubkey)
          event.sender.send('disconnectSuccess', { pubkey: data.pubkey })
        })
        .catch(error => console.log('disconnectPeer error: ', error))
      break
    default:
  }
}
<commit_msg>feature(setAlias): add setAlias to API<commit_after>/* eslint no-console: 0 */ // --> OFF

import * as invoicesController from './invoicesController'
import * as channelController from './channelController'
import * as walletController from './walletController'
import * as peersController from './peersController'
import * as paymentsController from './paymentsController'
import * as networkController from './networkController'

// TODO - GetChanInfo
// TODO - GetTransactions
// TODO - GetNodeInfo
// TODO - DescribeGraph
// TODO - GetNetworkInfo
// TODO - QueryRoutes
// TODO - DecodePayReq
// TODO - SendPayment
// TODO - DeleteAllPayments

export default function (lnd, event, msg, data) {
  switch (msg) {
    case 'info':
      networkController.getInfo(lnd)
        .then((infoData) => {
          event.sender.send('receiveInfo', infoData)
          event.sender.send('receiveCryptocurrency', infoData.chains[0])
        })
        .catch(error => console.log('info error: ', error))
      break
    case 'newaddress':
    // Data looks like { address: '' }
      walletController.newAddress(lnd, data.type)
        .then(({ address }) => event.sender.send('receiveAddress', address))
        .catch(error => console.log('newaddress error: ', error))
      break
    case 'setAlias':
    // Data looks like { new_alias: '' }
      walletController.setAlias(lnd, data)
        .then(() => event.sender.send('aliasSet'))
        .catch(error => console.log('setAlias error: ', error))
      break
    case 'peers':
    // Data looks like { peers: [] }
      peersController.listPeers(lnd)
        .then(peersData => event.sender.send('receivePeers', peersData))
        .catch(error => console.log('peers error: ', error))
      break
    case 'channels':
    // Data looks like
    // [ { channels: [] }, { total_limbo_balance: 0, pending_open_channels: [], pending_closing_channels: [], pending_force_closing_channels: [] } ]
      Promise.all([channelController.listChannels, channelController.pendingChannels].map(func => func(lnd)))
        .then(channelsData =>
          event.sender.send('receiveChannels', { channels: channelsData[0].channels, pendingChannels: channelsData[1] })
        )
        .catch(error => console.log('channels error: ', error))
      break
    case 'transactions':
    // Data looks like { transactions: [] }
      walletController.getTransactions(lnd)
        .then(transactionsData => event.sender.send('receiveTransactions', transactionsData))
        .catch(error => console.log('transactions error: ', error))
      break
    case 'payments':
    // Data looks like { payments: [] }
      paymentsController.listPayments(lnd)
        .then(paymentsData => event.sender.send('receivePayments', paymentsData))
        .catch(error => console.log('payments error: ', error))
      break
    case 'invoices':
    // Data looks like { invoices: [] }
      invoicesController.listInvoices(lnd)
        .then(invoicesData => event.sender.send('receiveInvoices', invoicesData))
        .catch(error => console.log('invoices error: ', error))
      break
    case 'invoice':
    // Data looks like { invoices: [] }
      invoicesController.getInvoice(data.payreq)
        .then(invoiceData => event.sender.send('receiveInvoice', invoiceData))
        .catch(error => console.log('invoice error: ', error))
      break
    case 'balance':
    // Balance looks like [ { balance: '129477456' }, { balance: '243914' } ]
      Promise.all([walletController.walletBalance, channelController.channelBalance].map(func => func(lnd)))
        .then(balance => event.sender.send('receiveBalance', { walletBalance: balance[0].balance, channelBalance: balance[1].balance }))
        .catch(error => console.log('balance error: ', error))
      break
    case 'createInvoice':
    // Invoice looks like { r_hash: Buffer, payment_request: '' }
    // { memo, value } = data
      invoicesController.addInvoice(lnd, data)
        .then(newinvoice =>
          event.sender.send(
            'createdInvoice',
            Object.assign(newinvoice, {
              memo: data.memo,
              value: data.value,
              r_hash: new Buffer(newinvoice.r_hash, 'hex').toString('hex'),
              creation_date: Date.now() / 1000
            })
          )
        )
        .catch(error => console.log('addInvoice error: ', error))
      break
    case 'sendPayment':
    // Payment looks like { payment_preimage: Buffer, payment_route: Object }
    // { paymentRequest } = data
      paymentsController.sendPaymentSync(lnd, data)
        .then(({ payment_route }) => event.sender.send('paymentSuccessful', Object.assign(data, { payment_route })))
        .catch(error => console.log('payinvoice error: ', error))
      break
    case 'sendCoins':
    // Transaction looks like { txid: String }
    // { amount, addr } = data
      walletController.sendCoins(lnd, data)
        .then(({ txid }) => event.sender.send('transactionSuccessful', { amount: data.amount, addr: data.addr, txid }))
        .catch(error => event.sender.send('transactionError', { error }))
      break
    case 'openChannel':
    // Response is empty. Streaming updates on channel status and updates
    // { pubkey, localamt, pushamt } = data
      channelController.openChannel(lnd, event, data)
        .then((channel) => {
          console.log('CHANNEL: ', channel)
          event.sender.send('channelSuccessful', { channel })
        })
        .catch(error => console.log('openChannel error: ', error))
      break
    case 'closeChannel':
    // Response is empty. Streaming updates on channel status and updates
    // { channel_point, force } = data
      channelController.closeChannel(lnd, event, data)
        .then((result) => {
          console.log('CLOSE CHANNEL: ', result)
          event.sender.send('closeChannelSuccessful')
        })
        .catch(error => console.log('closeChannel error: ', error))
      break
    case 'connectPeer':
    // Returns a peer_id. Pass the pubkey, host and peer_id so we can add a new peer to the list
    // { pubkey, host } = data
      peersController.connectPeer(lnd, data)
        .then(({ peer_id }) => {
          console.log('peer_id: ', peer_id)
          event.sender.send('connectSuccess', { pub_key: data.pubkey, address: data.host, peer_id })
        })
        .catch(error => console.log('connectPeer error: ', error))
      break
    case 'disconnectPeer':
    // Empty response. Pass back pubkey on success to remove it from the peers list
    // { pubkey } = data
      peersController.disconnectPeer(lnd, data)
        .then(() => {
          console.log('pubkey: ', data.pubkey)
          event.sender.send('disconnectSuccess', { pubkey: data.pubkey })
        })
        .catch(error => console.log('disconnectPeer error: ', error))
      break
    default:
  }
}
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import shortid from 'shortid'
import './style.css'

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

    this.state = {
      collapsed: true,
      data: props.data.map((item) => {
        var i = {...item};

        if (!i.id) {
          i.id = shortid.generate();
        }

        if(i.data) {
          i.data = i.data.map((child) => {
            if (!child.id) {
              return {
                id: shortid.generate(),
                parentId: i.id,
                ...child
              };
            }
            return {...child};
          });
        }

        return i;
      }),
      openedMap: null
    }
  }

  static propTypes = {
    data: React.PropTypes.array.isRequired
  }

  componentWillMount = () => {
    this.setState({
      openedMap: this.createOpenedMap(this.state.data),
      itemMap: this.createItemMap(this.state.data)
    })
  }

  createOpenedMap = (data) => {
    var openNodeMap = {};

    data.forEach((item) => {
      openNodeMap[item.id] = false;
    });

    return openNodeMap;
  }

  createItemMap = (data) => {
    var itemMap = {};

    data.forEach((item) => {
      if(item.data) {
        item.data.forEach((child) => {
          itemMap[child.id] = child;
        });
      }
      itemMap[item.id] = item;
    });

    return itemMap;
  }

  isBranchOpen = (branchId) => {
    return this.state.openedMap[branchId];
  }

  isCollapsed = () => {
    return this.state.collapsed;
  }

  toggleBranchState = (branchId) => {
    var openedMap = {...this.state.openedMap};

    openedMap[branchId] = !openedMap[branchId];

    this.setState({
      openedMap: openedMap
    })
  }

  collapseAll = () => {
      var openedMap = {...this.state.openedMap};

      for(let key in openedMap) {
        openedMap[key] = false;
      }

      this.setState({
        openedMap: openedMap,
      })

  }

  expandAll = () => {
    var openedMap = {...this.state.openedMap};

    for(let key in openedMap) {
      openedMap[key] = true;
    }

    this.setState({
      openedMap: openedMap,
      collapsed: false
    })
  }

  renderBranchOpenIcon = (branchId) => {
    return (
       {this.toggleBranchState(branchId)}}>
        <i className={`fa fa-${ this.isBranchOpen(branchId) ? 'minus-square-o' : 'plus-square-o' }`}>

    )
  }

  renderLeafs = (leafs, branchId) => {
    let branchLeafs = leafs.map((leaf) => {
      return (

            <i className={`fa fa-file`}>
            {leaf.name}

      )
    });

    return <ul className={`${ !this.isBranchOpen(branchId) ? 'hidden' : '' }`}>{branchLeafs}
;
  }

  renderBranches = () => {
    return this.state.data.map((branch) => {
      return (

          {branch.data ? this.renderBranchOpenIcon(branch.id) : ''}
          <a className={`tree__link ${!branch.data ? 'tree__link--empty' : ''} `}>
            <span className={`tree__folder `}>
            <i className={`fa fa-${this.isBranchOpen(branch.id) ? 'folder-open' : 'folder'}`}>

            {branch.name}

          {branch.data ? this.renderLeafs(branch.data, branch.id) : ''}

      )
    });
  }

  render = () => {
    return (

          {this.renderBranches()}

    )
  }
}

export default Tree;
<commit_msg>class Tree (code fixed)<commit_after>import React, {Component} from 'react'
import shortid from 'shortid'
import './style.css'

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

    this.state = {
      collapsed: true,
      data: props.data.map((item) => {
        var i = {...item};

        if (!i.id) {
          i.id = shortid.generate();
        }

        if(i.data) {
          i.data = i.data.map((child) => {
            if (!child.id) {
              return {
                id: shortid.generate(),
                parentId: i.id,
                ...child
              };
            }
            return {...child};
          });
        }

        return i;
      }),
      openedMap: null
    }
  }

  static propTypes = {
    data: React.PropTypes.array.isRequired
  }

  componentWillMount = () => {
    this.setState({
      openedMap: this._createOpenedMap(this.state.data),
      itemMap: this._createItemMap(this.state.data),
      branch: this._createBranch(this.state.data)
    }, () => {console.log(this.state)})
  }

  _createBranch = (data) => {
    var branch = {};

    data.forEach((item) => {
      if(item.data) {
        branch[item.id] = {};

        item.data.forEach((child) => {
          branch[item.id][child.id] = {...child}
        })
      }
      else
        branch[item.id] = {};
    })

    return branch;
  }

  _createOpenedMap = (data) => {
    var openNodeMap = {};

    data.forEach((item) => {
      openNodeMap[item.id] = false;
    });

    return openNodeMap;
  }

  _createItemMap = (data) => {
    var itemMap = {};

    data.forEach((item) => {
      if(item.data) {
        item.data.forEach((child) => {
          itemMap[child.id] = {...child};
        });
      }
      itemMap[item.id] = {...item};
      delete itemMap[item.id].data;
    });

    return itemMap;
  }

  isBranchOpen = (branchId) => {
    return this.state.openedMap[branchId];
  }

  isCollapsed = () => {
    return this.state.collapsed;
  }

  toggleBranchState = (branchId) => {
    var openedMap = {...this.state.openedMap};

    openedMap[branchId] = !openedMap[branchId];

    this.setState({
      openedMap: openedMap
    })
  }

  collapseAll = () => {
      var openedMap = {...this.state.openedMap};

      for(let key in openedMap) {
        openedMap[key] = false;
      }

      this.setState({
        openedMap: openedMap,
      })

  }

  expandAll = () => {
    var openedMap = {...this.state.openedMap};

    for(let key in openedMap) {
      openedMap[key] = true;
    }

    this.setState({
      openedMap: openedMap,
      collapsed: false
    })
  }

  hasChilds = (branchId) => {
    return (Object.keys(this.state.branch[branchId]).length > 0);
  }

  renderBranchOpenIcon = (branchId) => {
    return (
       {this.toggleBranchState(branchId)}}>
        <i className={`fa fa-${ this.isBranchOpen(branchId) ? 'minus-square-o' : 'plus-square-o' }`}>

    )
  }

  renderLeafs = (leafs, branchId) => {
    var bracheLeafs = [];

    for(let key in leafs) {
      var leaf = (

            <i className={`fa fa-file`}>
            {leafs[key].name}

      )

      bracheLeafs.push(leaf);
    }

    return <ul className={`${ !this.isBranchOpen(branchId) ? 'hidden' : '' }`}>{bracheLeafs}
;
  }

  renderBranches = () => {
    var branches = [];
    var _branch = this.state.branch;
    var _items = this.state.itemMap;

    for(let key in _branch) {
      let branch = (

        {this.hasChilds(key) ? this.renderBranchOpenIcon(key) : ''}
        <a className={`tree__link ${!this.hasChilds(key) ? 'tree__link--empty' : ''} `}>
            <span className={`tree__folder `}>
            <i className={`fa fa-${this.isBranchOpen(key) ? 'folder-open' : 'folder'}`}>

            {_items[key].name}

        {this.hasChilds(key) ? this.renderLeafs(_branch[key], key) : ''}

      );
      branches.push(branch);
    }

    return branches;
  }

  render = () => {
    return (

          {this.renderBranches()}

    )
  }
}

export default Tree;
<|endoftext|>


<commit_before>import {
  arrayJoin,
  logIfUnequal,
  formatFinancialYear,
  ratingColor,
  formatForType
} from '../utils.js';
import ColumnChart from 'municipal-money-charts/src/components/MunicipalCharts/ColumnChart.js';
import ComparisonMenu from './comparison-menu';
import "core-js/stable";
import "regenerator-runtime/runtime";

const indicatorMetricClass = {
  "good": ".indicator-metric--status-green",
  "ave": ".indicator-metric--status-yellow",
  "bad": ".indicator-metric--status-red",
  "": ".indicator-metric--no-status",
};

function comparePeriod( a, b ) {
  if ( a.period  b.period ){
    return 1;
  }
  return 0;
}

export class IndicatorSection {
  constructor(selector, key, pageData) {
    this.selector = selector;
    this.key = key;
    this.sectionData = pageData.indicators[key];
    this.medians = pageData.medians[key];
    this.geography = pageData.geography;
    this.amountTypes = pageData["amount_types_v1"];
    this.cubeNames = pageData["cube_names"];
    this.municipalCategoryDescriptions = pageData["municipal_category_descriptions"];
    this.$element = $(selector);
    logIfUnequal(1, this.$element.length);
    this.latestItem = this.sectionData.values[0];
    this.comparisons = [];

    const chartContainerSelector = `${this.selector} .indicator-chart`;
    this.chart = new ColumnChart(chartContainerSelector, [this.chartData()]);
    this.chartContainer = $(chartContainerSelector);
    this.chartContainer.addClass("column-chart");
    this.chartContainerParent = $(chartContainerSelector).parent();

    this._initAverageButtons();
    this._initComparisonButtons();
    this._initCategoryInfo();
    this._initCalculation();
    this._initSectionPeriod();
    this._initMetric();

    this.comparisonMenu = new ComparisonMenu(selector, this.key);
    this.comparisonMenu.$element.on("option-select", ((e) => {
      this.updateChartComparison(e.detail.option);
    }).bind(this));
  }

  formatMetric(value) {
    return formatForType(this.sectionData.result_type, value);
  }

  _initCategoryInfo() {
    const miifCategory = this.geography["miif_category"];
    const categoryName = this.geography["category_name"];
    const $category = this.$element.find(".muni-type__wrapper");
    const $label = $category.find(".label");
    const $description = $category.find(".tooltip__description");
    const $link = $category.find(".tooltip__link");
    $label.text(`${miifCategory} ${categoryName}`);
    $description.text(this.municipalCategoryDescriptions[miifCategory]);
    $link.attr("href", "/help#similar-munis");
  }

  _initCalculation() {
    // Render the calculation reference
    const referenceData = this.sectionData.ref;
    const $referenceEl = this.$element.find(".indicator-calculation__reference");
    if ($referenceEl.length && referenceData) {
      const $el = $('');
      $el.attr('href', referenceData.url);
      $el.text(referenceData.title);
      $referenceEl.append($el);
    }
    // Render the formula data
    const formulaDataV2 = this.sectionData.formula_v2;
    const formulaData = this.sectionData.formula;

    // Hide unused formula elements
    if (this.sectionData.last_year <= 2019){
      $(".is--post-2019-20").hide()
      $(".indicator-calculation__heading").hide()
    }
    if (this.sectionData.last_year >= 2023){
      $(".is--pre-2019-20").hide()
      $(".indicator-calculation__heading").hide()
    }

    if (formulaDataV2) {
      const textData = formulaDataV2.text;
      const $textEl = this.$element.find(".indicator-calculation__formula-text");
      if ($textEl.length && textData) {
          $textEl.text(textData);
      }
      this.setFormulaLink(formulaDataV2, ".is--post-2019-20")
    }

    if (formulaData) {
      const textData = formulaData.text;
      const $textEl = this.$element.find(".indicator-calculation__formula-text");
      if ($textEl.length && textData) {
          $textEl.text(textData);
      }
      this.sectionData.last_year = "2019"
      this.setFormulaLink(formulaData, ".is--pre-2019-20")
    }
  }

  _initSectionPeriod() {
    this.$element.find(".section-header__info-right").text(this.formatPeriod(this.latestItem.date));
  }

  _initMetric() {
    const $skeleton = this.$element.find(".indicator-metric");
    logIfUnequal(1, $skeleton.length);
    const templateClass = indicatorMetricClass[this.latestItem.rating];
    const $element = $(`.components ${templateClass}`).clone();
    logIfUnequal(1, $element.length);
    const value = this.formatMetric(this.latestItem.result);
    $element.find(".indicator-metric__value").text(value);
    $skeleton.hide();
    $element.insertBefore($skeleton);
    $skeleton.remove();
  }

  setFormulaLink(formulaData, container) {
    const geo_code = this.geography.geo_code;
    const last_year = this.sectionData.last_year;
    const actualData = formulaData.actual;

    const $actualEl = this.$element.find(container);
    if ($actualEl.length && actualData) {
      const components = Object.values(actualData).map((data) => {
        if (typeof data === 'string') {
          return data;
        } else {
          const $el = $('');
          let params = {};
          let text = '';
          let cube_name = this.cubeNames[data.cube];
          let amount_type = data.amount_type;
          amount_type = this.amountTypes[amount_type] || amount_type;
          // Set the query parameters
          params['municipalities'] = geo_code;
          params['year'] = last_year;
          params['items'] = data.item_codes;

          if (data.amount_type) {
            params['amountType'] = data.amount_type;
          }
          // Generate the text
          text += `[${cube_name}]`;
          if (data.item_description !== undefined && data.item_description !== "") {
            text += ` ${data.item_description}`;
          } else {
            text += ` item code ${data.item_codes.join(', ')}`;
          }
          if (data.amount_type) {
            text += `, ${amount_type}`;
          }
          params = new URLSearchParams(params).toString();
          const url = `${DATA_PORTAL_URL}/table/${data.cube}/?${params}`;
          $el.text(text);
          $el.attr('href', url);
          return $el;
        }
      })
      // Append the components to the element
      $actualEl.append(arrayJoin(components, ' '));
    }
  }

  chartData() {
    const items = this.sectionData.values.map((item) => {
      return {
        period: this.formatPeriod(item.date),
        fillColor: ratingColor(item.rating),
        value: item.result,
      };
    });
    items.reverse();
    return {
      municipality: {
        code: this.geography.geo_code,
        name: this.geography.short_name,
        province_code: this.geography.province_code
      },
      data: items,
      resultType: this.resultType(),
    };
  }
  comparisonChartData(profile, muni) {
    return {
      "municipality": {
        "code": profile.demarcation.code,
        "name": muni.name,
        "province_code": muni.province_code,
      },
      "data": profile.indicators[this.key].values.map(period => {
        return {
          "period": this.formatPeriod(period.date),
          "fillColor": "#ccc",
          "value": period.result,
        };
      }),
    };
  }

  formatMedians() {
    const national = Object.entries(this.medians.national.dev_cat).map(([key, value]) => {
      return {
        period: this.formatPeriod(key),
        value: value,
      };});
    const provincial = Object.entries(this.medians.provincial.dev_cat).map(([key, value]) => {
      return {
        period: this.formatPeriod(key),
        value: value,
      };});
    national.sort(comparePeriod);
    provincial.sort(comparePeriod);
    return {
      national: national,
      provincial: provincial,
    };
  }

  resultType() {
    return {
      "R": "currency",
      "%": "percentage",
    }[this.sectionData.result_type] || this.sectionData.result_type;
  }

  formatSpecifier() {
    return {
      "R": "$,.0f",
    }[this.sectionData.result_type];
  }

  formatValue(value) {
    return locale.format(this.formatSpecifier())(value);
  }

  formatPeriod(period) {
    return formatFinancialYear(period);
  }

  _initAverageButtons() {
    const $provinceButton = $(' <button class="button" style="display: unset"></button>');
    $provinceButton.text(` in ${this.geography.province_name}`);
    $provinceButton.on("click", (function() {
      this.chart.loadMedians(this.formatMedians().provincial);
      ga('send', 'event', 'chart-averages', `${this.key} provincial`);
    }).bind(this));

    const $nationalButton = $(' <button class="button" style="display: unset">nationally</button>');
    $nationalButton.on("click", (function() {
      this.chart.loadMedians(this.formatMedians().national);
      ga('send', 'event', 'chart-averages', `${this.key} national`);
    }).bind(this));

    const averageControls = $("

");
    averageControls.append('Show average for similar municipalities ');
    if (this.geography.category_name !== "metro municipality") {
      averageControls.append([
        $provinceButton,
        " or "
      ]);
    }
    averageControls.append($nationalButton);
    this.chartContainerParent.append(averageControls);

  }

  _initComparisonButtons() {
    this.comparisonButtonsContainer = $("

");
    this.comparisonButtonsContainer.insertBefore(this.chartContainer);
  }

  _updateComparisonButtons() {
    this.comparisonButtonsContainer.empty();
    this.comparisonButtonsContainer.append("Comparing ");
    [this.chartData(), ...(this.comparisons)].forEach((comparison) => {
      const button = $(' <button class="button" style="display: unset; margin: 2px 2px"></button> ');
      button.click(() => {
        this.chart.highlightCol(comparison.municipality.code);
        ga('send', 'event', 'chart-compare-highlight', `${this.key} ${comparison.municipality.code}`);
      });
      button.text(`${comparison.municipality.name}, ${comparison.municipality.province_code}`);
      this.comparisonButtonsContainer.append(button);
    });
  }

  async getSimilarMunis(comparisonOption) {
    try {
      const response = await $.ajax({
        url: '/api/geography/geography/'
      });

      var activeMunicipalities = response.filter(function( obj ) {
          return obj.is_disestablished !== true;
      });
      const miifGrouped  = _.groupBy(activeMunicipalities, "miif_category");
      let similarGroup = miifGrouped[this.geography.miif_category];
      // Remove current muni from selection
      similarGroup = similarGroup.filter(
        muni => muni["geo_code"] !== this.geography.geo_code
      );

      let groups;
      if (comparisonOption === "similar-nearby") {
        groups = _.groupBy(_.toArray(similarGroup), "parent_code");
        similarGroup = groups[this.geography.parent_code];
      } else if (comparisonOption === "similar-same-province") {
        groups = _.groupBy(similarGroup, "province_code");
        similarGroup = groups[this.geography.province_code];
      } else if (comparisonOption === "similar-nationally") {
      } else {
        console.error("unsupported comparisonOption", comparisonOption);
      }
      return similarGroup || [];
    } catch (error) {
      console.error(error);
      alert("Error looking up similar municipalities. Please try again.");
      return [];
    }
  }

  updateChartComparison(comparisonOption) {
    if (comparisonOption === "none") {
      this.chart.loadData([this.chartData()]);
    } else {
      this.getSimilarMunis(comparisonOption).then((similarGroup) => {
        if (similarGroup.length > 0) {
          const sampleMunis = _.sample(similarGroup, 3);
          const deferreds = sampleMunis.map((muni) => {
            const demarcationCode = muni.geo_code;
            const url = `/api/municipality-profile/${demarcationCode}/`;
            return $.get(url);
          });

          Promise.all(deferreds)
            .then((results) => {
              this.comparisons = results.map((result, index) => {
                return this.comparisonChartData(result, sampleMunis[index]);
              });
              this.chart.loadData([this.chartData(), ...(this.comparisons)]);
              this._updateComparisonButtons();
            })
            .catch((details) => {
              console.error("Error fetching comparison data: ", details);
              alert("Error loading comparison data. Please try again.");
            });
        } else {
          alert("Unfortunately there are no similar municipalities for the selection. Please try again.");
        }
      });
    }
  }
}

export class OverUnderSection extends IndicatorSection {
  formatMetric(value) {
    const overunder = value > 0 ? "overspent" : "underspent";
    return `${super.formatMetric(Math.abs(value))} ${overunder}`;
  }
}
<commit_msg>Setting last_year for formula links<commit_after>import {
  arrayJoin,
  logIfUnequal,
  formatFinancialYear,
  ratingColor,
  formatForType
} from '../utils.js';
import ColumnChart from 'municipal-money-charts/src/components/MunicipalCharts/ColumnChart.js';
import ComparisonMenu from './comparison-menu';
import "core-js/stable";
import "regenerator-runtime/runtime";

const indicatorMetricClass = {
  "good": ".indicator-metric--status-green",
  "ave": ".indicator-metric--status-yellow",
  "bad": ".indicator-metric--status-red",
  "": ".indicator-metric--no-status",
};

function comparePeriod( a, b ) {
  if ( a.period  b.period ){
    return 1;
  }
  return 0;
}

export class IndicatorSection {
  constructor(selector, key, pageData) {
    this.selector = selector;
    this.key = key;
    this.sectionData = pageData.indicators[key];
    this.medians = pageData.medians[key];
    this.geography = pageData.geography;
    this.amountTypes = pageData["amount_types_v1"];
    this.cubeNames = pageData["cube_names"];
    this.municipalCategoryDescriptions = pageData["municipal_category_descriptions"];
    this.$element = $(selector);
    logIfUnequal(1, this.$element.length);
    this.latestItem = this.sectionData.values[0];
    this.comparisons = [];

    const chartContainerSelector = `${this.selector} .indicator-chart`;
    this.chart = new ColumnChart(chartContainerSelector, [this.chartData()]);
    this.chartContainer = $(chartContainerSelector);
    this.chartContainer.addClass("column-chart");
    this.chartContainerParent = $(chartContainerSelector).parent();

    this._initAverageButtons();
    this._initComparisonButtons();
    this._initCategoryInfo();
    this._initCalculation();
    this._initSectionPeriod();
    this._initMetric();

    this.comparisonMenu = new ComparisonMenu(selector, this.key);
    this.comparisonMenu.$element.on("option-select", ((e) => {
      this.updateChartComparison(e.detail.option);
    }).bind(this));
  }

  formatMetric(value) {
    return formatForType(this.sectionData.result_type, value);
  }

  _initCategoryInfo() {
    const miifCategory = this.geography["miif_category"];
    const categoryName = this.geography["category_name"];
    const $category = this.$element.find(".muni-type__wrapper");
    const $label = $category.find(".label");
    const $description = $category.find(".tooltip__description");
    const $link = $category.find(".tooltip__link");
    $label.text(`${miifCategory} ${categoryName}`);
    $description.text(this.municipalCategoryDescriptions[miifCategory]);
    $link.attr("href", "/help#similar-munis");
  }

  _initCalculation() {
    // Render the calculation reference
    const referenceData = this.sectionData.ref;
    const $referenceEl = this.$element.find(".indicator-calculation__reference");
    if ($referenceEl.length && referenceData) {
      const $el = $('');
      $el.attr('href', referenceData.url);
      $el.text(referenceData.title);
      $referenceEl.append($el);
    }
    // Render the formula data
    const formulaDataV2 = this.sectionData.formula_v2;
    const formulaData = this.sectionData.formula;

    // Hide unused formula elements
    if (this.sectionData.last_year <= 2019){
      $(".is--post-2019-20").hide()
      $(".indicator-calculation__heading").hide()
    }
    if (this.sectionData.last_year >= 2023){
      $(".is--pre-2019-20").hide()
      $(".indicator-calculation__heading").hide()
    }

    if (formulaDataV2) {
      const textData = formulaDataV2.text;
      const $textEl = this.$element.find(".indicator-calculation__formula-text");
      if ($textEl.length && textData) {
          $textEl.text(textData);
      }
      this.setFormulaLink(formulaDataV2, ".is--post-2019-20", this.sectionData.last_year)
    }

    if (formulaData) {
      const textData = formulaData.text;
      const $textEl = this.$element.find(".indicator-calculation__formula-text");
      if ($textEl.length && textData) {
          $textEl.text(textData);
      }
      this.setFormulaLink(formulaData, ".is--pre-2019-20", "2019")
    }
  }

  _initSectionPeriod() {
    this.$element.find(".section-header__info-right").text(this.formatPeriod(this.latestItem.date));
  }

  _initMetric() {
    const $skeleton = this.$element.find(".indicator-metric");
    logIfUnequal(1, $skeleton.length);
    const templateClass = indicatorMetricClass[this.latestItem.rating];
    const $element = $(`.components ${templateClass}`).clone();
    logIfUnequal(1, $element.length);
    const value = this.formatMetric(this.latestItem.result);
    $element.find(".indicator-metric__value").text(value);
    $skeleton.hide();
    $element.insertBefore($skeleton);
    $skeleton.remove();
  }

  setFormulaLink(formulaData, container, last_year) {
    const geo_code = this.geography.geo_code;
    const actualData = formulaData.actual;

    const $actualEl = this.$element.find(container);
    if ($actualEl.length && actualData) {
      const components = Object.values(actualData).map((data) => {
        if (typeof data === 'string') {
          return data;
        } else {
          const $el = $('');
          let params = {};
          let text = '';
          let cube_name = this.cubeNames[data.cube];
          let amount_type = data.amount_type;
          amount_type = this.amountTypes[amount_type] || amount_type;
          // Set the query parameters
          params['municipalities'] = geo_code;
          params['year'] = last_year;
          params['items'] = data.item_codes;

          if (data.amount_type) {
            params['amountType'] = data.amount_type;
          }
          // Generate the text
          text += `[${cube_name}]`;
          if (data.item_description !== undefined && data.item_description !== "") {
            text += ` ${data.item_description}`;
          } else {
            text += ` item code ${data.item_codes.join(', ')}`;
          }
          if (data.amount_type) {
            text += `, ${amount_type}`;
          }
          params = new URLSearchParams(params).toString();
          const url = `${DATA_PORTAL_URL}/table/${data.cube}/?${params}`;
          $el.text(text);
          $el.attr('href', url);
          return $el;
        }
      })
      // Append the components to the element
      $actualEl.append(arrayJoin(components, ' '));
    }
  }

  chartData() {
    const items = this.sectionData.values.map((item) => {
      return {
        period: this.formatPeriod(item.date),
        fillColor: ratingColor(item.rating),
        value: item.result,
      };
    });
    items.reverse();
    return {
      municipality: {
        code: this.geography.geo_code,
        name: this.geography.short_name,
        province_code: this.geography.province_code
      },
      data: items,
      resultType: this.resultType(),
    };
  }
  comparisonChartData(profile, muni) {
    return {
      "municipality": {
        "code": profile.demarcation.code,
        "name": muni.name,
        "province_code": muni.province_code,
      },
      "data": profile.indicators[this.key].values.map(period => {
        return {
          "period": this.formatPeriod(period.date),
          "fillColor": "#ccc",
          "value": period.result,
        };
      }),
    };
  }

  formatMedians() {
    const national = Object.entries(this.medians.national.dev_cat).map(([key, value]) => {
      return {
        period: this.formatPeriod(key),
        value: value,
      };});
    const provincial = Object.entries(this.medians.provincial.dev_cat).map(([key, value]) => {
      return {
        period: this.formatPeriod(key),
        value: value,
      };});
    national.sort(comparePeriod);
    provincial.sort(comparePeriod);
    return {
      national: national,
      provincial: provincial,
    };
  }

  resultType() {
    return {
      "R": "currency",
      "%": "percentage",
    }[this.sectionData.result_type] || this.sectionData.result_type;
  }

  formatSpecifier() {
    return {
      "R": "$,.0f",
    }[this.sectionData.result_type];
  }

  formatValue(value) {
    return locale.format(this.formatSpecifier())(value);
  }

  formatPeriod(period) {
    return formatFinancialYear(period);
  }

  _initAverageButtons() {
    const $provinceButton = $(' <button class="button" style="display: unset"></button>');
    $provinceButton.text(` in ${this.geography.province_name}`);
    $provinceButton.on("click", (function() {
      this.chart.loadMedians(this.formatMedians().provincial);
      ga('send', 'event', 'chart-averages', `${this.key} provincial`);
    }).bind(this));

    const $nationalButton = $(' <button class="button" style="display: unset">nationally</button>');
    $nationalButton.on("click", (function() {
      this.chart.loadMedians(this.formatMedians().national);
      ga('send', 'event', 'chart-averages', `${this.key} national`);
    }).bind(this));

    const averageControls = $("

");
    averageControls.append('Show average for similar municipalities ');
    if (this.geography.category_name !== "metro municipality") {
      averageControls.append([
        $provinceButton,
        " or "
      ]);
    }
    averageControls.append($nationalButton);
    this.chartContainerParent.append(averageControls);

  }

  _initComparisonButtons() {
    this.comparisonButtonsContainer = $("

");
    this.comparisonButtonsContainer.insertBefore(this.chartContainer);
  }

  _updateComparisonButtons() {
    this.comparisonButtonsContainer.empty();
    this.comparisonButtonsContainer.append("Comparing ");
    [this.chartData(), ...(this.comparisons)].forEach((comparison) => {
      const button = $(' <button class="button" style="display: unset; margin: 2px 2px"></button> ');
      button.click(() => {
        this.chart.highlightCol(comparison.municipality.code);
        ga('send', 'event', 'chart-compare-highlight', `${this.key} ${comparison.municipality.code}`);
      });
      button.text(`${comparison.municipality.name}, ${comparison.municipality.province_code}`);
      this.comparisonButtonsContainer.append(button);
    });
  }

  async getSimilarMunis(comparisonOption) {
    try {
      const response = await $.ajax({
        url: '/api/geography/geography/'
      });

      var activeMunicipalities = response.filter(function( obj ) {
          return obj.is_disestablished !== true;
      });
      const miifGrouped  = _.groupBy(activeMunicipalities, "miif_category");
      let similarGroup = miifGrouped[this.geography.miif_category];
      // Remove current muni from selection
      similarGroup = similarGroup.filter(
        muni => muni["geo_code"] !== this.geography.geo_code
      );

      let groups;
      if (comparisonOption === "similar-nearby") {
        groups = _.groupBy(_.toArray(similarGroup), "parent_code");
        similarGroup = groups[this.geography.parent_code];
      } else if (comparisonOption === "similar-same-province") {
        groups = _.groupBy(similarGroup, "province_code");
        similarGroup = groups[this.geography.province_code];
      } else if (comparisonOption === "similar-nationally") {
      } else {
        console.error("unsupported comparisonOption", comparisonOption);
      }
      return similarGroup || [];
    } catch (error) {
      console.error(error);
      alert("Error looking up similar municipalities. Please try again.");
      return [];
    }
  }

  updateChartComparison(comparisonOption) {
    if (comparisonOption === "none") {
      this.chart.loadData([this.chartData()]);
    } else {
      this.getSimilarMunis(comparisonOption).then((similarGroup) => {
        if (similarGroup.length > 0) {
          const sampleMunis = _.sample(similarGroup, 3);
          const deferreds = sampleMunis.map((muni) => {
            const demarcationCode = muni.geo_code;
            const url = `/api/municipality-profile/${demarcationCode}/`;
            return $.get(url);
          });

          Promise.all(deferreds)
            .then((results) => {
              this.comparisons = results.map((result, index) => {
                return this.comparisonChartData(result, sampleMunis[index]);
              });
              this.chart.loadData([this.chartData(), ...(this.comparisons)]);
              this._updateComparisonButtons();
            })
            .catch((details) => {
              console.error("Error fetching comparison data: ", details);
              alert("Error loading comparison data. Please try again.");
            });
        } else {
          alert("Unfortunately there are no similar municipalities for the selection. Please try again.");
        }
      });
    }
  }
}

export class OverUnderSection extends IndicatorSection {
  formatMetric(value) {
    const overunder = value > 0 ? "overspent" : "underspent";
    return `${super.formatMetric(Math.abs(value))} ${overunder}`;
  }
}
<|endoftext|>


<commit_before>(function() {
    var tplAssessment;
    klp.init = function() {
        console.log("init programme page", PROGRAMME_ID);
        klp.router = new KLPRouter();
        klp.router.init();
        tplAssessmentClass = swig.compile($('#tpl-assessment-class').html());
        fetchProgrammeDetails();
    };

    function fetchProgrammeDetails() {
        var schoolId = klp.router.getHash().queryParams.school;
        var url = "programme/" + PROGRAMME_ID;
        var $xhr = klp.api.do(url, {
            'school': schoolId
        });
        $xhr.done(function(data) {
            var assessmentsContext = getContext(data);
            console.log("ass context", assessmentsContext);
            renderAssessments(assessmentsContext);
        });
    }

    function renderAssessments(assessmentClasses) {
        $('#assessmentsContainer').empty();
        var classes = _.keys(assessmentClasses);
        classes.forEach(function(className, i) {
            var html = tplAssessmentClass({
                'className': className,
                'assessments': assessmentClasses[className]
            });
            $('#assessmentsContainer').append(html);
        });
    }

    function getContext(data) {
        var assessmentsArray = data.features;
        var groupedByClass = _.groupBy(assessmentsArray, function(assessment) {
            return assessment.studentgroup;
        });
        _.each(_(groupedByClass).keys(), function(className) {
            groupedByClass[className] = getClassContext(groupedByClass[className]);
        });
        return groupedByClass;
        //console.log("group by", groupedByClass);
    }

    function getClassContext(classData) {
        var ret = [];
        _.each(classData, function(assessmentData) {
            ret.push(getAssessmentContext(assessmentData));
        });
        return ret;
    }

    function getAssessmentContext(assessmentData) {
        var genders = [];
        for (var j=0, length=assessmentData.singlescoredetails.gender.length; j < length; j++) {
            var scoreDetails = assessmentData.singlescoredetails.gender[j];
            var cohortsDetails = assessmentData.cohortsdetails.gender[j];
            var gender = {
                'name': scoreDetails.sex == 'male' ? 'Boys' : 'Girls',
                'singlescore': scoreDetails.singlescore,
                'cohorts': cohortsDetails.total
            };
            genders.push(gender);
        }
        assessmentData.genders = genders;

        var mts = [];
        for (var i=0, length = assessmentData.cohortsdetails.mt.length; i < length; i++) {
            var mt = {
                'name': assessmentData.cohortsdetails.mt[i].mt,
                'singlescore': assessmentData.singlescoredetails.mt[i].singlescore
            };
            mts.push(mt);
        }
        assessmentData.mts = mts;

        var compares = [];
        var boundary = assessmentData.singlescoredetails.boundary;
        var boundaryKeys = _.keys(boundary);
        _.each(boundaryKeys, function(key) {

            var compareObj = {
                'type': key,
                'name': boundary[key].length > 0 ? boundary[key][0].boundary__name : 'error', //FIXME
                'singlescore': boundary[key].length > 0 ? boundary[key][0].singlescore : 0
            };
            compares.push(compareObj);
        });
        assessmentData.compares = compares;

        return assessmentData;
    }

})();<commit_msg>fix front-end to deal with admin1, etc. no longer coming as array<commit_after>(function() {
    var tplAssessment;
    klp.init = function() {
        console.log("init programme page", PROGRAMME_ID);
        klp.router = new KLPRouter();
        klp.router.init();
        tplAssessmentClass = swig.compile($('#tpl-assessment-class').html());
        fetchProgrammeDetails();
    };

    function fetchProgrammeDetails() {
        var schoolId = klp.router.getHash().queryParams.school;
        var url = "programme/" + PROGRAMME_ID;
        var $xhr = klp.api.do(url, {
            'school': schoolId
        });
        $xhr.done(function(data) {
            var assessmentsContext = getContext(data);
            console.log("ass context", assessmentsContext);
            renderAssessments(assessmentsContext);
        });
    }

    function renderAssessments(assessmentClasses) {
        $('#assessmentsContainer').empty();
        var classes = _.keys(assessmentClasses);
        classes.forEach(function(className, i) {
            var html = tplAssessmentClass({
                'className': className,
                'assessments': assessmentClasses[className]
            });
            $('#assessmentsContainer').append(html);
        });
    }

    function getContext(data) {
        var assessmentsArray = data.features;
        var groupedByClass = _.groupBy(assessmentsArray, function(assessment) {
            return assessment.studentgroup;
        });
        _.each(_(groupedByClass).keys(), function(className) {
            groupedByClass[className] = getClassContext(groupedByClass[className]);
        });
        return groupedByClass;
        //console.log("group by", groupedByClass);
    }

    function getClassContext(classData) {
        var ret = [];
        _.each(classData, function(assessmentData) {
            ret.push(getAssessmentContext(assessmentData));
        });
        return ret;
    }

    function getAssessmentContext(assessmentData) {
        var genders = [];
        for (var j=0, length=assessmentData.singlescoredetails.gender.length; j < length; j++) {
            var scoreDetails = assessmentData.singlescoredetails.gender[j];
            var cohortsDetails = assessmentData.cohortsdetails.gender[j];
            var gender = {
                'name': scoreDetails.sex == 'male' ? 'Boys' : 'Girls',
                'singlescore': scoreDetails.singlescore,
                'cohorts': cohortsDetails.total
            };
            genders.push(gender);
        }
        assessmentData.genders = genders;

        var mts = [];
        for (var i=0, length = assessmentData.cohortsdetails.mt.length; i < length; i++) {
            var mt = {
                'name': assessmentData.cohortsdetails.mt[i].mt,
                'singlescore': assessmentData.singlescoredetails.mt[i].singlescore
            };
            mts.push(mt);
        }
        assessmentData.mts = mts;

        var compares = [];
        var boundary = assessmentData.singlescoredetails.boundary;
        var boundaryKeys = _.keys(boundary);
        _.each(boundaryKeys, function(key) {

            var compareObj = {
                'type': key,
                'name': boundary[key].boundary__name,
                'singlescore': boundary[key].singlescore
            };
            compares.push(compareObj);
        });
        assessmentData.compares = compares;

        return assessmentData;
    }

})();<|endoftext|>


<commit_before>var MapView = Backbone.View.extend({
  events: {
    'click .posBtn': 'locate'
  },
  initialize: function(){
    me = this;
    this.settings = window.settings;

    me.listenTo(this.collection, 'reset', me.drawStations);

    Backbone.Mediator.subscribe('station:locate', function(timeseries) {
      me.map.geomap("option", "center", timeseries.get('location').coordinates);
      me.map.geomap("option", "zoom", 12)
      me.map.geomap( "refresh" );
    }, this);
  },

  render: function() {
    map = $("
");
    map.attr("id", "map");
    this.$el.empty().append(map);

    var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
      cloudmadeAttribution = '© OpenStreetMap & Contributors',
      cloudmade = L.tileLayer(cloudmadeUrl, {maxZoom: 17, attribution: cloudmadeAttribution}),
      latlng = L.latLng(50, 10);
    L.Icon.Default.imagePath = '/img';

    this.map = L.map('map', {center: latlng, zoom: 5, layers: [cloudmade]});
    this.markers = L.markerClusterGroup();

    //my position btn
    posBtnWrapper = $("
").addClass('map-buttons');
    this.posBtn = $("").addClass('btn posBtn btn-primary');
    this.posBtn.html("");
    posBtnWrapper.append(this.posBtn);

    this.$el.append(posBtnWrapper);
  },
  drawStations: function() {
    if (this.collection.length > 0) {
      if (this.map.hasLayer(this.markers)) {
        this.map.removeLayer(this.markers);
      }

      this.markers = L.markerClusterGroup();
      var markers = this.markers;
      _.each(this.collection.models, function (elem, index) {
        var geom = elem.get('geometry').coordinates;
        var marker = L.marker(new L.LatLng(geom[1], geom[0]), { /* title: 'station name' */ });
        marker.on('click', me.findAndAdd);
        markers.addLayer(marker);
      });
      this.map.addLayer(this.markers);

      //zoom to extend
      this.map.fitBounds(boundingBoxFromStations(this.collection));
    }
  },
  locate: function(event) {
    this.posBtn.find('#posBtnIcon').removeClass("icon-screenshot");
    this.posBtn.find('#posBtnIcon').addClass("icon-spinner icon-spin");

    navigator.geolocation.getCurrentPosition(this.updateLocation, this.onError, { timeout: 5000, enableHighAccuracy: true });
  },
  updateLocation: function(position) {
    me.posBtn.find('#posBtnIcon').removeClass("icon-spinner icon-spin");
    me.posBtn.find('#posBtnIcon').addClass("icon-screenshot");

    if (me.map.hasLayer(me.yourPos)) {
      me.map.removeLayer(me.yourPos);
    }

    var myIcon = L.icon({
      iconUrl: '/img/marker.png',
      shadowUrl: '/img/marker-shadow.png',

      iconSize:     [40, 46], // size of the icon
      shadowSize:   [41, 41], // size of the shadow
      iconAnchor:   [20, 46], // point of the icon which will correspond to marker's location
      shadowAnchor: [10, 41],  // the same for the shadow
      popupAnchor:  [0, -46] // point from which the popup should open relative to the iconAnchor
    });

    me.yourPos = L.marker(new L.LatLng(position.coords.latitude, position.coords.longitude), { title: "You are here.", icon: myIcon });
    me.map.addLayer(me.yourPos);

    me.map.fitBounds([[position.coords.latitude, position.coords.longitude],[position.coords.latitude, position.coords.longitude]]);
  },
  onError: function(error) {
    me.posBtn.find('#posBtnIcon').removeClass("icon-spinner icon-spin");
    me.posBtn.find('#posBtnIcon').addClass("icon-screenshot");

    showErrorMessage("Can't locate you", error.message);
  },

  findAndAdd: function (e) {
    var lat = e.latlng.lat;
    var lng = e.latlng.lng;
    matches = me.collection.getByCoordinates([lng, lat]);
    stationsView = new StationsView({'collection': matches});

    $('#temp-modals').html(stationsView.render().el);
    $(stationsView.el).modal();
  }
});<commit_msg>fixes #8 the image path<commit_after>var MapView = Backbone.View.extend({
  events: {
    'click .posBtn': 'locate'
  },
  initialize: function(){
    me = this;
    this.settings = window.settings;

    me.listenTo(this.collection, 'reset', me.drawStations);

    Backbone.Mediator.subscribe('station:locate', function(timeseries) {
      me.map.geomap("option", "center", timeseries.get('location').coordinates);
      me.map.geomap("option", "zoom", 12)
      me.map.geomap( "refresh" );
    }, this);
  },

  render: function() {
    map = $("
");
    map.attr("id", "map");
    this.$el.empty().append(map);

    var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png',
      cloudmadeAttribution = '© OpenStreetMap & Contributors',
      cloudmade = L.tileLayer(cloudmadeUrl, {maxZoom: 17, attribution: cloudmadeAttribution}),
      latlng = L.latLng(50, 10);
    L.Icon.Default.imagePath = 'img';

    this.map = L.map('map', {center: latlng, zoom: 5, layers: [cloudmade]});
    this.markers = L.markerClusterGroup();

    //my position btn
    posBtnWrapper = $("
").addClass('map-buttons');
    this.posBtn = $("").addClass('btn posBtn btn-primary');
    this.posBtn.html("");
    posBtnWrapper.append(this.posBtn);

    this.$el.append(posBtnWrapper);
  },
  drawStations: function() {
    if (this.collection.length > 0) {
      if (this.map.hasLayer(this.markers)) {
        this.map.removeLayer(this.markers);
      }

      this.markers = L.markerClusterGroup();
      var markers = this.markers;
      _.each(this.collection.models, function (elem, index) {
        var geom = elem.get('geometry').coordinates;
        var marker = L.marker(new L.LatLng(geom[1], geom[0]), { /* title: 'station name' */ });
        marker.on('click', me.findAndAdd);
        markers.addLayer(marker);
      });
      this.map.addLayer(this.markers);

      //zoom to extend
      this.map.fitBounds(boundingBoxFromStations(this.collection));
    }
  },
  locate: function(event) {
    this.posBtn.find('#posBtnIcon').removeClass("icon-screenshot");
    this.posBtn.find('#posBtnIcon').addClass("icon-spinner icon-spin");

    navigator.geolocation.getCurrentPosition(this.updateLocation, this.onError, { timeout: 5000, enableHighAccuracy: true });
  },
  updateLocation: function(position) {
    me.posBtn.find('#posBtnIcon').removeClass("icon-spinner icon-spin");
    me.posBtn.find('#posBtnIcon').addClass("icon-screenshot");

    if (me.map.hasLayer(me.yourPos)) {
      me.map.removeLayer(me.yourPos);
    }

    var myIcon = L.icon({
      iconUrl: 'img/marker.png',
      shadowUrl: 'img/marker-shadow.png',

      iconSize:     [40, 46], // size of the icon
      shadowSize:   [41, 41], // size of the shadow
      iconAnchor:   [20, 46], // point of the icon which will correspond to marker's location
      shadowAnchor: [10, 41],  // the same for the shadow
      popupAnchor:  [0, -46] // point from which the popup should open relative to the iconAnchor
    });

    me.yourPos = L.marker(new L.LatLng(position.coords.latitude, position.coords.longitude), { title: "You are here.", icon: myIcon });
    me.map.addLayer(me.yourPos);

    me.map.fitBounds([[position.coords.latitude, position.coords.longitude],[position.coords.latitude, position.coords.longitude]]);
  },
  onError: function(error) {
    me.posBtn.find('#posBtnIcon').removeClass("icon-spinner icon-spin");
    me.posBtn.find('#posBtnIcon').addClass("icon-screenshot");

    showErrorMessage("Can't locate you", error.message);
  },

  findAndAdd: function (e) {
    var lat = e.latlng.lat;
    var lng = e.latlng.lng;
    matches = me.collection.getByCoordinates([lng, lat]);
    stationsView = new StationsView({'collection': matches});

    $('#temp-modals').html(stationsView.render().el);
    $(stationsView.el).modal();
  }
});<|endoftext|>


<commit_before>/**
 * Created by kamalnrf
 */

"use strict";

const newUser = require('./handlers/createUserOperator');
const msg_me = require('../msg-me');
const connection = require('./handlers/connection');

const message = (bot) => {

    bot.on('message', (payload, chat) => {
        console.log(payload);
        const message = payload.message.text;

        msg_me.isExiting(payload.sender.id)
            .then(existence => {
                if (existence === true ) {
                    msg_me.isConnected(payload.sender.id)
                        .then(connected => {
                            console.log(`Connection ${connected}`);
                            if (connected !== "true") {

                                chat.say("You need to establish a connection.")
                                    .then(() => {
                                        chat.conversation((convo) => {
                                            convo.sendTypingIndicator(1000)
                                                .then(() => connection.estConnection(convo, payload, bot))
                                                .catch(error => console.log(error));
                                        })
                                    })
                            }
                            else {
                                msg_me.conectedTo(payload.sender.id)
                                    .then(reciverID => {
                                        console.log()
                                        if (message !== 'end!')
                                            bot.say(reciverID, message);
                                        else if (message === 'end!'){
                                            if (msg_me.disconnect(payload.sender.id, reciverID)){
                                                bot.say(reciverID, "Your connection has been closed");
                                                chat.say("Your connection has been closed");
                                            }
                                        }
                                    }).catch(error => console.log(error));
                            }
                        }).catch(error => console.log(error));
                }
                else
                    chat.say("It seems you haven't created a username for you yet.")
                        .then(() => {
                            chat.conversation((convo) => {
                                convo.sendTypingIndicator(1000).then(() => newUser.createUser(convo, payload));
                            })
                        })
            })
            .catch(error => console.log(error));
    });

    bot.on('attachment', (payload, chat) => {
        console.log('An attachment was received!');
        console.log(payload);
        chat.say("We don't support sending attachments yet...");
    });

    console.log("Listening to messages from user");

};

module.exports = message;<commit_msg>patch<commit_after>/**
 * Created by kamalnrf
 */

"use strict";

const newUser = require('./handlers/createUserOperator');
const msg_me = require('../msg-me');
const connection = require('./handlers/connection');

const message = (bot) => {

    bot.on('message', (payload, chat) => {
        console.log(payload);
        const message = payload.message.text;

        msg_me.isExiting(payload.sender.id)
            .then(existence => {
                if (existence === true ) {
                    msg_me.isConnected(payload.sender.id)
                        .then(connected => {
                            console.log(`Connection ${connected}`);
                            if (connected !== "true") {

                                chat.say("You need to establish a connection.")
                                    .then(() => {
                                        chat.conversation((convo) => {
                                            convo.sendTypingIndicator(1000)
                                                .then(() => connection.estConnection(convo, payload, bot))
                                                .catch(error => console.log(error));
                                        })
                                    })
                            }
                            else {
                                msg_me.conectedTo(payload.sender.id)
                                    .then(reciverID => {
                                        if (message !== 'end!')
                                            bot.say(reciverID, message);
                                        else if (message === 'end!'){
                                            if (msg_me.disconnect(payload.sender.id, reciverID)){
                                                bot.say(reciverID, "Your connection has been closed");
                                                chat.say("Your connection has been closed");
                                            }
                                        }
                                    }).catch(error => console.log(error));
                            }
                        }).catch(error => console.log(error));
                }
                else
                    chat.say("It seems you haven't created a username for you yet.")
                        .then(() => {
                            chat.conversation((convo) => {
                                convo.sendTypingIndicator(1000).then(() => newUser.createUser(convo, payload));
                            })
                        })
            })
            .catch(error => console.log(error));
    });

    bot.on('attachment', (payload, chat) => {
        console.log('An attachment was received!');
        console.log(payload);
        chat.say("We don't support sending attachments yet...");
    });

    console.log("Listening to messages from user");

};

module.exports = message;<|endoftext|>


<commit_before>"use strict";

var Promise = require('bluebird')
  , uuid = require('uuid')
  , mmm = require('mmmagic')
  , _ = require('lodash')
  , config = require('../../config/config').load()
  , gm = require('gm')
  , fs = Promise.promisifyAll(require('fs'))
  , inherits = require('util').inherits
  , models = require('../models')
  , AbstractModel = models.AbstractModel
  , Post = models.Post
  , mkKey = require('../support/models').mkKey

exports.addModel = function(database) {
  /**
   * @constructor
   */
  var Attachment = function(params) {
    Attachment.super_.call(this)

    this.id = params.id
    this.file = params.file // FormData File object
    this.fileName = params.fileName // original file name, e.g. 'cute-little-kitten.jpg'
    this.fileSize = params.fileSize // file size in bytes
    this.mimeType = params.mimeType // used as a fallback, in case we can't detect proper one
    this.fileExtension = params.fileExtension // jpg|png|gif etc.
    this.noThumbnail = params.noThumbnail // if true, image thumbnail URL == original URL
    this.mediaType = params.mediaType // image | audio | general

    this.userId = params.userId
    this.postId = params.postId

    if (parseInt(params.createdAt, 10))
      this.createdAt = params.createdAt
    if (parseInt(params.updatedAt, 10))
      this.updatedAt = params.updatedAt
  }

  inherits(Attachment, AbstractModel)

  Attachment.className = Attachment
  Attachment.namespace = 'attachment'
  Attachment.findById = Attachment.super_.findById

  Attachment.prototype.validate = function() {
    return new Promise(function(resolve, reject) {
      var valid = this.file
        && Object.keys(this.file).length > 0
        && this.file.path
        && this.file.path.length > 0
        && this.userId
        && this.userId.length > 0

      if (valid) {
        resolve(valid)
      } else {
        reject(new Error('Invalid'))
      }
    }.bind(this))
  }

  Attachment.prototype.validateOnCreate = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      Promise.join(that.validate(),
                   that.validateUniquness(mkKey(['attachment', that.id])),
                   function(valid, idIsUnique) {
                     resolve(that)
                   })
        .catch(function(e) { reject(e) })
      })
  }

  Attachment.prototype.create = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      that.createdAt = new Date().getTime()
      that.updatedAt = new Date().getTime()
      that.postId = that.postId || ''
      that.id = uuid.v4()

      that.validateOnCreate()
        // Save file to FS
        .then(function(attachment) {
          attachment.fileName = attachment.file.name
          attachment.fileSize = attachment.file.size
          attachment.mimeType = attachment.file.type

          // TODO: extract to config
          var supportedExtensions = /\.(jpe?g|png|gif|mp3|pdf|ppt|txt|docx?)$/i
          if (attachment.fileName && attachment.fileName.match(supportedExtensions).length !== null) {
            attachment.fileExtension = attachment.fileName.match(supportedExtensions)[1].toLowerCase()
          } else {
            attachment.fileExtension = null
          }

          return that.handleMedia(attachment)
        })
        // Save record to DB
        .then(function(attachment) {
          var params = {
            fileName: attachment.fileName,
            fileSize: attachment.fileSize,
            mimeType: attachment.mimeType,
            mediaType: attachment.mediaType,
            fileExtension: attachment.fileExtension,
            noThumbnail: attachment.noThumbnail,
            userId: attachment.userId,
            postId: attachment.postId,
            createdAt: attachment.createdAt.toString(),
            updatedAt: attachment.updatedAt.toString()
          }
          return database.hmsetAsync(mkKey(['attachment', attachment.id]), params)
        })
        .then(function(res) { resolve(that) })
        .catch(function(e) { reject(e) })
    })
  }

  // Get user who created the attachment (via Promise, for serializer)
  Attachment.prototype.getCreatedBy = function() {
    return models.FeedFactory.findById(this.userId)
  }

  // Get public URL of attachment (via Promise, for serializer)
  Attachment.prototype.getUrl = function() {
    var that = this
    return new Promise(function(resolve, reject) {
      resolve(config.attachments.urlDir + that.getFilename())
    })
  }

  // Get public URL of attachment's thumbnail (via Promise, for serializer)
  Attachment.prototype.getThumbnailUrl = function() {
    var that = this
    return new Promise(function(resolve, reject) {
      if (that.noThumbnail === '1') {
        resolve(that.getUrl())
      } else {
        resolve(config.attachments.thumbnails.urlDir + that.getFilename())
      }
    })
  }

  // Get local filesystem path for original file
  Attachment.prototype.getPath = function() {
    return config.attachments.fsDir + this.getFilename()
  }

  // Get local filesystem path for thumbnail file
  Attachment.prototype.getThumbnailPath = function() {
    return config.attachments.thumbnails.fsDir + this.getFilename()
  }

  // Get file name
  Attachment.prototype.getFilename = function() {
    if (this.fileExtension) {
      return this.id + '.' + this.fileExtension
    }
    return this.id
  }

  // Rename file and process thumbnail, if necessary
  Attachment.prototype.handleMedia = async function(attachment) {
    var tmpPath = this.file.path
    var originalPath = this.getPath()

    await fs.rename(tmpPath, originalPath)

    const supportedImageTypes = ["image/jpeg", "image/gif", "image/png", "image/bmp"]
    const supportedAudioTypes = ["audio/mpeg", "audio/ogg", "audio/x-wav"]

    try {
      let magic = new mmm.Magic(mmm.MAGIC_MIME_TYPE)
      let detectFile = Promise.promisify(magic.detectFile, magic);

      this.mimeType = await detectFile(originalPath)
    } catch(e) {
      if (_.isEmpty(this.mimeType)) {
        throw e
      }
      // otherwise, we'll use the fallback provided by the user
    }

    if (supportedImageTypes.indexOf(this.mimeType) != -1) {
      let img = Promise.promisifyAll(gm(originalPath))
      let size = await img.size()

      this.mediaType = 'image'

      if (size.width > 525 || size.height > 175) {
        // Looks big enough, needs a resize
        this.noThumbnail = '0'

        img = img
          .resize(525, 175)
          .autoOrient()
          .quality(95)

        await img.write(this.getThumbnailPath())
      } else {
        // Since it's small, just use original image
        this.noThumbnail = '1'
      }
    } else if (supportedAudioTypes.indexOf(this.mimeType) != -1) {
      this.noThumbnail = '1'
      this.mediaType = 'audio'
    } else {
      this.noThumbnail = '1'
      this.mediaType = 'general'
    }

    return attachment
  }

  return Attachment
}
<commit_msg>treat mp4 (m4a) as audio<commit_after>"use strict";

var Promise = require('bluebird')
  , uuid = require('uuid')
  , mmm = require('mmmagic')
  , _ = require('lodash')
  , config = require('../../config/config').load()
  , gm = require('gm')
  , fs = Promise.promisifyAll(require('fs'))
  , inherits = require('util').inherits
  , models = require('../models')
  , AbstractModel = models.AbstractModel
  , Post = models.Post
  , mkKey = require('../support/models').mkKey

exports.addModel = function(database) {
  /**
   * @constructor
   */
  var Attachment = function(params) {
    Attachment.super_.call(this)

    this.id = params.id
    this.file = params.file // FormData File object
    this.fileName = params.fileName // original file name, e.g. 'cute-little-kitten.jpg'
    this.fileSize = params.fileSize // file size in bytes
    this.mimeType = params.mimeType // used as a fallback, in case we can't detect proper one
    this.fileExtension = params.fileExtension // jpg|png|gif etc.
    this.noThumbnail = params.noThumbnail // if true, image thumbnail URL == original URL
    this.mediaType = params.mediaType // image | audio | general

    this.userId = params.userId
    this.postId = params.postId

    if (parseInt(params.createdAt, 10))
      this.createdAt = params.createdAt
    if (parseInt(params.updatedAt, 10))
      this.updatedAt = params.updatedAt
  }

  inherits(Attachment, AbstractModel)

  Attachment.className = Attachment
  Attachment.namespace = 'attachment'
  Attachment.findById = Attachment.super_.findById

  Attachment.prototype.validate = function() {
    return new Promise(function(resolve, reject) {
      var valid = this.file
        && Object.keys(this.file).length > 0
        && this.file.path
        && this.file.path.length > 0
        && this.userId
        && this.userId.length > 0

      if (valid) {
        resolve(valid)
      } else {
        reject(new Error('Invalid'))
      }
    }.bind(this))
  }

  Attachment.prototype.validateOnCreate = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      Promise.join(that.validate(),
                   that.validateUniquness(mkKey(['attachment', that.id])),
                   function(valid, idIsUnique) {
                     resolve(that)
                   })
        .catch(function(e) { reject(e) })
      })
  }

  Attachment.prototype.create = function() {
    var that = this

    return new Promise(function(resolve, reject) {
      that.createdAt = new Date().getTime()
      that.updatedAt = new Date().getTime()
      that.postId = that.postId || ''
      that.id = uuid.v4()

      that.validateOnCreate()
        // Save file to FS
        .then(function(attachment) {
          attachment.fileName = attachment.file.name
          attachment.fileSize = attachment.file.size
          attachment.mimeType = attachment.file.type

          // TODO: extract to config
          var supportedExtensions = /\.(jpe?g|png|gif|mp3|m4a|pdf|ppt|txt|docx?)$/i

          if (attachment.fileName && attachment.fileName.match(supportedExtensions) !== null) {
            attachment.fileExtension = attachment.fileName.match(supportedExtensions)[1].toLowerCase()
          } else {
            attachment.fileExtension = null
          }

          return that.handleMedia(attachment)
        })
        // Save record to DB
        .then(function(attachment) {
          var params = {
            fileName: attachment.fileName,
            fileSize: attachment.fileSize,
            mimeType: attachment.mimeType,
            mediaType: attachment.mediaType,
            fileExtension: attachment.fileExtension,
            noThumbnail: attachment.noThumbnail,
            userId: attachment.userId,
            postId: attachment.postId,
            createdAt: attachment.createdAt.toString(),
            updatedAt: attachment.updatedAt.toString()
          }
          return database.hmsetAsync(mkKey(['attachment', attachment.id]), params)
        })
        .then(function(res) { resolve(that) })
        .catch(function(e) { reject(e) })
    })
  }

  // Get user who created the attachment (via Promise, for serializer)
  Attachment.prototype.getCreatedBy = function() {
    return models.FeedFactory.findById(this.userId)
  }

  // Get public URL of attachment (via Promise, for serializer)
  Attachment.prototype.getUrl = function() {
    var that = this
    return new Promise(function(resolve, reject) {
      resolve(config.attachments.urlDir + that.getFilename())
    })
  }

  // Get public URL of attachment's thumbnail (via Promise, for serializer)
  Attachment.prototype.getThumbnailUrl = function() {
    var that = this
    return new Promise(function(resolve, reject) {
      if (that.noThumbnail === '1') {
        resolve(that.getUrl())
      } else {
        resolve(config.attachments.thumbnails.urlDir + that.getFilename())
      }
    })
  }

  // Get local filesystem path for original file
  Attachment.prototype.getPath = function() {
    return config.attachments.fsDir + this.getFilename()
  }

  // Get local filesystem path for thumbnail file
  Attachment.prototype.getThumbnailPath = function() {
    return config.attachments.thumbnails.fsDir + this.getFilename()
  }

  // Get file name
  Attachment.prototype.getFilename = function() {
    if (this.fileExtension) {
      return this.id + '.' + this.fileExtension
    }
    return this.id
  }

  // Rename file and process thumbnail, if necessary
  Attachment.prototype.handleMedia = async function(attachment) {
    var tmpPath = this.file.path
    var originalPath = this.getPath()

    await fs.rename(tmpPath, originalPath)

    const supportedImageTypes = ["image/jpeg", "image/gif", "image/png", "image/bmp"]
    const supportedAudioTypes = ["audio/x-m4a", "audio/mp4", "audio/mpeg", "audio/ogg", "audio/x-wav"]

    try {
      let magic = new mmm.Magic(mmm.MAGIC_MIME_TYPE)
      let detectFile = Promise.promisify(magic.detectFile, magic);

      this.mimeType = await detectFile(originalPath)
    } catch(e) {
      if (_.isEmpty(this.mimeType)) {
        throw e
      }
      // otherwise, we'll use the fallback provided by the user
    }

    if (supportedImageTypes.indexOf(this.mimeType) != -1) {
      let img = Promise.promisifyAll(gm(originalPath))
      let size = await img.size()

      this.mediaType = 'image'

      if (size.width > 525 || size.height > 175) {
        // Looks big enough, needs a resize
        this.noThumbnail = '0'

        img = img
          .resize(525, 175)
          .autoOrient()
          .quality(95)

        await img.write(this.getThumbnailPath())
      } else {
        // Since it's small, just use original image
        this.noThumbnail = '1'
      }
    } else if (supportedAudioTypes.indexOf(this.mimeType) != -1) {
      this.noThumbnail = '1'
      this.mediaType = 'audio'
    } else {
      this.noThumbnail = '1'
      this.mediaType = 'general'
    }

    return attachment
  }

  return Attachment
}
<|endoftext|>


<commit_before>'use strict';

angular.module('auth', [
    'ngRoute',
    'ngMessages',
    'ui.bootstrap',
    'common',
    'common.servererror',
    'common.equals',
    'common.utils',
    'auth.services',
    'auth.login',
    'auth.register',
    'auth.password',
    'auth.confirmation'
])
    .constant('ACTIVE_APP', 'auth')
    .config(function ($routeProvider, $locationProvider, $httpProvider) {
        $httpProvider.interceptors.push('authInterceptor');
        $routeProvider.otherwise({redirectTo: '/auth/login'});
    })
    .factory('authInterceptor', function (BASE_URI) {
        return {
            request: function (config) {
                if (config.url.indexOf('auth/') === 0) {
                    config.url = BASE_URI + config.url;
                }
                return config;
            }
        };
    });

<commit_msg>chore: unnecessary inject removed<commit_after>'use strict';

angular.module('auth', [
    'ngRoute',
    'ngMessages',
    'ui.bootstrap',
    'common',
    'common.servererror',
    'common.equals',
    'common.utils',
    'auth.services',
    'auth.login',
    'auth.register',
    'auth.password',
    'auth.confirmation'
])
    .constant('ACTIVE_APP', 'auth')
    .config(function ($routeProvider, $httpProvider) {
        $httpProvider.interceptors.push('authInterceptor');
        $routeProvider.otherwise({redirectTo: '/auth/login'});
    })
    .factory('authInterceptor', function (BASE_URI) {
        return {
            request: function (config) {
                if (config.url.indexOf('auth/') === 0) {
                    config.url = BASE_URI + config.url;
                }
                return config;
            }
        };
    });

<|endoftext|>


<commit_before>module.exports = function({data, methods, computed}) {
  data.bookshelf = []
  data.bookshelf__search_term = ''

  methods.bookshelf__get = function(user_id) {
    let vm = this
    return req({
      url: '/bookshelf/'+user_id,
      cookies: true,
      json: true
    }).then(function(res){
      vm.bookshelf = res
    })
  }

  methods.bookshelf__findId = function(book_id) {
    let vm = this
    return vm.bookshelf.findIndex(function(b){
      return b.book.id === book_id
    })
  }

  methods.bookshelf__add = function(book_id) {
    let vm = this
    if (vm.user_id === undefined) {return}
    w.req({
      method: 'post',
      url: '/bookshelf__add',
      data: {book_id, user_id: vm.user_id},
      cookies: true,
      json: true
    }).then(function(res){
      vm.bookshelf__update(res)
    })
  }

  methods.bookshelf__remove = function(book_id) {
    let vm = this
    if (vm.user_id === undefined) {return}
    w.req({
      method: 'post',
      url: '/bookshelf__remove',
      data: {book_id, user_id: vm.user_id},
      cookies: true,
      json: true
    }).then(function(res){
      if (res.err !== undefined) {return}
      var i = vm.bookshelf__findId(book_id)
      if (i !== -1) {
        vm.bookshelf.splice(i, 1)
      }
    })

  }

  methods.bookshelf__update = function(b){
    var i = vm.bookshelf__findId(b.book.id)
    if (i === -1) {
      vm.bookshelf.unshift(b)
    }
    if (i !== -1) {
      Vue.set(vm.bookshelf, i, b)
    }
  }

  methods.bookshelf__is_in_array = function(book_id) {
    let vm = this
    return vm.bookshelf__findId(book_id) !== -1
  }

  methods.bookshelf__view = function(books, search_term) {
    let vm = this
    var search_term = search_term.toLowerCase().trim()
    var search_reg = search_term
    // var search_reg = search_term.split('').join('.*')

    return books.filter(function(b){
      var book = b.book
      if (search_term.length === 0) {return true}

      const search_result = ['title', 'subtitle', 'authors'].reduce(function(bool, field){
        if (bool === true) {return true}
        if (book[field] === undefined) {return false}
        if (field === 'authors') {
          return book.authors.join(', ').toLowerCase().match(search_reg) !== null
        }
        return book[field].toLowerCase().match(search_reg) !== null
      }, false)

      return search_result
    })
  }

  methods.bookshelf_authors = function(book){
    if (book.book.authors === undefined) {
      console.log(book)
      return ''}
    return book.book.authors.join(', ')
  }
}
<commit_msg>remove some logging<commit_after>module.exports = function({data, methods, computed}) {
  data.bookshelf = []
  data.bookshelf__search_term = ''

  methods.bookshelf__get = function(user_id) {
    let vm = this
    return req({
      url: '/bookshelf/'+user_id,
      cookies: true,
      json: true
    }).then(function(res){
      vm.bookshelf = res
    })
  }

  methods.bookshelf__findId = function(book_id) {
    let vm = this
    return vm.bookshelf.findIndex(function(b){
      return b.book.id === book_id
    })
  }

  methods.bookshelf__add = function(book_id) {
    let vm = this
    if (vm.user_id === undefined) {return}
    w.req({
      method: 'post',
      url: '/bookshelf__add',
      data: {book_id, user_id: vm.user_id},
      cookies: true,
      json: true
    }).then(function(res){
      vm.bookshelf__update(res)
    })
  }

  methods.bookshelf__remove = function(book_id) {
    let vm = this
    if (vm.user_id === undefined) {return}
    w.req({
      method: 'post',
      url: '/bookshelf__remove',
      data: {book_id, user_id: vm.user_id},
      cookies: true,
      json: true
    }).then(function(res){
      if (res.err !== undefined) {return}
      var i = vm.bookshelf__findId(book_id)
      if (i !== -1) {
        vm.bookshelf.splice(i, 1)
      }
    })

  }

  methods.bookshelf__update = function(b){
    var i = vm.bookshelf__findId(b.book.id)
    if (i === -1) {
      vm.bookshelf.unshift(b)
    }
    if (i !== -1) {
      Vue.set(vm.bookshelf, i, b)
    }
  }

  methods.bookshelf__is_in_array = function(book_id) {
    let vm = this
    return vm.bookshelf__findId(book_id) !== -1
  }

  methods.bookshelf__view = function(books, search_term) {
    let vm = this
    var search_term = search_term.toLowerCase().trim()
    var search_reg = search_term
    // var search_reg = search_term.split('').join('.*')

    return books.filter(function(b){
      var book = b.book
      if (search_term.length === 0) {return true}

      const search_result = ['title', 'subtitle', 'authors'].reduce(function(bool, field){
        if (bool === true) {return true}
        if (book[field] === undefined) {return false}
        if (field === 'authors') {
          return book.authors.join(', ').toLowerCase().match(search_reg) !== null
        }
        return book[field].toLowerCase().match(search_reg) !== null
      }, false)

      return search_result
    })
  }

  methods.bookshelf_authors = function(book){
    if (book.book.authors === undefined) {return ''}
    return book.book.authors.join(', ')
  }
}
<|endoftext|>