<commit_before>'use strict'

let _ = require('underscore')
let Rat = require('../db').Rat
let Rescue = require('../db').Rescue

let MongoRescue = require('../models/rescue')
let Errors = require('../errors')
let websocket = require('../websocket')
let Permission = require('../permission')

class Controller {
  static read (query) {
    return new Promise(function (resolve, reject) {
      let limit = parseInt(query.limit) || 25
      delete query.limit

      let offset = parseInt(query.offset) || 0
      delete query.offset

      let dbQuery = {
        where: query,
        limit: limit,
        offset: offset,
        include: [
          {
            model: Rat,
            as: 'rats',
            required: true
          }
        ]
      }

      Rescue.findAndCountAll(dbQuery).then(function (result) {
        let meta = {
          count: result.rows.length,
          limit: limit,
          offset: offset,
          total: result.count
        }

        /* For backwards compatibility reasons we return only the list of rat
        foreign keys, not their objects */
        let rescues = result.rows.map(function (rescueInstance) {
          let rescue = convertRescueToAPIResult(rescueInstance)
          return rescue
        })

        resolve({
          data: rescues,
          meta: meta
        })
      }).catch(function (error) {
        let errorObj = Errors.server_error
        errorObj.detail = error
        reject({
          error: errorObj,
          meta: {}
        })
      })
    })
  }

  static create (query, connection) {
    return new Promise(function (resolve, reject) {
      let ratIds = query.rats
      delete query.rats
      let firstLimpet = query.firstLimpet
      delete query.firstLimpet

      Rescue.create(query).then(function (rescue) {
        let getRatOperations = []

        if (ratIds) {
          for (let ratId of ratIds) {
            getRatOperations.push(Rat.findById(ratId))
          }
        }

        Promise.all(getRatOperations).then(function (rats) {
          let associations = []
          for (let rat of rats) {
            associations.push(rescue.addRat(rat))

            if (firstLimpet) {
              if (rat.id === firstLimpet) {
                associations.push(rescue.setFirstLimpet(rat))
              }
            }
          }

          Promise.all(associations).then(function () {
            findRescueWithRats({ id: rescue.id }).then(function (rescueInstance) {
              let rescue = convertRescueToAPIResult(rescueInstance)

              let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                return cl.clientId !== connection.clientId
              })
              websocket.broadcast(allClientsExcludingSelf, {
                action: 'rescue:created'
              }, rescue)

              resolve({
                data: rescue,
                meta: {}
              })
            })
          }).catch(function (error) {
            let errorModel = Errors.server_error
            errorModel.detail = error
            reject({
              error: errorModel,
              meta: {}
            })
          })
        }).catch(function (error) {
          let errorModel = Errors.server_error
          errorModel.detail = error
          reject({
            error: errorModel,
            meta: {}
          })
        })

      }).catch(function (error) {
        let errorModel = Errors.server_error
        errorModel.detail = error
        reject({
          error: errorModel,
          meta: {}
        })
      })
    })
  }

  static update (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      console.log(connection.isUnauthenticated())
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error })
        return
      }

      if (query.id) {
        retrieveMongoRescueById(query.id).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = userEntitledToMongoRescueAccess(rescue, connection.user) ? 'self.rescue.update' : 'rescue.update'

          Permission.require(permission, connection.user).then(function () {
            for (let key in data) {
              if (key === 'client') {
                _.extend(rescue.client, data)
              } else {
                rescue[key] = data[key]
              }
            }

            rescue.save(function (error, rescue) {
              if (error) {
                let errorModel = Errors.server_error
                errorModel.detail = error
                reject({
                  error: errorModel,
                  meta: {}
                })
              } else {
                let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                  return cl.clientId !== connection.clientId
                })
                websocket.broadcast(allClientsExcludingSelf, {
                  action: 'rescue:updated'
                }, rescue)
                resolve({
                  data: rescue,
                  meta: {}
                })
              }
            })
          }, function (err) {
            reject({ error: err })
          })
        }, function () {
          let errorModel = Errors.not_found
          errorModel.detail = query.id
          reject({
            error: errorModel,
            meta: {}
          })
        })
      }
    })
  }

  static delete (query) {

  }

  static assign (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error, meta: {} })
        return
      }

      if (query.id) {
        findRescueWithRats({ id: query.id }).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = getRescuePermissionType(rescue, connection.user)

          Permission.require(permission, connection.user).then(function () {
            Rat.findById(data.ratId).then(function (rat) {
              rescue.addRat(rat).then(function () {
                findRescueWithRats({ id: query.id }).then(function (rescueInstance) {
                  let rescue = convertRescueToAPIResult(rescueInstance)

                  let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                    return cl.clientId !== connection.clientId
                  })
                  websocket.broadcast(allClientsExcludingSelf, {
                    action: 'rescue:updated'
                  }, rescue)

                  resolve({
                    data: rescue,
                    meta: {}
                  })
                }).catch(function (error) {
                  reject({ error: Errors.throw('server_error', error), meta: {} })
                })
              }).catch(function (error) {
                reject({ error: Errors.throw('server_error', error), meta: {} })
              })
            }).catch(function (error) {
              reject({ error: Errors.throw('server_error', error), meta: {} })
            })
          }, function (error) {
            reject({ error: error })
          })
        }, function (error) {
          reject({ error: Errors.throw('server_error', error), meta: {} })
        })
      } else {
        reject({ error: Errors.throw('bad_request', 'Missing rescue id'), meta: {} })
      }
    })
  }

  static unassign (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error, meta: {} })
        return
      }

      if (query.id) {
        findRescueWithRats({ id: query.id }).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = getRescuePermissionType(rescue, connection.user)

          Permission.require(permission, connection.user).then(function () {
            Rat.findById(data.ratId).then(function (rat) {
              rescue.removeRat(rat).then(function () {
                findRescueWithRats({ id: query.id }).then(function (rescueInstance) {
                  let rescue = convertRescueToAPIResult(rescueInstance)

                  let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                    return cl.clientId !== connection.clientId
                  })
                  websocket.broadcast(allClientsExcludingSelf, {
                    action: 'rescue:updated'
                  }, rescue)

                  resolve({
                    data: rescue,
                    meta: {}
                  })
                }).catch(function (error) {
                  reject({ error: Errors.throw('server_error', error), meta: {} })
                })
              }).catch(function (error) {
                reject({ error: Errors.throw('server_error', error), meta: {} })
              })
            }).catch(function (error) {
              reject({ error: Errors.throw('server_error', error), meta: {} })
            })
          }, function (error) {
            reject({ error: error })
          })
        }, function (error) {
          reject({ error: Errors.throw('server_error', error), meta: {} })
        })
      } else {
        reject({ error: Errors.throw('bad_request', 'Missing rescue id'), meta: {} })
      }
    })
  }

  static addquote (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error })
        return
      }

      if (query.id) {
        retrieveMongoRescueById(query.id).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = userEntitledToMongoRescueAccess(rescue, connection.user) ? 'self.rescue.update' : 'rescue.update'

          Permission.require(permission, connection.user).then(function () {
            let update = {
              '$push': {
                quotes: data
              }
            }

            let options = {
              new: true
            }

            MongoRescue.findByIdAndUpdate(query.id, update, options, function (err, rescue) {
              if (err) {
                reject({ error: err, meta: {} })
              } else if (!rescue) {
                reject({ error: '404', meta: {} })
              } else {
                let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                  return cl.clientId !== connection.clientId
                })
                websocket.broadcast(allClientsExcludingSelf, {
                  action: 'rescue:updated'
                }, rescue)
                resolve({ data: rescue, meta: {} })
              }
            })
          }, function () {

          })
        }, function () {

        })
      }
    })
  }
}

class HTTP {
  static assign (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)

    Controller.assign(request.params, request, request.params).then(function (data) {
      response.model.data = data.data
      response.status(200)
      next()
    }).catch(function (error) {
      response.model.errors.push(error.error)
      response.status(error.error.code)
      next()
    })
  }

  static unassign (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)

    Controller.unassign(request.params, request, request.params).then(function (data) {
      response.model.data = data.data
      response.status(200)
      next()
    }).catch(function (error) {
      response.model.errors.push(error.error)
      response.status(error.error.code)
    })
  }

  static addquote (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)
    Controller.addquote(request.body.quotes, null, request.params).then(function (data) {
      response.model.data = data.data
      response.status(200)
      next()
    }, function (error) {
      response.model.errors.push(error.error)
      response.status(400)
    })
  }

  static get (request, response, next) {
    Controller.read(request.query).then(function (res) {
      let data = res.data
      let meta = res.meta

      response.model.data = data
      response.model.meta = meta
      response.status = 400
      next()
    }).catch(function (error) {
      response.model.errors.push(error.error)
      response.status(error.error.code)
      next()
    })
  }

  static getById (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)
    let id = request.params.id

    MongoRescue.findById(id).populate('rats').exec(function (error, rescue) {
      if (error) {
        response.model.errors.push(error)
        response.status(400)
      } else {
        response.model.data = rescue
        response.status(200)
      }

      next()
    })
  }

  static post (request, response, next) {
    Controller.create(request.body, {}).then(function (res) {
      response.model.data = res.data
      response.status(201)
      next()
    }, function (error) {
      response.model.errors.push(error)
      response.status(400)
      next()
    })
  }

  static put (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)

    Controller.update(request.body, request, request.params).then(function (data) {
      response.model.data = data.data
      response.status(201)
      next()
    }).catch(function (error) {
      response.model.errors.push(error)
      response.status(error.error.code)
      next()
    })
  }

  static delete (request, response, next) {

  }
}

function retrieveMongoRescueById (id) {
  return new Promise(function (resolve, reject) {
    MongoRescue.findById(id).populate('rats').exec(function (error, rescue) {
      if (error) {
        reject(error)
      } else {
        resolve(rescue)
      }
    })
  })
}

function getRescuePermissionType (rescue, user) {
  return userEntitledToMongoRescueAccess(rescue, user) ? 'self.rescue.update' : 'rescue.update'
}

function userEntitledToMongoRescueAccess (rescue, user) {
  if (rescue.open === true) {
    return true
  }

  for (let CMDR of user.CMDRs) {
    if (rescue.rats.includes(CMDR)) {
      return true
    }
  }
  return false
}

function convertRescueToAPIResult (rescueInstance) {
  let rescue = rescueInstance.toJSON()
  let reducedRats = rescue.rats.map(function (rat) {
    return rat.id
  })
  rescue.rats = reducedRats

  rescue.firstLimpet = rescue.firstLimpetId
  delete rescue.firstLimpetId
  return rescue
}

function findRescueWithRats (where) {
  return Rescue.findOne({
    where: where,
    include: [
      {
        model: Rat,
        as: 'rats',
        required: true
      }
    ]
  })
}

module.exports = { Controller, HTTP }
<commit_msg>Migrate rescue addquote to postgres<commit_after>'use strict'

let _ = require('underscore')
let Rat = require('../db').Rat
let Rescue = require('../db').Rescue

let MongoRescue = require('../models/rescue')
let Errors = require('../errors')
let websocket = require('../websocket')
let Permission = require('../permission')

class Controller {
  static read (query) {
    return new Promise(function (resolve, reject) {
      let limit = parseInt(query.limit) || 25
      delete query.limit

      let offset = parseInt(query.offset) || 0
      delete query.offset

      let dbQuery = {
        where: query,
        limit: limit,
        offset: offset,
        include: [
          {
            model: Rat,
            as: 'rats',
            required: true
          }
        ]
      }

      Rescue.findAndCountAll(dbQuery).then(function (result) {
        let meta = {
          count: result.rows.length,
          limit: limit,
          offset: offset,
          total: result.count
        }

        /* For backwards compatibility reasons we return only the list of rat
        foreign keys, not their objects */
        let rescues = result.rows.map(function (rescueInstance) {
          let rescue = convertRescueToAPIResult(rescueInstance)
          return rescue
        })

        resolve({
          data: rescues,
          meta: meta
        })
      }).catch(function (error) {
        let errorObj = Errors.server_error
        errorObj.detail = error
        reject({
          error: errorObj,
          meta: {}
        })
      })
    })
  }

  static create (query, connection) {
    return new Promise(function (resolve, reject) {
      let ratIds = query.rats
      delete query.rats
      let firstLimpet = query.firstLimpet
      delete query.firstLimpet

      Rescue.create(query).then(function (rescue) {
        let getRatOperations = []

        if (ratIds) {
          for (let ratId of ratIds) {
            getRatOperations.push(Rat.findById(ratId))
          }
        }

        Promise.all(getRatOperations).then(function (rats) {
          let associations = []
          for (let rat of rats) {
            associations.push(rescue.addRat(rat))

            if (firstLimpet) {
              if (rat.id === firstLimpet) {
                associations.push(rescue.setFirstLimpet(rat))
              }
            }
          }

          Promise.all(associations).then(function () {
            findRescueWithRats({ id: rescue.id }).then(function (rescueInstance) {
              let rescue = convertRescueToAPIResult(rescueInstance)

              let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                return cl.clientId !== connection.clientId
              })
              websocket.broadcast(allClientsExcludingSelf, {
                action: 'rescue:created'
              }, rescue)

              resolve({
                data: rescue,
                meta: {}
              })
            })
          }).catch(function (error) {
            let errorModel = Errors.server_error
            errorModel.detail = error
            reject({
              error: errorModel,
              meta: {}
            })
          })
        }).catch(function (error) {
          let errorModel = Errors.server_error
          errorModel.detail = error
          reject({
            error: errorModel,
            meta: {}
          })
        })

      }).catch(function (error) {
        let errorModel = Errors.server_error
        errorModel.detail = error
        reject({
          error: errorModel,
          meta: {}
        })
      })
    })
  }

  static update (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error })
        return
      }

      if (query.id) {
        retrieveMongoRescueById(query.id).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = userEntitledToMongoRescueAccess(rescue, connection.user) ? 'self.rescue.update' : 'rescue.update'

          Permission.require(permission, connection.user).then(function () {
            for (let key in data) {
              if (key === 'client') {
                _.extend(rescue.client, data)
              } else {
                rescue[key] = data[key]
              }
            }

            rescue.save(function (error, rescue) {
              if (error) {
                let errorModel = Errors.server_error
                errorModel.detail = error
                reject({
                  error: errorModel,
                  meta: {}
                })
              } else {
                let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                  return cl.clientId !== connection.clientId
                })
                websocket.broadcast(allClientsExcludingSelf, {
                  action: 'rescue:updated'
                }, rescue)
                resolve({
                  data: rescue,
                  meta: {}
                })
              }
            })
          }, function (err) {
            reject({ error: err })
          })
        }, function () {
          let errorModel = Errors.not_found
          errorModel.detail = query.id
          reject({
            error: errorModel,
            meta: {}
          })
        })
      }
    })
  }

  static delete () {

  }

  static assign (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error, meta: {} })
        return
      }

      if (query.id) {
        findRescueWithRats({ id: query.id }).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = getRescuePermissionType(rescue, connection.user)

          Permission.require(permission, connection.user).then(function () {
            Rat.findById(data.ratId).then(function (rat) {
              rescue.addRat(rat).then(function () {
                findRescueWithRats({ id: query.id }).then(function (rescueInstance) {
                  let rescue = convertRescueToAPIResult(rescueInstance)

                  let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                    return cl.clientId !== connection.clientId
                  })
                  websocket.broadcast(allClientsExcludingSelf, {
                    action: 'rescue:updated'
                  }, rescue)

                  resolve({
                    data: rescue,
                    meta: {}
                  })
                }).catch(function (error) {
                  reject({ error: Errors.throw('server_error', error), meta: {} })
                })
              }).catch(function (error) {
                reject({ error: Errors.throw('server_error', error), meta: {} })
              })
            }).catch(function (error) {
              reject({ error: Errors.throw('server_error', error), meta: {} })
            })
          }, function (error) {
            reject({ error: error })
          })
        }, function (error) {
          reject({ error: Errors.throw('server_error', error), meta: {} })
        })
      } else {
        reject({ error: Errors.throw('bad_request', 'Missing rescue id'), meta: {} })
      }
    })
  }

  static unassign (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error, meta: {} })
        return
      }

      if (query.id) {
        findRescueWithRats({ id: query.id }).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = getRescuePermissionType(rescue, connection.user)

          Permission.require(permission, connection.user).then(function () {
            Rat.findById(data.ratId).then(function (rat) {
              rescue.removeRat(rat).then(function () {
                findRescueWithRats({ id: query.id }).then(function (rescueInstance) {
                  let rescue = convertRescueToAPIResult(rescueInstance)

                  let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                    return cl.clientId !== connection.clientId
                  })
                  websocket.broadcast(allClientsExcludingSelf, {
                    action: 'rescue:updated'
                  }, rescue)

                  resolve({
                    data: rescue,
                    meta: {}
                  })
                }).catch(function (error) {
                  reject({ error: Errors.throw('server_error', error), meta: {} })
                })
              }).catch(function (error) {
                reject({ error: Errors.throw('server_error', error), meta: {} })
              })
            }).catch(function (error) {
              reject({ error: Errors.throw('server_error', error), meta: {} })
            })
          }, function (error) {
            reject({ error: error })
          })
        }, function (error) {
          reject({ error: Errors.throw('server_error', error), meta: {} })
        })
      } else {
        reject({ error: Errors.throw('bad_request', 'Missing rescue id'), meta: {} })
      }
    })
  }

  static addquote (data, connection, query) {
    return new Promise(function (resolve, reject) {
      // Modifying a rescue requires an authenticated user
      if (connection.isUnauthenticated()) {
        let error = Permission.authenticationError('rescue.update')
        reject({ error: error, meta: {} })
        return
      }

      if (query.id) {
        findRescueWithRats({ id: query.id }).then(function (rescue) {
          // If the rescue is closed or the user is not involved with the rescue, we will require moderator permission
          let permission = getRescuePermissionType(rescue, connection.user)

          Permission.require(permission, connection.user).then(function () {
            let updatedQuotes = rescue.quotes.concat(data)
            Rescue.update(
              {
                quotes: updatedQuotes
              }, {
                where: { id: rescue.id }
              }).then(function () {
                findRescueWithRats({ id: query.id }).then(function (rescueInstance) {
                  let rescue = convertRescueToAPIResult(rescueInstance)

                  let allClientsExcludingSelf = websocket.socket.clients.filter(function (cl) {
                    return cl.clientId !== connection.clientId
                  })
                  websocket.broadcast(allClientsExcludingSelf, {
                    action: 'rescue:updated'
                  }, rescue)
                  resolve({ data: rescue, meta: {} })
                }).catch(function (error) {
                  reject({ error: Errors.throw('server_error', error), meta: {} })
                })
              }).catch(function (error) {
                reject({ error: Errors.throw('server_error', error), meta: {} })
              }
            )
          }, function (error) {
            reject({ error: error })
          })
        }, function (error) {
          reject({ error: Errors.throw('server_error', error), meta: {} })
        })
      } else {
        reject({ error: Errors.throw('bad_request', 'Missing rescue id'), meta: {} })
      }
    })
  }
}

class HTTP {
  static assign (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)

    Controller.assign(request.params, request, request.params).then(function (data) {
      response.model.data = data.data
      response.status(200)
      next()
    }).catch(function (error) {
      response.model.errors.push(error.error)
      response.status(error.error.code)
      next()
    })
  }

  static unassign (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)

    Controller.unassign(request.params, request, request.params).then(function (data) {
      response.model.data = data.data
      response.status(200)
      next()
    }).catch(function (error) {
      response.model.errors.push(error.error)
      response.status(error.error.code)
      next()
    })
  }

  static addquote (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)
    Controller.addquote(request.body.quotes, request, request.params).then(function (data) {
      response.model.data = data.data
      response.status(200)
      next()
    }, function (error) {
      response.model.errors.push(error.error)
      response.status(error.error.code)
      next()
    })
  }

  static get (request, response, next) {
    Controller.read(request.query).then(function (res) {
      let data = res.data
      let meta = res.meta

      response.model.data = data
      response.model.meta = meta
      response.status = 400
      next()
    }).catch(function (error) {
      response.model.errors.push(error.error)
      response.status(error.error.code)
      next()
    })
  }

  static getById (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)
    let id = request.params.id

    MongoRescue.findById(id).populate('rats').exec(function (error, rescue) {
      if (error) {
        response.model.errors.push(error)
        response.status(400)
      } else {
        response.model.data = rescue
        response.status(200)
      }

      next()
    })
  }

  static post (request, response, next) {
    Controller.create(request.body, {}).then(function (res) {
      response.model.data = res.data
      response.status(201)
      next()
    }, function (error) {
      response.model.errors.push(error)
      response.status(400)
      next()
    })
  }

  static put (request, response, next) {
    response.model.meta.params = _.extend(response.model.meta.params, request.params)

    Controller.update(request.body, request, request.params).then(function (data) {
      response.model.data = data.data
      response.status(201)
      next()
    }).catch(function (error) {
      response.model.errors.push(error)
      response.status(error.error.code)
      next()
    })
  }

  static delete () {

  }
}

function retrieveMongoRescueById (id) {
  return new Promise(function (resolve, reject) {
    MongoRescue.findById(id).populate('rats').exec(function (error, rescue) {
      if (error) {
        reject(error)
      } else {
        resolve(rescue)
      }
    })
  })
}

function getRescuePermissionType (rescue, user) {
  return userEntitledToMongoRescueAccess(rescue, user) ? 'self.rescue.update' : 'rescue.update'
}

function userEntitledToMongoRescueAccess (rescue, user) {
  if (rescue.open === true) {
    return true
  }

  for (let CMDR of user.CMDRs) {
    if (rescue.rats.includes(CMDR)) {
      return true
    }
  }
  return false
}

function convertRescueToAPIResult (rescueInstance) {
  let rescue = rescueInstance.toJSON()
  let reducedRats = rescue.rats.map(function (rat) {
    return rat.id
  })
  rescue.rats = reducedRats

  rescue.firstLimpet = rescue.firstLimpetId
  delete rescue.firstLimpetId
  return rescue
}

function findRescueWithRats (where) {
  return Rescue.findOne({
    where: where,
    include: [
      {
        model: Rat,
        as: 'rats',
        required: true
      }
    ]
  })
}

module.exports = { Controller, HTTP }
<|endoftext|>


<commit_before>import queryString from 'query-string';

import Movie from '../models/Movie';

const getSortObj = (sort) => {
  let sortObj;
  switch (sort) {
    default:
      sortObj = { year: -1 };
  }
  return sortObj;
};

export const getSearch = async (req, res) => {
  const { query, name } = req;
  let matchObj;

  if (name) {
    const regex = new RegExp(name);

    matchObj = {
      $or: [
        { 'title.fr': regex },
        { 'title.en': regex },
        { director: regex },
      ],
    };
  }

  // create aggregate params
  const sortObj = getSortObj(query.sort);

  // define number of results per requests
  const toSkip = !query.start ? 0 : parseInt(query.start, 10);
  const numberPerRequest = 10;

  // get users from db
  const cursor = Movie.find(
    matchObj,
    null,
    {
      sort: sortObj,
      skip: toSkip,
      limit: numberPerRequest,
    }
  );

  const movies = await cursor.exec();

  // format server response
  const resObj = { error: '', movies };
  if (movies.length === numberPerRequest) {
    query.start = toSkip + numberPerRequest;
    resObj.nextHref = `/api/gallery/search?${queryString.stringify(query)}`;
  }

  // send response and end request
  return res.send(resObj);
};

export default getSearch;
<commit_msg>search ok<commit_after>import queryString from 'query-string';

import Movie from '../models/Movie';

const getSortObj = (sort) => {
  let sortObj;
  switch (sort) {
    default:
      sortObj = { year: -1 };
  }
  return sortObj;
};

export const getSearch = async (req, res) => {
  const { query } = req;
  let matchObj = {};

  if (query.name) {
    const regex = new RegExp(query.name);

    matchObj = {
      $or: [
        { 'title.fr': regex },
        { 'title.en': regex },
        { director: regex },
        { stars: regex },
      ],
    };
  }

  // create aggregate params
  const sortObj = getSortObj(query.sort);

  // define number of results per requests
  const toSkip = !query.start ? 0 : parseInt(query.start, 10);
  const numberPerRequest = 10;

  // get users from db
  console.log(matchObj);
  const cursor = Movie.find(
    matchObj,
    null,
    {
      sort: sortObj,
      skip: toSkip,
      limit: numberPerRequest,
    }
  );

  const movies = await cursor.exec();

  // format server response
  const resObj = { error: '', movies };
  if (movies.length === numberPerRequest) {
    query.start = toSkip + numberPerRequest;
    resObj.nextHref = `/api/gallery/search?${queryString.stringify(query)}`;
  }

  // send response and end request
  return res.send(resObj);
};

export default getSearch;
<|endoftext|>


<commit_before>//console.log('===== Class Person ==================');
var Person = function (){
	console.log('Constructor: Person');
	this.name = "";
	this.birthday = "";
	this.getName = function(){
		return this.name;
	}
	this.setName = function(name){
		this.name = name;
	}
	this.getBirthday = function(){
		return this.birthday;
	}
	this.setBirthday = function(birthday){
		this.birthday = birthday;
	}
	this.sayHello = function(){
		return this.name + " : " + this.birthday;
	}
};

//console.log('===== Class Student ==================');
var Student = function(){
	Person.call(this);
	console.log('Constructor: Student');
	this.sayHello = function(){
		return "I'm a student. " + this.name + " : " + this.birthday;
	}
}
Student.prototype = Object.create( Person.prototype );

//console.log('===== Class Teacher ==================');
var Teacher = function(){
	Person.call(this);
	console.log('Constructor: Teacher');
	this.sayHello = function(){
		return "I'm an teacher. " + this.name + " : " + this.birthday;
	}
}
Teacher.prototype = Object.create( Person.prototype );

//console.log('===== Class MathTeacher ==================');
var MathTeacher = function(){
	Teacher.call(this);
	console.log('Constructor: MathTeacher');
	this.sayHello = function(){
		return "I'm an math teacher. " + this.name + " : " + this.birthday;
	}
}
MathTeacher.prototype = Object.create( Teacher.prototype );

(function(){

var personsList = [];

console.log('===== Create Student 1 ==================');
var c1 = new Student;
personsList.push(c1);
c1.setName('John');
c1.setBirthday('2000/3/8');
console.log(c1.sayHello());

console.log('===== Create Student 2 ==================');
var c2 = new Student;
personsList.push(c2);
c2.setName('Mary');
c2.setBirthday('2000/10/30');
console.log(c2.sayHello());

console.log('===== Create Teacher 1 ==================');
var i1 = new Teacher;
personsList.push(i1);
i1.setName('Bill');
i1.setBirthday('1980/12/3');
console.log(i1.sayHello());

console.log('===== Create MathTeacher 2 ==================');
var i2 = new MathTeacher;
personsList.push(i2);
i2.setName('Gary');
i2.setBirthday('1970/1/22');
console.log(i2.sayHello());

console.log('===== Test polymorphism ==================');
for(var o in personsList){
	if( personsList[o] instanceof Person ){
		console.log(personsList[o].sayHello());
	}
}

})();
<commit_msg>Use prototype practice for better performance. Reference: http://jsperf.com/class-practice<commit_after>//console.log('===== Class Person ==================');
var Person = function (){
	console.log('Constructor: Person');
	this.name = "";
	this.birthday = "";
};
Person.prototype.getName = function(){
	return this.name;
}
Person.prototype.setName = function(name){
	this.name = name;
}
Person.prototype.getBirthday = function(){
	return this.birthday;
}
Person.prototype.setBirthday = function(birthday){
	this.birthday = birthday;
}
Person.prototype.sayHello = function(){
	return this.name + " : " + this.birthday;
}

//console.log('===== Class Student ==================');
var Student = function(){
	Person.call(this);
	console.log('Constructor: Student');
}
Student.prototype.sayHello = function(){
	return "I'm a student. " + this.name + " : " + this.birthday;
}
Student.prototype = Object.create( Person.prototype );

//console.log('===== Class Teacher ==================');
var Teacher = function(){
	Person.call(this);
	console.log('Constructor: Teacher');
}
Teacher.prototype.sayHello = function(){
	return "I'm an teacher. " + this.name + " : " + this.birthday;
}
Teacher.prototype = Object.create( Person.prototype );

//console.log('===== Class MathTeacher ==================');
var MathTeacher = function(){
	Teacher.call(this);
	console.log('Constructor: MathTeacher');
}
MathTeacher.prototype.sayHello = function(){
	return "I'm an math teacher. " + this.name + " : " + this.birthday;
}
MathTeacher.prototype = Object.create( Teacher.prototype );

(function(){

var personsList = [];

console.log('===== Create Student 1 ==================');
var c1 = new Student;
personsList.push(c1);
c1.setName('John');
c1.setBirthday('2000/3/8');
console.log(c1.sayHello());

console.log('===== Create Student 2 ==================');
var c2 = new Student;
personsList.push(c2);
c2.setName('Mary');
c2.setBirthday('2000/10/30');
console.log(c2.sayHello());

console.log('===== Create Teacher 1 ==================');
var i1 = new Teacher;
personsList.push(i1);
i1.setName('Bill');
i1.setBirthday('1980/12/3');
console.log(i1.sayHello());

console.log('===== Create MathTeacher 2 ==================');
var i2 = new MathTeacher;
personsList.push(i2);
i2.setName('Gary');
i2.setBirthday('1970/1/22');
console.log(i2.sayHello());

console.log('===== Test polymorphism ==================');
for(var o in personsList){
	if( personsList[o] instanceof Person ){
		console.log(personsList[o].sayHello());
	}
}

})();
<|endoftext|>


<commit_before>import _ from 'pluralize'
import { normalizeScore, normalizeReason, regex } from './helpers'

// Main rule wrapper with properties normalization
function rule (props) {
  props.score = normalizeScore(props.score)
  props.reason = normalizeReason(props.reason, props.count)
  return props
}

export function reuseTCPconnections ({ page, entries, ...opts }) {
  const { penalty = 5, limit = 1 } = opts
  const subEntries = entries.filter(entry =>
    entry.isValid &&
    !entry.isRedirect &&
    !(entry.resHeaders.connection !== 'close')
  )
  const count = subEntries.length
  let score = 100
  if (count > limit) {
    score -= penalty * (count - limit)
  }
  return rule({
    count: count,
    description: 'Persistent connections allow multiple HTTP requests use the same TCP connection, thus eliminates TCP handshakes and slow-start latency overhead. Leverage persistent connections whenever possible.',
    reason: `There ${_('is', count)} ${count} immediately terminated ${_('connection', count)}`,
    score: score,
    title: 'Reuse TCP connections',
    type: 'general',
    values: subEntries.map(entry => entry.url.href),
    weight: 8
  })
}
export function cacheAssets ({ page, entries, ...opts }) {
  const { limit = 1, penalty = 5 } = opts
  const values = entries.filter((entry) => {
    const headers = entry.resHeaders
    return !entry.isRedirect &&
      entry.isValid &&
      !entry.isRedirect &&
      (!headers['cache-control'] || !headers['expires'])
  })
  const count = values.length
  let score = 100
  if (count > limit) {
    score -= penalty * (count - limit)
  }
  return rule({
    count: count,
    description: 'Reduce the load time of your page by storing commonly used files on your visitors browser.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} without expiration time`,
    score: score,
    title: 'Cache resources on the client',
    type: 'general',
    values: values.map(entry => entry.url.href),
    weight: 8
  })
}
export function compressAssets ({ page, entries, ...opts }) {
  const { minSize = 256, penalty = 5 } = opts
  const values = entries.filter((entry) => {
    const headers = entry.resHeaders
    return (
      entry.isValid &&
      !entry.isRedirect &&
      entry.content.size >= minSize &&
      regex.text.test(entry.content.mimeType) &&
      !regex.encoding.test(headers['content-encoding'])
    )
  })
  const count = values.length
  const score = 100 - (penalty * count)
  return rule({
    count: count,
    description: 'Application resources should be transferred with the minimum number of bytes. Always apply the best compression method for each transferred asset.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} without compression`,
    score: score,
    title: 'Compress assets',
    type: 'general',
    values: values.map(entry => entry.url.href),
    weight: 7
  })
}

export function reduceRedirects ({ page, entries, ...opts }) {
  const { penalty = 25 } = opts
  const values = entries.filter(entry => entry.isRedirect)
  const internalRedirects = values.filter(entry =>
      entry.status === 307 &&
      entry.statusText === 'Internal Redirect' &&
      entry.resHeaders['non-authoritative-reason']
  )
  const count = values.length
  const subCount = count - internalRedirects.length
  return rule({
    count: count,
    description: 'HTTP redirects impose high latency overhead. The optimal number of redirects is zero.',
    reason: `There ${_('is', values.length)} ${_('redirect', values.length, true)}`,
    score: 100 - (penalty * subCount),
    title: 'Minimize number of HTTP redirects',
    type: 'general',
    values: values.map((entry) =>
      `(${entry.status} ${entry.statusText || 'Redirect'}) ${entry.url.href} -> ${entry.redirectUrl || '-'}`
    ),
    weight: 7
  })
}

export function reduceDNSlookups ({ page, ...opts }) {
  const { limit = 4, penalty = 5 } = opts
  const { dns, dnsLookups } = page
  let score = 100
  if (dnsLookups > limit) {
    score -= penalty * (dnsLookups - limit)
  }
  return rule({
    count: dnsLookups,
    description: 'Making requests to a large number of different hosts can hurt performance.',
    score: score,
    title: 'Reduce DNS lookups',
    type: 'general',
    values: Object.keys(dns).map(key => `${key} (${dns[key]})`),
    weight: 4
  })
}

export function eliminateBrokenRequests ({ entries, ...opts }) {
  const { penalty = 10 } = opts
  const values = entries.filter(entry => entry.status >= 400)
  const count = values.length
  const score = 100 - (count * penalty)
  return rule({
    count: count,
    description: 'Avoid fetching content that does not exist.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that not exists`,
    score: score,
    title: 'Eliminate requests to non-existent or broken resources.',
    type: 'general',
    values: values.map(entry => `(${entry.status}) ${entry.url.href}`),
    weight: 2
  })
}

export function useHttp2 ({ entries }) {
  const requests = entries.filter(entry => entry.isValid && !entry.isRedirect)
  const values = requests.filter(entry => !entry.isHttp2)
  const count = values.length
  const score = 100 - Math.round(100 * (count / requests.length))
  return rule({
    count: count,
    description: 'HTTP/2 enables more efficient use of network resources and reduced latency by enabling request and response multiplexing, header compression, prioritization, and more.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that are not using HTTP/2 connections`,
    score: score,
    title: 'Serve your content using HTTP/2',
    type: 'h2',
    values: values.map(entry => entry.url.href),
    weight: 4
  })
}

export function eliminateDomainSharding ({ page, entries, opts = {} }) {
  const { penalty = 25, limit = 1 } = opts
  const h2entries = entries.filter(entry =>
    entry.isHttp2 &&
    entry.isValid &&
    !entry.isRedirect
  )
  const reverseDns = h2entries
    .reduce((obj, { hostname, ip }) => {
      const domains = obj[ip]
      obj[ip] = domains ? domains.add(hostname) : new Set([hostname])
      return obj
    }, {})
  const values = Object.keys(reverseDns)
  const count = values.length - limit
  const score = h2entries.length > 0 ? 100 - (count * penalty) : null
  return rule({
    count: count,
    description: 'Under HTTP/1 parallelism is limited by number of TCP connections (in practice ~6 connections per origin). However, each of these connections incur unnecessary overhead and compete with each other for bandwidth. Domain sharding should be avoided in HTTP/2.',
    score: score,
    title: 'Elimiminate domain sharding',
    type: 'h2',
    values: values.map(ip => `${ip} -> ${[...reverseDns[ip]].join(', ')}`),
    weight: 4
  })
}

export function avoidConcatenating ({ page, entries, ...opts }) {
  const { maxSize = 70000, penalty = 5 } = opts
  const values = entries.filter(entry =>
    entry.isHttp2 &&
    entry.isValid &&
    entry.content.size >= maxSize &&
    regex.jsOrCss.test(entry.content.mimeType)
  )
  const count = values.length
  return rule({
    count: count,
    description: 'Ship small granular resources and optimize caching policies. Significant wins in compression are the only cases where it might be useful to benefit from resource concatenating.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that should avoid concatenating.`,
    score: page.isHttp2 ? 100 - (penalty * count) : null,
    title: 'Avoid resource concatenating',
    type: 'h2',
    values: values.map(entry => `${entry.url.href} (${entry.content.size} Bytes)`),
    weight: 2
  })
}

export function useServerPush ({ page, entries, ...opts }) {
  const { maxSize = 5000 } = opts
  const values = entries.filter(entry => (
    entry.isValid &&
    entry.isHttp2 &&
    regex.jsOrCss.test(entry.content.mimeType) &&
    entry.content.size <= maxSize
  ))
  const count = values.length
  return rule({
    count: count,
    description: 'Server push enables the server to send multiple responses (in parallel) for a single client request, thus eliminates entire roundtrips of unnecessary network latency.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that can be server pushed with HTTP/2`,
    score: null,
    title: 'Eliminate roundtrips with Server Push',
    type: 'h2',
    values: values.map(entry => `${entry.url.href} (${entry.content.size} Bytes)`),
    weight: 3
  })
}
<commit_msg>Lower "DomainSharding" penalty<commit_after>import _ from 'pluralize'
import { normalizeScore, normalizeReason, regex } from './helpers'

// Main rule wrapper with properties normalization
function rule (props) {
  props.score = normalizeScore(props.score)
  props.reason = normalizeReason(props.reason, props.count)
  return props
}

export function reuseTCPconnections ({ page, entries, ...opts }) {
  const { penalty = 5, limit = 1 } = opts
  const subEntries = entries.filter(entry =>
    entry.isValid &&
    !entry.isRedirect &&
    !(entry.resHeaders.connection !== 'close')
  )
  const count = subEntries.length
  let score = 100
  if (count > limit) {
    score -= penalty * (count - limit)
  }
  return rule({
    count: count,
    description: 'Persistent connections allow multiple HTTP requests use the same TCP connection, thus eliminates TCP handshakes and slow-start latency overhead. Leverage persistent connections whenever possible.',
    reason: `There ${_('is', count)} ${count} immediately terminated ${_('connection', count)}`,
    score: score,
    title: 'Reuse TCP connections',
    type: 'general',
    values: subEntries.map(entry => entry.url.href),
    weight: 8
  })
}
export function cacheAssets ({ page, entries, ...opts }) {
  const { limit = 1, penalty = 5 } = opts
  const values = entries.filter((entry) => {
    const headers = entry.resHeaders
    return !entry.isRedirect &&
      entry.isValid &&
      !entry.isRedirect &&
      (!headers['cache-control'] || !headers['expires'])
  })
  const count = values.length
  let score = 100
  if (count > limit) {
    score -= penalty * (count - limit)
  }
  return rule({
    count: count,
    description: 'Reduce the load time of your page by storing commonly used files on your visitors browser.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} without expiration time`,
    score: score,
    title: 'Cache resources on the client',
    type: 'general',
    values: values.map(entry => entry.url.href),
    weight: 8
  })
}
export function compressAssets ({ page, entries, ...opts }) {
  const { minSize = 256, penalty = 5 } = opts
  const values = entries.filter((entry) => {
    const headers = entry.resHeaders
    return (
      entry.isValid &&
      !entry.isRedirect &&
      entry.content.size >= minSize &&
      regex.text.test(entry.content.mimeType) &&
      !regex.encoding.test(headers['content-encoding'])
    )
  })
  const count = values.length
  const score = 100 - (penalty * count)
  return rule({
    count: count,
    description: 'Application resources should be transferred with the minimum number of bytes. Always apply the best compression method for each transferred asset.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} without compression`,
    score: score,
    title: 'Compress assets',
    type: 'general',
    values: values.map(entry => entry.url.href),
    weight: 7
  })
}

export function reduceRedirects ({ page, entries, ...opts }) {
  const { penalty = 25 } = opts
  const values = entries.filter(entry => entry.isRedirect)
  const internalRedirects = values.filter(entry =>
      entry.status === 307 &&
      entry.statusText === 'Internal Redirect' &&
      entry.resHeaders['non-authoritative-reason']
  )
  const count = values.length
  const subCount = count - internalRedirects.length
  return rule({
    count: count,
    description: 'HTTP redirects impose high latency overhead. The optimal number of redirects is zero.',
    reason: `There ${_('is', values.length)} ${_('redirect', values.length, true)}`,
    score: 100 - (penalty * subCount),
    title: 'Minimize number of HTTP redirects',
    type: 'general',
    values: values.map((entry) =>
      `(${entry.status} ${entry.statusText || 'Redirect'}) ${entry.url.href} -> ${entry.redirectUrl || '-'}`
    ),
    weight: 7
  })
}

export function reduceDNSlookups ({ page, ...opts }) {
  const { limit = 4, penalty = 5 } = opts
  const { dns, dnsLookups } = page
  let score = 100
  if (dnsLookups > limit) {
    score -= penalty * (dnsLookups - limit)
  }
  return rule({
    count: dnsLookups,
    description: 'Making requests to a large number of different hosts can hurt performance.',
    score: score,
    title: 'Reduce DNS lookups',
    type: 'general',
    values: Object.keys(dns).map(key => `${key} (${dns[key]})`),
    weight: 4
  })
}

export function eliminateBrokenRequests ({ entries, ...opts }) {
  const { penalty = 10 } = opts
  const values = entries.filter(entry => entry.status >= 400)
  const count = values.length
  const score = 100 - (count * penalty)
  return rule({
    count: count,
    description: 'Avoid fetching content that does not exist.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that not exists`,
    score: score,
    title: 'Eliminate requests to non-existent or broken resources.',
    type: 'general',
    values: values.map(entry => `(${entry.status}) ${entry.url.href}`),
    weight: 2
  })
}

export function useHttp2 ({ entries }) {
  const requests = entries.filter(entry => entry.isValid && !entry.isRedirect)
  const values = requests.filter(entry => !entry.isHttp2)
  const count = values.length
  const score = 100 - Math.round(100 * (count / requests.length))
  return rule({
    count: count,
    description: 'HTTP/2 enables more efficient use of network resources and reduced latency by enabling request and response multiplexing, header compression, prioritization, and more.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that are not using HTTP/2 connections`,
    score: score,
    title: 'Serve your content using HTTP/2',
    type: 'h2',
    values: values.map(entry => entry.url.href),
    weight: 4
  })
}

export function eliminateDomainSharding ({ page, entries, opts = {} }) {
  const { penalty = 20, limit = 1 } = opts
  const h2entries = entries.filter(entry =>
    entry.isHttp2 &&
    entry.isValid &&
    !entry.isRedirect
  )
  const reverseDns = h2entries
    .reduce((obj, { hostname, ip }) => {
      const domains = obj[ip]
      obj[ip] = domains ? domains.add(hostname) : new Set([hostname])
      return obj
    }, {})
  const values = Object.keys(reverseDns)
  const count = values.length - limit
  const score = h2entries.length > 0 ? 100 - (count * penalty) : null
  return rule({
    count: count,
    description: 'Under HTTP/1 parallelism is limited by number of TCP connections (in practice ~6 connections per origin). However, each of these connections incur unnecessary overhead and compete with each other for bandwidth. Domain sharding should be avoided in HTTP/2.',
    score: score,
    title: 'Elimiminate domain sharding',
    type: 'h2',
    values: values.map(ip => `${ip} -> ${[...reverseDns[ip]].join(', ')}`),
    weight: 4
  })
}

export function avoidConcatenating ({ page, entries, ...opts }) {
  const { maxSize = 70000, penalty = 5 } = opts
  const values = entries.filter(entry =>
    entry.isHttp2 &&
    entry.isValid &&
    entry.content.size >= maxSize &&
    regex.jsOrCss.test(entry.content.mimeType)
  )
  const count = values.length
  return rule({
    count: count,
    description: 'Ship small granular resources and optimize caching policies. Significant wins in compression are the only cases where it might be useful to benefit from resource concatenating.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that should avoid concatenating.`,
    score: page.isHttp2 ? 100 - (penalty * count) : null,
    title: 'Avoid resource concatenating',
    type: 'h2',
    values: values.map(entry => `${entry.url.href} (${entry.content.size} Bytes)`),
    weight: 2
  })
}

export function useServerPush ({ page, entries, ...opts }) {
  const { maxSize = 5000 } = opts
  const values = entries.filter(entry => (
    entry.isValid &&
    entry.isHttp2 &&
    regex.jsOrCss.test(entry.content.mimeType) &&
    entry.content.size <= maxSize
  ))
  const count = values.length
  return rule({
    count: count,
    description: 'Server push enables the server to send multiple responses (in parallel) for a single client request, thus eliminates entire roundtrips of unnecessary network latency.',
    reason: `There ${_('is', count)} ${_('resource', count, true)} that can be server pushed with HTTP/2`,
    score: null,
    title: 'Eliminate roundtrips with Server Push',
    type: 'h2',
    values: values.map(entry => `${entry.url.href} (${entry.content.size} Bytes)`),
    weight: 3
  })
}
<|endoftext|>


<commit_before>import { formatNumber, formatPercentage } from 'common/format';

import Status from './Modules/Status';
import HealingDone from './Modules/HealingDone';
import DamageDone from './Modules/DamageDone';
import DamageTaken from './Modules/DamageTaken';

import Combatants from './Modules/Combatants';
import AbilityTracker from './Modules/AbilityTracker';
import AlwaysBeCasting from './Modules/AlwaysBeCasting';
import Enemies from './Modules/Enemies';
import HealEventTracker from './Modules/HealEventTracker';
import ManaValues from './Modules/ManaValues';
import SpellManaCost from './Modules/SpellManaCost';

import CritEffectBonus from './Modules/Helpers/CritEffectBonus';
import Bloodlust from './Modules/Helpers/Bloodlust';

// Shared Legendaries
import Prydaz from './Modules/Items/Prydaz';
import Velens from './Modules/Items/Velens';
import SephuzsSecret from './Modules/Items/SephuzsSecret';
import KiljaedensBurningWish from './Modules/Items/KiljaedensBurningWish';
import ArchimondesHatredReborn from './Modules/Items/ArchimondesHatredReborn';
// Shared Epics
import DrapeOfShame from './Modules/Items/DrapeOfShame';
import DarkmoonDeckPromises from './Modules/Items/DarkmoonDeckPromises';
import AmalgamsSeventhSpine from './Modules/Items/AmalgamsSeventhSpine';
import ArchiveOfFaith from './Modules/Items/ArchiveOfFaith';
import BarbaricMindslaver from './Modules/Items/BarbaricMindslaver';
import SeaStar from './Modules/Items/SeaStarOfTheDepthmother';
import DeceiversGrandDesign from './Modules/Items/DeceiversGrandDesign';
import PrePotion from './Modules/Items/PrePotion';
import GnawedThumbRing from './Modules/Items/GnawedThumbRing';
import VialOfCeaselessToxins from './Modules/Items/VialOfCeaselessToxins';
import SpecterOfBetrayal from './Modules/Items/SpecterOfBetrayal';
import EngineOfEradication from './Modules/Items/EngineOfEradication';
import TarnishedSentinelMedallion from './Modules/Items/TarnishedSentinelMedallion';
import SpectralThurible from './Modules/Items/SpectralThurible';
import TerrorFromBelow from './Modules/Items/TerrorFromBelow';
import TomeOfUnravelingSanity from './Modules/Items/TomeOfUnravelingSanity';

// Shared Buffs
import VantusRune from './Modules/VantusRune';

import ParseResults from './ParseResults';

const debug = false;

class CombatLogParser {
  static abilitiesAffectedByHealingIncreases = [];

  static defaultModules = {
    status: Status,
    healingDone: HealingDone,
    damageDone: DamageDone,
    damageTaken: DamageTaken,

    combatants: Combatants,
    enemies: Enemies,
    spellManaCost: SpellManaCost,
    abilityTracker: AbilityTracker,
    healEventTracker: HealEventTracker,
    alwaysBeCasting: AlwaysBeCasting,
    manaValues: ManaValues,
    vantusRune: VantusRune,

    critEffectBonus: CritEffectBonus,
    bloodlust: Bloodlust,

    // Items:
    // Legendaries:
    prydaz: Prydaz,
    velens: Velens,
    sephuzsSecret: SephuzsSecret,
    kiljaedensBurningWish: KiljaedensBurningWish,
    archimondesHatredReborn: ArchimondesHatredReborn,
    // Epics:
    drapeOfShame: DrapeOfShame,
    amalgamsSeventhSpine: AmalgamsSeventhSpine,
    darkmoonDeckPromises: DarkmoonDeckPromises,
    prePotion: PrePotion,
    gnawedThumbRing: GnawedThumbRing,
    // Tomb trinkets:
    archiveOfFaith: ArchiveOfFaith,
    barbaricMindslaver: BarbaricMindslaver,
    seaStar: SeaStar,
    deceiversGrandDesign: DeceiversGrandDesign,
    vialCeaslessToxins: VialOfCeaselessToxins,
    specterOfBetrayal: SpecterOfBetrayal,
    engineOfEradication: EngineOfEradication,
    tarnishedSentinelMedallion: TarnishedSentinelMedallion,
    spectralThurible: SpectralThurible,
    terrorFromBelow: TerrorFromBelow,
    tomeOfUnravelingSanity: TomeOfUnravelingSanity,
  };
  // Override this with spec specific modules
  static specModules = {};

  report = null;
  player = null;
  fight = null;

  modules = {};
  get activeModules() {
    return Object.keys(this.modules)
      .map(key => this.modules[key])
      .filter(module => module.active);
  }

  get playerId() {
    return this.player.id;
  }

  /** @returns {Combatant} */
  get selectedCombatant() {
    return this.modules.combatants.selected;
  }

  get currentTimestamp() {
    return this.finished ? this.fight.end_time : this._timestamp;
  }
  get fightDuration() {
    return this.currentTimestamp - this.fight.start_time;
  }
  get finished() {
    return this.modules.status.finished;
  }

  get playersById() {
    return this.report.friendlies.reduce((obj, player) => {
      obj[player.id] = player;
      return obj;
    }, {});
  }

  constructor(report, player, fight) {
    this.report = report;
    this.player = player;
    this.fight = fight;

    this.initializeModules({
      ...this.constructor.defaultModules,
      ...this.constructor.specModules,
    });
  }

  initializeModules(modules) {
    const failedModules = [];
    Object.keys(modules).forEach(desiredModuleName => {
      const moduleClass = modules[desiredModuleName];
      if (!moduleClass) {
        return;
      }

      const availableDependencies = {};
      const missingDependencies = [];
      if (moduleClass.dependencies) {
        Object.keys(moduleClass.dependencies).forEach(desiredDependencyName => {
          const dependencyClass = moduleClass.dependencies[desiredDependencyName];

          const dependencyModule = this.findModule(dependencyClass);
          if (dependencyModule) {
            availableDependencies[desiredDependencyName] = dependencyModule;
          } else {
            missingDependencies.push(dependencyClass);
          }
        });
      }

      if (missingDependencies.length === 0) {
        if (debug) {
          if (Object.keys(availableDependencies).length === 0) {
            console.log('Loading', moduleClass.name);
          } else {
            console.log('Loading', moduleClass.name, 'with dependencies:', Object.keys(availableDependencies));
          }
        }
        this.modules[desiredModuleName] = new moduleClass(this, availableDependencies, Object.keys(this.modules).length);
      } else {
        debug && console.warn(moduleClass.name, 'could not be loaded, missing dependencies:', missingDependencies.map(d => d.name));
        failedModules.push(desiredModuleName);
      }
    });

    if (failedModules.length !== 0) {
      debug && console.warn(`${failedModules.length} modules failed to load, trying again:`, failedModules.map(key => modules[key].name));
      const newBatch = {};
      failedModules.forEach(key => {
        newBatch[key] = modules[key];
      });
      this.initializeModules(newBatch);
    }
  }
  findModule(type) {
    return Object.keys(this.modules)
      .map(key => this.modules[key])
      .find(module => module instanceof type);
  }

  _debugEventHistory = [];
  parseEvents(events) {
    events = this.reorderEvents(events);
    if (process.env.NODE_ENV === 'development') {
      this._debugEventHistory = [
        ...this._debugEventHistory,
        ...events,
      ];
    }
    return new Promise((resolve, reject) => {
      events.forEach(event => {
        if (this.error) {
          throw new Error(this.error);
        }
        this._timestamp = event.timestamp;

        // Triggering a lot of events here for development pleasure; does this have a significant performance impact?
        this.triggerEvent(event.type, event);
      });

      resolve(events.length);
    });
  }

  reorderEvents(events) {
    this.activeModules
      .sort((a, b) => a.priority - b.priority) // lowest should go first, as `priority = 0` will have highest prio
      .forEach(module => {
        if (module.reorderEvents) {
          events = module.reorderEvents(events);
        }
      });
    return events;
  }

  triggerEvent(eventType, event, ...args) {
    this.activeModules
      .sort((a, b) => a.priority - b.priority) // lowest should go first, as `priority = 0` will have highest prio
      .forEach(module => module.triggerEvent(eventType, event, ...args));
  }

  byPlayer(event, playerId = this.player.id) {
    return (event.sourceID === playerId);
  }
  toPlayer(event, playerId = this.player.id) {
    return (event.targetID === playerId);
  }

  // TODO: Damage taken from LOTM

  getPercentageOfTotalHealingDone(healingDone) {
    return healingDone / this.modules.healingDone.total.effective;
  }
  formatItemHealingDone(healingDone) {
    return `${formatPercentage(this.getPercentageOfTotalHealingDone(healingDone))} % / ${formatNumber(healingDone / this.fightDuration * 1000)} HPS`;
  }
  formatItemAbsorbDone(absorbDone) {
    return `${formatNumber(absorbDone)}`;
  }
  getPercentageOfTotalDamageDone(damageDone) {
    return damageDone / this.modules.damageDone.total.effective;
  }
  formatItemDamageDone(damageDone) {
    return `${formatPercentage(this.getPercentageOfTotalDamageDone(damageDone))} % / ${formatNumber(damageDone / this.fightDuration * 1000)} DPS`;
  }

  generateResults() {
    const results = new ParseResults();

    this.activeModules
      .sort((a, b) => b.priority - a.priority)
      .forEach(module => {
        if (module.statistic) {
          const statistic = module.statistic();
          if (statistic) {
            results.statistics.push({
              statistic,
              order: module.statisticOrder,
            });
          }
        }
        if (module.item) {
          const item = module.item();
          if (item) {
            results.items.push(item);
          }
        }
        if (module.tab) {
          const tab = module.tab();
          if (tab) {
            results.tabs.push(tab);
          }
        }
        if (module.suggestions) {
          module.suggestions(results.suggestions.when);
        }
      });

    return results;
  }
}

export default CombatLogParser;
<commit_msg>Add `selectedCombatant` is deprecated warning<commit_after>import { formatNumber, formatPercentage } from 'common/format';

import Status from './Modules/Status';
import HealingDone from './Modules/HealingDone';
import DamageDone from './Modules/DamageDone';
import DamageTaken from './Modules/DamageTaken';

import Combatants from './Modules/Combatants';
import AbilityTracker from './Modules/AbilityTracker';
import AlwaysBeCasting from './Modules/AlwaysBeCasting';
import Enemies from './Modules/Enemies';
import HealEventTracker from './Modules/HealEventTracker';
import ManaValues from './Modules/ManaValues';
import SpellManaCost from './Modules/SpellManaCost';

import CritEffectBonus from './Modules/Helpers/CritEffectBonus';
import Bloodlust from './Modules/Helpers/Bloodlust';

// Shared Legendaries
import Prydaz from './Modules/Items/Prydaz';
import Velens from './Modules/Items/Velens';
import SephuzsSecret from './Modules/Items/SephuzsSecret';
import KiljaedensBurningWish from './Modules/Items/KiljaedensBurningWish';
import ArchimondesHatredReborn from './Modules/Items/ArchimondesHatredReborn';
// Shared Epics
import DrapeOfShame from './Modules/Items/DrapeOfShame';
import DarkmoonDeckPromises from './Modules/Items/DarkmoonDeckPromises';
import AmalgamsSeventhSpine from './Modules/Items/AmalgamsSeventhSpine';
import ArchiveOfFaith from './Modules/Items/ArchiveOfFaith';
import BarbaricMindslaver from './Modules/Items/BarbaricMindslaver';
import SeaStar from './Modules/Items/SeaStarOfTheDepthmother';
import DeceiversGrandDesign from './Modules/Items/DeceiversGrandDesign';
import PrePotion from './Modules/Items/PrePotion';
import GnawedThumbRing from './Modules/Items/GnawedThumbRing';
import VialOfCeaselessToxins from './Modules/Items/VialOfCeaselessToxins';
import SpecterOfBetrayal from './Modules/Items/SpecterOfBetrayal';
import EngineOfEradication from './Modules/Items/EngineOfEradication';
import TarnishedSentinelMedallion from './Modules/Items/TarnishedSentinelMedallion';
import SpectralThurible from './Modules/Items/SpectralThurible';
import TerrorFromBelow from './Modules/Items/TerrorFromBelow';
import TomeOfUnravelingSanity from './Modules/Items/TomeOfUnravelingSanity';

// Shared Buffs
import VantusRune from './Modules/VantusRune';

import ParseResults from './ParseResults';

const debug = false;

let _selectedCombatantDeprectatedWarningSent = false;

class CombatLogParser {
  static abilitiesAffectedByHealingIncreases = [];

  static defaultModules = {
    status: Status,
    healingDone: HealingDone,
    damageDone: DamageDone,
    damageTaken: DamageTaken,

    combatants: Combatants,
    enemies: Enemies,
    spellManaCost: SpellManaCost,
    abilityTracker: AbilityTracker,
    healEventTracker: HealEventTracker,
    alwaysBeCasting: AlwaysBeCasting,
    manaValues: ManaValues,
    vantusRune: VantusRune,

    critEffectBonus: CritEffectBonus,
    bloodlust: Bloodlust,

    // Items:
    // Legendaries:
    prydaz: Prydaz,
    velens: Velens,
    sephuzsSecret: SephuzsSecret,
    kiljaedensBurningWish: KiljaedensBurningWish,
    archimondesHatredReborn: ArchimondesHatredReborn,
    // Epics:
    drapeOfShame: DrapeOfShame,
    amalgamsSeventhSpine: AmalgamsSeventhSpine,
    darkmoonDeckPromises: DarkmoonDeckPromises,
    prePotion: PrePotion,
    gnawedThumbRing: GnawedThumbRing,
    // Tomb trinkets:
    archiveOfFaith: ArchiveOfFaith,
    barbaricMindslaver: BarbaricMindslaver,
    seaStar: SeaStar,
    deceiversGrandDesign: DeceiversGrandDesign,
    vialCeaslessToxins: VialOfCeaselessToxins,
    specterOfBetrayal: SpecterOfBetrayal,
    engineOfEradication: EngineOfEradication,
    tarnishedSentinelMedallion: TarnishedSentinelMedallion,
    spectralThurible: SpectralThurible,
    terrorFromBelow: TerrorFromBelow,
    tomeOfUnravelingSanity: TomeOfUnravelingSanity,
  };
  // Override this with spec specific modules
  static specModules = {};

  report = null;
  player = null;
  fight = null;

  modules = {};
  get activeModules() {
    return Object.keys(this.modules)
      .map(key => this.modules[key])
      .filter(module => module.active);
  }

  get playerId() {
    return this.player.id;
  }

  /** @returns {Combatant} */
  get selectedCombatant() {
    if (!_selectedCombatantDeprectatedWarningSent) {
      console.error('Using `this.owner.selectedCombatant` is deprectated. Instead you should add the `Combatants` module as a dependency and use `this.combatants.selected`.');
      _selectedCombatantDeprectatedWarningSent = true;
    }
    return this.modules.combatants.selected;
  }

  get currentTimestamp() {
    return this.finished ? this.fight.end_time : this._timestamp;
  }
  get fightDuration() {
    return this.currentTimestamp - this.fight.start_time;
  }
  get finished() {
    return this.modules.status.finished;
  }

  get playersById() {
    return this.report.friendlies.reduce((obj, player) => {
      obj[player.id] = player;
      return obj;
    }, {});
  }

  constructor(report, player, fight) {
    this.report = report;
    this.player = player;
    this.fight = fight;

    this.initializeModules({
      ...this.constructor.defaultModules,
      ...this.constructor.specModules,
    });
  }

  initializeModules(modules) {
    const failedModules = [];
    Object.keys(modules).forEach(desiredModuleName => {
      const moduleClass = modules[desiredModuleName];
      if (!moduleClass) {
        return;
      }

      const availableDependencies = {};
      const missingDependencies = [];
      if (moduleClass.dependencies) {
        Object.keys(moduleClass.dependencies).forEach(desiredDependencyName => {
          const dependencyClass = moduleClass.dependencies[desiredDependencyName];

          const dependencyModule = this.findModule(dependencyClass);
          if (dependencyModule) {
            availableDependencies[desiredDependencyName] = dependencyModule;
          } else {
            missingDependencies.push(dependencyClass);
          }
        });
      }

      if (missingDependencies.length === 0) {
        if (debug) {
          if (Object.keys(availableDependencies).length === 0) {
            console.log('Loading', moduleClass.name);
          } else {
            console.log('Loading', moduleClass.name, 'with dependencies:', Object.keys(availableDependencies));
          }
        }
        this.modules[desiredModuleName] = new moduleClass(this, availableDependencies, Object.keys(this.modules).length);
      } else {
        debug && console.warn(moduleClass.name, 'could not be loaded, missing dependencies:', missingDependencies.map(d => d.name));
        failedModules.push(desiredModuleName);
      }
    });

    if (failedModules.length !== 0) {
      debug && console.warn(`${failedModules.length} modules failed to load, trying again:`, failedModules.map(key => modules[key].name));
      const newBatch = {};
      failedModules.forEach(key => {
        newBatch[key] = modules[key];
      });
      this.initializeModules(newBatch);
    }
  }
  findModule(type) {
    return Object.keys(this.modules)
      .map(key => this.modules[key])
      .find(module => module instanceof type);
  }

  _debugEventHistory = [];
  parseEvents(events) {
    events = this.reorderEvents(events);
    if (process.env.NODE_ENV === 'development') {
      this._debugEventHistory = [
        ...this._debugEventHistory,
        ...events,
      ];
    }
    return new Promise((resolve, reject) => {
      events.forEach(event => {
        if (this.error) {
          throw new Error(this.error);
        }
        this._timestamp = event.timestamp;

        // Triggering a lot of events here for development pleasure; does this have a significant performance impact?
        this.triggerEvent(event.type, event);
      });

      resolve(events.length);
    });
  }

  reorderEvents(events) {
    this.activeModules
      .sort((a, b) => a.priority - b.priority) // lowest should go first, as `priority = 0` will have highest prio
      .forEach(module => {
        if (module.reorderEvents) {
          events = module.reorderEvents(events);
        }
      });
    return events;
  }

  triggerEvent(eventType, event, ...args) {
    this.activeModules
      .sort((a, b) => a.priority - b.priority) // lowest should go first, as `priority = 0` will have highest prio
      .forEach(module => module.triggerEvent(eventType, event, ...args));
  }

  byPlayer(event, playerId = this.player.id) {
    return (event.sourceID === playerId);
  }
  toPlayer(event, playerId = this.player.id) {
    return (event.targetID === playerId);
  }

  // TODO: Damage taken from LOTM

  getPercentageOfTotalHealingDone(healingDone) {
    return healingDone / this.modules.healingDone.total.effective;
  }
  formatItemHealingDone(healingDone) {
    return `${formatPercentage(this.getPercentageOfTotalHealingDone(healingDone))} % / ${formatNumber(healingDone / this.fightDuration * 1000)} HPS`;
  }
  formatItemAbsorbDone(absorbDone) {
    return `${formatNumber(absorbDone)}`;
  }
  getPercentageOfTotalDamageDone(damageDone) {
    return damageDone / this.modules.damageDone.total.effective;
  }
  formatItemDamageDone(damageDone) {
    return `${formatPercentage(this.getPercentageOfTotalDamageDone(damageDone))} % / ${formatNumber(damageDone / this.fightDuration * 1000)} DPS`;
  }

  generateResults() {
    const results = new ParseResults();

    this.activeModules
      .sort((a, b) => b.priority - a.priority)
      .forEach(module => {
        if (module.statistic) {
          const statistic = module.statistic();
          if (statistic) {
            results.statistics.push({
              statistic,
              order: module.statisticOrder,
            });
          }
        }
        if (module.item) {
          const item = module.item();
          if (item) {
            results.items.push(item);
          }
        }
        if (module.tab) {
          const tab = module.tab();
          if (tab) {
            results.tabs.push(tab);
          }
        }
        if (module.suggestions) {
          module.suggestions(results.suggestions.when);
        }
      });

    return results;
  }
}

export default CombatLogParser;
<|endoftext|>


<commit_before>define([ 'gizmo/superdesk'],
function(Gizmo)
{
    // Post
	return Gizmo.Model.extend
	({
		url: new Gizmo.Url('/Post'),
		orderSync: function(id, before)
		{
			var reorderHref = this.href+'/Post/'+id+'/Reorder?before='+before;
//			console.log(reorderHref);
			var
				self = this,
				dataAdapter = function(){ return self.syncAdapter.request.apply(self.syncAdapter, arguments); },
                ret = dataAdapter(reorderHref).update();
			return ret;
		},
		removeSync: function()
		{
			var removeHref = this.href;
			if(this.href.indexOf('LiveDesk/Blog') !== -1 ) {
				removeHref = removeHref.replace(/LiveDesk\/Blog\/\d/,'Superdesk')
			}
			var
				self = this,
				dataAdapter = function(){ return self.syncAdapter.request.apply(self.syncAdapter, arguments); },
                ret = dataAdapter(removeHref).remove().done(function() {
                    self.triggerHandler('delete');
                    self._uniq && self._uniq.remove(self.hash());
				});
			return ret;
		},
		publishSync: function()
		{
			var publishHref = this.href+'/Publish';
			var
				self = this,
				dataAdapter = function(){ return self.syncAdapter.request.apply(self.syncAdapter, arguments); },
                ret = dataAdapter(publishHref).insert({},{headers: { 'X-Filter': 'CId, Order'}}).done(function(data){
					self.parse(data);
					self.Class.triggerHandler('publish', self);
				});
			return ret;
		}
	}, { register: 'Post' } );
});<commit_msg>NR-486: Create New Blog -> Seems there is no way to delete posts from Timeline<commit_after>define([ 'gizmo/superdesk'],
function(Gizmo)
{
    // Post
	return Gizmo.Model.extend
	({
		url: new Gizmo.Url('/Post'),
		orderSync: function(id, before)
		{
			var reorderHref = this.href+'/Post/'+id+'/Reorder?before='+before;
//			console.log(reorderHref);
			var
				self = this,
				dataAdapter = function(){ return self.syncAdapter.request.apply(self.syncAdapter, arguments); },
                ret = dataAdapter(reorderHref).update();
			return ret;
		},
		removeSync: function()
		{
			var removeHref = this.href;
			if(this.href.indexOf('LiveDesk/Blog') !== -1 ) {
				removeHref = removeHref.replace(/LiveDesk\/Blog\/[\d]+/,'Superdesk')
			}
			var
				self = this,
				dataAdapter = function(){ return self.syncAdapter.request.apply(self.syncAdapter, arguments); },
                ret = dataAdapter(removeHref).remove().done(function() {
                    self.triggerHandler('delete');
                    self._uniq && self._uniq.remove(self.hash());
				});
			return ret;
		},
		publishSync: function()
		{
			var publishHref = this.href+'/Publish';
			var
				self = this,
				dataAdapter = function(){ return self.syncAdapter.request.apply(self.syncAdapter, arguments); },
                ret = dataAdapter(publishHref).insert({},{headers: { 'X-Filter': 'CId, Order'}}).done(function(data){
					self.parse(data);
					self.Class.triggerHandler('publish', self);
				});
			return ret;
		}
	}, { register: 'Post' } );
});<|endoftext|>


<commit_before>"use strict";

var CSSCore = require("react/lib/CSSCore");
var ReactTransitionEvents = require("react/lib/ReactTransitionEvents");
var TICK = 17;
var {toString} = Object.prototype;

module.exports = {
  getDefaultProps() {
    return {
      transition: null,//some examples defined in index.scss (scale, fadeInOut, rotate)
      showAnimation: "animated bounceIn", //or other animations from animate.css
      hideAnimation: "animated bounceOut",
      timeOut: 5000,
      extendedTimeOut: 1000,
    };
  },

  componentWillMount: function () {
    this.classNameQueue = [];
    this.isHiding = false;
    this.intervalId = null;
  },

  componentDidMount() {
    var {props} = this;
    this._show(props);

    var onHideComplete = () => {
      if (this.isHiding) {
        this._set_is_hiding(false);
        ReactTransitionEvents.removeEndEventListener(node, onHideComplete);
        this._handle_remove();
      }
    };

    var node = this.getDOMNode();
    ReactTransitionEvents.addEndEventListener(node, onHideComplete);

    if (0 < props.timeOut) {
      this._set_interval_id(
        setTimeout(this.hideToast, props.timeOut)
      );
    }
  },

  _set_transition(hide) {
    var animationType = hide ? "leave" : "enter";
    var node = this.getDOMNode();
    var className = this.props.transition + "-" + animationType;
    var activeClassName = className + "-active";

    var endListener = function (e) {
      if (e && e.target !== node) {
        return;
      }

      CSSCore.removeClass(node, className);
      CSSCore.removeClass(node, activeClassName);

      ReactTransitionEvents.removeEndEventListener(node, endListener);
    };

    ReactTransitionEvents.addEndEventListener(node, endListener);

    CSSCore.addClass(node, className);

    // Need to do this to actually trigger a transition.
    this._queue_class(activeClassName);
  },

  _clear_transition(hide) {
    var node = this.getDOMNode();
    var animationType = hide ? "leave" : "enter";
    var className = this.props.transition + "-" + animationType;
    var activeClassName = className + "-active";

    CSSCore.removeClass(node, className);
    CSSCore.removeClass(node, activeClassName);
  },

  _set_animation(hide) {
    var node = this.getDOMNode();
    var animations = this._get_animation_classes(hide);
    var endListener = function (e) {
      if (e && e.target !== node) {
        return;
      }

      animations.forEach((anim) => {
        CSSCore.removeClass(node, anim);
      });

      ReactTransitionEvents.removeEndEventListener(node, endListener);
    };

    ReactTransitionEvents.addEndEventListener(node, endListener);

    animations.forEach((anim) => {
      CSSCore.addClass(node, anim);
    });
  },

  _get_animation_classes(hide) {
    var {props} = this;
    var animations = hide ? props.hideAnimation : props.showAnimation;
    if ("[object Array]" === toString.call(animations)) {
      return animations;
    }
    else if ("string" === typeof animations) {
      return animations.split(" ");
    }
  },

  _clear_animation(hide) {
    var animations = this._get_animation_classes(hide);
    animations.forEach((animation) => {
      CSSCore.removeClass(this.getDOMNode(), animation);
    });
  },

  _queue_class(className) {
    this.classNameQueue.push(className);

    if (!this.timeout) {
      this.timeout = setTimeout(this._flush_class_name_queue, TICK);
    }
  },

  _flush_class_name_queue() {
    if (this.isMounted()) {
      this.classNameQueue.forEach(
        CSSCore.addClass.bind(CSSCore, this.getDOMNode())
      );
    }
    this.classNameQueue.length = 0;
    this.timeout = null;
  },

  _show() {
    var {props} = this;
    if (props.transition) {
      this._set_transition();
    } else if (props.showAnimation) {
      this._set_animation();
    }
  },

  handleMouseEnter() {
    clearTimeout(this.intervalId);
    this._set_interval_id(null);
    if (this.isHiding) {
      this._set_is_hiding(false);

      var {props} = this;
      if (props.hideAnimation) {
        this._clear_animation(true);
      }
      else if (props.transition) {
        this._clear_transition(true);
      }
    }
  },

  handleMouseLeave() {
    var {props} = this;
    if (!this.isHiding &&
      (0 < props.timeOut || 0 < props.extendedTimeOut)) {
      this._set_interval_id(
        setTimeout(this.hideToast, props.extendedTimeOut)
      );
    }
  },

  hideToast(override) {
    var {props} = this;
    if (this.isHiding || (null == this.intervalId && !override)) {
      return;
    }

    this._set_is_hiding(true);
    if (props.transition) {
      this._set_transition(true);
    } else if (props.hideAnimation) {
      this._set_animation(true);
    } else {
      this._handle_remove();
    }
  },

  _set_interval_id(intervalId) {
    this.intervalId = intervalId;
  },

  _set_is_hiding(isHiding) {
    this.isHiding = isHiding;
  },
};
<commit_msg>fix(ToastMessage): need clear setTimeout when component unmount<commit_after>"use strict";

var CSSCore = require("react/lib/CSSCore");
var ReactTransitionEvents = require("react/lib/ReactTransitionEvents");
var TICK = 17;
var {toString} = Object.prototype;

module.exports = {
  getDefaultProps() {
    return {
      transition: null,//some examples defined in index.scss (scale, fadeInOut, rotate)
      showAnimation: "animated bounceIn", //or other animations from animate.css
      hideAnimation: "animated bounceOut",
      timeOut: 5000,
      extendedTimeOut: 1000,
    };
  },

  componentWillMount: function () {
    this.classNameQueue = [];
    this.isHiding = false;
    this.intervalId = null;
  },

  componentDidMount() {
    var {props} = this;
    this._show(props);

    var onHideComplete = () => {
      if (this.isHiding) {
        this._set_is_hiding(false);
        ReactTransitionEvents.removeEndEventListener(node, onHideComplete);
        this._handle_remove();
      }
    };

    var node = this.getDOMNode();
    ReactTransitionEvents.addEndEventListener(node, onHideComplete);

    if (0 < props.timeOut) {
      this._set_interval_id(
        setTimeout(this.hideToast, props.timeOut)
      );
    }
  },
  componentWillUnmount() {
    if(this.intervalId){
      clearTimeout(this.intervalId);
    }
  },
  _set_transition(hide) {
    var animationType = hide ? "leave" : "enter";
    var node = this.getDOMNode();
    var className = this.props.transition + "-" + animationType;
    var activeClassName = className + "-active";

    var endListener = function (e) {
      if (e && e.target !== node) {
        return;
      }

      CSSCore.removeClass(node, className);
      CSSCore.removeClass(node, activeClassName);

      ReactTransitionEvents.removeEndEventListener(node, endListener);
    };

    ReactTransitionEvents.addEndEventListener(node, endListener);

    CSSCore.addClass(node, className);

    // Need to do this to actually trigger a transition.
    this._queue_class(activeClassName);
  },

  _clear_transition(hide) {
    var node = this.getDOMNode();
    var animationType = hide ? "leave" : "enter";
    var className = this.props.transition + "-" + animationType;
    var activeClassName = className + "-active";

    CSSCore.removeClass(node, className);
    CSSCore.removeClass(node, activeClassName);
  },

  _set_animation(hide) {
    var node = this.getDOMNode();
    var animations = this._get_animation_classes(hide);
    var endListener = function (e) {
      if (e && e.target !== node) {
        return;
      }

      animations.forEach((anim) => {
        CSSCore.removeClass(node, anim);
      });

      ReactTransitionEvents.removeEndEventListener(node, endListener);
    };

    ReactTransitionEvents.addEndEventListener(node, endListener);

    animations.forEach((anim) => {
      CSSCore.addClass(node, anim);
    });
  },

  _get_animation_classes(hide) {
    var {props} = this;
    var animations = hide ? props.hideAnimation : props.showAnimation;
    if ("[object Array]" === toString.call(animations)) {
      return animations;
    }
    else if ("string" === typeof animations) {
      return animations.split(" ");
    }
  },

  _clear_animation(hide) {
    var animations = this._get_animation_classes(hide);
    animations.forEach((animation) => {
      CSSCore.removeClass(this.getDOMNode(), animation);
    });
  },

  _queue_class(className) {
    this.classNameQueue.push(className);

    if (!this.timeout) {
      this.timeout = setTimeout(this._flush_class_name_queue, TICK);
    }
  },

  _flush_class_name_queue() {
    if (this.isMounted()) {
      this.classNameQueue.forEach(
        CSSCore.addClass.bind(CSSCore, this.getDOMNode())
      );
    }
    this.classNameQueue.length = 0;
    this.timeout = null;
  },

  _show() {
    var {props} = this;
    if (props.transition) {
      this._set_transition();
    } else if (props.showAnimation) {
      this._set_animation();
    }
  },

  handleMouseEnter() {
    clearTimeout(this.intervalId);
    this._set_interval_id(null);
    if (this.isHiding) {
      this._set_is_hiding(false);

      var {props} = this;
      if (props.hideAnimation) {
        this._clear_animation(true);
      }
      else if (props.transition) {
        this._clear_transition(true);
      }
    }
  },

  handleMouseLeave() {
    var {props} = this;
    if (!this.isHiding &&
      (0 < props.timeOut || 0 < props.extendedTimeOut)) {
      this._set_interval_id(
        setTimeout(this.hideToast, props.extendedTimeOut)
      );
    }
  },

  hideToast(override) {
    var {props} = this;
    if (this.isHiding || (null == this.intervalId && !override)) {
      return;
    }

    this._set_is_hiding(true);
    if (props.transition) {
      this._set_transition(true);
    } else if (props.hideAnimation) {
      this._set_animation(true);
    } else {
      this._handle_remove();
    }
  },

  _set_interval_id(intervalId) {
    this.intervalId = intervalId;
  },

  _set_is_hiding(isHiding) {
    this.isHiding = isHiding;
  },
};
<|endoftext|>