<commit_before>import _ from 'lodash'
import path from 'path'
import multer from 'multer'
import multers3 from 'multer-s3'
import aws from 'aws-sdk'

import injectScript from 'raw-loader!./inject.js'
import injectStyle from 'raw-loader!./inject.css'
import notificationSound from 'raw-loader!../static/notification.mp3'

import serveStatic from 'serve-static'

import db from './db'
import users from './users'

const ERR_USER_ID_REQ = '`userId` is required and must be valid'
const ERR_MSG_TYPE = '`type` is required and must be valid'
const ERR_CONV_ID_REQ = '`conversationId` is required and must be valid'

module.exports = async (bp, config) => {
  const diskStorage = multer.diskStorage({
    limits: {
      files: 1,
      fileSize: 5242880 // 5MB
    },
    filename: function(req, file, cb) {
      const userId = _.get(req, 'params.userId') || 'anonymous'
      const ext = path.extname(file.originalname)

      cb(null, `${userId}_${new Date().getTime()}${ext}`)
    }
  })

  let upload = multer({ storage: diskStorage })

  if (config.uploadsUseS3) {
    /*
      You can override AWS's default settings here. Example:
      { region: 'us-east-1', apiVersion: '2014-10-01', credentials: {...} }
     */
    const awsConfig = {
      region: config.uploadsS3Region,
      credentials: {
        accessKeyId: config.uploadsS3AWSAccessKey,
        secretAccessKey: config.uploadsS3AWSAccessSecret
      }
    }

    if (!awsConfig.credentials.accessKeyId && !awsConfig.credentials.secretAccessKey) {
      delete awsConfig.credentials
    }

    if (!awsConfig.region) {
      delete awsConfig.region
    }

    const s3 = new aws.S3(awsConfig)
    const s3Storage = multers3({
      s3: s3,
      bucket: config.uploadsS3Bucket || 'uploads',
      contentType: multers3.AUTO_CONTENT_TYPE,
      cacheControl: 'max-age=31536000', // one year caching
      acl: 'public-read',
      key: function(req, file, cb) {
        const userId = _.get(req, 'params.userId') || 'anonymous'
        const ext = path.extname(file.originalname)

        cb(null, `${userId}_${new Date().getTime()}${ext}`)
      }
    })

    upload = multer({ storage: s3Storage })
  }

  const knex = await bp.db.get()

  const { listConversations, getConversation, appendUserMessage, getOrCreateRecentConversation } = db(knex, bp.botfile)

  const { getOrCreateUser } = await users(bp, config)

  const router = bp.getRouter('botpress-platform-webchat', { auth: false })

  const asyncApi = fn => async (req, res, next) => {
    try {
      await fn(req, res, next)
    } catch (err) {
      res.status(500).send(err && err.message)
    }
  }

  router.get('/inject.js', (req, res) => {
    res.contentType('text/javascript')
    res.send(injectScript)
  })

  router.get('/inject.css', (req, res) => {
    res.contentType('text/css')
    res.send(injectStyle)
  })

  const modulePath = bp._loadedModules['@botpress/channel-web'].root
  const staticFolder = path.join(modulePath, './static')
  router.use('/static', serveStatic(staticFolder))

  // ?conversationId=xxx (optional)
  router.post(
    '/messages/:userId',
    asyncApi(async (req, res) => {
      const { userId } = req.params || {}

      if (!validateUserId(userId)) {
        return res.status(400).send(ERR_USER_ID_REQ)
      }

      await getOrCreateUser(userId) // Just to create the user if it doesn't exist

      const payload = req.body || {}
      let { conversationId } = req.query || {}
      conversationId = conversationId && parseInt(conversationId)

      if (!_.includes(['text', 'quick_reply', 'form', 'login_prompt'], payload.type)) {
        // TODO: Support files
        return res.status(400).send(ERR_MSG_TYPE)
      }

      if (!conversationId) {
        conversationId = await getOrCreateRecentConversation(userId)
      }

      await sendNewMessage(userId, conversationId, payload)

      return res.sendStatus(200)
    })
  )

  // ?conversationId=xxx (required)
  router.post(
    '/messages/:userId/files',
    upload.single('file'),
    asyncApi(async (req, res) => {
      const { userId } = req.params || {}

      if (!validateUserId(userId)) {
        return res.status(400).send(ERR_USER_ID_REQ)
      }

      await getOrCreateUser(userId) // Just to create the user if it doesn't exist

      let { conversationId } = req.query || {}
      conversationId = conversationId && parseInt(conversationId)

      if (!conversationId) {
        return res.status(400).send(ERR_CONV_ID_REQ)
      }

      const payload = {
        text: `Uploaded a file [${req.file.originalname}]`,
        type: 'file',
        data: {
          storage: req.file.location ? 's3' : 'local',
          url: req.file.location || null,
          name: req.file.originalname,
          mime: req.file.contentType || req.file.mimetype,
          size: req.file.size
        }
      }

      await sendNewMessage(userId, conversationId, payload)

      return res.sendStatus(200)
    })
  )

  router.get('/conversations/:userId/:conversationId', async (req, res) => {
    const { userId, conversationId } = req.params || {}

    if (!validateUserId(userId)) {
      return res.status(400).send(ERR_USER_ID_REQ)
    }

    const conversation = await getConversation(userId, conversationId)

    return res.send(conversation)
  })

  router.get('/conversations/:userId', async (req, res) => {
    const { userId } = req.params || {}

    if (!validateUserId(userId)) {
      return res.status(400).send(ERR_USER_ID_REQ)
    }

    await getOrCreateUser(userId) // Just to create the user if it doesn't exist

    const conversations = await listConversations(userId)

    return res.send([...conversations])
  })

  function validateUserId(userId) {
    return /[a-z0-9-_]+/i.test(userId)
  }

  async function sendNewMessage(userId, conversationId, payload) {
    if (!payload.text || !_.isString(payload.text) || payload.text.length > 360) {
      throw new Error('Text must be a valid string of less than 360 chars')
    }

    const sanitizedPayload = _.pick(payload, ['text', 'type', 'data'])

    // Because we don't necessarily persist what we emit/received
    const persistedPayload = Object.assign({}, sanitizedPayload)

    // We remove the password from the persisted messages for security reasons
    if (payload.type === 'login_prompt') {
      persistedPayload.data = _.omit(persistedPayload.data, ['password'])
    }

    if (payload.type === 'form') {
      persistedPayload.data.formId = payload.formId
    }

    const message = await appendUserMessage(userId, conversationId, persistedPayload)

    Object.assign(message, {
      __room: 'visitor:' + userId // This is used to send to the relevant user's socket
    })

    bp.events.emit('guest.webchat.message', message)

    const user = await getOrCreateUser(userId)

    return bp.middlewares.sendIncoming(
      Object.assign(
        {
          platform: 'webchat',
          type: payload.type,
          user: user,
          text: payload.text,
          raw: Object.assign({}, sanitizedPayload, {
            conversationId
          })
        },
        payload.data
      )
    )
  }

  router.post(
    '/events/:userId',
    asyncApi(async (req, res) => {
      const { type, payload } = req.body || {}
      const { userId } = req.params || {}
      const user = await getOrCreateUser(userId)
      bp.middlewares.sendIncoming({
        platform: 'webchat',
        type,
        user,
        text: payload.text,
        raw: _.pick(payload, ['text', 'type', 'data']),
        ...payload.data
      })
      res.status(200).send({})
    })
  )

  router.post(
    '/conversations/:userId/:conversationId/reset',
    asyncApi(async (req, res) => {
      const { userId, conversationId } = req.params
      const user = await getOrCreateUser(userId)

      const payload = {
        text: `Reset the conversation`,
        type: 'session_reset'
      }

      await sendNewMessage(userId, conversationId, payload)
      await bp.dialogEngine.stateManager.deleteState(user.id)
      res.status(200).send({})
    })
  )

  return router
}
<commit_msg>channel-web: Read name from package.json, allows for forks.<commit_after>import _ from 'lodash'
import path from 'path'
import multer from 'multer'
import multers3 from 'multer-s3'
import aws from 'aws-sdk'

import injectScript from 'raw-loader!./inject.js'
import injectStyle from 'raw-loader!./inject.css'
import notificationSound from 'raw-loader!../static/notification.mp3'

import serveStatic from 'serve-static'

import db from './db'
import users from './users'

const ERR_USER_ID_REQ = '`userId` is required and must be valid'
const ERR_MSG_TYPE = '`type` is required and must be valid'
const ERR_CONV_ID_REQ = '`conversationId` is required and must be valid'

module.exports = async (bp, config) => {
  const diskStorage = multer.diskStorage({
    limits: {
      files: 1,
      fileSize: 5242880 // 5MB
    },
    filename: function(req, file, cb) {
      const userId = _.get(req, 'params.userId') || 'anonymous'
      const ext = path.extname(file.originalname)

      cb(null, `${userId}_${new Date().getTime()}${ext}`)
    }
  })

  let upload = multer({ storage: diskStorage })

  if (config.uploadsUseS3) {
    /*
      You can override AWS's default settings here. Example:
      { region: 'us-east-1', apiVersion: '2014-10-01', credentials: {...} }
     */
    const awsConfig = {
      region: config.uploadsS3Region,
      credentials: {
        accessKeyId: config.uploadsS3AWSAccessKey,
        secretAccessKey: config.uploadsS3AWSAccessSecret
      }
    }

    if (!awsConfig.credentials.accessKeyId && !awsConfig.credentials.secretAccessKey) {
      delete awsConfig.credentials
    }

    if (!awsConfig.region) {
      delete awsConfig.region
    }

    const s3 = new aws.S3(awsConfig)
    const s3Storage = multers3({
      s3: s3,
      bucket: config.uploadsS3Bucket || 'uploads',
      contentType: multers3.AUTO_CONTENT_TYPE,
      cacheControl: 'max-age=31536000', // one year caching
      acl: 'public-read',
      key: function(req, file, cb) {
        const userId = _.get(req, 'params.userId') || 'anonymous'
        const ext = path.extname(file.originalname)

        cb(null, `${userId}_${new Date().getTime()}${ext}`)
      }
    })

    upload = multer({ storage: s3Storage })
  }

  const knex = await bp.db.get()

  const { listConversations, getConversation, appendUserMessage, getOrCreateRecentConversation } = db(knex, bp.botfile)

  const { getOrCreateUser } = await users(bp, config)

  const router = bp.getRouter('botpress-platform-webchat', { auth: false })

  const asyncApi = fn => async (req, res, next) => {
    try {
      await fn(req, res, next)
    } catch (err) {
      res.status(500).send(err && err.message)
    }
  }

  router.get('/inject.js', (req, res) => {
    res.contentType('text/javascript')
    res.send(injectScript)
  })

  router.get('/inject.css', (req, res) => {
    res.contentType('text/css')
    res.send(injectStyle)
  })

  const pkg = require('../package.json');
  const modulePath = bp._loadedModules[pkg.name].root
  const staticFolder = path.join(modulePath, './static')
  router.use('/static', serveStatic(staticFolder))

  // ?conversationId=xxx (optional)
  router.post(
    '/messages/:userId',
    asyncApi(async (req, res) => {
      const { userId } = req.params || {}

      if (!validateUserId(userId)) {
        return res.status(400).send(ERR_USER_ID_REQ)
      }

      await getOrCreateUser(userId) // Just to create the user if it doesn't exist

      const payload = req.body || {}
      let { conversationId } = req.query || {}
      conversationId = conversationId && parseInt(conversationId)

      if (!_.includes(['text', 'quick_reply', 'form', 'login_prompt'], payload.type)) {
        // TODO: Support files
        return res.status(400).send(ERR_MSG_TYPE)
      }

      if (!conversationId) {
        conversationId = await getOrCreateRecentConversation(userId)
      }

      await sendNewMessage(userId, conversationId, payload)

      return res.sendStatus(200)
    })
  )

  // ?conversationId=xxx (required)
  router.post(
    '/messages/:userId/files',
    upload.single('file'),
    asyncApi(async (req, res) => {
      const { userId } = req.params || {}

      if (!validateUserId(userId)) {
        return res.status(400).send(ERR_USER_ID_REQ)
      }

      await getOrCreateUser(userId) // Just to create the user if it doesn't exist

      let { conversationId } = req.query || {}
      conversationId = conversationId && parseInt(conversationId)

      if (!conversationId) {
        return res.status(400).send(ERR_CONV_ID_REQ)
      }

      const payload = {
        text: `Uploaded a file [${req.file.originalname}]`,
        type: 'file',
        data: {
          storage: req.file.location ? 's3' : 'local',
          url: req.file.location || null,
          name: req.file.originalname,
          mime: req.file.contentType || req.file.mimetype,
          size: req.file.size
        }
      }

      await sendNewMessage(userId, conversationId, payload)

      return res.sendStatus(200)
    })
  )

  router.get('/conversations/:userId/:conversationId', async (req, res) => {
    const { userId, conversationId } = req.params || {}

    if (!validateUserId(userId)) {
      return res.status(400).send(ERR_USER_ID_REQ)
    }

    const conversation = await getConversation(userId, conversationId)

    return res.send(conversation)
  })

  router.get('/conversations/:userId', async (req, res) => {
    const { userId } = req.params || {}

    if (!validateUserId(userId)) {
      return res.status(400).send(ERR_USER_ID_REQ)
    }

    await getOrCreateUser(userId) // Just to create the user if it doesn't exist

    const conversations = await listConversations(userId)

    return res.send([...conversations])
  })

  function validateUserId(userId) {
    return /[a-z0-9-_]+/i.test(userId)
  }

  async function sendNewMessage(userId, conversationId, payload) {
    if (!payload.text || !_.isString(payload.text) || payload.text.length > 360) {
      throw new Error('Text must be a valid string of less than 360 chars')
    }

    const sanitizedPayload = _.pick(payload, ['text', 'type', 'data'])

    // Because we don't necessarily persist what we emit/received
    const persistedPayload = Object.assign({}, sanitizedPayload)

    // We remove the password from the persisted messages for security reasons
    if (payload.type === 'login_prompt') {
      persistedPayload.data = _.omit(persistedPayload.data, ['password'])
    }

    if (payload.type === 'form') {
      persistedPayload.data.formId = payload.formId
    }

    const message = await appendUserMessage(userId, conversationId, persistedPayload)

    Object.assign(message, {
      __room: 'visitor:' + userId // This is used to send to the relevant user's socket
    })

    bp.events.emit('guest.webchat.message', message)

    const user = await getOrCreateUser(userId)

    return bp.middlewares.sendIncoming(
      Object.assign(
        {
          platform: 'webchat',
          type: payload.type,
          user: user,
          text: payload.text,
          raw: Object.assign({}, sanitizedPayload, {
            conversationId
          })
        },
        payload.data
      )
    )
  }

  router.post(
    '/events/:userId',
    asyncApi(async (req, res) => {
      const { type, payload } = req.body || {}
      const { userId } = req.params || {}
      const user = await getOrCreateUser(userId)
      bp.middlewares.sendIncoming({
        platform: 'webchat',
        type,
        user,
        text: payload.text,
        raw: _.pick(payload, ['text', 'type', 'data']),
        ...payload.data
      })
      res.status(200).send({})
    })
  )

  router.post(
    '/conversations/:userId/:conversationId/reset',
    asyncApi(async (req, res) => {
      const { userId, conversationId } = req.params
      const user = await getOrCreateUser(userId)

      const payload = {
        text: `Reset the conversation`,
        type: 'session_reset'
      }

      await sendNewMessage(userId, conversationId, payload)
      await bp.dialogEngine.stateManager.deleteState(user.id)
      res.status(200).send({})
    })
  )

  return router
}
<|endoftext|>


<commit_before>var Iconv = require('iconv-lite');
var ParserCache = {};

function StringParser (encoding, options) {
  options = options || {};
  this.encoding = encoding;
  this.encoder = Iconv.getEncoder(encoding, options);
  this.decoder = Iconv.getDecoder(encoding, options);
}

var prepareParser = function (encoding, options) {
  // if available use existing parser
  if (ParserCache[encoding]) {
    return ParserCache[encoding];
  }

  // other wise prepare, cache and return
  ParserCache[encoding] = new StringParser(encoding, options);
  return ParserCache[encoding];
};

exports.decode = function (buffer, encoding) {
  var parser = prepareParser(encoding);

  var res = parser.decoder.write(buffer);
  var trail = parser.decoder.end();

  return trail ? (res + trail) : res;
};

exports.encode = function (string, encoding) {
  var parser = prepareParser(encoding);

  var res = parser.encoder.write(string);
  var trail = parser.encoder.end();

  return (trail && trail.length > 0) ? Buffer.concat([res, trail]) : res;
};
<commit_msg>removing parser cache :cat:<commit_after>var Iconv = require('iconv-lite');

exports.decode = function (buffer, encoding, options) {
  var decoder = Iconv.getDecoder(encoding, options || {});

  var res = decoder.write(buffer);
  var trail = decoder.end();

  return trail ? (res + trail) : res;
};

exports.encode = function (string, encoding, options) {
  var encoder = Iconv.getEncoder(encoding, options || {});

  var res = encoder.write(string);
  var trail = encoder.end();

  return (trail && trail.length > 0) ? Buffer.concat([res, trail]) : res;
};
<|endoftext|>


<commit_before>suite('group-player', function() {
  setup(function() {
    document.timeline.players = [];
  });

  function simpleAnimationGroup() {
    return new AnimationGroup([new Animation(document.body, [], 2000), new Animation(document.body, [], 1000), new Animation(document.body, [], 3000)]);
  }

  function simpleAnimationSequence() {
    return new AnimationSequence([new Animation(document.body, [], 2000), new Animation(document.body, [], 1000), new Animation(document.body, [], 3000)]);
  }

  // topTimes and restOfTimes contain duplets (startTime, currentTime)
  // and triplets (startTime, currentTime, startOffset)
  function checkTimes(player, topTimes, restOfTimes) {
    _checkTimes(player, topTimes, 0, 'toplevel');
    _checkTimes(player, restOfTimes, 0, 'internals');
  }

  function _checkTimes(player, timingList, index, trace) {
    if (timingList.length == 0) {
      return;
    }
    if (timingList[0] == null || typeof timingList[0] == 'number') {
      assert.equal(player._startTime, timingList[0], trace + ' startTime');
      assert.equal(player.currentTime, timingList[1], trace + ' currentTime');
      if (timingList.length == 3)
        assert.equal(player._startOffset, timingList[2], trace + ' startOffset');
    } else {
      _checkTimes(player.childPlayers[index], timingList[0], 0, trace + ' ' + index);
      _checkTimes(player, timingList.slice(1), index + 1, trace);
    }
  }

  test('playing an animationGroup works as expected', function() {
    tick(100);
    var p = document.timeline.play(simpleAnimationGroup());
    checkTimes(p, [100, 0], [[100, 0, 0], [100, 0, 0], [100, 0, 0]]);
    tick(300);
    checkTimes(p, [100, 200], [[100, 200], [100, 200], [100, 200]]);
    tick(1200);
    checkTimes(p, [100, 1100], [[100, 1100], [100, 1000], [100, 1100]]);
    tick(2200);
    checkTimes(p, [100, 2100], [[100, 2000], [100, 1000], [100, 2100]]);
    tick(3200);
    // TODO Limit toplevel currentTime
    checkTimes(p, [100, 3100], [[100, 2000], [100, 1000], [100, 3000]]);
  });

  test('playing an animationSequence works as expected', function() {
    tick(110);
    var p = document.timeline.play(simpleAnimationSequence());
    checkTimes(p, [110, 0], [[110, 0], [2110, -2000, 2000], [3110, -3000, 3000]]);
    tick(210);
    checkTimes(p, [110, 100], [[110, 100], [2110, -1900, 2000], [3110, -2900, 3000]]);
  });

  function complexAnimationTree(createLeaf) {
    // The following animation structure looks like:
    // 44444
    // 11
    //   33
    //   2
    // 0
    return new AnimationGroup([
      createLeaf('4px', 5),
      new AnimationSequence([
        createLeaf('1px', 2),
        new AnimationGroup([
          createLeaf('3px', 2),
          createLeaf('2px', 1),
        ]),
      ]),
      createLeaf('0px', 1),
    ]);
  }

  test('complex animation tree timing while playing', function() {
    function createLeaf(value, duration) {
      return new Animation(document.body, [], duration);
    }
    tick(100);
    var player = document.timeline.play(complexAnimationTree(createLeaf));
    checkTimes(player, [100, 0], [
      [100, 0, 0], [// 4
        [100, 0, 0], [// 1
          [102, -2, 0], // 3
          [102, -2, 0]]], // 2
      [100, 0, 0]]); // 0
    tick(101);
    checkTimes(player, [100, 1], [
      [100, 1, 0], [// 4
        [100, 1, 0], [// 1
          [102, -1, 0], // 3
          [102, -1, 0]]], // 2
      [100, 1, 0]]); // 0
    tick(102);
    checkTimes(player, [100, 2], [
      [100, 2, 0], [// 4
        [100, 2, 0], [// 1
          [102, 0, 0], // 3
          [102, 0, 0]]], // 2
      [100, 2, 0]]); // 0
  });

  test('effects apply in the correct order', function() {
    var target = document.createElement('div');
    document.documentElement.appendChild(target);
    function createLeaf(value, duration) {
      return new Animation(target, [{marginLeft: value}, {marginLeft: value}], duration);
    }
    tick(0);
    var player = document.timeline.play(complexAnimationTree(createLeaf));
    player.currentTime = 0;
    assert.equal(getComputedStyle(target).marginLeft, '0px');
    player.currentTime = 1;
    assert.equal(getComputedStyle(target).marginLeft, '1px');
    player.currentTime = 2;
    assert.equal(getComputedStyle(target).marginLeft, '2px');
    player.currentTime = 3;
    assert.equal(getComputedStyle(target).marginLeft, '3px');
    player.currentTime = 4;
    assert.equal(getComputedStyle(target).marginLeft, '4px');
    player.currentTime = 5;
    assert.equal(getComputedStyle(target).marginLeft, '0px');
    target.remove();
  });
});
<commit_msg>Fix test up<commit_after>suite('group-player', function() {
  setup(function() {
    document.timeline.players = [];
  });

  function simpleAnimationGroup() {
    return new AnimationGroup([new Animation(document.body, [], 2000), new Animation(document.body, [], 1000), new Animation(document.body, [], 3000)]);
  }

  function simpleAnimationSequence() {
    return new AnimationSequence([new Animation(document.body, [], 2000), new Animation(document.body, [], 1000), new Animation(document.body, [], 3000)]);
  }

  // topTimes and restOfTimes contain duplets (startTime, currentTime)
  // and triplets (startTime, currentTime, startOffset)
  function checkTimes(player, topTimes, restOfTimes, description) {
    description = description ? (description + ' ') : '';
    _checkTimes(player, topTimes, 0, description + 'toplevel');
    _checkTimes(player, restOfTimes, 0, description + 'internals');
  }

  function _checkTimes(player, timingList, index, trace) {
    if (timingList.length == 0) {
      return;
    }
    if (timingList[0] == null || typeof timingList[0] == 'number') {
      assert.equal(player._startTime, timingList[0], trace + ' startTime');
      assert.equal(player.currentTime, timingList[1], trace + ' currentTime');
      if (timingList.length == 3)
        assert.equal(player._startOffset, timingList[2], trace + ' startOffset');
    } else {
      _checkTimes(player.childPlayers[index], timingList[0], 0, trace + ' ' + index);
      _checkTimes(player, timingList.slice(1), index + 1, trace);
    }
  }

  test('playing an animationGroup works as expected', function() {
    tick(100);
    var p = document.timeline.play(simpleAnimationGroup());
    checkTimes(p, [100, 0], [[100, 0, 0], [100, 0, 0], [100, 0, 0]]);
    tick(300);
    checkTimes(p, [100, 200], [[100, 200], [100, 200], [100, 200]]);
    tick(1200);
    checkTimes(p, [100, 1100], [[100, 1100], [100, 1000], [100, 1100]]);
    tick(2200);
    checkTimes(p, [100, 2100], [[100, 2000], [100, 1000], [100, 2100]]);
    tick(3200);
    // TODO Limit toplevel currentTime
    checkTimes(p, [100, 3100], [[100, 2000], [100, 1000], [100, 3000]]);
  });

  test('playing an animationSequence works as expected', function() {
    tick(110);
    var p = document.timeline.play(simpleAnimationSequence());
    checkTimes(p, [110, 0], [[110, 0], [2110, -2000, 2000], [3110, -3000, 3000]]);
    tick(210);
    checkTimes(p, [110, 100], [[110, 100], [2110, -1900, 2000], [3110, -2900, 3000]]);
  });

  function complexAnimationTree(createLeaf) {
    // The following animation structure looks like:
    // 44444
    // 11
    //   33
    //   2
    // 0
    return new AnimationGroup([
      createLeaf('4px', 5),
      new AnimationSequence([
        createLeaf('1px', 2),
        new AnimationGroup([
          createLeaf('3px', 2),
          createLeaf('2px', 1),
        ]),
      ]),
      createLeaf('0px', 1),
    ]);
  }

  test('complex animation tree timing while playing', function() {
    function createLeaf(value, duration) {
      return new Animation(document.body, [], duration);
    }
    tick(100);
    var player = document.timeline.play(complexAnimationTree(createLeaf));
    checkTimes(player, [100, 0], [
      [100, 0, 0], [ // 4
        [100, 0, 0], [ // 1
          [102, -2, 0], // 3
          [102, -2, 0]]], // 2
      [100, 0, 0], // 0
    ], 't = 100');
    tick(101);
    checkTimes(player, [100, 1], [
      [100, 1, 0], [ // 4
        [100, 1, 0], [ // 1
          [102, -1, 0], // 3
          [102, -1, 0]]], // 2
      [100, 1, 0], // 0
    ], 't = 101');
    tick(102);
    checkTimes(player, [100, 2], [
      [100, 2, 0], [ // 4
        [100, 2, 0], [ // 1
          [102, 0, 0], // 3
          [102, 0, 0]]], // 2
      [100, 1, 0], // 0
    ], 't = 102');
  });

  test('effects apply in the correct order', function() {
    var target = document.createElement('div');
    document.documentElement.appendChild(target);
    function createLeaf(value, duration) {
      return new Animation(target, [{marginLeft: value}, {marginLeft: value}], duration);
    }
    tick(0);
    var player = document.timeline.play(complexAnimationTree(createLeaf));
    player.currentTime = 0;
    assert.equal(getComputedStyle(target).marginLeft, '0px');
    player.currentTime = 1;
    assert.equal(getComputedStyle(target).marginLeft, '1px');
    player.currentTime = 2;
    assert.equal(getComputedStyle(target).marginLeft, '2px');
    player.currentTime = 3;
    assert.equal(getComputedStyle(target).marginLeft, '3px');
    player.currentTime = 4;
    assert.equal(getComputedStyle(target).marginLeft, '4px');
    player.currentTime = 5;
    assert.equal(getComputedStyle(target).marginLeft, '0px');
    target.remove();
  });
});
<|endoftext|>


<commit_before>// Copyright (c) 2012, Joyent, Inc. All rights reserved.
var assert = require('assert-plus');
var EventEmitter = require('events').EventEmitter;
var HeartbeatServer = require('./heartbeatServer');
var HeartbeatClient = require('./heartbeatClient');
var path = require('path');
var PostgresMgr = require('./postgresMgr');
var util = require('util');
var uuid = require('node-uuid');
var vasync = require('vasync');
var verror = require('verror');
var zkplus = require('zkplus');

/**
 * Represents a Postgres shard. We want to be as aggressive as possible on
 * pruning errors, which means the shard will emit error events which should
 * indicate to the consumer to restart the shard.
 *
 * Note the Shard assumes that both path, and registrarPath znodes have been
 * created.
 * @constructor
 *
 * @param {string} path The path under which znodes for this shard are stored.
 * Note, it's assumed this path already exists at shard creation time.
 * @param {string} registrarPath The path where registrar info is stored.
 * @param {object} zkCfg The ZK configs.
 * @param {string} url The url of the current node.
 * @param {object} log The bunyan log object.
 */
function Shard(options) {
        assert.object(options, 'options');
        assert.object(options.log, 'options.log');
        assert.string(options.shardPath, 'options.shardPath');
        assert.object(options.zkCfg, 'options.zkCfg');
        assert.object(options.postgresMgrCfg, 'options.postgresMgrCfg');
        assert.object(options.heartbeaterCfg, 'options.heartbeaterCfg');
        assert.number(options.backupPort, 'options.backupPort');
        assert.number(options.postgresPort, 'options.postgresPort');
        assert.string(options.url, 'options.url');
        assert.number(options.heartbeatPort, 'options.heartbeatPort');
        assert.number(options.heartbeatInterval, 'options.heartbeatInterval');
        assert.number(options.ttl, 'options.ttl');

        EventEmitter.call(this);
        var self = this;
        this._log = options.log;

        /**
        * The path under which znodes for this shard are stored
        */
        this._shardPath = options.shardPath;

        /**
         * The registrar path, since the shardPath is usually
         * /prefix/com.joyent.foo.bar.baz, we strip out the first /prefix
         * replace . with / and append /pg
         */
        this._registrarPathPrefix =
                '/' +
                (self._shardPath.substring(self._shardPath.indexOf('/', 1) + 1))
                .split('.').reverse().join('/');

        this._registrarPath = this._registrarPathPrefix + '/pg';

        this._ttl = options.ttl;

        /**
        * The zk cfg
        */
        this._zkCfg = options.zkCfg;

        /**
        * The url of this peer
        */
        this._url = options.url;

        /**
        * This object's uuid
        */
        this._uuid = uuid();

        /**
        * The postgres manager
        */
        this._postgresMgr = new PostgresMgr(options.postgresMgrCfg);
        this._postgresPort = options.postgresPort;

        this._heartbeatServer = new HeartbeatServer({
                log: self._log,
                port: options.heartbeatPort
        });

        this._heartbeatPort = options.heartbeatPort;

        this._heartbeatClient = null;

        this._backupPort = options.backupPort;

        this._heartbeatInterval = options.heartbeatInterval;

        this._isLeader = null;

        this._currStandby = null;

        this._transitionQueue = new TransitionQueue({
                log: self._log
        });

        /**
        * The ZK session
        */
        this._zk = zkplus.createClient(self._zkCfg);
        this._election = null;

        self._zk.on('connect', function() {
                _cleanup(self, function(err) {
                        if (err) {
                                self._log.error({err: err},
                                'unable to clean up previous emphemeral nodes');
                                self.emit('error', err);
                        }
                        self._election = zkplus.createGenericElection({
                                client: self._zk,
                                path: self._shardPath,
                                pathPrefix: self._url,
                                object: {}
                        });
                        _init(self);
                });
        });

        self._zk.on('session_expired', function() {
                self._log.error('zk session expired');
                self.emit('error', 'zk session expired');
        });
}

module.exports = Shard;
util.inherits(Shard, EventEmitter);

/**
 * Transition functions
 */

function _leader(ctx, cb) {
        var self = ctx.self;
        var postgresMgr = self._postgresMgr;
        var log = self._log;
        log.info({
                currStandby: self._currStandby
        },'going to primary mode');

        if (self._heartbeatClient) {
                log.info('stopping heartbeat');
                self._heartbeatClient.stop();
        }

        postgresMgr.primary(self._currStandby, function(err) {
                if (err) {
                        log.error({
                                err: err
                        }, 'unable to transition to primary');
                        throw new verror.VError(err);
                }

                _writeRegistrar(self, function(err2) {
                        if (err2) {
                                log.error({
                                        err: err2
                                }, 'unable to write primary registrar');
                                throw new verror.VError(err2);
                        }
                        self._isLeader = true;
                        return cb();
                });
        });
}

function _newLeader(ctx, cb) {
        var self = ctx.self;
        var log = self._log;
        var myLeader = ctx.leader;
        var pgUrl = _transformPgUrl(self, myLeader);
        var backupUrl = _transformBackupUrl(self, myLeader);
        var heartbeatUrl = _transformHeartbeatUrl(self, myLeader);
        var postgresMgr = self._postgresMgr;
        log.info({
                leader: myLeader,
                primaryUrl: pgUrl,
                backupUrl: backupUrl,
                heartbeatUrl: heartbeatUrl
        }, 'going to standby mode');

        postgresMgr.standby(pgUrl, backupUrl, function(err) {
                if (err) {
                        log.error({
                                err: err,
                                pgUrl: pgUrl,
                                backupUrl: backupUrl
                        }, 'unable to transition to standby');
                        throw new verror.VError(err);
                }

                if (self._heartbeatClient) {
                        log.info('stopping heartbeat');
                        self._heartbeatClient.stop();
                }

                // start heartbeating
                self._heartbeatClient = new HeartbeatClient({
                        log: log,
                        primaryUrl: heartbeatUrl,
                        url: self._url,
                        heartbeatInterval: self._heartbeatInterval
                });

                log.info('starting heartbeat');
                self._heartbeatClient.start(function() {
                        cb();
                });
        });
}

function _newStandby(ctx, cb) {
        var self = ctx.self;
        var log = self._log;
        var postgresMgr = self._postgresMgr;
        var standby = ctx.standby;
        var standbyUrl = _transformPgUrl(self, standby);

        if (standbyUrl === self._currStandby) {
                log.info({
                        prevStandby: self._currStandby,
                        standbyUrl: standbyUrl
                }, 'standby not changed');

                return cb();
        }

        self._currStandby = standbyUrl;
        if (!self._election.amLeader) {
                log.info({
                        currStandby: self._currStandby
                }, 'not primary, ignoring standby heartbeats,' +
                ' but updating currStandby');

                return cb();
        }

        log.info({
                standby: standbyUrl
        }, 'going to primary mode with sync standby');

        postgresMgr.updateStandby(standbyUrl, function(err) {
                if (err) {
                        log.error({
                                err: err,
                                standby: standbyUrl
                        }, 'unable to transition to primary');
                        throw new verror.VError(err);
                }

                log.info({
                        standby: standbyUrl
                }, 'finished primary transition');
                return cb();
        });

        return true;
}

function _expired(ctx, cb) {
        var self = ctx.self;
        var postgresMgr = self._postgresMgr;
        var standby = ctx.standby;
        var log = self._log;
        var standbyUrl = _transformPgUrl(self, standby);

        if (standbyUrl !== self._currStandby) {
                log.info({
                        currStandby: self._currStandby,
                        expiredStandby: standbyUrl
                }, 'expired standby does not match currStandby,' +
                ' ignoring');
                return cb();
        }

        self._currStandby = null;

        // only remove standby if currently leader. If not leader,
        // the async standby doesn't require any action.
        if (self._election.amLeader) {
                log.info({
                        standby: standbyUrl
                }, 'standby expired, going to primary standalone mode');
                postgresMgr.updateStandby(null, function(err) {
                        if (err) {
                                log.error({
                                        err: err
                                }, 'unable to transition to primary');
                                throw new verror.VError(err);
                        }
                        return cb();
                });
        } else {
                log.info({
                        standby: standbyUrl
                }, 'async standby expired, I am not primary, ignoring');
                return cb();
        }
}

/**
 * Private Functions
 */

function _cleanup(self, callback) {
        var log = self._log;
        var zk = self._zk;

        log.info({
                shardPath: self._shardPath,
                url: self._url
        },'entering cleanup');
        var tasks = [
                function mkdirp(_, cb) {
                        zk.mkdirp(self._shardPath, function(err) {
                                log.debug({
                                        path: self._shardPath,
                                        err: err
                                }, 'returned from mkdirp');
                                cb(err);
                        });
                },
                function readdir(_, cb) {
                        log.debug({
                                shardPath: self._shardPath
                        }, 'listing election dir');
                        zk.readdir(self._shardPath, function(err, nodes) {
                                log.debug({
                                        err: err,
                                        nodes: nodes
                                }, 'returned from readdir');
                                if (err) {
                                        return cb(err);
                                } else {
                                        _.nodes = nodes;
                                        return cb();
                                }
                        });
                },
                function determineNodes(_, cb) {
                        log.debug({
                                nodes: _.nodes
                        }, 'entering determine nodes');
                        var nodes = [];
                        for (var i = 0; i < _.nodes.length; i++) {
                                var node = _.nodes[i];
                                if (node.split('-')[0] === self._url) {
                                        nodes.push(node);
                                }
                        }

                        _.nodes = nodes;
                        cb();
                },
                function delPrevNodes(_, cb) {
                        log.debug({
                                nodes: _.nodes
                        },'entering delPrevNodes');
                        var done = 0;
                        for (var i = 0; i < _.nodes.length; i++) {
                                var spath = self._shardPath + '/' + _.nodes[i];
                                log.debug({
                                        node: spath
                                }, 'deleting prev node');
                                zk.rmr(spath, function(err) {
                                        if (err) {
                                                return cb(err);
                                        } else {
                                                done++;
                                                var nLength = _.nodes.length;
                                                if (done === (nLength - 1)) {
                                                        return cb();
                                                }
                                        }
                                        return true;
                                });

                        }

                        return cb();
                }
        ];

        vasync.pipeline({funcs: tasks, arg: {}}, function(err) {
                if (err) {
                        log.error('Unable to delete previous emphemeral nodes');
                        return callback(err);
                } else {
                        log.info('finished deleting previous emphemeral nodes');
                        return callback();
                }
        });
}

function _init(self) {
        var log = self._log;
        log.info('initializing shard');
        var queue = self._transitionQueue;

        self._election.on('leader', function() {
                log.info('leader event');
                var req = {
                        invoke: _leader,
                        ctx: {
                                self: self
                        },
                        transition: 'leader'
                };

                queue.push(req);
        });

        self._election.on('newLeader', function(myLeader) {
                log.info({
                        newLeader: myLeader
                }, 'newLeader event');
                var req = {
                        invoke: _newLeader,
                        ctx: {
                                self: self,
                                leader: myLeader
                        },
                        transition: 'newLeader'
                };

                queue.push(req);
        });

        self._heartbeatServer.on('newStandby', function(standby) {
                log.info({
                        standby: standby
                }, 'newStandby event');
                var req = {
                        invoke: _newStandby,
                        ctx: {
                                self: self,
                                standby: standby
                        },
                        transition: 'newStandby'
                };

                queue.push(req);
        });

        self._heartbeatServer.on('expired', function(standby) {
                log.info({
                        standby: standby
                }, 'expired event');
                var req = {
                        invoke: _expired,
                        ctx: {
                                self: self,
                                standby: standby
                        },
                        transition: 'expired'
                };

                queue.push(req);
        });

        self._heartbeatServer.init();
        self._election.vote(function(err) {
                if (err) {
                        throw new verror.VError(err);
                }

                log.info('finished vote');
        });
}

function _transformPgUrl(self, url) {
        return 'tcp://postgres@' + url.split('-')[0] + ':' +
                self._postgresPort + '/postgres';
}

function _transformBackupUrl(self, url) {
        return  'http://' + url.split('-')[0] + ':' + self._backupPort;
}

function _transformHeartbeatUrl(self, url) {
        return  'http://' + url.split('-')[0]+ ':' + self._heartbeatPort;
}

function _writeRegistrar(self, callback) {
        var log = self._log;
        var zk = self._zk;

        var opts = {
                flags:  ['ephemeral'],
                object: {
                        type: 'database',
                        database: {
                                primary: _transformPgUrl(self, self._url),
                                ttl: self._ttl
                        }
                }
        };

        log.info({
                registrarPath: self._registrarPath,
                url: _transformPgUrl(self, self._url),
                opts: opts
        }, 'entering write registrar');

        var tasks = [
                function mkdirp(_, cb) {
                        zk.mkdirp(self._registrarPathPrefix, function(err) {
                                var err2;
                                if (err)  {
                                        err2 = verror.VError(err);
                                }
                                cb(err2);
                        });
                },
                function rm(_, cb) {
                        zk.rmr(self._registrarPath, function() {
                                // don't check for err on rmr, it could fail if
                                // the node DNE
                                cb();
                        });
                },
                function create(_, cb) {
                        zk.create(self._registrarPath, opts, function(err) {
                                var err2;
                                if (err)  {
                                        err2 = verror.VError(err);
                                }
                                cb(err2);
                        });
                }
        ];

        vasync.pipeline({funcs: tasks, arg: {}}, function(err) {
                if (err) {
                        log.error('Unable to write registrar');
                        return callback(err);
                } else {
                        log.info('finished writing registrar');
                        return callback();
                }
        });

        return true;
}

/**
 * Queue used to serialize state transitions
 */
function TransitionQueue(options) {
        var self = this;
        this._queue = [];
        this._log = options.log;
        this._emitter = new EventEmitter();

        var log = self._log;
        var isPop = false;
        self._emitter.on('push', function() {
                if (isPop) {
                        log.info({
                                queue_length: self._queue.length
                        }, 'waiting for current transition to finish before ' +
                        'executing pushed req');
                        return;
                }

                exec();
        });

        function exec() {
                isPop = true;
                var req = self._queue.shift();
                req.invoke(req.ctx, function() {
                        log.info({
                                transition: req.transition
                        }, 'finished transition');
                        if (self._queue.length > 0) {
                                log.info({
                                        queue_length: self._queue.length
                                }, 'more elements in the queue,' +
                                ' continuing exec');
                                exec();
                        } else {
                                log.info({
                                        queue_length: self._queue.length
                                }, 'no more elements in queue');
                                isPop = false;
                        }
                });
        }
}

TransitionQueue.prototype.push = function push(req) {
        var self = this;
        var log = self._log;
        log.info({
                req: req.transition
        }, 'pushing request');
        self._queue.push(req);
        self._emitter.emit('push');
};

<commit_msg>MANTA-375 add docs<commit_after>// Copyright (c) 2012, Joyent, Inc. All rights reserved.
var assert = require('assert-plus');
var EventEmitter = require('events').EventEmitter;
var HeartbeatServer = require('./heartbeatServer');
var HeartbeatClient = require('./heartbeatClient');
var path = require('path');
var PostgresMgr = require('./postgresMgr');
var util = require('util');
var uuid = require('node-uuid');
var vasync = require('vasync');
var verror = require('verror');
var zkplus = require('zkplus');

/**
 * Represents a Postgres node in a shard. We want to be as aggressive as
 * possible on pruning errors, which means the shard will emit error events
 * which should indicate to the consumer to restart the shard.
 *
 * @constructor
 *
 * @param {string} shardPath The path under which znodes for this shard are
 * stored.
 * @param {object} zkCfg The ZK configs.
 * @param {object} postgresMgrCfg The PG configs.
 * @param {object} heartbeaterCfg The heartbeater configs.
 * @param {integer} postgresPort
 * @param {integer} backupPort The port used by the backup service.
 * @param {string} url The url of the current node.
 * @param {integer} heartbeatInterval The interval of the heartbeater.
 * @param {integer} ttl The ttl of the registrar node.
 * @param {object} log The bunyan log object.
 */
function Shard(options) {
        assert.object(options, 'options');
        assert.object(options.log, 'options.log');
        assert.string(options.shardPath, 'options.shardPath');
        assert.object(options.zkCfg, 'options.zkCfg');
        assert.object(options.postgresMgrCfg, 'options.postgresMgrCfg');
        assert.object(options.heartbeaterCfg, 'options.heartbeaterCfg');
        assert.number(options.backupPort, 'options.backupPort');
        assert.number(options.postgresPort, 'options.postgresPort');
        assert.string(options.url, 'options.url');
        assert.number(options.heartbeatPort, 'options.heartbeatPort');
        assert.number(options.heartbeatInterval, 'options.heartbeatInterval');
        assert.number(options.ttl, 'options.ttl');

        EventEmitter.call(this);
        var self = this;
        this._log = options.log;

        /**
        * The path under which znodes for this shard are stored
        */
        this._shardPath = options.shardPath;

        /**
         * The registrar path, since the shardPath is usually
         * /prefix/com.joyent.foo.bar.baz, we strip out the first /prefix
         * replace . with / and append /pg
         */
        this._registrarPathPrefix =
                '/' +
                (self._shardPath.substring(self._shardPath.indexOf('/', 1) + 1))
                .split('.').reverse().join('/');

        this._registrarPath = this._registrarPathPrefix + '/pg';

        this._ttl = options.ttl;

        /**
        * The zk cfg
        */
        this._zkCfg = options.zkCfg;

        /**
        * The url of this peer
        */
        this._url = options.url;

        /**
        * This object's uuid
        */
        this._uuid = uuid();

        /**
        * The postgres manager
        */
        this._postgresMgr = new PostgresMgr(options.postgresMgrCfg);
        this._postgresPort = options.postgresPort;

        this._heartbeatServer = new HeartbeatServer({
                log: self._log,
                port: options.heartbeatPort
        });

        this._heartbeatPort = options.heartbeatPort;

        this._heartbeatClient = null;

        this._backupPort = options.backupPort;

        this._heartbeatInterval = options.heartbeatInterval;

        this._currStandby = null;

        /**
         * The queue used to manage transition events such that they occur
         * serially and automically.
         */
        this._transitionQueue = new TransitionQueue({
                log: self._log
        });

        /**
        * The ZK session
        */
        this._zk = zkplus.createClient(self._zkCfg);
        this._election = null;

        self._zk.on('connect', function() {
                _cleanup(self, function(err) {
                        if (err) {
                                self._log.error({err: err},
                                'unable to clean up previous emphemeral nodes');
                                self.emit('error', err);
                        }
                        self._election = zkplus.createGenericElection({
                                client: self._zk,
                                path: self._shardPath,
                                pathPrefix: self._url,
                                object: {}
                        });
                        _init(self);
                });
        });

        self._zk.on('session_expired', function() {
                self._log.error('zk session expired');
                self.emit('error', 'zk session expired');
        });
}

module.exports = Shard;
util.inherits(Shard, EventEmitter);

/**
 * Transition functions
 */

function _leader(ctx, cb) {
        var self = ctx.self;
        var postgresMgr = self._postgresMgr;
        var log = self._log;
        log.info({
                currStandby: self._currStandby
        },'going to primary mode');

        if (self._heartbeatClient) {
                log.info('stopping heartbeat');
                self._heartbeatClient.stop();
        }

        postgresMgr.primary(self._currStandby, function(err) {
                if (err) {
                        log.error({
                                err: err
                        }, 'unable to transition to primary');
                        throw new verror.VError(err);
                }

                _writeRegistrar(self, function(err2) {
                        if (err2) {
                                log.error({
                                        err: err2
                                }, 'unable to write primary registrar');
                                throw new verror.VError(err2);
                        }
                        return cb();
                });
        });
}

function _newLeader(ctx, cb) {
        var self = ctx.self;
        var log = self._log;
        var myLeader = ctx.leader;
        var pgUrl = _transformPgUrl(self, myLeader);
        var backupUrl = _transformBackupUrl(self, myLeader);
        var heartbeatUrl = _transformHeartbeatUrl(self, myLeader);
        var postgresMgr = self._postgresMgr;
        log.info({
                leader: myLeader,
                primaryUrl: pgUrl,
                backupUrl: backupUrl,
                heartbeatUrl: heartbeatUrl
        }, 'going to standby mode');

        postgresMgr.standby(pgUrl, backupUrl, function(err) {
                if (err) {
                        log.error({
                                err: err,
                                pgUrl: pgUrl,
                                backupUrl: backupUrl
                        }, 'unable to transition to standby');
                        throw new verror.VError(err);
                }

                if (self._heartbeatClient) {
                        log.info('stopping heartbeat');
                        self._heartbeatClient.stop();
                }

                // start heartbeating
                self._heartbeatClient = new HeartbeatClient({
                        log: log,
                        primaryUrl: heartbeatUrl,
                        url: self._url,
                        heartbeatInterval: self._heartbeatInterval
                });

                log.info('starting heartbeat');
                self._heartbeatClient.start(function() {
                        cb();
                });
        });
}

function _newStandby(ctx, cb) {
        var self = ctx.self;
        var log = self._log;
        var postgresMgr = self._postgresMgr;
        var standby = ctx.standby;
        var standbyUrl = _transformPgUrl(self, standby);

        if (standbyUrl === self._currStandby) {
                log.info({
                        prevStandby: self._currStandby,
                        standbyUrl: standbyUrl
                }, 'standby not changed');

                return cb();
        }

        self._currStandby = standbyUrl;
        if (!self._election.amLeader) {
                log.info({
                        currStandby: self._currStandby
                }, 'not primary, ignoring standby heartbeats,' +
                ' but updating currStandby');

                return cb();
        }

        log.info({
                standby: standbyUrl
        }, 'going to primary mode with sync standby');

        postgresMgr.updateStandby(standbyUrl, function(err) {
                if (err) {
                        log.error({
                                err: err,
                                standby: standbyUrl
                        }, 'unable to transition to primary');
                        throw new verror.VError(err);
                }

                log.info({
                        standby: standbyUrl
                }, 'finished primary transition');
                return cb();
        });

        return true;
}

function _expired(ctx, cb) {
        var self = ctx.self;
        var postgresMgr = self._postgresMgr;
        var standby = ctx.standby;
        var log = self._log;
        var standbyUrl = _transformPgUrl(self, standby);

        if (standbyUrl !== self._currStandby) {
                log.info({
                        currStandby: self._currStandby,
                        expiredStandby: standbyUrl
                }, 'expired standby does not match currStandby,' +
                ' ignoring');
                return cb();
        }

        self._currStandby = null;

        // only remove standby if currently leader. If not leader,
        // the async standby doesn't require any action.
        if (self._election.amLeader) {
                log.info({
                        standby: standbyUrl
                }, 'standby expired, going to primary standalone mode');
                postgresMgr.updateStandby(null, function(err) {
                        if (err) {
                                log.error({
                                        err: err
                                }, 'unable to transition to primary');
                                throw new verror.VError(err);
                        }
                        return cb();
                });
        } else {
                log.info({
                        standby: standbyUrl
                }, 'async standby expired, I am not primary, ignoring');
                return cb();
        }
}

/**
 * Private Functions
 */

/**
 * Delete any previous emphemeral zk-nodes belonging to this pg node.
 */
function _cleanup(self, callback) {
        var log = self._log;
        var zk = self._zk;

        log.info({
                shardPath: self._shardPath,
                url: self._url
        },'entering cleanup');
        var tasks = [
                function mkdirp(_, cb) {
                        zk.mkdirp(self._shardPath, function(err) {
                                log.debug({
                                        path: self._shardPath,
                                        err: err
                                }, 'returned from mkdirp');
                                cb(err);
                        });
                },
                function readdir(_, cb) {
                        log.debug({
                                shardPath: self._shardPath
                        }, 'listing election dir');
                        zk.readdir(self._shardPath, function(err, nodes) {
                                log.debug({
                                        err: err,
                                        nodes: nodes
                                }, 'returned from readdir');
                                if (err) {
                                        return cb(err);
                                } else {
                                        _.nodes = nodes;
                                        return cb();
                                }
                        });
                },
                function determineNodes(_, cb) {
                        log.debug({
                                nodes: _.nodes
                        }, 'entering determine nodes');
                        var nodes = [];
                        for (var i = 0; i < _.nodes.length; i++) {
                                var node = _.nodes[i];
                                if (node.split('-')[0] === self._url) {
                                        nodes.push(node);
                                }
                        }

                        _.nodes = nodes;
                        cb();
                },
                function delPrevNodes(_, cb) {
                        log.debug({
                                nodes: _.nodes
                        },'entering delPrevNodes');
                        var done = 0;
                        for (var i = 0; i < _.nodes.length; i++) {
                                var spath = self._shardPath + '/' + _.nodes[i];
                                log.debug({
                                        node: spath
                                }, 'deleting prev node');
                                zk.rmr(spath, function(err) {
                                        if (err) {
                                                return cb(err);
                                        } else {
                                                done++;
                                                var nLength = _.nodes.length;
                                                if (done === (nLength - 1)) {
                                                        return cb();
                                                }
                                        }
                                        return true;
                                });

                        }

                        return cb();
                }
        ];

        vasync.pipeline({funcs: tasks, arg: {}}, function(err) {
                if (err) {
                        log.error('Unable to delete previous emphemeral nodes');
                        return callback(err);
                } else {
                        log.info('finished deleting previous emphemeral nodes');
                        return callback();
                }
        });
}

/**
 * initialize this node
 */
function _init(self) {
        var log = self._log;
        log.info('initializing shard');
        var queue = self._transitionQueue;

        self._election.on('leader', function() {
                log.info('leader event');
                var req = {
                        invoke: _leader,
                        ctx: {
                                self: self
                        },
                        transition: 'leader'
                };

                queue.push(req);
        });

        self._election.on('newLeader', function(myLeader) {
                log.info({
                        newLeader: myLeader
                }, 'newLeader event');
                var req = {
                        invoke: _newLeader,
                        ctx: {
                                self: self,
                                leader: myLeader
                        },
                        transition: 'newLeader'
                };

                queue.push(req);
        });

        self._heartbeatServer.on('newStandby', function(standby) {
                log.info({
                        standby: standby
                }, 'newStandby event');
                var req = {
                        invoke: _newStandby,
                        ctx: {
                                self: self,
                                standby: standby
                        },
                        transition: 'newStandby'
                };

                queue.push(req);
        });

        self._heartbeatServer.on('expired', function(standby) {
                log.info({
                        standby: standby
                }, 'expired event');
                var req = {
                        invoke: _expired,
                        ctx: {
                                self: self,
                                standby: standby
                        },
                        transition: 'expired'
                };

                queue.push(req);
        });

        self._heartbeatServer.init();
        self._election.vote(function(err) {
                if (err) {
                        throw new verror.VError(err);
                }

                log.info('finished vote');
        });
}

function _transformPgUrl(self, url) {
        return 'tcp://postgres@' + url.split('-')[0] + ':' +
                self._postgresPort + '/postgres';
}

function _transformBackupUrl(self, url) {
        return  'http://' + url.split('-')[0] + ':' + self._backupPort;
}

function _transformHeartbeatUrl(self, url) {
        return  'http://' + url.split('-')[0]+ ':' + self._heartbeatPort;
}

function _writeRegistrar(self, callback) {
        var log = self._log;
        var zk = self._zk;

        var opts = {
                flags:  ['ephemeral'],
                object: {
                        type: 'database',
                        database: {
                                primary: _transformPgUrl(self, self._url),
                                ttl: self._ttl
                        }
                }
        };

        log.info({
                registrarPath: self._registrarPath,
                url: _transformPgUrl(self, self._url),
                opts: opts
        }, 'entering write registrar');

        var tasks = [
                function mkdirp(_, cb) {
                        zk.mkdirp(self._registrarPathPrefix, function(err) {
                                var err2;
                                if (err)  {
                                        err2 = verror.VError(err);
                                }
                                cb(err2);
                        });
                },
                function rm(_, cb) {
                        zk.rmr(self._registrarPath, function() {
                                // don't check for err on rmr, it could fail if
                                // the node DNE
                                cb();
                        });
                },
                function create(_, cb) {
                        zk.create(self._registrarPath, opts, function(err) {
                                var err2;
                                if (err)  {
                                        err2 = verror.VError(err);
                                }
                                cb(err2);
                        });
                }
        ];

        vasync.pipeline({funcs: tasks, arg: {}}, function(err) {
                if (err) {
                        log.error('Unable to write registrar');
                        return callback(err);
                } else {
                        log.info('finished writing registrar');
                        return callback();
                }
        });

        return true;
}

/**
 * Queue used to serialize state transitions
 */
function TransitionQueue(options) {
        var self = this;
        this._queue = [];
        this._log = options.log;
        this._emitter = new EventEmitter();

        var log = self._log;
        var isPop = false;
        self._emitter.on('push', function() {
                if (isPop) {
                        log.info({
                                queue_length: self._queue.length
                        }, 'waiting for current transition to finish before ' +
                        'executing pushed req');
                        return;
                }

                exec();
        });

        function exec() {
                isPop = true;
                var req = self._queue.shift();
                req.invoke(req.ctx, function() {
                        log.info({
                                transition: req.transition
                        }, 'finished transition');
                        if (self._queue.length > 0) {
                                log.info({
                                        queue_length: self._queue.length
                                }, 'more elements in the queue,' +
                                ' continuing exec');
                                exec();
                        } else {
                                log.info({
                                        queue_length: self._queue.length
                                }, 'no more elements in queue');
                                isPop = false;
                        }
                });
        }
}

TransitionQueue.prototype.push = function push(req) {
        var self = this;
        var log = self._log;
        log.info({
                req: req.transition
        }, 'pushing request');
        self._queue.push(req);
        self._emitter.emit('push');
};

<|endoftext|>


<commit_before>/* eslint-env es6 */
/* eslint no-global-assign: 0 */
"use strict";

const mock = require('mock-require');
const { requireUncached } = require('./TestTools');
const consoleOriginal = global.console;

const intrinsicProps = [
  // Common
  'id', 'label',

  // Constraint
  'angularStiffness', 'bodyA', 'bodyB', 'damping', 'length', 'stiffness',

  // Body
  'area', 'axes', 'collisionFilter', 'category', 'mask',
  'group', 'density', 'friction', 'frictionAir', 'frictionStatic', 'inertia', 'inverseInertia', 'inverseMass', 'isSensor',
  'isSleeping', 'isStatic', 'mass', 'parent', 'parts', 'restitution', 'sleepThreshold', 'slop',
  'timeScale', 'vertices',

  // Composite
  'bodies', 'constraints', 'composites'
];

const prepareMatter = (options) => {
  const Matter = requireUncached(options.useDev ? '../build/matter.dev' : '../build/matter');

  if (Matter.Common._nextId !== 0) {
    throw 'Matter instance has already been used.';
  }

  const noop = () => ({ collisionFilter: {}, mouse: {} });

  Matter.Render.create = () => ({ options: {}, bounds: { min: { x: 0, y: 0 }, max: { x: 800, y: 600 }}});
  Matter.Render.run = Matter.Render.lookAt = noop;
  Matter.Runner.create = Matter.Runner.run = noop;
  Matter.MouseConstraint.create = Matter.Mouse.create = noop;
  Matter.Common.info = Matter.Common.warn = Matter.Common.log;

  return Matter;
};

const prepareEnvironment = Matter => {
  mock('matter-js', Matter);
  global.Matter = Matter;

  const logs = [];
  global.document = global.window = { addEventListener: () => {} };
  global.console = {
    log: (...args) => {
      logs.push(args.join(' '));
    }
  };

  return logs;
};

const resetEnvironment = () => {
  global.console = consoleOriginal;
  global.window = undefined;
  global.document = undefined;
  global.Matter = undefined;
  mock.stopAll();
};

const limitPrecision = (val, precision=3) => parseFloat(val.toPrecision(precision));

const sortById = (objs) => {
  objs.sort((objA, objB) => objA.id - objB.id);
  return objs;
};

const engineCapture = (engine, Matter) => ({
  timestamp: limitPrecision(engine.timing.timestamp),
  extrinsic: worldCaptureExtrinsic(engine.world, Matter),
  intrinsic: worldCaptureIntrinsic(engine.world, Matter)
});

const worldCaptureExtrinsic = (world, Matter) => ({
  bodies: sortById(Matter.Composite.allBodies(world)).reduce((bodies, body) => {
      bodies[body.id] = [
          body.position.x,
          body.position.y,
          body.positionPrev.x,
          body.positionPrev.y,
          body.angle,
          body.anglePrev,
          ...body.vertices.reduce((flat, vertex) => (flat.push(vertex.x, vertex.y), flat), [])
      ];

      return bodies;
  }, {}),
  constraints: sortById(Matter.Composite.allConstraints(world)).reduce((constraints, constraint) => {
      const positionA = Matter.Constraint.pointAWorld(constraint);
      const positionB = Matter.Constraint.pointBWorld(constraint);

      constraints[constraint.id] = [
          positionA.x,
          positionA.y,
          positionB.x,
          positionB.y
      ];

      return constraints;
  }, {})
});

const worldCaptureIntrinsic = (world, Matter) => worldCaptureIntrinsicBase({
  bodies: sortById(Matter.Composite.allBodies(world)).reduce((bodies, body) => {
      bodies[body.id] = body;
      return bodies;
  }, {}),
  constraints: sortById(Matter.Composite.allConstraints(world)).reduce((constraints, constraint) => {
      constraints[constraint.id] = constraint;
      return constraints;
  }, {}),
  composites: sortById(Matter.Composite.allComposites(world)).reduce((composites, composite) => {
      composites[composite.id] = {
          bodies: sortById(Matter.Composite.allBodies(composite)).map(body => body.id),
          constraints: sortById(Matter.Composite.allConstraints(composite)).map(constraint => constraint.id),
          composites: sortById(Matter.Composite.allComposites(composite)).map(composite => composite.id)
      };
      return composites;
  }, {})
});

const worldCaptureIntrinsicBase = (obj, depth=0) => {
  if (obj === Infinity) {
      return 'Infinity';
  } else if (typeof obj === 'number') {
      return limitPrecision(obj);
  } else if (Array.isArray(obj)) {
      return obj.map(item => worldCaptureIntrinsicBase(item, depth + 1));
  } else if (typeof obj !== 'object') {
      return obj;
  }

  const result = Object.entries(obj)
      .filter(([key]) => depth <= 1 || intrinsicProps.includes(key))
      .reduce((cleaned, [key, val]) => {
          if (val && val.id && String(val.id) !== key) {
              val = val.id;
          }

          if (Array.isArray(val) && !['composites', 'constraints', 'bodies'].includes(key)) {
              val = `[${val.length}]`;
          }

          cleaned[key] = worldCaptureIntrinsicBase(val, depth + 1);
          return cleaned;
      }, {});

  return Object.keys(result).sort()
      .reduce((sorted, key) => (sorted[key] = result[key], sorted), {});
};

const runExample = options => {
  const Matter = prepareMatter(options);
  const logs = prepareEnvironment(Matter);

  const Examples = requireUncached('../examples/index');
  const example = Examples[options.name]();
  const engine = example.engine;

  let totalMemory = 0;
  let totalDuration = 0;
  let overlapTotal = 0;
  let overlapCount = 0;

  const bodies = Matter.Composite.allBodies(engine.world);

  if (options.jitter) {
    for (let i = 0; i < bodies.length; i += 1) {
      const body = bodies[i];

      Matter.Body.applyForce(body, body.position, {
        x: Math.cos(i * i) * options.jitter * body.mass,
        y: Math.sin(i * i) * options.jitter * body.mass
      });
    }
  }

  global.gc();

  for (let i = 0; i < options.totalUpdates; i += 1) {
      const startTime = process.hrtime();
      totalMemory += process.memoryUsage().heapUsed;

      Matter.Engine.update(engine, 1000 / 60);

      const duration = process.hrtime(startTime);
      totalDuration += duration[0] * 1e9 + duration[1];
      totalMemory += process.memoryUsage().heapUsed;

      const pairsList = engine.pairs.list;
      const pairsListLength = engine.pairs.list.length;

      for (let p = 0; p < pairsListLength; p += 1) {
        const pair = pairsList[p];
        const separation = pair.separation - pair.slop;

        if (pair.isActive && !pair.isSensor) {
          overlapTotal += separation > 0 ? separation : 0;
          overlapCount += 1;
        }
      }
  }

  resetEnvironment();

  return {
    name: options.name,
    duration: totalDuration,
    overlap: overlapTotal / (overlapCount || 1),
    memory: totalMemory,
    logs,
    ...engineCapture(engine, Matter)
  };
};

module.exports = { runExample };<commit_msg>added stable sorting to test worker and refactor<commit_after>/* eslint-env es6 */
/* eslint no-global-assign: 0 */
"use strict";

const mock = require('mock-require');
const { requireUncached } = require('./TestTools');
const consoleOriginal = global.console;

const runExample = options => {
  const Matter = prepareMatter(options);
  const logs = prepareEnvironment(Matter);

  const Examples = requireUncached('../examples/index');
  const example = Examples[options.name]();
  const engine = example.engine;

  let totalMemory = 0;
  let totalDuration = 0;
  let overlapTotal = 0;
  let overlapCount = 0;

  global.gc();

  for (let i = 0; i < options.updates; i += 1) {
      const startTime = process.hrtime();
      totalMemory += process.memoryUsage().heapUsed;

      Matter.Engine.update(engine, 1000 / 60);

      const duration = process.hrtime(startTime);
      totalDuration += duration[0] * 1e9 + duration[1];
      totalMemory += process.memoryUsage().heapUsed;

      const pairsList = engine.pairs.list;
      const pairsListLength = engine.pairs.list.length;

      for (let p = 0; p < pairsListLength; p += 1) {
        const pair = pairsList[p];
        const separation = pair.separation - pair.slop;

        if (pair.isActive && !pair.isSensor) {
          overlapTotal += separation > 0 ? separation : 0;
          overlapCount += 1;
        }
      }
  }

  resetEnvironment();

  return {
    name: options.name,
    duration: totalDuration,
    overlap: overlapTotal / (overlapCount || 1),
    memory: totalMemory,
    logs: logs,
    extrinsic: captureExtrinsics(engine, Matter),
    intrinsic: captureIntrinsics(engine, Matter),
  };
};

const prepareMatter = (options) => {
  const Matter = requireUncached(options.useDev ? '../build/matter.dev' : '../build/matter');

  if (Matter.Common._nextId !== 0) {
    throw 'Matter instance has already been used.';
  }

  const noop = () => ({ collisionFilter: {}, mouse: {} });

  Matter.Render.create = () => ({ options: {}, bounds: { min: { x: 0, y: 0 }, max: { x: 800, y: 600 }}});
  Matter.Render.run = Matter.Render.lookAt = noop;
  Matter.Runner.create = Matter.Runner.run = noop;
  Matter.MouseConstraint.create = Matter.Mouse.create = noop;
  Matter.Common.info = Matter.Common.warn = Matter.Common.log;

  if (options.stableSort) {
    const MatterSATCollides = Matter.SAT.collides;
    Matter.SAT.collides = function(bodyA, bodyB, previousCollision, pairActive) {
      const _bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
      const _bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
      return MatterSATCollides(_bodyA, _bodyB, previousCollision, pairActive);
    };

    Matter.after('Detector.collisions', function() { this.sort(collisionCompareId); });
    Matter.after('Composite.allBodies', function() { sortById(this); });
    Matter.after('Composite.allConstraints', function() { sortById(this); });
    Matter.after('Composite.allComposites', function() { sortById(this); });

    Matter.before('Pairs.update', function(pairs) {
      pairs.list.sort((pairA, pairB) => collisionCompareId(pairA.collision, pairB.collision));
    });

    Matter.after('Pairs.update', function(pairs) {
      pairs.list.sort((pairA, pairB) => collisionCompareId(pairA.collision, pairB.collision));
    });
  }

  if (options.jitter) {
    Matter.after('Body.create', function() {
      Matter.Body.applyForce(this, this.position, {
        x: Math.cos(this.id * this.id) * options.jitter * this.mass,
        y: Math.sin(this.id * this.id) * options.jitter * this.mass
      });
    });
  }

  return Matter;
};

const prepareEnvironment = Matter => {
  mock('matter-js', Matter);
  global.Matter = Matter;

  const logs = [];
  global.document = global.window = { addEventListener: () => {} };
  global.console = {
    log: (...args) => {
      logs.push(args.join(' '));
    }
  };

  return logs;
};

const resetEnvironment = () => {
  global.console = consoleOriginal;
  global.window = undefined;
  global.document = undefined;
  global.Matter = undefined;
  mock.stopAll();
};

const captureExtrinsics = ({ world }, Matter) => ({
  bodies: Matter.Composite.allBodies(world).reduce((bodies, body) => {
      bodies[body.id] = [
          body.position.x,
          body.position.y,
          body.positionPrev.x,
          body.positionPrev.y,
          body.angle,
          body.anglePrev,
          ...body.vertices.reduce((flat, vertex) => (flat.push(vertex.x, vertex.y), flat), [])
      ];

      return bodies;
  }, {}),
  constraints: Matter.Composite.allConstraints(world).reduce((constraints, constraint) => {
      const positionA = Matter.Constraint.pointAWorld(constraint);
      const positionB = Matter.Constraint.pointBWorld(constraint);

      constraints[constraint.id] = [
          positionA.x,
          positionA.y,
          positionB.x,
          positionB.y
      ];

      return constraints;
  }, {})
});

const captureIntrinsics = ({ world }, Matter) => formatIntrinsics({
  bodies: Matter.Composite.allBodies(world).reduce((bodies, body) => {
      bodies[body.id] = body;
      return bodies;
  }, {}),
  constraints: Matter.Composite.allConstraints(world).reduce((constraints, constraint) => {
      constraints[constraint.id] = constraint;
      return constraints;
  }, {}),
  composites: Matter.Composite.allComposites(world).reduce((composites, composite) => {
      composites[composite.id] = {
          bodies: Matter.Composite.allBodies(composite).map(body => body.id),
          constraints: Matter.Composite.allConstraints(composite).map(constraint => constraint.id),
          composites: Matter.Composite.allComposites(composite).map(composite => composite.id)
      };
      return composites;
  }, {})
});

const formatIntrinsics = (obj, depth=0) => {
  if (obj === Infinity) {
      return 'Infinity';
  } else if (typeof obj === 'number') {
      return limitPrecision(obj);
  } else if (Array.isArray(obj)) {
      return obj.map(item => formatIntrinsics(item, depth + 1));
  } else if (typeof obj !== 'object') {
      return obj;
  }

  const result = Object.entries(obj)
      .filter(([key]) => depth <= 1 || intrinsicProperties.includes(key))
      .reduce((cleaned, [key, val]) => {
          if (val && val.id && String(val.id) !== key) {
              val = val.id;
          }

          if (Array.isArray(val) && !['composites', 'constraints', 'bodies'].includes(key)) {
              val = `[${val.length}]`;
          }

          cleaned[key] = formatIntrinsics(val, depth + 1);
          return cleaned;
      }, {});

  return Object.keys(result).sort()
      .reduce((sorted, key) => (sorted[key] = result[key], sorted), {});
};

const intrinsicProperties = [
  // Common
  'id', 'label',

  // Constraint
  'angularStiffness', 'bodyA', 'bodyB', 'damping', 'length', 'stiffness',

  // Body
  'area', 'axes', 'collisionFilter', 'category', 'mask',
  'group', 'density', 'friction', 'frictionAir', 'frictionStatic', 'inertia', 'inverseInertia', 'inverseMass', 'isSensor',
  'isSleeping', 'isStatic', 'mass', 'parent', 'parts', 'restitution', 'sleepThreshold', 'slop',
  'timeScale', 'vertices',

  // Composite
  'bodies', 'constraints', 'composites'
];

const collisionId = (collision) =>
  Math.min(collision.bodyA.id, collision.bodyB.id) + Math.max(collision.bodyA.id, collision.bodyB.id) * 10000;

const collisionCompareId = (collisionA, collisionB) => collisionId(collisionA) - collisionId(collisionB);

const sortById = (objs) => objs.sort((objA, objB) => objA.id - objB.id);

const limitPrecision = (val, precision=3) => parseFloat(val.toPrecision(precision));

module.exports = { runExample };<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { DRAFTJS_BLOCK_KEY } from './constants';

// Get a component's display name
const getDisplayName = WrappedComponent => {
  const component = WrappedComponent.WrappedComponent || WrappedComponent;
  return component.displayName || component.name || 'Component';
};

export default ({ store }) => WrappedComponent =>
  class BlockDraggableDecorator extends Component {
    static displayName = `BlockDraggable(${getDisplayName(WrappedComponent)})`;
    // eslint-disable-next-line no-redeclare
    static WrappedComponent =
      WrappedComponent.WrappedComponent || WrappedComponent;

    // Handle start-drag and set dataTransfer data with blockKey
    startDrag = event => {
      event.dataTransfer.dropEffect = 'move'; // eslint-disable-line no-param-reassign
      // declare data and give info that its an existing key and a block needs to be moved
      event.dataTransfer.setData(
        'text',
        `${DRAFTJS_BLOCK_KEY}:${this.props.block.key}`
      );
    };

    render() {
      // If this is rendered before the store is initialized default to read only
      // NOTE(@mxstbr): Reference issue: draft-js-plugins/draft-js-plugins#926
      const readOnly = store.getReadOnly ? store.getReadOnly() : true;
      return (
        <WrappedComponent
          {...this.props}
          onDragStart={!readOnly ? this.startDrag : undefined}
        />
      );
    }
  };
<commit_msg>feat(drag): handle drag and drop (#1481)<commit_after>import React from 'react';
import { DRAFTJS_BLOCK_KEY } from './constants';

// Get a component's display name
const getDisplayName = WrappedComponent => {
  const component = WrappedComponent.WrappedComponent || WrappedComponent;
  return component.displayName || component.name || 'Component';
};

export default ({ store }) => WrappedComponent => {
  const BlockDraggableDecorator = props => {
    // Handle start-drag and set dataTransfer data with blockKey
    const startDrag = event => {
      event.dataTransfer.dropEffect = 'move'; // eslint-disable-line no-param-reassign
      // declare data and give info that its an existing key and a block needs to be moved
      event.dataTransfer.setData(
        'text',
        `${DRAFTJS_BLOCK_KEY}:${props.block.key}`
      );
    };

    // If this is rendered before the store is initialized default to read only
    // NOTE(@mxstbr): Reference issue: draft-js-plugins/draft-js-plugins#926
    const readOnly = store.getReadOnly ? store.getReadOnly() : true;

    return (
      <WrappedComponent
        {...props}
        onDragStart={!readOnly ? startDrag : undefined}
      />
    );
  };

  BlockDraggableDecorator.displayName = `BlockDraggable(${getDisplayName(
    WrappedComponent
  )})`;
  // eslint-disable-next-line no-redeclare
  BlockDraggableDecorator.WrappedComponent =
    WrappedComponent.WrappedComponent || WrappedComponent;

  return BlockDraggableDecorator;
};
<|endoftext|>


<commit_before>module.exports = function(config) {
    config.set({
        frameworks: ['jasmine', 'browserify'],
        files: [
            'test/**/*.js'
        ],
        preprocessors: {
            'test/**/*.js': ['browserify']
        },
        browsers: ['PhantomJS'],
        browserify: {
            debug: true
        }
    });
};
<commit_msg>Update karma, it will run once with npm test<commit_after>module.exports = function(config) {
    config.set({
        frameworks: ['jasmine', 'browserify'],
        files: [
            'test/**/*.js'
        ],
        preprocessors: {
            'test/**/*.js': ['browserify']
        },
        browsers: ['PhantomJS'],
        browserify: {
            debug: true
        },
        singleRun: true
    });
};
<|endoftext|>


<commit_before>var trialMap;

demo.trial = function(){};
demo.trial.prototype = {
    preload: function(){
        loadImages();

        // make sure to set this to the size of the tile map
        game.world.setBounds(0, 0, 1000,1000);

        // Vaidehi! Help!
        // load in tile map assets
        game.load.tilemap('trial', 'assets/tilemaps/TrialMap.json', null, Phaser.Tilemap.TILED_JSON);
        game.load.image('ice_sprite', 'assets/tilemaps/IceStage/ice_sprite.png');
        game.load.image('new_ladder_sprite', 'assets/tilemaps/IceStage/ice_ladder_sprite.png');
        game.load.image('blue_snow', 'assets/tilemaps/IceStage/blue_snow.png');
//        game.load.image('green_snow', 'assets/tilemaps/IceStage/green_snow.png');
        game.load.image('purple_snow', 'assets/tilemaps/IceStage/purple_snow.png');
        game.load.image('new_ladder_sprite_top', 'assets/tilemaps/IceStage/ice_ladder_sprite.png');

        loadCatBoss();
    },

    create: function(){
//        game.physics.startSystem(Phaser.Physics.ARCADE);
//        cursors = game.input.keyboard.createCursorKeys();

        // stop all other sounds
        game.sound.stopAll();
        game.camera.y = game.world.height;

        // play background music
        guitar2 = game.add.audio('guitar2');
        guitar2.play('','',0.3,true,true);

        game.stage.backgroundColor = '#DDDDDD';

//        cursors = game.input.keyboard.createCursorKeys();

        // Vaidehi! Help!
        // add in the tile map * make into function - creates dictionary and cycles through dictionary
        trialMap = game.add.tilemap('trial');
        trialMap.addTilesetImage('ice_sprite');
        trialMap.addTilesetImage('new_ladder_sprite');
        trialMap.addTilesetImage('new_ladder_sprite_top');
        trialMap.addTilesetImage('blue_snow');
        trialMap.addTilesetImage('purple_snow');
//        iceMap.addTilesetImage('green_snow');
        triallayer1 = trialMap.createLayer('Platforms');
        triallayer2 = trialMap.createLayer('Ladders');
        triallayer1.resizeWorld();
        triallayer2.resizeWorld();
        game.physics.arcade.enable(triallayer1);
        game.physics.arcade.enable(triallayer2);

        trialMap.setCollisionBetween(1, 7, true, triallayer1)
        trialMap.setCollisionBetween(8, 9, true, triallayer2)

        createChameleon(500, 600);
        make_fruit_groups();
        make_enemy_groups();
        make_balloon_group();
        addMovingPlatforms();
        make_healthpack_groups();

        // place fruit
        placeFruit(500, 600, "bluefruit");
        placeFruit(500, 600, "redfruit");
        placeFruit(500, 600, "greenfruit");
        placeFruit(500, 600, "yellowfruit");
        placeFruit(500, 600, "purplefruit");
        placeFruit(500, 600, "orangefruit");

        placeMP(150, 200, 3, 1, 0, 3, 0, 100);

        place_cat_boss(600, 200);

        // place health bar
        place_hearts(450, 0);
        createInventory(0,525);
        add_pause_darkener();

    },
    update: function(){
        move_camera(1,1);
        game.physics.arcade.collide(player, triallayer1);

        // check for ballooning
        if (player.ballooning){
            chameleon_float();
        } else {
            chameleonmove();
            var tile_arr = get_surrounding_tiles(triallayer2, trialMap);
            ladder_movement(tile_arr, 8, 9);
        }

        birds_group.forEach(moveBird, this);
        snakes_group.forEach(moveSnake, this);
        moving_platform_group.forEach(movingPlatformsUpdate, this);
        update_health(player.health);

        var layer_list = [triallayer1, triallayer2]
        if (game.camera.y != 0) {
            // catboss doesn't wake up until camera reaches 0
            cat_boss.throw_ball_timer = game.time.time + 1000;
        }
        cat_boss_move(layer_list)

    },
//    render: function() {
//        game.debug.body(cat_boss)
//    }
};
<commit_msg>Created enemies to jump at player and respawn fruits for boss fights<commit_after>var trialMap;
var trial_jump_bird;
var trial_respawn_fruits = {};

demo.trial = function(){};
demo.trial.prototype = {
    preload: function(){
        loadImages();

        // make sure to set this to the size of the tile map
        game.world.setBounds(0, 0, 1000,1000);

        // Vaidehi! Help!
        // load in tile map assets
        game.load.tilemap('trial', 'assets/tilemaps/TrialMap.json', null, Phaser.Tilemap.TILED_JSON);
        game.load.image('ice_sprite', 'assets/tilemaps/IceStage/ice_sprite.png');
        game.load.image('new_ladder_sprite', 'assets/tilemaps/IceStage/ice_ladder_sprite.png');
        game.load.image('blue_snow', 'assets/tilemaps/IceStage/blue_snow.png');
//        game.load.image('green_snow', 'assets/tilemaps/IceStage/green_snow.png');
        game.load.image('purple_snow', 'assets/tilemaps/IceStage/purple_snow.png');
        game.load.image('new_ladder_sprite_top', 'assets/tilemaps/IceStage/ice_ladder_sprite.png');

//        loadCatBoss();
    },

    create: function(){
//        game.physics.startSystem(Phaser.Physics.ARCADE);
//        cursors = game.input.keyboard.createCursorKeys();

        // stop all other sounds
        game.sound.stopAll();
        game.camera.y = game.world.height;

        // play background music
        guitar2 = game.add.audio('guitar2');
        guitar2.play('','',0.3,true,true);

        game.stage.backgroundColor = '#DDDDDD';

//        cursors = game.input.keyboard.createCursorKeys();

        // Vaidehi! Help!
        // add in the tile map * make into function - creates dictionary and cycles through dictionary
        trialMap = game.add.tilemap('trial');
        trialMap.addTilesetImage('ice_sprite');
        trialMap.addTilesetImage('new_ladder_sprite');
        trialMap.addTilesetImage('new_ladder_sprite_top');
        trialMap.addTilesetImage('blue_snow');
        trialMap.addTilesetImage('purple_snow');
//        iceMap.addTilesetImage('green_snow');
        triallayer1 = trialMap.createLayer('Platforms');
        triallayer2 = trialMap.createLayer('Ladders');
        triallayer1.resizeWorld();
        triallayer2.resizeWorld();
        game.physics.arcade.enable(triallayer1);
        game.physics.arcade.enable(triallayer2);

        trialMap.setCollisionBetween(1, 7, true, triallayer1)
        trialMap.setCollisionBetween(8, 9, true, triallayer2)

        createChameleon(500, 600);
        make_fruit_groups();
        make_enemy_groups();
        make_balloon_group();
        addMovingPlatforms();
        make_healthpack_groups();

        // place fruit
        placeFruit(500, 600, "bluefruit");
        placeFruit(500, 600, "redfruit");
        placeFruit(500, 600, "greenfruit");
        placeFruit(500, 600, "yellowfruit");
        placeFruit(500, 600, "purplefruit");
        placeFruit(500, 600, "orangefruit");

        trial_respawn_fruits.red = placeFruit(200, 200, "redfruit");
        trial_respawn_fruits.red.reset_time = game.time.time + 2000;

        placeMP(150, 200, 3, 1, 0, 3, 0, 100);

        // Jump scare enemies!
        trial_jump_bird = placeBird(100, 0, ["red"]);
        trial_jump_bird.mytween = game.add.tween(trial_jump_bird).to({x: 100, y:400}, 1000, Phaser.Easing.Linear.None, false, 0, 0, false);

//        place_cat_boss(600, 200);

        // place health bar
        place_hearts(450, 0);
        createInventory(0,525);
        add_pause_darkener();

    },
    update: function(){
        move_camera(1,1);
        if (game.camera.y < 200) {
            trial_jump_bird.mytween.start()
        }
        game.physics.arcade.collide(player, triallayer1);

        // check for ballooning
        if (player.ballooning){
            chameleon_float();
        } else {
            chameleonmove();
            var tile_arr = get_surrounding_tiles(triallayer2, trialMap);
            ladder_movement(tile_arr, 8, 9);
        }

        birds_group.forEach(moveBird, this);
        snakes_group.forEach(moveSnake, this);
        moving_platform_group.forEach(movingPlatformsUpdate, this);
        update_health(player.health);

        if (!trial_respawn_fruits.red.alive && game.time.time > trial_respawn_fruits.red.reset_time) {
            trial_respawn_fruits.red.revive()
        } else if (trial_respawn_fruits.red.alive) {
            trial_respawn_fruits.red.reset_time = game.time.time + 2000;
        }

//        var layer_list = [triallayer1, triallayer2]
//        if (game.camera.y != 0) {
//            // catboss doesn't wake up until camera reaches 0
//            cat_boss.throw_ball_timer = game.time.time + 1000;
//        }
//        cat_boss_move(layer_list)

    },
//    render: function() {
//        game.debug.body(cat_boss)
//    }
};
<|endoftext|>


<commit_before>module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-coverage'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma')
    ],
    client:{
      clearContext: true // leave Jasmine Spec Runner output visible in browser
    },
    files: [
      { pattern: './src/test.ts', watched: false }
    ],
    preprocessors: {
      './src/test.ts': ['@angular/cli'],
      './src/**/**/*.js': ['coverage']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    coverageReporter: {
      reporters: [{type: 'lcov'}]
     },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
 };<commit_msg>First commit<commit_after>module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular/cli'],
    plugins: [
      require('karma-jasmine'),
      require('karma-coverage'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    files: [
      { pattern: './src/test.ts', watched: false }
    ],
    preprocessors: {
      './src/test.ts': ['@angular/cli'],
      './src/**/**/*.js': ['coverage']
    },
    mime: {
      'text/x-typescript': ['ts','tsx']
    },
    coverageReporter: {
      reporters: [{type: 'lcov'}]
     },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
 };<|endoftext|>