<commit_before>const url = require('url')
const mongoose = require('mongoose')
const moment = require('moment')
const got = require('got')
const MapboxClient = require('mapbox')
const assert = require('../assert')

const mapbox = new MapboxClient(process.env.MAPBOX_ACCESS_TOKEN)

const HOUSEHOLD_DEFAULT_DEDUCTION = 30
const DYNAMIC_KEYS = [
  'name',
  'email',
  'lng',
  'lat',
  'address',
  'yearOfConst',
  'area',
  'numOfApartments',
  'meters',
  'needUpdate',
  'hasRegistered',
  'ventilationType',
  'incHouseholdElectricity',
  'hasLaundryRoom',
  'hasGarage',
  'hasCharger',
  'hasEnergyProduction',
  'hasSolarPanels',
  'hasGeothermalHeating',
  'hasRepresentative',
  'hasConsumptionMapping',
  'hasGoalManagement',
  'hasBelysningsutmaningen'
]
const CLIMATEZONE_EXCEPTIONS = [
  [11.79628, 57.666494, 12.088997, 57.787798], // Göteborg
  [12.261314, 57.630268, 12.414577, 57.734324], // Härryda
  [11.949245, 57.606115, 12.082203, 57.679487], // Mölndal
  [12.078297, 57.709873, 12.15362, 57.77442], // Partille
  [11.588586, 57.627249, 11.709036, 57.800938] // Öckerö (estimate)
]
const CLIMATEZONES = [
  ['Norrbottens län', 'Västerbottens län', 'Jämtlands län'],
  ['Västernorrlands län', 'Gävleborgs län', 'Dalarnas län', 'Värmlands län'],
  ['Jönköpings län', 'Kronobergs län', 'Östergötlands län', 'Södermanlands län',
    'Örebro län', 'Västmanlands län', 'Stockholms län', 'Uppsala län',
    'Gotlands län', 'Västra Götalands län'],
  ['Kalmar län', 'Blekinge län', 'Skåne län', 'Hallands län']
]

const CooperativeSchema = new mongoose.Schema({
  name: {
    type: String,
    unique: true
  },
  metryId: {
    type: String,
    requried: true
  },
  email: String,
  lat: Number,
  lng: Number,
  address: {
    address: String,
    postalCode: String,
    postalTown: String
  },
  climateZone: {
    type: Number,
    min: 1,
    max: 4
  },
  yearOfConst: Number,
  area: Number,
  numOfApartments: Number,
  ventilationType: [String],
  incHouseholdElectricity: Boolean,
  hasLaundryRoom: Boolean,
  hasGarage: Boolean,
  hasCharger: Boolean,
  hasEnergyProduction: Boolean,
  hasSolarPanels: Boolean,
  hasGeothermalHeating: Boolean,
  hasRepresentative: Boolean,
  hasConsumptionMapping: Boolean,
  hasGoalManagement: Boolean,
  hasBelysningsutmaningen: Boolean,
  needUpdate: Boolean,
  hasRegistered: Boolean,
  meters: [
    new mongoose.Schema({
      type: String,
      meterId: String,
      valid: Boolean
    })
  ],
  performances: [
    new mongoose.Schema({
      year: Number,
      month: Number,
      value: Number,
      area: Number,
      isGuesstimate: Boolean,
      incHouseholdElectricity: Boolean
    })
  ],
  actions: {
    type: [{
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Action'
    }],
    default: []
  },
  editors: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  }]
})

/**
 * Clean up orphaned actions on cooperative remove
 */

CooperativeSchema.pre('remove', function (next) {
  this.model('Action').remove({ cooperative: this._id }, next)
})

/**
 * Update climate zone when updating coordinates
 */

CooperativeSchema.pre('save', function (next) {
  if ((this.lat && this.isModified('lat')) || (this.lng && this.isModified('lng'))) {
    getClimateZone(this.lat, this.lng).then(zone => {
      this.climateZone = zone
      next()
    }, next)
  } else {
    next()
  }
})

/**
 * Prevent JSON responses from including populated fields
 */

CooperativeSchema.methods.toJSON = function toJSON () {
  const props = this.toObject()

  props._id = this._id.toString()
  props.actions = props.actions.map(action => (action._id || action).toString())
  props.editors = props.editors.map(editor => (editor._id || editor).toString())

  return props
}

const Cooperatives = mongoose.model('Cooperative', CooperativeSchema)

exports.create = function create (props, user) {
  return Cooperatives.create({
    name: props.name,
    metryId: props.metryId,
    email: props.email,
    lng: props.lng,
    lat: props.lat,
    yearOfConst: props.yearOfConst,
    area: props.area,
    numOfApartments: props.numOfApartments,
    meters: props.meters,
    ventilationType: props.ventilationType,
    needUpdate: props.needUpdate,
    hasRegistered: props.hasRegistered,
    editors: user ? [ typeof user === 'object' ? user._id : user ] : []
  })
}

exports.all = async function all () {
  const cooperatives = await Cooperatives.find({
    hasRegistered: true,
    lat: { $exists: true },
    lng: { $exists: true }
  })

  await Promise.all(cooperatives.map(calculatePerformance))

  return cooperatives
}

exports.get = async function get (id) {
  const cooperative = await Cooperatives
    .findOne({ _id: id })
    .populate('actions')
    .populate('editors')
    .exec()

  assert(cooperative, 404, 'Cooperative not found')

  await calculatePerformance(cooperative)

  return cooperative
}

exports.update = async function update (id, data, user) {
  const cooperative = await Cooperatives.findOne({ _id: id })

  assert(cooperative, 404, 'Cooperative not found')

  /**
   * Pick properties to update
   */

  const selection = {}
  const areaChanged = data.area && (cooperative.area !== data.area)
  Object.keys(data).filter(key => DYNAMIC_KEYS.includes(key)).forEach(key => {
    selection[key] = data[key]
  })

  /**
   * Update meter location if coordinates have changed
   */

  if (selection.lat || selection.lng) {
    if (selection.lat !== cooperative.lat || selection.lng !== cooperative.lng) {
      await Promise.all(cooperative.meters.map(meter => got(url.resolve(
        process.env.METRY_ENDPOINT,
        `meters/${meter.meterId}`
      ), {
        json: true,
        method: 'PUT',
        body: { location: [selection.lng, selection.lat] },
        headers: { 'Authorization': `Bearer ${user.accessToken}` }
      }))).then(responses => {
        for (let i = 0, len = responses.length, body; i < len; i++) {
          body = responses[i].body
          assert(body.code === 200, body.code, body.message)
          const meter = cooperative.meters.find(meter => {
            return meter.meterId === body.data._id
          })
          meter.valid = true
        }
      })
    }
  }

  /**
   * Assign all data to cooperative
   */

  Object.assign(cooperative, selection)

  if (areaChanged || data.meters) {
    // Remove last perfomance calculation if performance critical data has changed
    cooperative.performances.$pop()

    // Recalculate latest performance
    await calculatePerformance(cooperative)
  }

  return cooperative.save()
}

exports.getConsumption = async function getConsumption (id, options) {
  const cooperative = await Cooperatives.findOne({ _id: id })

  assert(cooperative, 404, 'Cooperative not found')

  const types = options.types.filter(type => type !== 'none')
  const hasType = cooperative.meters.find(meter => {
    return types.includes(meter.type)
  })

  if (hasType) {
    return queryConsumption(cooperative, options)
  } else {
    return []
  }
}

exports.getAll = function getAll () {
  return Cooperatives.find({})
}

async function getClimateZone (lat, lng) {
  const isZone4 = CLIMATEZONE_EXCEPTIONS.find(bbox => {
    const sw = bbox.slice(0, 2)
    const ne = bbox.slice(2, 4)
    return lng > sw[0] && lng < ne[0] && lat < sw[1] && lat > ne[1]
  })

  if (isZone4) {
    return 4
  }

  const response = await mapbox.geocodeReverse(
    { latitude: lat, longitude: lng },
    { types: 'region', limit: 1 }
  )

  const region = response.entity.features[0].place_name.split(',')[0]
  const zone = CLIMATEZONES.find(regions => {
    return regions.includes(region)
  })

  return zone ? CLIMATEZONES.indexOf(zone) + 1 : null
}

async function queryConsumption (cooperative, options) {
  const { types, granularity, from, to, normalized } = options
  const { area } = cooperative
  const meters = cooperative.meters.filter(meter => {
    return meter.valid && types.includes(meter.type)
  })
  const metrics = normalized ? 'energy_norm' : 'energy'

  if (!meters.length) {
    return []
  }

  let endpoint = [
    url.resolve(process.env.METRY_ENDPOINT, 'consumptions'),
    'sum',
    granularity,
    from + (to ? `-${to}` : '')
  ].join('/')

  endpoint += `?metrics=${metrics}`
  endpoint += `&meters=${meters.map(meter => meter.meterId).join(',')}`

  const response = await got(endpoint, { json: true }).catch(err => {
    assert.reject(err.statusCode, err.statusMessage)
  })

  if (response.body.code !== 200) {
    assert.reject(response.body.code, response.body.message)
  }

  const data = response.body.data[0].periods[0][metrics]

  return data.map(value => value / (area || 1))
}

async function calculatePerformance (cooperative) {
  if (!cooperative.area) return

  const props = cooperative.toObject()
  const now = new Date()

  // Lookup performance for current month
  let performance = cooperative.performances.find(byDate(now))
  if (!performance) {
    // Fallback to previous month
    performance = cooperative.performances.find(
      byDate(moment(now).subtract(1, 'months').toDate())
    )
  }

  // Exit if there's a recent performance calculation we can use
  if (performance) return

  // Check that the cooperative has valid meters before trying to calculate
  const isValid = ['heat', 'electricity'].reduce((result, type) => {
    return result && !!cooperative.meters.find(meter => {
      return meter.type === type && meter.valid
    })
  }, true)

  if (!isValid) return

  const from = moment(now).subtract(11, 'months').format('YYYYMM')
  const to = moment(now).format('YYYYMM')
  const options = { granularity: 'month', from, to }

  const [heat, electricity] = await Promise.all([
    { types: ['heat'], normalized: true },
    { types: ['electricity'] }
  ].map(opts => queryConsumption(props, Object.assign({}, options, opts))))

  // Only calculate performance when we have a complete dataset
  if (heat.filter(Boolean).length !== 12) return
  if (electricity.filter(Boolean).length !== 12) return

  const values = electricity.map((value, index) => {
    return (value || 0) + (heat[index] || 0)
  })

  if (cooperative.incHouseholdElectricity === false) {
    return setPerfomance(values)
  }

  let isGuesstimate = false
  let deductHouseholdConsumption = cooperative.incHouseholdElectricity
  const total = electricity.reduce((mem, value) => mem + value, 0)

  if (typeof cooperative.incHouseholdElectricity === 'undefined') {
    if (total > 50) {
      deductHouseholdConsumption = true
    } else if (total > 20) {
      isGuesstimate = true
    }
  }

  return setPerfomance(values, isGuesstimate, deductHouseholdConsumption)

  /**
   * Set cooperative performance with given values
   * @param {array} values List of values from which to calculate performance
   * @param {boolean} isGuesstimate Whether the values were guestimated
   */

  function setPerfomance (values, isGuesstimate = false, deduct = false) {
    let value = values.reduce((memo, num) => memo + num, 0)

    // Exit if there were no values found
    if (!value) return

    // Deduct household consumption
    if (deduct) value -= HOUSEHOLD_DEFAULT_DEDUCTION

    cooperative.performances.push({
      year: now.getFullYear(),
      month: now.getMonth(),
      area: cooperative.area,
      value: value,
      isGuesstimate: isGuesstimate,
      incHouseholdElectricity: deduct
    })

    return cooperative.save()
  }

  /**
   * Create an iterator function that matches given date with
   * year and month of iteratiee properties
   * @param  {Date} date Date to match against
   * @return {function}      Iterator function
   */

  function byDate (date) {
    const year = date.getFullYear()
    const month = date.getMonth()

    return props => props.year === year && props.month === month
  }
}

exports.model = Cooperatives
<commit_msg>Offset performance calculation if missing several months<commit_after>const url = require('url')
const mongoose = require('mongoose')
const moment = require('moment')
const got = require('got')
const MapboxClient = require('mapbox')
const assert = require('../assert')

const mapbox = new MapboxClient(process.env.MAPBOX_ACCESS_TOKEN)

const HOUSEHOLD_DEFAULT_DEDUCTION = 30
const DYNAMIC_KEYS = [
  'name',
  'email',
  'lng',
  'lat',
  'address',
  'yearOfConst',
  'area',
  'numOfApartments',
  'meters',
  'needUpdate',
  'hasRegistered',
  'ventilationType',
  'incHouseholdElectricity',
  'hasLaundryRoom',
  'hasGarage',
  'hasCharger',
  'hasEnergyProduction',
  'hasSolarPanels',
  'hasGeothermalHeating',
  'hasRepresentative',
  'hasConsumptionMapping',
  'hasGoalManagement',
  'hasBelysningsutmaningen'
]
const CLIMATEZONE_EXCEPTIONS = [
  [11.79628, 57.666494, 12.088997, 57.787798], // Göteborg
  [12.261314, 57.630268, 12.414577, 57.734324], // Härryda
  [11.949245, 57.606115, 12.082203, 57.679487], // Mölndal
  [12.078297, 57.709873, 12.15362, 57.77442], // Partille
  [11.588586, 57.627249, 11.709036, 57.800938] // Öckerö (estimate)
]
const CLIMATEZONES = [
  ['Norrbottens län', 'Västerbottens län', 'Jämtlands län'],
  ['Västernorrlands län', 'Gävleborgs län', 'Dalarnas län', 'Värmlands län'],
  ['Jönköpings län', 'Kronobergs län', 'Östergötlands län', 'Södermanlands län',
    'Örebro län', 'Västmanlands län', 'Stockholms län', 'Uppsala län',
    'Gotlands län', 'Västra Götalands län'],
  ['Kalmar län', 'Blekinge län', 'Skåne län', 'Hallands län']
]

const CooperativeSchema = new mongoose.Schema({
  name: {
    type: String,
    unique: true
  },
  metryId: {
    type: String,
    requried: true
  },
  email: String,
  lat: Number,
  lng: Number,
  address: {
    address: String,
    postalCode: String,
    postalTown: String
  },
  climateZone: {
    type: Number,
    min: 1,
    max: 4
  },
  yearOfConst: Number,
  area: Number,
  numOfApartments: Number,
  ventilationType: [String],
  incHouseholdElectricity: Boolean,
  hasLaundryRoom: Boolean,
  hasGarage: Boolean,
  hasCharger: Boolean,
  hasEnergyProduction: Boolean,
  hasSolarPanels: Boolean,
  hasGeothermalHeating: Boolean,
  hasRepresentative: Boolean,
  hasConsumptionMapping: Boolean,
  hasGoalManagement: Boolean,
  hasBelysningsutmaningen: Boolean,
  needUpdate: Boolean,
  hasRegistered: Boolean,
  meters: [
    new mongoose.Schema({
      type: String,
      meterId: String,
      valid: Boolean
    })
  ],
  performances: [
    new mongoose.Schema({
      year: Number,
      month: Number,
      value: Number,
      area: Number,
      isGuesstimate: Boolean,
      incHouseholdElectricity: Boolean
    })
  ],
  actions: {
    type: [{
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Action'
    }],
    default: []
  },
  editors: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  }]
})

/**
 * Clean up orphaned actions on cooperative remove
 */

CooperativeSchema.pre('remove', function (next) {
  this.model('Action').remove({ cooperative: this._id }, next)
})

/**
 * Update climate zone when updating coordinates
 */

CooperativeSchema.pre('save', function (next) {
  if ((this.lat && this.isModified('lat')) || (this.lng && this.isModified('lng'))) {
    getClimateZone(this.lat, this.lng).then(zone => {
      this.climateZone = zone
      next()
    }, next)
  } else {
    next()
  }
})

/**
 * Prevent JSON responses from including populated fields
 */

CooperativeSchema.methods.toJSON = function toJSON () {
  const props = this.toObject()

  props._id = this._id.toString()
  props.actions = props.actions.map(action => (action._id || action).toString())
  props.editors = props.editors.map(editor => (editor._id || editor).toString())

  return props
}

const Cooperatives = mongoose.model('Cooperative', CooperativeSchema)

exports.create = function create (props, user) {
  return Cooperatives.create({
    name: props.name,
    metryId: props.metryId,
    email: props.email,
    lng: props.lng,
    lat: props.lat,
    yearOfConst: props.yearOfConst,
    area: props.area,
    numOfApartments: props.numOfApartments,
    meters: props.meters,
    ventilationType: props.ventilationType,
    needUpdate: props.needUpdate,
    hasRegistered: props.hasRegistered,
    editors: user ? [ typeof user === 'object' ? user._id : user ] : []
  })
}

exports.all = async function all () {
  const cooperatives = await Cooperatives.find({
    hasRegistered: true,
    lat: { $exists: true },
    lng: { $exists: true }
  })

  await Promise.all(cooperatives.map(calculatePerformance))

  return cooperatives
}

exports.get = async function get (id) {
  const cooperative = await Cooperatives
    .findOne({ _id: id })
    .populate('actions')
    .populate('editors')
    .exec()

  assert(cooperative, 404, 'Cooperative not found')

  await calculatePerformance(cooperative)

  return cooperative
}

exports.update = async function update (id, data, user) {
  const cooperative = await Cooperatives.findOne({ _id: id })

  assert(cooperative, 404, 'Cooperative not found')

  /**
   * Pick properties to update
   */

  const selection = {}
  const areaChanged = data.area && (cooperative.area !== data.area)
  Object.keys(data).filter(key => DYNAMIC_KEYS.includes(key)).forEach(key => {
    selection[key] = data[key]
  })

  /**
   * Update meter location if coordinates have changed
   */

  if (selection.lat || selection.lng) {
    if (selection.lat !== cooperative.lat || selection.lng !== cooperative.lng) {
      await Promise.all(cooperative.meters.map(meter => got(url.resolve(
        process.env.METRY_ENDPOINT,
        `meters/${meter.meterId}`
      ), {
        json: true,
        method: 'PUT',
        body: { location: [selection.lng, selection.lat] },
        headers: { 'Authorization': `Bearer ${user.accessToken}` }
      }))).then(responses => {
        for (let i = 0, len = responses.length, body; i < len; i++) {
          body = responses[i].body
          assert(body.code === 200, body.code, body.message)
          const meter = cooperative.meters.find(meter => {
            return meter.meterId === body.data._id
          })
          meter.valid = true
        }
      })
    }
  }

  /**
   * Assign all data to cooperative
   */

  Object.assign(cooperative, selection)

  if (areaChanged || data.meters) {
    // Remove last perfomance calculation if performance critical data has changed
    cooperative.performances.$pop()

    // Recalculate latest performance
    await calculatePerformance(cooperative)
  }

  return cooperative.save()
}

exports.getConsumption = async function getConsumption (id, options) {
  const cooperative = await Cooperatives.findOne({ _id: id })

  assert(cooperative, 404, 'Cooperative not found')

  const types = options.types.filter(type => type !== 'none')
  const hasType = cooperative.meters.find(meter => {
    return types.includes(meter.type)
  })

  if (hasType) {
    return queryConsumption(cooperative, options)
  } else {
    return []
  }
}

exports.getAll = function getAll () {
  return Cooperatives.find({})
}

async function getClimateZone (lat, lng) {
  const isZone4 = CLIMATEZONE_EXCEPTIONS.find(bbox => {
    const sw = bbox.slice(0, 2)
    const ne = bbox.slice(2, 4)
    return lng > sw[0] && lng < ne[0] && lat < sw[1] && lat > ne[1]
  })

  if (isZone4) {
    return 4
  }

  const response = await mapbox.geocodeReverse(
    { latitude: lat, longitude: lng },
    { types: 'region', limit: 1 }
  )

  const region = response.entity.features[0].place_name.split(',')[0]
  const zone = CLIMATEZONES.find(regions => {
    return regions.includes(region)
  })

  return zone ? CLIMATEZONES.indexOf(zone) + 1 : null
}

async function queryConsumption (cooperative, options) {
  const { types, granularity, from, to, normalized } = options
  const { area } = cooperative
  const meters = cooperative.meters.filter(meter => {
    return meter.valid && types.includes(meter.type)
  })
  const metrics = normalized ? 'energy_norm' : 'energy'

  if (!meters.length) {
    return []
  }

  let endpoint = [
    url.resolve(process.env.METRY_ENDPOINT, 'consumptions'),
    'sum',
    granularity,
    from + (to ? `-${to}` : '')
  ].join('/')

  endpoint += `?metrics=${metrics}`
  endpoint += `&meters=${meters.map(meter => meter.meterId).join(',')}`

  const response = await got(endpoint, { json: true }).catch(err => {
    assert.reject(err.statusCode, err.statusMessage)
  })

  if (response.body.code !== 200) {
    assert.reject(response.body.code, response.body.message)
  }

  const data = response.body.data[0].periods[0][metrics]

  return data.map(value => value / (area || 1))
}

async function calculatePerformance (cooperative, offset) {
  if (!cooperative.area) return

  const props = cooperative.toObject()
  let now = new Date()

  if (offset) now = moment(now).subtract(offset, 'months').toDate()

  // Lookup performance for current month
  let performance = cooperative.performances.find(byDate(now))
  if (!offset && !performance) {
    // Fallback to previous month if not specifically offsetting calculation
    performance = cooperative.performances.find(
      byDate(moment(now).subtract(1, 'months').toDate())
    )
  }

  // Exit if there's a recent performance calculation we can use
  if (performance) return

  // Check that the cooperative has valid meters before trying to calculate
  const isValid = ['heat', 'electricity'].reduce((result, type) => {
    return result && !!cooperative.meters.find(meter => {
      return meter.type === type && meter.valid
    })
  }, true)

  if (!isValid) return

  const from = moment(now).subtract(11, 'months').format('YYYYMM')
  const to = moment(now).format('YYYYMM')
  const options = { granularity: 'month', from, to }

  const [heat, electricity] = await Promise.all([
    { types: ['heat'], normalized: true },
    { types: ['electricity'] }
  ].map(opts => queryConsumption(props, Object.assign({}, options, opts))))

  // Only calculate performance when we have a complete dataset
  if (
    heat.filter(Boolean).length !== 12 ||
    electricity.filter(Boolean).length !== 12
  ) {
    // Lookup latest performance calculation
    const latest = props.performances[props.performances.length - 1]
    if (!latest) return

    // Figure out how many months are missing
    const missing = moment(now).diff([latest.year, latest.month], 'months')
    if (offset || missing <= 1) return

    // Try and fill in missing months if there's more than one month missing
    return calculatePerformance(cooperative, 1)
  }

  const values = electricity.map((value, index) => {
    return (value || 0) + (heat[index] || 0)
  })

  if (cooperative.incHouseholdElectricity === false) {
    return setPerfomance(values)
  }

  let isGuesstimate = false
  let deductHouseholdConsumption = cooperative.incHouseholdElectricity
  const total = electricity.reduce((mem, value) => mem + value, 0)

  if (typeof cooperative.incHouseholdElectricity === 'undefined') {
    if (total > 50) {
      deductHouseholdConsumption = true
    } else if (total > 20) {
      isGuesstimate = true
    }
  }

  return setPerfomance(values, isGuesstimate, deductHouseholdConsumption)

  /**
   * Set cooperative performance with given values
   * @param {array} values List of values from which to calculate performance
   * @param {boolean} isGuesstimate Whether the values were guestimated
   */

  function setPerfomance (values, isGuesstimate = false, deduct = false) {
    let value = values.reduce((memo, num) => memo + num, 0)

    // Exit if there were no values found
    if (!value) return

    // Deduct household consumption
    if (deduct) value -= HOUSEHOLD_DEFAULT_DEDUCTION

    cooperative.performances.push({
      year: now.getFullYear(),
      month: now.getMonth(),
      area: cooperative.area,
      value: value,
      isGuesstimate: isGuesstimate,
      incHouseholdElectricity: deduct
    })

    return cooperative.save()
  }

  /**
   * Create an iterator function that matches given date with
   * year and month of iteratiee properties
   * @param  {Date} date Date to match against
   * @return {function}      Iterator function
   */

  function byDate (date) {
    const year = date.getFullYear()
    const month = date.getMonth()

    return props => props.year === year && props.month === month
  }
}

exports.model = Cooperatives
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Jive Software
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

var jive = require("jive-sdk");
var q = require("q");
var store = jive.service.persistence();
var JiveApi = require("./JiveApiFacade");
var JiveOauth = require("./JiveOauth");
var objectMerge = require("object-merge");

var STORE_NAME = "places";

/**
 * save will store the object with the given placeUrl as the key. If the object
 * already exists then it does a recursive overwrite of members. Meaning it only
 * overwrites members currently there at any level.
 * @param {string} placeUrl the key to identify the place
 * @param {object} set of members to place/overwrite on the record.
 * @param {boolean} dontStamp if this is true then the cache is not invalidated after save.
 * @return {promise} the new record is returned. External properties are not gathered to
 * allow client code to do validation.
 */
exports.save = function(placeUrl, newObject, dontStamp){
    if(!placeUrl || placeUrl === "" || typeof placeUrl !== "string"){
        throw Error("Invalid Place");
    }
    return store.findByID(STORE_NAME, placeUrl).then(function (found) {
        var record = found || {};
        record = objectMerge(JSON.parse(JSON.stringify(record)), newObject || {});
        var delimitter = "/";
        var tokens = placeUrl.split(delimitter);
        var domainTokens = tokens.slice(0, 3);

        record.jiveUrl =  domainTokens.join(delimitter);
        record.placeID =  tokens[tokens.length -1];
        record.placeUrl = placeUrl;
        record.invalidCache = !dontStamp;
        return store.save(STORE_NAME, placeUrl, record).then(function(){
            return record;
        });
    })
};

/**
 * refresh the cache for a given record. The cache is not left in an invalid state.
 * @param {string} placeUrl the key for the given place
 * @return {object} the record is returned with refreshed cache elements
 */
exports.invalidateCache = function(placeUrl){
    if(!placeUrl || placeUrl === "" || typeof placeUrl !== "string"){
        throw Error("Invalid Place");
    }
    var self = this;
    return self.save(placeUrl).then(function (record) {
        return pullExternalPropertiesIn(self, record);
    })
};

function pullExternalPropertiesIn(self,linked){
    if (
        linked &&
        linked.jive &&
        (linked.github && (!linked.github.repoOwner || !linked.github.repo) || linked.invalidCache)
     ){
        //cache repo information
        return jive.community.findByJiveURL(linked.jiveUrl).then(function (community) {
            var jauth = new JiveOauth(linked.placeUrl,linked.jive.access_token, linked.jive.refresh_token);
            var japi = new JiveApi(community, jauth);
            return japi.getAllExtProps("places/" + linked.placeID).then(function (extprops) {   // -> GET [jiveURL]/api/core/v3/places/[placeID]/extProps
                linked.github.repo = extprops.github4jiveRepo;
                linked.github.repoOwner = extprops.github4jiveRepoOwner;
                var githubReplacement = {"github": linked.github};
                return self.save(linked.placeUrl, githubReplacement, true);
            })
        });

    }else{
        return linked;
    }
}

/**
 * Get all place records out of the store
 * @return {[object]} returns array of all place records.
 */
exports.getAllPlaces = function(){
    var self = this;
    return store.find(STORE_NAME).then(function (linkedPlaces) {
        return q.all(linkedPlaces.map(function (linked) {
            return pullExternalPropertiesIn(self, linked);
        }));
    });
};

/**
 * Retrieve a record by its place api url
 * @param {string} placeurl the full api url for the place
 * @return {object} the record with hydrated cache elements.
 */
exports.getPlaceByUrl = function(placeUrl){
    var self = this;
    return store.findByID(STORE_NAME, placeUrl).then(function (linked) {
        return pullExternalPropertiesIn(self,linked);
    });
};
<commit_msg>Update placeStore.js<commit_after>/*
 * Copyright 2014 Jive Software
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

var jive = require("jive-sdk");
var q = require("q");
var store = jive.service.persistence();
var JiveApi = require("./JiveApiFacade");
var JiveOauth = require("./JiveOauth");
var objectMerge = require("object-merge");

var STORE_NAME = "places";

/**
 * save will store the object with the given placeUrl as the key. If the object
 * already exists then it does a recursive overwrite of members. Meaning it only
 * overwrites members currently there at any level.
 * @param {string} placeUrl the key to identify the place
 * @param {object} set of members to place/overwrite on the record.
 * @param {boolean} dontStamp if this is true then the cache is not invalidated after save.
 * @return {promise} the new record is returned. External properties are not gathered to
 * allow client code to do validation.
 */
exports.save = function(placeUrl, newObject, dontStamp){
    if(!placeUrl || placeUrl === "" || typeof placeUrl !== "string"){
        throw Error("Invalid Place");
    }
    return store.findByID(STORE_NAME, placeUrl).then(function (found) {
        var record = found || {};
        record = objectMerge(JSON.parse(JSON.stringify(record)), newObject || {});
        var delimitter = "/";
        var tokens = placeUrl.split(delimitter);
        var domainTokens = tokens.slice(0, 3);

        record.jiveUrl =  domainTokens.join(delimitter);
        record.placeID =  tokens[tokens.length -1];
        record.placeUrl = placeUrl;
        record.invalidCache = !dontStamp;
        return store.save(STORE_NAME, placeUrl, record).then(function(){
            return record;
        });
    })
};

/**
 * refresh the cache for a given record. The cache is not left in an invalid state.
 * @param {string} placeUrl the key for the given place
 * @return {object} the record is returned with refreshed cache elements
 */
exports.invalidateCache = function(placeUrl){
    if(!placeUrl || placeUrl === "" || typeof placeUrl !== "string"){
        throw Error("Invalid Place");
    }
    var self = this;
    return self.save(placeUrl).then(function (record) {
        return pullExternalPropertiesIn(self, record);
    })
};

function pullExternalPropertiesIn(self,place){
    if (
        place &&
        place.jive &&
        (place.github && (!place.github.repoOwner || !place.github.repo) || place.invalidCache)
     ){
        //cache repo information
        return jive.community.findByJiveURL(place.jiveUrl).then(function (community) {
            var jauth = new JiveOauth(place.placeUrl, place.jive.access_token, place.jive.refresh_token);
            var japi = new JiveApi(community, jauth);
            return japi.getAllExtProps("places/" + place.placeID).then(function (extprops) {   // -> GET [jiveURL]/api/core/v3/places/[placeID]/extProps
                place.github.repo = extprops.github4jiveRepo;
                place.github.repoOwner = extprops.github4jiveRepoOwner;
                var githubReplacement = {"github": place.github};
                return self.save(place.placeUrl, githubReplacement, true);
            })
        });

    } else {
        return place;
    }
}

/**
 * Get all place records out of the store
 * @return {[object]} returns array of all place records.
 */
exports.getAllPlaces = function(){
    var self = this;
    return store.find(STORE_NAME).then(function (linkedPlaces) {
        return q.all(linkedPlaces.map(function (linked) {
            return pullExternalPropertiesIn(self, linked);
        }));
    });
};

/**
 * Retrieve a record by its place api url
 * @param {string} placeurl the full api url for the place
 * @return {object} the record with hydrated cache elements.
 */
exports.getPlaceByUrl = function(placeUrl){
    var self = this;
    return store.findByID(STORE_NAME, placeUrl).then(function (linked) {
        return pullExternalPropertiesIn(self,linked);
    });
};
<|endoftext|>


<commit_before>'use strict';

var AblySubscriber = function AblySubscriber(options) {
    this.test = options.test;
    if (options.hasOwnProperty('variant')) {
        this.variant = options.variant;
    }
    this.callback = options.callback;
};

AblySubscriber.prototype.notify = function(test) {
    var self = this;
    setTimeout(function notifySubscriberNow() {
        self.callback(test);
    }, 1);
};

AblySubscriber.prototype.matchesTest = function(test) {
    return this.test === test.name;
};

AblySubscriber.prototype.matchesTestAndVariant = function(test, variant) {
    if (this.hasOwnProperty('variant')) {
        return this.test === test && this.variant === variant;
    } else {
        return this.test === test;
    }
};

module.exports = AblySubscriber;
<commit_msg>Fix Unexpected 'else' after 'return' (no-else-return)<commit_after>'use strict';

var AblySubscriber = function AblySubscriber(options) {
    this.test = options.test;
    if (options.hasOwnProperty('variant')) {
        this.variant = options.variant;
    }
    this.callback = options.callback;
};

AblySubscriber.prototype.notify = function(test) {
    var self = this;
    setTimeout(function notifySubscriberNow() {
        self.callback(test);
    }, 1);
};

AblySubscriber.prototype.matchesTest = function(test) {
    return this.test === test.name;
};

AblySubscriber.prototype.matchesTestAndVariant = function(test, variant) {
    if (this.hasOwnProperty('variant')) {
        return this.test === test && this.variant === variant;
    }

    return this.test === test;
};

module.exports = AblySubscriber;
<|endoftext|>


<commit_before><commit_msg>fixes #10 by updating devDependencies to current versions and switching requirment from minor to major releases<commit_after><|endoftext|>


<commit_before>// test/lib/TempDir.js
//

'use strict';

var cmds = require('../../lib/commands');

var fs = require('fs');
var path = require('path');

var mkdirp = require('mkdirp');
var rimraf = require('rimraf');

var TempDir = {
  location: null,

  prepare: function () {
    mkdirp.sync(this.location);
  },

  clean: function () {
    rimraf.sync(this.location);
  },

  getPath: function (name) {
    return path.join(this.location, name);
  },

  read: function (name) {
    return fs.readFileSync(this.getPath(name), 'utf8');
  },

  readJson: function (name) {
    return JSON.parse(this.read(name));
  },

  exists: function (name) {
    return fs.accessSync(path.join(this.location, name), fs.F_OK) ? false : true;
  },

};

module.exports = TempDir;
<commit_msg>Update test/lib/TempDir.js<commit_after>// test/lib/TempDir.js
//

'use strict';

var cmds = require('../../lib/commands');

var fs   = require('fs');
var os   = require('os');
var path = require('path');

var mkdirp = require('mkdirp');
var rimraf = require('rimraf');
var uuid   = require('uuid');

var tmpLocation = path.join(
  os.tmpdir(),
  'collider-tests',
  uuid.v4().slice(0, 8)
);

after(function () {
  rimraf.sync(tmpLocation);
});

function TempDir() {
  this.path = path.join(tmpLocation, uuid.v4());
}

TempDir.prototype.prepare = function (files) {
  rimraf.sync(this.path);
  mkdirp.sync(this.path);

  return this;
};

TempDir.prototype.getPath = function (name) {
  return path.join(this.path, name);
};

TempDir.prototype.read = function (name) {
  return fs.readFileSync(this.getPath(name), 'utf8');
};

TempDir.prototype.readJson = function (name) {
  return JSON.parse(this.read(name));
};

TempDir.prototype.exists = function (name) {
  return fs.accessSync(path.join(this.path, name), fs.F_OK);
};

module.exports = TempDir;
<|endoftext|>


<commit_before>'use strict';

var InstallFlow = require('../elements/install-flow.js');

var Installation = class {
  constructor(variant, title) {
    this.flow = new InstallFlow(variant);
    this.func = null;
    this.onAccountCreated = null;
    this.onFlowSkipped = null;
    this.title = title || 'Welcome to Kite';
  }
  accountCreated(func) {
    this.onAccountCreated = func;
  }
  flowSkipped(func) {
    this.onFlowSkipped = func;
  }
  destroy() {
    if (this.flow.accountCreated) {
      if (typeof (this.onAccountCreated) === 'function') {
        this.onAccountCreated();
      }
    } else {
      if (typeof (this.onFlowSkipped()) === 'function') {
        this.onFlowSkipped();
      }
    }
    this.flow.destroy();
  }
  getTitle() {
    return this.title;
  }
  get element() {
    return this.flow.element;
  }
};

module.exports = Installation;
<commit_msg>:bug: Fix invoking possibly null function in destroy<commit_after>'use strict';

var InstallFlow = require('../elements/install-flow.js');

var Installation = class {
  constructor(variant, title) {
    this.flow = new InstallFlow(variant);
    this.func = null;
    this.onAccountCreated = null;
    this.onFlowSkipped = null;
    this.title = title || 'Welcome to Kite';
  }
  accountCreated(func) {
    this.onAccountCreated = func;
  }
  flowSkipped(func) {
    this.onFlowSkipped = func;
  }
  destroy() {
    if (this.flow.accountCreated) {
      if (typeof (this.onAccountCreated) === 'function') {
        this.onAccountCreated();
      }
    } else {
      if (typeof this.onFlowSkipped === 'function') {
        this.onFlowSkipped();
      }
    }
    this.flow.destroy();
  }
  getTitle() {
    return this.title;
  }
  get element() {
    return this.flow.element;
  }
};

module.exports = Installation;
<|endoftext|>


<commit_before>// Import action types and our communication helper
// ===========================================================================
import * as ACTIONS from './types';
import { reduce, isPlainObject, omitBy, isUndefined, keys } from 'lodash';
import fetch from '../fetch';
import moment from 'moment';

// Action constructor (for default AJAX comunnication)
// Since most of our actions are the same - i create this
// ===========================================================================
export const createAction = (entity, action) => {

  // Default wrapper for [redux-thunk] actions
  // ===========================================================================
  return (data, options) => (dispatch) => {

    let url;
    let type;

    let opts = Object.assign({
      state: true,
      message: true,
      ignoreError: false,
      id: null
    }, options);

    let id = (data) ? data.id || opts.id : opts.id;
    let messageId = moment().unix();

    // Compose data for call
    // ===========================================================================
    let reqData = reduce(data, (acc, v, k) => {
      if (isPlainObject(v)) {
        acc[k] = JSON.stringify(v);
      } else if (!isUndefined(v)) {
        acc[k] = v;
      }
      return acc;
    }, {id});

    switch (action) {
      case 3:
        if (entity === 'user') {
          url = 'user';
          type = ACTIONS['GET_USER'];
        } else {
          url = entity+'s';
          type = ACTIONS[`GET_${entity.toUpperCase()}S`];
        }
        break;
      case 4:
        url = 'add_'+entity;
        type = ACTIONS[`ADD_${entity.toUpperCase()}`];
        break;
      case 5:
        url = entity;
        type = ACTIONS[`EDIT_${entity.toUpperCase()}`];
        break;
      case 6:
        url = 'remove_'+entity;
        type = ACTIONS[`DELETE_${entity.toUpperCase()}`];
        break;
      case 7:
        url = `sort_${entity}s`;
        type = ACTIONS[`SORT_${entity.toUpperCase()}S`];
        break;
      case 8:
        url = entity;
        type = ACTIONS[`${entity.toUpperCase()}`];
        break;
      default:
        throw {}
    }

    // Set app state to [loading]
    // ===========================================================================
    if (opts.state) {
      dispatch({
        type: ACTIONS['SET_APP_STATE'],
        state: action
      });
    }

    // Send message
    // ===========================================================================
    if (opts.message) {
      dispatch(sendMessage({
        id: messageId,
        type: 'loading',
        entity,
        action,
        entityId: id
      }));
    }

    // Fire a call to server
    // ===========================================================================
    return fetch(url, reqData).then(payload => {

      // Set app state to idle
      // ===========================================================================
      if (opts.state) {
        dispatch({
          type: ACTIONS['SET_APP_STATE'],
          state: 2
        });
      }

      if (payload.error && !opts.ignoreError) {
        // Fire [error] action if error found
        // ===========================================================================
        throw {
          id: messageId,
          entity,
          action,
          entityId: id,
          error: payload.error
        };
      } else if (payload.message || payload.success) {
        payload = data;
      }

      // Fire message to display proper message if responce contains only it
      // ===========================================================================
      if (opts.message) {
        dispatch(sendMessage({
          type: 'success',
          text: payload.success || opts.message
        }, messageId));
      }

      // Dispatch proper action
      // ===========================================================================
      delete payload.callback;
      if (action === 5) delete payload.id;
      return dispatch({
        type,
        payload,
        id
      });
    });

  }
}

// Set app state (simple and SYNC)
// ===========================================================================
export const setAppState = (state) => ({ type: ACTIONS['SET_APP_STATE'], state });

// Throw action related to error (SYNC)
// ===========================================================================
export const throwError = (error) => (dispatch) => {
  let action = {
    type: ACTIONS['ERROR'],
    payload: {
      id: moment().unix(),
      visible: true,
      type: 'error',
      text: ''
    }
  }

  if (error instanceof Error) {
    action.payload.type = 'error';
    action.payload.text = error.stack;
  } else if (error.event) {
    action.payload.type = 'error';
    action.payload.text = error.url;
  } else {
    if (error.messageId) {
      action.id = error.messageId;
      action.payload = error;
    } else {
      Object.assign(action.payload, error);
    }
  }

  return dispatch(action);
};

export const sendMessage = (mess, id) => {
  if (id) {
    return {
      type: ACTIONS['EDIT_MESSAGE'],
      id,
      payload: mess
    };
  } else {
    return {
      type: ACTIONS['PUSH_MESSAGE'],
      payload: Object.assign({
        id: moment().unix(),
        visible: true
      }, mess)
    };
  }
};

// Get results for a single column
// @data - column data Object required
// ===========================================================================
export const getResults = (data, options) => (dispatch) => {
  let messageId = moment().unix();
  let opts = Object.assign({
    id: null,
    message: true
  }, options);

  if (!opts.id) {
    throw {
      id: messageId,
      error: 'Id of a column is required -> results will be stored by this key'
    }
  }

  // Set state to loading
  // ===========================================================================
  dispatch({
    state: 3,
    type: ACTIONS['LINKS_STATE'],
    id: opts.id
  });

  if (opts.message) {
    dispatch(sendMessage({
      type: 'loading',
      id: messageId,
      entityId: opts.id,
      entity: 'results',
      action: 3
    }))
  }

  // Run actual call
  // ===========================================================================
  return fetch('links', omitBy(data, isUndefined)).then((payload) => {
    if (opts.message) {
      dispatch(sendMessage({
        type: 'success',
        entityId: opts.id,
        entity: 'results',
        action: 3
      }, messageId))
    }
    dispatch({
      type: ACTIONS['GET_LINKS'],
      id: opts.id,
      state: 2,
      payload
    });
  });
}

// Get results for all columns with a time delay
// ===========================================================================
export const getAllResults = (data) => (dispatch) => {
  let columns;
  let ids = {};
  let messageId = moment().unix();

  // Pick exacly columns data to iterate over for Results fetching
  // ===========================================================================
  data.forEach((item) => {
    if(item && item.type === ACTIONS['GET_COLUMNS']) {
      columns = item.payload;
    }
  });

  // Send message at a start
  // ===========================================================================
  dispatch(sendMessage({
    id: messageId,
    type: 'loading',
    entityId: keys(ids).join(','),
    entity: 'results',
    action: 3
  }));

  // Create our [Top-level] Promise chain
  // ===========================================================================
  Promise.all(
    columns.map((column, i) => {
      // If column hidden - do nothing
      // ===========================================================================
      if (!column.open) return null;

      // Define time delay and set id to hash of columns being fetched
      // ===========================================================================
      let delay = (i > 4) ? i*1200 : 0;
      ids[column.id] = true;

      // Promise wrapper around timeout
      // ===========================================================================
      return new Promise((resolve, reject) => {
        // Run our call and simple forward results to [Upper-level] promise chain
        // ===========================================================================
        setTimeout(() => dispatch(getResults(column.data, { id: column.id, message: false })).then(resolve).catch(reject), delay);
      }).then(() => {
        // When code is done - update our message by removing [ID] of column
        // wich result loading is done from list
        // ===========================================================================
        delete ids[column.id];
        return dispatch(sendMessage({
          entityId: keys(ids).join(',')
        }, messageId));
      });
    })
  ).then(() => dispatch(sendMessage({visible: false}, messageId))).catch(err => dispatch(throwError(err)));
}

// Create specific action to fetch All data from a server:
// On app init - for example (utilizing Promise.all)
// ===========================================================================
export const fetchData = (getUser) => (dispatch) => {
  let opts = {
    state: false,
    message: false
  };

  return Promise.all([
    (getUser) ? dispatch(createAction('user', 3)(null, opts)) : null,
    dispatch(createAction('alert', 3)(null, opts)),
    dispatch(createAction('report', 3)(null, opts)),
    dispatch(createAction('set', 3)(null, opts)),
    dispatch(createAction('source', 3)(null, opts)),
    dispatch(createAction('column', 3)({data: 1}, opts))
  ])
};<commit_msg>Fix initial message call place<commit_after>// Import action types and our communication helper
// ===========================================================================
import * as ACTIONS from './types';
import { reduce, isPlainObject, omitBy, isUndefined, keys } from 'lodash';
import fetch from '../fetch';
import moment from 'moment';

// Action constructor (for default AJAX comunnication)
// Since most of our actions are the same - i create this
// ===========================================================================
export const createAction = (entity, action) => {

  // Default wrapper for [redux-thunk] actions
  // ===========================================================================
  return (data, options) => (dispatch) => {

    let url;
    let type;

    let opts = Object.assign({
      state: true,
      message: true,
      ignoreError: false,
      id: null
    }, options);

    let id = (data) ? data.id || opts.id : opts.id;
    let messageId = moment().unix();

    // Compose data for call
    // ===========================================================================
    let reqData = reduce(data, (acc, v, k) => {
      if (isPlainObject(v)) {
        acc[k] = JSON.stringify(v);
      } else if (!isUndefined(v)) {
        acc[k] = v;
      }
      return acc;
    }, {id});

    switch (action) {
      case 3:
        if (entity === 'user') {
          url = 'user';
          type = ACTIONS['GET_USER'];
        } else {
          url = entity+'s';
          type = ACTIONS[`GET_${entity.toUpperCase()}S`];
        }
        break;
      case 4:
        url = 'add_'+entity;
        type = ACTIONS[`ADD_${entity.toUpperCase()}`];
        break;
      case 5:
        url = entity;
        type = ACTIONS[`EDIT_${entity.toUpperCase()}`];
        break;
      case 6:
        url = 'remove_'+entity;
        type = ACTIONS[`DELETE_${entity.toUpperCase()}`];
        break;
      case 7:
        url = `sort_${entity}s`;
        type = ACTIONS[`SORT_${entity.toUpperCase()}S`];
        break;
      case 8:
        url = entity;
        type = ACTIONS[`${entity.toUpperCase()}`];
        break;
      default:
        throw {}
    }

    // Set app state to [loading]
    // ===========================================================================
    if (opts.state) {
      dispatch({
        type: ACTIONS['SET_APP_STATE'],
        state: action
      });
    }

    // Send message
    // ===========================================================================
    if (opts.message) {
      dispatch(sendMessage({
        id: messageId,
        type: 'loading',
        entity,
        action,
        entityId: id
      }));
    }

    // Fire a call to server
    // ===========================================================================
    return fetch(url, reqData).then(payload => {

      // Set app state to idle
      // ===========================================================================
      if (opts.state) {
        dispatch({
          type: ACTIONS['SET_APP_STATE'],
          state: 2
        });
      }

      if (payload.error && !opts.ignoreError) {
        // Fire [error] action if error found
        // ===========================================================================
        throw {
          id: messageId,
          entity,
          action,
          entityId: id,
          error: payload.error
        };
      } else if (payload.message || payload.success) {
        payload = data;
      }

      // Fire message to display proper message if responce contains only it
      // ===========================================================================
      if (opts.message) {
        dispatch(sendMessage({
          type: 'success',
          text: payload.success || opts.message
        }, messageId));
      }

      // Dispatch proper action
      // ===========================================================================
      delete payload.callback;
      if (action === 5) delete payload.id;
      return dispatch({
        type,
        payload,
        id
      });
    });

  }
}

// Set app state (simple and SYNC)
// ===========================================================================
export const setAppState = (state) => ({ type: ACTIONS['SET_APP_STATE'], state });

// Throw action related to error (SYNC)
// ===========================================================================
export const throwError = (error) => (dispatch) => {
  let action = {
    type: ACTIONS['ERROR'],
    payload: {
      id: moment().unix(),
      visible: true,
      type: 'error',
      text: ''
    }
  }

  if (error instanceof Error) {
    action.payload.type = 'error';
    action.payload.text = error.stack;
  } else if (error.event) {
    action.payload.type = 'error';
    action.payload.text = error.url;
  } else {
    if (error.messageId) {
      action.id = error.messageId;
      action.payload = error;
    } else {
      Object.assign(action.payload, error);
    }
  }

  return dispatch(action);
};

export const sendMessage = (mess, id) => {
  if (id) {
    return {
      type: ACTIONS['EDIT_MESSAGE'],
      id,
      payload: mess
    };
  } else {
    return {
      type: ACTIONS['PUSH_MESSAGE'],
      payload: Object.assign({
        id: moment().unix(),
        visible: true
      }, mess)
    };
  }
};

// Get results for a single column
// @data - column data Object required
// ===========================================================================
export const getResults = (data, options) => (dispatch) => {
  let messageId = moment().unix();
  let opts = Object.assign({
    id: null,
    message: true
  }, options);

  if (!opts.id) {
    throw {
      id: messageId,
      error: 'Id of a column is required -> results will be stored by this key'
    }
  }

  // Set state to loading
  // ===========================================================================
  dispatch({
    state: 3,
    type: ACTIONS['LINKS_STATE'],
    id: opts.id
  });

  if (opts.message) {
    dispatch(sendMessage({
      type: 'loading',
      id: messageId,
      entityId: opts.id,
      entity: 'results',
      action: 3
    }))
  }

  // Run actual call
  // ===========================================================================
  return fetch('links', omitBy(data, isUndefined)).then((payload) => {
    if (opts.message) {
      dispatch(sendMessage({
        type: 'success',
        entityId: opts.id,
        entity: 'results',
        action: 3
      }, messageId))
    }
    dispatch({
      type: ACTIONS['GET_LINKS'],
      id: opts.id,
      state: 2,
      payload
    });
  });
}

// Get results for all columns with a time delay
// ===========================================================================
export const getAllResults = (data) => (dispatch) => {
  let columns;
  let ids = {};
  let messageId = moment().unix();

  // Pick exacly columns data to iterate over for Results fetching
  // ===========================================================================
  data.forEach((item) => {
    if(item && item.type === ACTIONS['GET_COLUMNS']) {
      columns = item.payload;
    }
  });

  // Create our [Top-level] Promise chain
  // ===========================================================================
  Promise.all(
    columns.map((column, i) => {
      // If column hidden - do nothing
      // ===========================================================================
      if (!column.open) return null;

      // Define time delay and set id to hash of columns being fetched
      // ===========================================================================
      let delay = (i > 4) ? i*1200 : 0;
      ids[column.id] = true;

      // Promise wrapper around timeout
      // ===========================================================================
      return new Promise((resolve, reject) => {
        // Run our call and simple forward results to [Upper-level] promise chain
        // ===========================================================================
        setTimeout(() => dispatch(getResults(column.data, { id: column.id, message: false })).then(resolve).catch(reject), delay);
      }).then(() => {
        // When code is done - update our message by removing [ID] of column
        // wich result loading is done from list
        // ===========================================================================
        delete ids[column.id];
        return dispatch(sendMessage({
          entityId: keys(ids).join(',')
        }, messageId));
      });
    })
  ).then(() => dispatch(sendMessage({visible: false}, messageId))).catch(err => dispatch(throwError(err)));

  // Send message at a start
  // ===========================================================================
  dispatch(sendMessage({
    id: messageId,
    type: 'loading',
    entityId: keys(ids).join(','),
    entity: 'results',
    action: 3
  }));
}

// Create specific action to fetch All data from a server:
// On app init - for example (utilizing Promise.all)
// ===========================================================================
export const fetchData = (getUser) => (dispatch) => {
  let opts = {
    state: false,
    message: false
  };

  return Promise.all([
    (getUser) ? dispatch(createAction('user', 3)(null, opts)) : null,
    dispatch(createAction('alert', 3)(null, opts)),
    dispatch(createAction('report', 3)(null, opts)),
    dispatch(createAction('set', 3)(null, opts)),
    dispatch(createAction('source', 3)(null, opts)),
    dispatch(createAction('column', 3)({data: 1}, opts))
  ])
};<|endoftext|>


<commit_before>/* eslint-env mocha */
'use strict'

const expect = require('chai').expect

const geoip = require('../')
const IPFS = require('ipfs-api')
const ctl = require('ipfsd-ctl')

describe('lookup', function () {
  this.timeout(20 * 1000)
  let ipfs

  before((done) => {
    ctl.disposable((err, node) => {
      if (err) throw err

      node.startDaemon((err) => {
        if (err) throw err
        ipfs = IPFS(node.apiAddr)
        done()
      })
    })
  })

  it('fails on 127.0.0.1', (done) => {
    geoip.lookup(ipfs, '127.0.0.1', function (err, result) {
      expect(err).to.have.property('message', 'Unmapped range')
      done()
    })
  })

  it('looks up 8.8.8.8', (done) => {
    geoip.lookup(ipfs, '8.8.8.8', function (err, result) {
      expect(err).to.not.exist
      expect(
        result
      ).to.be.eql({
        country_name: 'United States',
        country_code: 'US',
        region_code: 'CA',
        city: 'Mountain View',
        postal_code: 94040,
        latitude: 37.386,
        longitude: -122.0838,
        metro_code: 807,
        area_code: 650,
        planet: 'Earth'
      })

      done()
    })
  })

  describe('lookupPretty', () => {
    it('fails on 127.0.0.1', (done) => {
      geoip.lookupPretty(ipfs, '/ip4/127.0.0.1', function (err, result) {
        expect(err).to.have.property('message', 'Unmapped range')
        done()
      })
    })

    it('looks up 8.8.8.8', (done) => {
      geoip.lookupPretty(ipfs, '/ip4/8.8.8.8', function (err, result) {
        expect(err).to.not.exist
        expect(
          result.formatted
        ).to.be.eql('Mountain View, CA, United States, Earth')
        done()
      })
    })
  })
})
<commit_msg>test: Give travis more time<commit_after>/* eslint-env mocha */
'use strict'

const expect = require('chai').expect

const geoip = require('../')
const IPFS = require('ipfs-api')
const ctl = require('ipfsd-ctl')

describe('lookup', function () {
  this.timeout(100 * 1000)
  let ipfs

  before((done) => {
    ctl.disposable((err, node) => {
      if (err) throw err

      node.startDaemon((err) => {
        if (err) throw err
        ipfs = IPFS(node.apiAddr)
        done()
      })
    })
  })

  it('fails on 127.0.0.1', (done) => {
    geoip.lookup(ipfs, '127.0.0.1', function (err, result) {
      expect(err).to.have.property('message', 'Unmapped range')
      done()
    })
  })

  it('looks up 8.8.8.8', (done) => {
    geoip.lookup(ipfs, '8.8.8.8', function (err, result) {
      expect(err).to.not.exist
      expect(
        result
      ).to.be.eql({
        country_name: 'United States',
        country_code: 'US',
        region_code: 'CA',
        city: 'Mountain View',
        postal_code: 94040,
        latitude: 37.386,
        longitude: -122.0838,
        metro_code: 807,
        area_code: 650,
        planet: 'Earth'
      })

      done()
    })
  })

  describe('lookupPretty', () => {
    it('fails on 127.0.0.1', (done) => {
      geoip.lookupPretty(ipfs, '/ip4/127.0.0.1', function (err, result) {
        expect(err).to.have.property('message', 'Unmapped range')
        done()
      })
    })

    it('looks up 8.8.8.8', (done) => {
      geoip.lookupPretty(ipfs, '/ip4/8.8.8.8', function (err, result) {
        expect(err).to.not.exist
        expect(
          result.formatted
        ).to.be.eql('Mountain View, CA, United States, Earth')
        done()
      })
    })
  })
})
<|endoftext|>


<commit_before><commit_msg>modify app.js<commit_after><|endoftext|>


<commit_before>
(function($, deck, undefined) {
    // This next line is the color plugin from jquery
    (function(d){d.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(f,e){d.fx.step[e]=function(g){if(!g.colorInit){g.start=c(g.elem,e);g.end=b(g.end);g.colorInit=true}g.elem.style[e]="rgb("+[Math.max(Math.min(parseInt((g.pos*(g.end[0]-g.start[0]))+g.start[0]),255),0),Math.max(Math.min(parseInt((g.pos*(g.end[1]-g.start[1]))+g.start[1]),255),0),Math.max(Math.min(parseInt((g.pos*(g.end[2]-g.start[2]))+g.start[2]),255),0)].join(",")+")"}});function b(f){var e;if(f&&f.constructor==Array&&f.length==3){return f}if(e=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(f)){return[parseInt(e[1]),parseInt(e[2]),parseInt(e[3])]}if(e=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(f)){return[parseFloat(e[1])*2.55,parseFloat(e[2])*2.55,parseFloat(e[3])*2.55]}if(e=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(f)){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}if(e=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(f)){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}if(e=/rgba\(0, 0, 0, 0\)/.exec(f)){return a.transparent}return a[d.trim(f).toLowerCase()]}function c(g,e){var f;do{f=d.curCSS(g,e);if(f!=""&&f!="transparent"||d.nodeName(g,"body")){break}e="backgroundColor"}while(g=g.parentNode);return b(f)}var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]}})(jQuery);

    var $d = $(document);
    var may = function(o,f) {return f ? f.bind(o) : function() {}};

    $.extend(true, $[deck].defaults, {
        selectors: {
            animShow: ".anim-show",
            animHide: ".anim-hide",
            animAddClass: ".anim-addclass",
            animRemoveClass: ".anim-removeclass",
            animAttribute: ".anim-attribute",
            // specific ones
            animPlay: ".anim-play",
            animPause: ".anim-pause",
            animViewboxAs: ".anim-viewboxas",
            //
            animContinue: ".anim-continue"
        },
        anim: {
            duration: 400
        }
    });

    var waitFor = 0
    $[deck]('extend', 'animWaitMore', function(){ waitFor++ });
    $[deck]('extend', 'animWaitLess', function(){ waitFor-- });

    var doInitIfReady = function hoho() {
        if (waitFor>0) {
            setTimeout(doInitIfReady, 10) // retry until all is loaded
            return;
        }
        // first we define some tools and grab some info from deck.js
        var o = $[deck]('getOptions');
        var context = function(el) {
            return {
                what: function() {return $(el).attr("data-what")},
                dur: function() {return $(el).attr("data-dur")*1 || o.anim.duration},
                classs: function() {return $(el).attr("data-class")},
                attribute: function() {return $(el).attr("data-attr").split(':')[0]},
                as: function() {return $(el).attr("data-as")},
                value: function() {return $(el).attr("data-attr").split(':')[1]},
                toplevel: function() {return $[deck]('getToplevelSlideOf', el).node},
                all: function() {return $(this.what(),this.toplevel())}
            }
        };
        var classical = function(selector, methods) {
            $(selector).each(function(i, el) {
                var c = context(el);
                may(methods, methods.create)(c);
                $(el).bind('deck.toplevelBecameCurrent', function(_, direction) {
                    may(methods, methods.init)(c);
                }).bind('deck.afterToplevelBecameCurrent', function(_, direction) {
                    may(methods, methods.fast)(c);
                }).bind('deck.lostCurrent', function(_, direction, from, to) {
                    if (direction == 'forward' || Math.abs(from - to)>1 ) return; // if a big step, let the "step" extension do its job
                    may(methods, methods.undo)(c);
                }).bind('deck.becameCurrent', function(_, direction, from, to) {
                    if (direction == 'reverse' || Math.abs(from - to)>1 ) return; // if a big step, let the "step" extension do its job
                    may(methods, methods.doit)(c);
                });
            });
        };

        // here come the real animations
        classical(o.selectors.animShow, {
            init: function(c) {c.all().animate({'opacity': 0.}, 0)},
            undo: function(c) {c.all().animate({'opacity': 0.}, c.dur()/100)},
            doit: function(c) {c.all().animate({'opacity': 1.}, c.dur())},
            fast: function(c) {c.all().animate({'opacity': 1.}, 0)}
        });
        classical(o.selectors.animHide, {
            init: function(c) {c.all().animate({'opacity': 1.}, 0)},
            undo: function(c) {c.all().animate({'opacity': 1.}, c.dur()/100)},
            doit: function(c) {c.all().animate({'opacity': 0.}, c.dur())},
            fast: function(c) {c.all().animate({'opacity': 0.}, 0)}
        });
        classical(o.selectors.animAddClass, {
            init: function(c) {c.all().removeClass(c.classs())},
            undo: function(c) {c.all().removeClass(c.classs())},
            doit: function(c) {c.all().addClass(c.classs())},
            fast: function(c) {c.all().addClass(c.classs())}
        });
        classical(o.selectors.animRemoveClass, {
            init: function(c) {c.all().addClass(c.classs())},
            undo: function(c) {c.all().addClass(c.classs())},
            doit: function(c) {c.all().removeClass(c.classs())},
            fast: function(c) {c.all().removeClass(c.classs())}
        });
        classical(o.selectors.animAttribute, {
            init: function(c) {
                c.previousElement = [];
                c.all().css(c.attribute(), '') // for the jquery anim to work the css attribute should not be defined in the element (in the html) so we suppose it is empty by default (and thus, if it is not empty, it means it has been set by jquery)
            },
            undo: function(c) {
                var k = c.attribute()
                for (i in c.previousElement) { // use the saved list of elements and values
                    var whatTo = {}
                    whatTo[k] = c.previousCss[i]
                    $(c.previousElement[i]).animate(whatTo, 0)
                }
            },
            doit: function(c, factor) {
                if (factor === undefined) factor = 1
                var k = c.attribute()
                c.previousCss = []
                c.previousElement = []
                c.all().each( function(){c.previousElement.push(this); c.previousCss.push($(this).css(k))}) // save a list of elements and values
                var whatTo = {}
                whatTo[c.attribute()] = c.value()
                c.all().animate(whatTo, c.dur()*factor)
            },
            fast: function(c) {this.doit(c,0)}
        });
        classical(o.selectors.animPlay, {
            init: function(c) {c.all().each(function(){this.pause(); try{this.currentTime=0}catch(e){} })},
            undo: function(c) {c.all().each(function(){this.pause()})},
            doit: function(c) {c.all().each(function(){this.play()})},
            fast: function(c) {c.all().each(function(){this.play()})}
        });
        classical(o.selectors.animPause, {
            undo: function(c) {c.all().each(function(){this.play()})},
            doit: function(c) {c.all().each(function(){this.pause()})},
            fast: function(c) {c.all().each(function(){this.pause()})}
        });
        classical(o.selectors.animViewboxAs, {
            create: function(c) {c.whatFrom = {}},
            init: function(c) {this.undo(c)},
            undo: function(c) {c.all().animate(c.whatFrom, 0)},
            doit: function(c, factor) {
                if (factor === undefined) factor = 1
                var attr = "svgViewBox";
                var whatTo = {};
                var asWhat = $(c.as());
                var a = function (i) {return asWhat.attr(i)}
                // todo should do as with the generic attribute above (maintain a list)
                c.whatFrom[attr] = c.all().first().get(0).attributes.getNamedItem('viewBox').nodeValue // custom access to the svg viewbox attribute
                var toViewBox = a('x')+" "+a('y')+" "+a('width')+" "+a('height');
                whatTo[attr] = toViewBox;
                c.all().animate(whatTo, c.dur()*factor)
            },
            fast: function(c) {this.doit(c, 0)}
        });
        classical(o.selectors.animContinue, {
            doit: function(c) {setTimeout(function(){$[deck]('next')}, 1)}
            // do not do it in fast mode
        });
        // handle the chained undo for "anim-continue"
        $(o.selectors.animContinue).each(function(i, curSlide) {
            $(curSlide).bind('deck.becameCurrent', function(_, direction) {
                if (direction == 'forward') return;
                setTimeout(function(){$[deck]('prev')}, 1)
            });

        });

        // finally force "refresh" (notification of slide change)
        var current = $[deck]('getSlide')
        var icur = 0
        for (; icur < $[deck]('getSlides').length; icur++) {
            if ($[deck]('getSlides')[icur] == current) break;
        }
	$d.trigger("deck.change", [icur, 0]);
	$d.trigger("deck.change", [0, icur]);

    }
    $(document).bind('deck.init', function() {
        setTimeout(doInitIfReady, 10) // try the first time after init
    });

})(jQuery, 'deck');

<commit_msg>[anim] tagging the container with "deck-anim-ready" class after initialization of anims<commit_after>
(function($, deck, undefined) {
    // This next line is the color plugin from jquery
    (function(d){d.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(f,e){d.fx.step[e]=function(g){if(!g.colorInit){g.start=c(g.elem,e);g.end=b(g.end);g.colorInit=true}g.elem.style[e]="rgb("+[Math.max(Math.min(parseInt((g.pos*(g.end[0]-g.start[0]))+g.start[0]),255),0),Math.max(Math.min(parseInt((g.pos*(g.end[1]-g.start[1]))+g.start[1]),255),0),Math.max(Math.min(parseInt((g.pos*(g.end[2]-g.start[2]))+g.start[2]),255),0)].join(",")+")"}});function b(f){var e;if(f&&f.constructor==Array&&f.length==3){return f}if(e=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(f)){return[parseInt(e[1]),parseInt(e[2]),parseInt(e[3])]}if(e=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(f)){return[parseFloat(e[1])*2.55,parseFloat(e[2])*2.55,parseFloat(e[3])*2.55]}if(e=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(f)){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}if(e=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(f)){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}if(e=/rgba\(0, 0, 0, 0\)/.exec(f)){return a.transparent}return a[d.trim(f).toLowerCase()]}function c(g,e){var f;do{f=d.curCSS(g,e);if(f!=""&&f!="transparent"||d.nodeName(g,"body")){break}e="backgroundColor"}while(g=g.parentNode);return b(f)}var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]}})(jQuery);

    var $d = $(document);
    var may = function(o,f) {return f ? f.bind(o) : function() {}};

    $.extend(true, $[deck].defaults, {
        selectors: {
            animShow: ".anim-show",
            animHide: ".anim-hide",
            animAddClass: ".anim-addclass",
            animRemoveClass: ".anim-removeclass",
            animAttribute: ".anim-attribute",
            // specific ones
            animPlay: ".anim-play",
            animPause: ".anim-pause",
            animViewboxAs: ".anim-viewboxas",
            //
            animContinue: ".anim-continue"
        },
        classes: {
            animReady: "deck-anim-ready"
        },
        anim: {
            duration: 400
        }
    });

    var waitFor = 0
    $[deck]('extend', 'animWaitMore', function(){ waitFor++ });
    $[deck]('extend', 'animWaitLess', function(){ waitFor-- });

    var doInitIfReady = function hoho() {
        if (waitFor>0) {
            setTimeout(doInitIfReady, 10) // retry until all is loaded
            return;
        }
        // first we define some tools and grab some info from deck.js
        var o = $[deck]('getOptions');
        var context = function(el) {
            return {
                what: function() {return $(el).attr("data-what")},
                dur: function() {return $(el).attr("data-dur")*1 || o.anim.duration},
                classs: function() {return $(el).attr("data-class")},
                attribute: function() {return $(el).attr("data-attr").split(':')[0]},
                as: function() {return $(el).attr("data-as")},
                value: function() {return $(el).attr("data-attr").split(':')[1]},
                toplevel: function() {return $[deck]('getToplevelSlideOf', el).node},
                all: function() {return $(this.what(),this.toplevel())}
            }
        };
        var classical = function(selector, methods) {
            $(selector).each(function(i, el) {
                var c = context(el);
                may(methods, methods.create)(c);
                $(el).bind('deck.toplevelBecameCurrent', function(_, direction) {
                    may(methods, methods.init)(c);
                }).bind('deck.afterToplevelBecameCurrent', function(_, direction) {
                    may(methods, methods.fast)(c);
                }).bind('deck.lostCurrent', function(_, direction, from, to) {
                    if (direction == 'forward' || Math.abs(from - to)>1 ) return; // if a big step, let the "step" extension do its job
                    may(methods, methods.undo)(c);
                }).bind('deck.becameCurrent', function(_, direction, from, to) {
                    if (direction == 'reverse' || Math.abs(from - to)>1 ) return; // if a big step, let the "step" extension do its job
                    may(methods, methods.doit)(c);
                });
            });
        };

        // here come the real animations
        classical(o.selectors.animShow, {
            init: function(c) {c.all().animate({'opacity': 0.}, 0)},
            undo: function(c) {c.all().animate({'opacity': 0.}, c.dur()/100)},
            doit: function(c) {c.all().animate({'opacity': 1.}, c.dur())},
            fast: function(c) {c.all().animate({'opacity': 1.}, 0)}
        });
        classical(o.selectors.animHide, {
            init: function(c) {c.all().animate({'opacity': 1.}, 0)},
            undo: function(c) {c.all().animate({'opacity': 1.}, c.dur()/100)},
            doit: function(c) {c.all().animate({'opacity': 0.}, c.dur())},
            fast: function(c) {c.all().animate({'opacity': 0.}, 0)}
        });
        classical(o.selectors.animAddClass, {
            init: function(c) {c.all().removeClass(c.classs())},
            undo: function(c) {c.all().removeClass(c.classs())},
            doit: function(c) {c.all().addClass(c.classs())},
            fast: function(c) {c.all().addClass(c.classs())}
        });
        classical(o.selectors.animRemoveClass, {
            init: function(c) {c.all().addClass(c.classs())},
            undo: function(c) {c.all().addClass(c.classs())},
            doit: function(c) {c.all().removeClass(c.classs())},
            fast: function(c) {c.all().removeClass(c.classs())}
        });
        classical(o.selectors.animAttribute, {
            init: function(c) {
                c.previousElement = [];
                c.all().css(c.attribute(), '') // for the jquery anim to work the css attribute should not be defined in the element (in the html) so we suppose it is empty by default (and thus, if it is not empty, it means it has been set by jquery)
            },
            undo: function(c) {
                var k = c.attribute()
                for (i in c.previousElement) { // use the saved list of elements and values
                    var whatTo = {}
                    whatTo[k] = c.previousCss[i]
                    $(c.previousElement[i]).animate(whatTo, 0)
                }
            },
            doit: function(c, factor) {
                if (factor === undefined) factor = 1
                var k = c.attribute()
                c.previousCss = []
                c.previousElement = []
                c.all().each( function(){c.previousElement.push(this); c.previousCss.push($(this).css(k))}) // save a list of elements and values
                var whatTo = {}
                whatTo[c.attribute()] = c.value()
                c.all().animate(whatTo, c.dur()*factor)
            },
            fast: function(c) {this.doit(c,0)}
        });
        classical(o.selectors.animPlay, {
            init: function(c) {c.all().each(function(){this.pause(); try{this.currentTime=0}catch(e){} })},
            undo: function(c) {c.all().each(function(){this.pause()})},
            doit: function(c) {c.all().each(function(){this.play()})},
            fast: function(c) {c.all().each(function(){this.play()})}
        });
        classical(o.selectors.animPause, {
            undo: function(c) {c.all().each(function(){this.play()})},
            doit: function(c) {c.all().each(function(){this.pause()})},
            fast: function(c) {c.all().each(function(){this.pause()})}
        });
        classical(o.selectors.animViewboxAs, {
            create: function(c) {c.whatFrom = {}},
            init: function(c) {this.undo(c)},
            undo: function(c) {c.all().animate(c.whatFrom, 0)},
            doit: function(c, factor) {
                if (factor === undefined) factor = 1
                var attr = "svgViewBox";
                var whatTo = {};
                var asWhat = $(c.as());
                var a = function (i) {return asWhat.attr(i)}
                // todo should do as with the generic attribute above (maintain a list)
                c.whatFrom[attr] = c.all().first().get(0).attributes.getNamedItem('viewBox').nodeValue // custom access to the svg viewbox attribute
                var toViewBox = a('x')+" "+a('y')+" "+a('width')+" "+a('height');
                whatTo[attr] = toViewBox;
                c.all().animate(whatTo, c.dur()*factor)
            },
            fast: function(c) {this.doit(c, 0)}
        });
        classical(o.selectors.animContinue, {
            doit: function(c) {setTimeout(function(){$[deck]('next')}, 1)}
            // do not do it in fast mode
        });
        // handle the chained undo for "anim-continue"
        $(o.selectors.animContinue).each(function(i, curSlide) {
            $(curSlide).bind('deck.becameCurrent', function(_, direction) {
                if (direction == 'forward') return;
                setTimeout(function(){$[deck]('prev')}, 1)
            });

        });

        // finally force "refresh" (notification of slide change)
        var current = $[deck]('getSlide')
        var icur = 0
        for (; icur < $[deck]('getSlides').length; icur++) {
            if ($[deck]('getSlides')[icur] == current) break;
        }
	$d.trigger("deck.change", [icur, 0]);
	$d.trigger("deck.change", [0, icur]);

        var container = $[deck]('getContainer');
        $(container).addClass(o.classes.animReady)
    }
    $(document).bind('deck.init', function() {
        setTimeout(doInitIfReady, 10) // try the first time after init
    });

})(jQuery, 'deck');

<|endoftext|>


<commit_before>/**
 * @fileOverview The policies model.
 */
var Promise = require('bluebird');
var middlewarify = require('middlewarify');
// var log = require('../util/logger').getLogger('kansas.model.policy');

var Model = require('./model-redis');

var helpers = require('../util/helpers');

var Token = module.exports = Model.extend(function() {
  // wire set/del/get
  middlewarify.make(this, 'set', this.hmset.bind(this, []));
  middlewarify.make(this, 'get', this.hdel.bind(this));
  middlewarify.make(this, 'del', this.hget.bind(this, []));

  this.namespace = 'policy';
  this.idProp = 'id';
});

/**
 * Will create a new policy and return the value.
 *
 * @param {Object} userValues The item to save, expects:
 *   @param {string} name The policy name.
 *   @param {integer} maxTokens Maximum number of tokens.
 *   @param {integer} limit Maximum requests limit per given period (now only month).
 *   @param {kansas.model.periodBucket.Period} period The period.
 * @return {Promise(Object)} A promise with the new item.
 */
Token.prototype.create = Promise.method(function(userValues) {
  var item = Object.create(null);
  item.id = helpers.generateRandomString(8);
  item.name = userValues.name;
  item.maxTokens = userValues.maxTokens;
  item.limit = userValues.limit;
  item.period = userValues.period;
  item.createdOn = (new Date()).toISOString();
  return this.set(item.id, item);
});
<commit_msg>switch policy to memory store<commit_after>/*global -Map, -Set*/
/**
 * @fileOverview The policies model, stores policies in memory.
 */
var Map = require('collections').Map;
// var log = require('../util/logger').getLogger('kansas.model.Policy');

var Model = require('./model-redis');

var Policy = module.exports = Model.extend(function() {
  this.store = new Map();

  this.get = this.store.get.bind(this.store);
  this.has = this.store.has.bind(this.store);
});

/**
 * Will create a new policy and return the value.
 *
 * @param {Object} userValues The item to save, expects:
 *   @param {string} name The policy name.
 *   @param {integer} maxTokens Maximum number of tokens.
 *   @param {integer} limit Maximum requests limit per given period (now only month).
 *   @param {kansas.model.periodBucket.Period} period The period.
 * @return {Object} The policy item
 */
Policy.prototype.create = function(userValues) {
  if (this.store.has(userValues.name)) {
    return this.store.get(userValues.name);
  }

  var item = Object.create(null);
  item.name = userValues.name;
  item.maxTokens = userValues.maxTokens;
  item.limit = userValues.limit;
  item.period = userValues.period;
  this.store.set(userValues.name, item);
  return item;
};
<|endoftext|>