<commit_before>'use strict'

const debug = require('debug')('redis-cache-decorator')
const toArray = require('stream-to-array')
const Promise = require('bluebird')
const destroy = require('destroy')
const Stream = require('stream')
const assert = require('assert')

const ENCODINGS = require('./encodings')
const utils = require('./utils')

// create a base
module.exports = constructorOptions => {
  assert(constructorOptions, 'Constructor options are required!')
  const client = constructorOptions.client
  const subscriber = constructorOptions.subscriber
  assert(client, 'A redis client as `.client` is required!')
  assert(subscriber, 'A redis subscription client as `.subscriber` is required!')

  // easy way to disabled
  const disabled = constructorOptions.disabled || constructorOptions.disable || false
  // prefix
  const constructorNamespace = constructorOptions.namespace || ''
  const defaultEncoding = constructorOptions.encoding || 'json'
  assert(ENCODINGS.has(defaultEncoding), `Encoding is not supported: ${defaultEncoding}`)
  // 30 seconds
  const defaultTTL = utils.isNumberOrString(constructorOptions.ttl)
    ? utils.ms(constructorOptions.ttl)
    : utils.ms('30s')
  // prefix
  const defaultTimeout = utils.isNumberOrString(constructorOptions.timeout)
    ? utils.ms(constructorOptions.timeout)
    : utils.ms('30s')
  // onError
  const defaultOnError = constructorOptions.onError || utils._onError
  // default poll factor based on the timeout
  const defaultPollFactor = constructorOptions.pollFactor || (1 / 10)
  // default minimum poll interval in ms
  const defaultMinimumTimeoutPollInterval = utils.ms(constructorOptions.minimumPollInterval || 100)
  // function to create a time out error
  const defaultCreateTimeoutError = constructorOptions.createTimeoutError || utils._createTimeoutError

  // add decorator options
  return decoratorOptions => {
    if (typeof decoratorOptions !== 'object') throw new TypeError('Decorator options are required!')
    if (typeof decoratorOptions.namespace !== 'string') throw new TypeError('A .namespace is required for each decorator.')
    const namespace = [constructorNamespace, decoratorOptions.namespace].join(':')
    const encoding = decoratorOptions.encoding || defaultEncoding
    const encoder = ENCODINGS.get(encoding)
    assert(encoder, `Encoding is not supported: ${encoding}`)
    const ttl = utils.ms(utils.isNumberOrString(decoratorOptions.ttl)
      ? decoratorOptions.ttl
      : defaultTTL)
    const timeout = utils.ms(utils.isNumberOrString(decoratorOptions.timeout)
      ? decoratorOptions.timeout
      : defaultTimeout)
    assert(timeout > 0, 'The timeout must be > 0')
    const pollFactor = decoratorOptions.pollFactor || defaultPollFactor
    const minimumPollInterval = utils.ms(utils.isNumberOrString(decoratorOptions.minimumPollInterval)
      ? decoratorOptions.minimumPollInterval
      : defaultMinimumTimeoutPollInterval)
    const pollInterval = utils.ms(decoratorOptions.pollInterval || Math.max(timeout * pollFactor, minimumPollInterval))
    const onError = decoratorOptions.onError || defaultOnError
    const createTimeoutError = decoratorOptions.createTimeoutError || defaultCreateTimeoutError

    debug('options: %o', {
      disabled,
      namespace,
      ttl,
      timeout,
      pollFactor,
      minimumPollInterval,
      pollInterval,
    })

    // input the underlying function
    return fn => {
      if (typeof fn !== 'function') throw new TypeError('Decorated input must be a function!')

      // main logic
      const exec = Promise.coroutine(function * (args, hash) {
        // check whether it's running or anything
        let state = yield getCurrentState()

        // return the cached version
        if (state.value != null) return encoder.decode(state.value)

        // cleanup listeners when listing for events
        let cleanupListeners = utils.noop
        // whether cleanup has already happened
        let cleaned = false
        // whether execution has occured already
        let executed = false
        // whether the result is as a stream
        let stream = null
        // if it's currently running, wait for the value
        if (state.running) return yield waitForResult()

        // execute - might need something like this for the redlock algorithm?
        const EXEC_ID = Date.now() + Math.random().toString(36)
        // note in redis that it's running
        // don't wait for a response - might avoid some race conditions
        const isSet = yield client.setnx(`${hash}:running`, EXEC_ID)
        if (isSet) return yield execute() // we set it, so we execute

        /* someone else is setting it... */

        // start listening for the result
        const resultPromise = waitForResult()
        // check whether the result has been saved in the mean time
        state = yield getCurrentState()

        // the chance of this happening (setting a result when executing SETNX) is very low
        /* istanbul ignore if */
        if (state.value != null) {
          cleanupListeners()
          resultPromise.catch(onError)  // always handle the promise
          return encoder.decode(state.value)
        }

        // if it's not running, execute again
        // but why would this happen?
        /* istanbul ignore next */
        if (!state.running) {
          resultPromise.catch(onError) // always handle the promise
          return yield execute()
        }

        return yield resultPromise

        // if the current query is a stream, try to destroy it if we're returning data
        // TODO: tests
        function destroyCurrentStream () {
          if (stream) destroy(stream)
        }

        // executes the result
        function execute () {
          debug('executing')

          {
            // cleanup listeners just in case
            cleanupListeners()
            // if we're executing after a failed set,
            // set it again just in case
            const batch = client.multi()
            batch.setnx(`${hash}:running`, EXEC_ID)
            // set a timeout
            client.pexpire(`${hash}:running`, timeout)
            batch.exec().catch(onError)
          }

          // promise that polls whether the function has timed out
          let timeout_id
          const timeout_promise = new Promise((resolve, reject) => {
            timeout_id = pollCurrentState(resolve, reject)
          })

          // promise that actuall executes the function
          // NOTE: execution still completes and saves even if timed out
          const execution_promise = utils.executeFunction(fn, args)
          .then(streamToValue)
          .then(value => {
            cleanupTimeout()
            debug('returned value: %o', value)
            const encodedValue = encoder.encode(value) // story it as a string
            debug('encoded value: %o', encodedValue)
            const batch = client.multi()
              .del(`${hash}:running`, `${hash}:error`)
              // NOTE: don't know how buffers transmit over pubsub
              .publish(`${hash}:value`, encoding === 'buffer' ? '1' : encodedValue) // publish the result to all listeners
            // only cache if a TTL is set
            if (ttl) {
              if (encoding === 'buffer') {
                client.set(`${hash}:value`, encodedValue, 'PX', ttl).catch(onError)
              } else {
                batch.set(`${hash}:value`, encodedValue, 'PX', ttl)
              }
            }
            batch.exec().catch(onError)
            return value
          }).catch(_err => {
            cleanupTimeout()
            // handle errors
            const err = utils.stringifyError(_err)
            client.multi()
              .del(`${hash}:running`, `${hash}:value`)
              // publish the error to all listeners
              .publish(`${hash}:error`, err)
              .exec().catch(onError)
            throw _err
          })

          return Promise.race([
            timeout_promise,
            execution_promise,
          ])

          function cleanupTimeout () {
            debug('execution is done!')
            executed = true
            clearInterval(timeout_id)
          }
        }

        // converts a stream response into a value that can be saved in redis
        function streamToValue (value) {
          // handle streams
          if (value && typeof value.pipe === 'function' && value._readableState) {
            // expose the stream
            stream = value

            switch (encoding) {
              case 'json': {
                assert(value._readableState.objectMode, 'I do not know how to handle streams that are not in `objectMode` when saving in with encoding = `json`. Please change your `encoding=` option to `string` or `buffer`')
                // just convert it to an array
                return toArray(value)
              }
              case 'string': {
                value.setEncoding('utf8')
                return toArray(value).then(arr => arr.join(''))
              }
              case 'buffer': {
                return toArray(value).then(arr => Buffer.concat(arr))
              }
            }
          }

          /* istanbul ignore if */
          if (value instanceof Stream) {
            throw new Error('I do not know how to handle this type of stream. It does not have a `.pipe()` function or it does not have a `._readableState.`')
          }

          return value
        }

        // get whether there's a cached value and/or if a query is currently running
        function getCurrentState () {
          const formatResults = results => {
            const state = {
              value: results[0],
              running: results[1],
            }
            debug('state: %o', state)
            return state
          }

          if (encoding === 'buffer') {
            // TODO: should really be using `.multi()` here, but `getBuffer()` is broken in it
            return Promise.all([
              client.getBuffer(`${hash}:value`),
              client.get(`${hash}:running`),
            ]).then(formatResults)
          }

          return client.mget([
            `${hash}:value`,
            `${hash}:running`,
          ]).then(formatResults)
        }

        // poll for updates to the state
        function pollCurrentState (resolve, reject) {
          return setInterval(() => {
            debug('polling for state')
            getCurrentState().then(val => {
              debug('polled for state')
              debug('cleaned: %s', cleaned)
              debug('executed: %s', executed)

              state = val
              // we check here incase cleanup happens during `getCurrentState()` is resolving
              // though I guess it really doesn't matter because resolve/reject is smart
              /* istanbul ignore if */
              if (cleaned || executed) return

              // set somehow, i guess if two functions were running concurrently
              /* istanbul ignore if */
              if (state.value != null) {
                cleanupListeners()
                destroyCurrentStream()
                try {
                  resolve(encoder.decode(state.value))
                } catch (err) {
                  /* istanbul ignore next */
                  reject(err)
                }
                return
              }

              // no longer running
              if (!state.running) {
                cleanupListeners()
                destroyCurrentStream()
                reject(createTimeoutError())
                return
              }
            }).catch(onError)
          }, pollInterval)
        }

        // wait for the result from redis channels and polling
        function waitForResult () {
          debug('waiting for result')
          // subscribe to the channels
          const pattern = `${hash}:*`

          return new Promise((resolve, reject) => {
            // expose the cleanup function outside the scope
            cleanupListeners = cleanup

            subscriber.psubscribe(pattern).catch(reject)

            // add a message handler
            subscriber.setMaxListeners(subscriber.getMaxListeners() + 1)
            subscriber.on('pmessage', listener)

            // poll to check the state
            // NOTE: was thinking of using keyevents,
            // but this is better because you need to access the key
            // to trigger an eviction anyways
            // timeout for polling
            const timeout_id = pollCurrentState(resolve, reject)

            function listener (pattern, channel, message) {
              if (channel === `${hash}:value`) {
                cleanup()
                debug('message: %s', message)

                // i don't know how to transfer buffer's as a message
                if (encoding === 'buffer') {
                  return getCurrentState().then(_val => {
                    state = _val
                    return encoder.decode(state.value)
                  }).then(resolve, reject)
                }

                try {
                  resolve(encoder.decode(message))
                } catch (err) {
                  // when would this happen?
                  /* istanbul ignore next */
                  reject(err)
                }
              } else if (channel === `${hash}:error`) {
                cleanup()
                try {
                  reject(utils.parseError(message))
                } catch (err) {
                  // when would this happen?
                  /* istanbul ignore next */
                  reject(err)
                }
              }
            }

            function cleanup () {
              debug('cleaning up waiting for the result')
              cleaned = true
              clearInterval(timeout_id)
              subscriber.punsubscribe(pattern).catch(reject)
              subscriber.removeListener('pmessage', listener)
              subscriber.setMaxListeners(subscriber.getMaxListeners() - 1)
            }
          })
        }
      })

      // return the decorated version of the function
      return function () {
        // get the arguments
        const args = []
        for (let i = 0; i < arguments.length; i++) args.push(arguments[i])

        // bypass the cache
        if (disabled) return utils.executeFunction(fn, args)

        // create a hash of the arguments
        const hash = utils.createHash(namespace, args)

        // create a promise for the execution
        const promise = exec(args, hash)

        // attach the has to the promise
        Object.assign(promise, {
          hash,
        })

        // return the promise
        return promise
      }
    }
  }
}
<commit_msg>test: remove some coverage ignores that should be tested or removed<commit_after>'use strict'

const debug = require('debug')('redis-cache-decorator')
const toArray = require('stream-to-array')
const Promise = require('bluebird')
const destroy = require('destroy')
const Stream = require('stream')
const assert = require('assert')

const ENCODINGS = require('./encodings')
const utils = require('./utils')

// create a base
module.exports = constructorOptions => {
  assert(constructorOptions, 'Constructor options are required!')
  const client = constructorOptions.client
  const subscriber = constructorOptions.subscriber
  assert(client, 'A redis client as `.client` is required!')
  assert(subscriber, 'A redis subscription client as `.subscriber` is required!')

  // easy way to disabled
  const disabled = constructorOptions.disabled || constructorOptions.disable || false
  // prefix
  const constructorNamespace = constructorOptions.namespace || ''
  const defaultEncoding = constructorOptions.encoding || 'json'
  assert(ENCODINGS.has(defaultEncoding), `Encoding is not supported: ${defaultEncoding}`)
  // 30 seconds
  const defaultTTL = utils.isNumberOrString(constructorOptions.ttl)
    ? utils.ms(constructorOptions.ttl)
    : utils.ms('30s')
  // prefix
  const defaultTimeout = utils.isNumberOrString(constructorOptions.timeout)
    ? utils.ms(constructorOptions.timeout)
    : utils.ms('30s')
  // onError
  const defaultOnError = constructorOptions.onError || utils._onError
  // default poll factor based on the timeout
  const defaultPollFactor = constructorOptions.pollFactor || (1 / 10)
  // default minimum poll interval in ms
  const defaultMinimumTimeoutPollInterval = utils.ms(constructorOptions.minimumPollInterval || 100)
  // function to create a time out error
  const defaultCreateTimeoutError = constructorOptions.createTimeoutError || utils._createTimeoutError

  // add decorator options
  return decoratorOptions => {
    if (typeof decoratorOptions !== 'object') throw new TypeError('Decorator options are required!')
    if (typeof decoratorOptions.namespace !== 'string') throw new TypeError('A .namespace is required for each decorator.')
    const namespace = [constructorNamespace, decoratorOptions.namespace].join(':')
    const encoding = decoratorOptions.encoding || defaultEncoding
    const encoder = ENCODINGS.get(encoding)
    assert(encoder, `Encoding is not supported: ${encoding}`)
    const ttl = utils.ms(utils.isNumberOrString(decoratorOptions.ttl)
      ? decoratorOptions.ttl
      : defaultTTL)
    const timeout = utils.ms(utils.isNumberOrString(decoratorOptions.timeout)
      ? decoratorOptions.timeout
      : defaultTimeout)
    assert(timeout > 0, 'The timeout must be > 0')
    const pollFactor = decoratorOptions.pollFactor || defaultPollFactor
    const minimumPollInterval = utils.ms(utils.isNumberOrString(decoratorOptions.minimumPollInterval)
      ? decoratorOptions.minimumPollInterval
      : defaultMinimumTimeoutPollInterval)
    const pollInterval = utils.ms(decoratorOptions.pollInterval || Math.max(timeout * pollFactor, minimumPollInterval))
    const onError = decoratorOptions.onError || defaultOnError
    const createTimeoutError = decoratorOptions.createTimeoutError || defaultCreateTimeoutError

    debug('options: %o', {
      disabled,
      namespace,
      ttl,
      timeout,
      pollFactor,
      minimumPollInterval,
      pollInterval,
    })

    // input the underlying function
    return fn => {
      if (typeof fn !== 'function') throw new TypeError('Decorated input must be a function!')

      // main logic
      const exec = Promise.coroutine(function * (args, hash) {
        // check whether it's running or anything
        let state = yield getCurrentState()

        // return the cached version
        if (state.value != null) return encoder.decode(state.value)

        // cleanup listeners when listing for events
        let cleanupListeners = utils.noop
        // whether cleanup has already happened
        let cleaned = false
        // whether execution has occured already
        let executed = false
        // whether the result is as a stream
        let stream = null
        // if it's currently running, wait for the value
        if (state.running) return yield waitForResult()

        // execute - might need something like this for the redlock algorithm?
        const EXEC_ID = Date.now() + Math.random().toString(36)
        // note in redis that it's running
        // don't wait for a response - might avoid some race conditions
        const isSet = yield client.setnx(`${hash}:running`, EXEC_ID)
        if (isSet) return yield execute() // we set it, so we execute

        /* someone else is setting it... */

        // start listening for the result
        const resultPromise = waitForResult()
        // check whether the result has been saved in the mean time
        state = yield getCurrentState()

        // the chance of this happening (setting a result when executing SETNX) is very low
        if (state.value != null) {
          cleanupListeners()
          resultPromise.catch(onError)  // always handle the promise
          return encoder.decode(state.value)
        }

        // if it's not running, execute again
        // but why would this happen?
        if (!state.running) {
          resultPromise.catch(onError) // always handle the promise
          return yield execute()
        }

        return yield resultPromise

        // if the current query is a stream, try to destroy it if we're returning data
        // TODO: tests
        function destroyCurrentStream () {
          if (stream) destroy(stream)
        }

        // executes the result
        function execute () {
          debug('executing')

          {
            // cleanup listeners just in case
            cleanupListeners()
            // if we're executing after a failed set,
            // set it again just in case
            const batch = client.multi()
            batch.setnx(`${hash}:running`, EXEC_ID)
            // set a timeout
            client.pexpire(`${hash}:running`, timeout)
            batch.exec().catch(onError)
          }

          // promise that polls whether the function has timed out
          let timeout_id
          const timeout_promise = new Promise((resolve, reject) => {
            timeout_id = pollCurrentState(resolve, reject)
          })

          // promise that actuall executes the function
          // NOTE: execution still completes and saves even if timed out
          const execution_promise = utils.executeFunction(fn, args)
          .then(streamToValue)
          .then(value => {
            cleanupTimeout()
            debug('returned value: %o', value)
            const encodedValue = encoder.encode(value) // story it as a string
            debug('encoded value: %o', encodedValue)
            const batch = client.multi()
              .del(`${hash}:running`, `${hash}:error`)
              // NOTE: don't know how buffers transmit over pubsub
              .publish(`${hash}:value`, encoding === 'buffer' ? '1' : encodedValue) // publish the result to all listeners
            // only cache if a TTL is set
            if (ttl) {
              if (encoding === 'buffer') {
                client.set(`${hash}:value`, encodedValue, 'PX', ttl).catch(onError)
              } else {
                batch.set(`${hash}:value`, encodedValue, 'PX', ttl)
              }
            }
            batch.exec().catch(onError)
            return value
          }).catch(_err => {
            cleanupTimeout()
            // handle errors
            const err = utils.stringifyError(_err)
            client.multi()
              .del(`${hash}:running`, `${hash}:value`)
              // publish the error to all listeners
              .publish(`${hash}:error`, err)
              .exec().catch(onError)
            throw _err
          })

          return Promise.race([
            timeout_promise,
            execution_promise,
          ])

          function cleanupTimeout () {
            debug('execution is done!')
            executed = true
            clearInterval(timeout_id)
          }
        }

        // converts a stream response into a value that can be saved in redis
        function streamToValue (value) {
          // handle streams
          if (value && typeof value.pipe === 'function' && value._readableState) {
            // expose the stream
            stream = value

            switch (encoding) {
              case 'json': {
                assert(value._readableState.objectMode, 'I do not know how to handle streams that are not in `objectMode` when saving in with encoding = `json`. Please change your `encoding=` option to `string` or `buffer`')
                // just convert it to an array
                return toArray(value)
              }
              case 'string': {
                value.setEncoding('utf8')
                return toArray(value).then(arr => arr.join(''))
              }
              case 'buffer': {
                return toArray(value).then(arr => Buffer.concat(arr))
              }
            }
          }

          /* istanbul ignore if */
          if (value instanceof Stream) {
            throw new Error('I do not know how to handle this type of stream. It does not have a `.pipe()` function or it does not have a `._readableState.`')
          }

          return value
        }

        // get whether there's a cached value and/or if a query is currently running
        function getCurrentState () {
          const formatResults = results => {
            const state = {
              value: results[0],
              running: results[1],
            }
            debug('state: %o', state)
            return state
          }

          if (encoding === 'buffer') {
            // TODO: should really be using `.multi()` here, but `getBuffer()` is broken in it
            return Promise.all([
              client.getBuffer(`${hash}:value`),
              client.get(`${hash}:running`),
            ]).then(formatResults)
          }

          return client.mget([
            `${hash}:value`,
            `${hash}:running`,
          ]).then(formatResults)
        }

        // poll for updates to the state
        function pollCurrentState (resolve, reject) {
          return setInterval(() => {
            debug('polling for state')
            getCurrentState().then(val => {
              debug('polled for state')
              debug('cleaned: %s', cleaned)
              debug('executed: %s', executed)

              state = val

              // we check here incase cleanup happens during `getCurrentState()` is resolving
              // though I guess it really doesn't matter because resolve/reject is smart
              if (cleaned || executed) return

              // set somehow, i guess if two functions were running concurrently
              if (state.value != null) {
                cleanupListeners()
                destroyCurrentStream()
                try {
                  resolve(encoder.decode(state.value))
                } catch (err) {
                  /* istanbul ignore next */
                  reject(err)
                }
                return
              }

              // no longer running
              if (!state.running) {
                cleanupListeners()
                destroyCurrentStream()
                reject(createTimeoutError())
                return
              }
            }).catch(onError)
          }, pollInterval)
        }

        // wait for the result from redis channels and polling
        function waitForResult () {
          debug('waiting for result')
          // subscribe to the channels
          const pattern = `${hash}:*`

          return new Promise((resolve, reject) => {
            // expose the cleanup function outside the scope
            cleanupListeners = cleanup

            subscriber.psubscribe(pattern).catch(reject)

            // add a message handler
            subscriber.setMaxListeners(subscriber.getMaxListeners() + 1)
            subscriber.on('pmessage', listener)

            // poll to check the state
            // NOTE: was thinking of using keyevents,
            // but this is better because you need to access the key
            // to trigger an eviction anyways
            // timeout for polling
            const timeout_id = pollCurrentState(resolve, reject)

            function listener (pattern, channel, message) {
              if (channel === `${hash}:value`) {
                cleanup()
                debug('message: %s', message)

                // i don't know how to transfer buffer's as a message
                if (encoding === 'buffer') {
                  return getCurrentState().then(_val => {
                    state = _val
                    return encoder.decode(state.value)
                  }).then(resolve, reject)
                }

                try {
                  resolve(encoder.decode(message))
                } catch (err) {
                  // when would this happen?
                  /* istanbul ignore next */
                  reject(err)
                }
              } else if (channel === `${hash}:error`) {
                cleanup()
                try {
                  reject(utils.parseError(message))
                } catch (err) {
                  // when would this happen?
                  /* istanbul ignore next */
                  reject(err)
                }
              }
            }

            function cleanup () {
              debug('cleaning up waiting for the result')
              cleaned = true
              clearInterval(timeout_id)
              subscriber.punsubscribe(pattern).catch(reject)
              subscriber.removeListener('pmessage', listener)
              subscriber.setMaxListeners(subscriber.getMaxListeners() - 1)
            }
          })
        }
      })

      // return the decorated version of the function
      return function () {
        // get the arguments
        const args = []
        for (let i = 0; i < arguments.length; i++) args.push(arguments[i])

        // bypass the cache
        if (disabled) return utils.executeFunction(fn, args)

        // create a hash of the arguments
        const hash = utils.createHash(namespace, args)

        // create a promise for the execution
        const promise = exec(args, hash)

        // attach the has to the promise
        Object.assign(promise, {
          hash,
        })

        // return the promise
        return promise
      }
    }
  }
}
<|endoftext|>


<commit_before>"use strict";
require('es6-shim');
require('prfun');

var program = require('commander');
var json = require('../package.json');

var fs = require('fs');
var Pusher = require('pusher-client');
var url = require('url');

var p = require('./p');
var request = require('./retry-request');

var CHILD_IGNORE = { childOptions: { stdio: 'ignore' } };
var readFile = Promise.promisify(fs.readFile, false, fs);

program
	.version(json.version)
	.usage('[options] [github project]')
	.option(
		'--travis-api <url>',
		'Travis API endpoint [https://api.travis-ci.org]',
		'https://api.travis-ci.org'
	).option(
		'-j, --job <n>',
		'Only follow the specified job (job numbers start at 1)'
	);

program.parse(process.argv);

if (program.args.length > 1) {
	console.error('Too many arguments.');
	return 1;
}

var getUserAgent = function(repo) {
	return json.name + '/' + json.version + ' (' +
		'node ' + process.version + ' ' + process.platform + ' ' + process.arch +
		') ' + repo;
};

var getRepoName = function() {
	return Promise.resolve(program.args[0]).then(function(reponame) {
		if (reponame) { return reponame; }
		// look in ./package.json
		return readFile(
			'./package.json', { encoding: 'utf8' }
		).then(function(data) {
			var json = JSON.parse(data);
			if (json.repository && json.repository.type === 'git') {
				var m =
					/^(https?:\/\/github.com\/)?([^\/]+)\/([^\/]+?)(\.git)?$/.
					exec(json.repository.url || '');
				if (m) {
					return m[2] + '/' + m[3];
				}
			}
		});
	}).catch(function(e) { /* ignore */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		// XXX look in .git/config?
		throw new Error("Can't determine github repo name.");
	});
};

// ensure that there is a github remote, named 'github'
var ensureRemote = function(repo) {
	return p.spawn('git', ['remote', 'show', 'github'], CHILD_IGNORE).then(function() {
		/* ok, github already exists */
	}, function(e) {
		/* doesn't exist, create it! */
		return p.spawn('git', ['remote','add','github','git@github.com:'+repo]);
	});
};

// get the hash of HEAD
var getGitHead = function() {
	return p.execFile('git', ['rev-parse', 'HEAD']).then(function(out) {
		return out.stdout.trim();
	});
};

// create the new branch name
var getBranchName = function(hash) {
	return json.name + '-' + hash.slice(0, 8);
};

// push to github
var ensureBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', 'github', 'HEAD:'+branchname], CHILD_IGNORE);
	});
};

var deleteBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', 'github', ':'+branchname], CHILD_IGNORE);
	});
};

var apiRequest = function(path, opts) {
	opts = opts || {};
	var apiURL = url.resolve(program.travisApi, path);
	var mimeType = 'application/vnd.travis-ci.2+json';
	if (opts.chunked) {
		mimeType += '; chunked=true';
	}
	return request({
		url: apiURL,
		encoding: 'utf8',
		pool: false,
		headers: {
			'User-Agent': getUserAgent(),
			'Accept': mimeType
		},
		retries: opts.retries,
		delay: opts.delay,
		timeout: opts.timeout
	}).then(function(resp) {
		if (resp.response.statusCode !== 200) {
			throw new Error("Bad status: "+resp.response.statusCode);
		}
		return opts.raw ? resp.body : JSON.parse(resp.body);
	});
};

var getTravisConfig = function() {
	return apiRequest('/config').get('config');
};

var getTravisBuild = function(repo, branch, retry) {
	return apiRequest('/repos/'+repo+'/branches/'+branch, {
		timeout: 30 * 1000,
		delay:   5 * 1000, /* check every 5 seconds */
		retries: retry ? 3 * 12 : 0 /* for up to 3 minutes */
	}).get('branch');
};

var getTravisJob = function(job_id) {
	return apiRequest('/jobs/'+job_id).get('job');
};

var getTravisLog = function(log_id, chunked) {
	return apiRequest('/logs/'+log_id, {
		chunked: chunked,
	}).get('log');
};

var Log = function() {
	this.chunks = [];
	this.next = 1;
	this._last = -1;
};
Log.prototype.add = function(n, data) {
	this.chunks[n] = data;
	this.advance();
	return (n === this._last);
};
Log.prototype.advance = function() {
	while (this.chunks[this.next] !== undefined) {
		process.stdout.write(this.chunks[this.next++]);
	}
};
Log.prototype.last = function(n) {
	this._last = n;
};

var doit = function() {
	var state = {}, pusher;
	var initState = function() {
		return Promise.all([
			getTravisConfig().then(function(config) {
				state.config = config;
				pusher = new Pusher(state.config.pusher.key);
				if (false) { /* debugging */
					pusher.connection.on('message', function(message) {
						console.log('GOT PUSHER MESSAGE', message);
					});
				}
			}),
			getGitHead().then(function(hash) {
				state.githash = hash;
				state.branch = getBranchName(hash);
			}),
			getRepoName().then(function(repo) {
				state.repo = repo;
			})
		]).then(function() {
			// check to see if builds on this branch already exist
			return getTravisBuild(state.repo, state.branch, false).catch(function(e) {
				// doesn't yet exist; push to github and then retry query until
				// we see the build show up in travis.
				return ensureBranch(state.repo, state.branch).then(function() {
					return getTravisBuild(state.repo, state.branch, true);
				});
			});
		}).then(function(build) {
			// ok, we've got a build on this branch!
			state.build = build;
			return state;
		});
	};
	var watchJob = function(job_num) {
		return Promise.resolve().then(function() {
			state.job_num = job_num;
			state.job_id = state.build.job_ids[state.job_num - 1];
			console.log(
				'== Travis build', state.build.number+'.'+state.job_num, '=='
			);
			// check job #
			if (!state.job_id) {
				throw new Error(
					"Bad job number: " + state.job_num +
						" ("+state.build.job_ids.length+" jobs)"
				);
			}
			return getTravisJob(state.job_id);
		}).then(function(job) {
			state.job = job;
			var log = new Log();
			if (/^(passed|failed)$/.test(state.job.state || '')) {
				// fetch all the logs, easy-peasy
				return getTravisLog(state.job.log_id, false/*not chunked*/).then(function(resp_log) {
					log.last(1);
					log.add(1, resp_log.body);
				});
			}
			// hm, we need to stream the logs.
			var channelName = 'job-' + state.job_id;
			var channel = pusher.subscribe(channelName);
			var streamResolve;
			var handleChunk = function(data) {
				var content = data._log !== undefined ? data._log : data.content;
				if (data.final) {
					log.last(data.number + 1); // weird!
				}
				var last = log.add(data.number, content);
				if (last) { streamResolve(); }
			};
			return Promise.all([
				// fetch chunked log to fill in gaps
				new Promise(function(resolve, reject) {
					channel.bind('pusher:subscription_succeeded', function() {
						getTravisLog(state.job.log_id, true /* chunked */).then(function(resp_log) {
							resp_log.parts.forEach(handleChunk);
						}).then(resolve, reject);
					});
				}),
				// tail log for this job
				new Promise(function(resolve, reject) {
					streamResolve = resolve;
					channel.bind('job:log', handleChunk);
					channel.bind('job:finished', streamResolve);
				}).then(function() {
					pusher.unsubscribe(channelName);
				})
			]);
		}).then(function() {
			return getTravisJob(state.job_id);
		}).then(function(job) {
			state.job = job;
			return state.job.state;
		});
	};
	var cleanup = function() {
		return Promise.resolve().then(function() {
			pusher.disconnect();
			return getTravisBuild(state.repo, state.branch, false);
		}).then(function(build) {
			state.build = build;
			// if build is complete (all jobs), we can clean up the branch
			if (/^(passed|failed)$/.test(state.build.state || '')) {
				return deleteBranch(state.repo, state.branch).catch(function(e) {
					/* ignore failure, maybe already deleted */
				});
			}
		});
	};

	// ok, put all the pieces together
	return initState().then(function() {
		if (program.job !== undefined) {
			// watch one particular job
			return watchJob((+program.job));
		}
		// watch all jobs
		return Promise.reduce(state.build.job_ids, function(_,jid,index) {
			return watchJob(index + 1);
		}, null);
	}).then(function() {
		return cleanup();
	}).then(function() {
		if (program.job !== undefined) {
			// return our job status
			return state.job.state;
		}
		// return our build status
		return state.build.state;
	});
};

doit().then(function(status) {
	if (status !== 'passed') {
		process.exit(1);
	}
}).done();
<commit_msg>Ensure that we exit cleanly without hanging.<commit_after>"use strict";
require('es6-shim');
require('prfun');

var program = require('commander');
var json = require('../package.json');

var fs = require('fs');
var Pusher = require('pusher-client');
var url = require('url');

var p = require('./p');
var request = require('./retry-request');

var CHILD_IGNORE = { childOptions: { stdio: 'ignore' } };
var readFile = Promise.promisify(fs.readFile, false, fs);

program
	.version(json.version)
	.usage('[options] [github project]')
	.option(
		'--travis-api <url>',
		'Travis API endpoint [https://api.travis-ci.org]',
		'https://api.travis-ci.org'
	).option(
		'-j, --job <n>',
		'Only follow the specified job (job numbers start at 1)'
	);

program.parse(process.argv);

if (program.args.length > 1) {
	console.error('Too many arguments.');
	return 1;
}

var getUserAgent = function(repo) {
	return json.name + '/' + json.version + ' (' +
		'node ' + process.version + ' ' + process.platform + ' ' + process.arch +
		') ' + repo;
};

var getRepoName = function() {
	return Promise.resolve(program.args[0]).then(function(reponame) {
		if (reponame) { return reponame; }
		// look in ./package.json
		return readFile(
			'./package.json', { encoding: 'utf8' }
		).then(function(data) {
			var json = JSON.parse(data);
			if (json.repository && json.repository.type === 'git') {
				var m =
					/^(https?:\/\/github.com\/)?([^\/]+)\/([^\/]+?)(\.git)?$/.
					exec(json.repository.url || '');
				if (m) {
					return m[2] + '/' + m[3];
				}
			}
		});
	}).catch(function(e) { /* ignore */ }).then(function(reponame) {
		if (reponame) { return reponame; }
		// XXX look in .git/config?
		throw new Error("Can't determine github repo name.");
	});
};

// ensure that there is a github remote, named 'github'
var ensureRemote = function(repo) {
	return p.spawn('git', ['remote', 'show', 'github'], CHILD_IGNORE).then(function() {
		/* ok, github already exists */
	}, function(e) {
		/* doesn't exist, create it! */
		return p.spawn('git', ['remote','add','github','git@github.com:'+repo]);
	});
};

// get the hash of HEAD
var getGitHead = function() {
	return p.execFile('git', ['rev-parse', 'HEAD']).then(function(out) {
		return out.stdout.trim();
	});
};

// create the new branch name
var getBranchName = function(hash) {
	return json.name + '-' + hash.slice(0, 8);
};

// push to github
var ensureBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', 'github', 'HEAD:'+branchname], CHILD_IGNORE);
	});
};

var deleteBranch = function(repo, branchname) {
	return ensureRemote(repo).then(function() {
		return p.spawn('git', ['push', 'github', ':'+branchname], {
			timeout: 60*1000,
			childOptions: CHILD_IGNORE.childOptions
		});
	});
};

var apiRequest = function(path, opts) {
	opts = opts || {};
	var apiURL = url.resolve(program.travisApi, path);
	var mimeType = 'application/vnd.travis-ci.2+json';
	if (opts.chunked) {
		mimeType += '; chunked=true';
	}
	return request({
		url: apiURL,
		encoding: 'utf8',
		pool: false,
		headers: {
			'User-Agent': getUserAgent(),
			'Accept': mimeType
		},
		retries: opts.retries,
		delay: opts.delay,
		timeout: opts.timeout
	}).then(function(resp) {
		if (resp.response.statusCode !== 200) {
			throw new Error("Bad status: "+resp.response.statusCode);
		}
		return opts.raw ? resp.body : JSON.parse(resp.body);
	});
};

var getTravisConfig = function() {
	return apiRequest('/config').get('config');
};

var getTravisBuild = function(repo, branch, retry) {
	return apiRequest('/repos/'+repo+'/branches/'+branch, {
		timeout: 30 * 1000,
		delay:   5 * 1000, /* check every 5 seconds */
		retries: retry ? 3 * 12 : 0 /* for up to 3 minutes */
	}).get('branch');
};

var getTravisJob = function(job_id) {
	return apiRequest('/jobs/'+job_id).get('job');
};

var getTravisLog = function(log_id, chunked) {
	return apiRequest('/logs/'+log_id, {
		chunked: chunked,
	}).get('log');
};

var Log = function() {
	this.chunks = [];
	this.next = 1;
	this._last = -1;
};
Log.prototype.add = function(n, data) {
	this.chunks[n] = data;
	this.advance();
	return (n === this._last);
};
Log.prototype.advance = function() {
	while (this.chunks[this.next] !== undefined) {
		process.stdout.write(this.chunks[this.next++]);
	}
};
Log.prototype.last = function(n) {
	this._last = n;
};

var doit = function() {
	var state = {}, pusher;
	var initState = function() {
		return Promise.all([
			getTravisConfig().then(function(config) {
				state.config = config;
				pusher = new Pusher(state.config.pusher.key);
				if (false) { /* debugging */
					pusher.connection.on('message', function(message) {
						console.log('GOT PUSHER MESSAGE', message);
					});
				}
			}),
			getGitHead().then(function(hash) {
				state.githash = hash;
				state.branch = getBranchName(hash);
			}),
			getRepoName().then(function(repo) {
				state.repo = repo;
			})
		]).then(function() {
			// check to see if builds on this branch already exist
			return getTravisBuild(state.repo, state.branch, false).catch(function(e) {
				// doesn't yet exist; push to github and then retry query until
				// we see the build show up in travis.
				return ensureBranch(state.repo, state.branch).then(function() {
					return getTravisBuild(state.repo, state.branch, true);
				});
			});
		}).then(function(build) {
			// ok, we've got a build on this branch!
			state.build = build;
			return state;
		});
	};
	var watchJob = function(job_num) {
		return Promise.resolve().then(function() {
			state.job_num = job_num;
			state.job_id = state.build.job_ids[state.job_num - 1];
			console.log(
				'== Travis build', state.build.number+'.'+state.job_num, '=='
			);
			// check job #
			if (!state.job_id) {
				throw new Error(
					"Bad job number: " + state.job_num +
						" ("+state.build.job_ids.length+" jobs)"
				);
			}
			return getTravisJob(state.job_id);
		}).then(function(job) {
			state.job = job;
			var log = new Log();
			if (/^(passed|failed)$/.test(state.job.state || '')) {
				// fetch all the logs, easy-peasy
				return getTravisLog(state.job.log_id, false/*not chunked*/).then(function(resp_log) {
					log.last(1);
					log.add(1, resp_log.body);
				});
			}
			// hm, we need to stream the logs.
			var channelName = 'job-' + state.job_id;
			var channel = pusher.subscribe(channelName);
			var streamResolve;
			var handleChunk = function(data) {
				var content = data._log !== undefined ? data._log : data.content;
				if (data.final) {
					log.last(data.number + 1); // weird!
				}
				var last = log.add(data.number, content);
				if (last) { streamResolve(); }
			};
			return Promise.all([
				// fetch chunked log to fill in gaps
				new Promise(function(resolve, reject) {
					channel.bind('pusher:subscription_succeeded', function() {
						getTravisLog(state.job.log_id, true /* chunked */).then(function(resp_log) {
							resp_log.parts.forEach(handleChunk);
						}).then(resolve, reject);
					});
				}),
				// tail log for this job
				new Promise(function(resolve, reject) {
					streamResolve = resolve;
					channel.bind('job:log', handleChunk);
					channel.bind('job:finished', streamResolve);
				}).then(function() {
					pusher.unsubscribe(channelName);
				})
			]);
		}).then(function() {
			return getTravisJob(state.job_id);
		}).then(function(job) {
			state.job = job;
			return state.job.state;
		});
	};
	var cleanup = function() {
		return Promise.resolve().then(function() {
			pusher.disconnect();
			return getTravisBuild(state.repo, state.branch, false);
		}).then(function(build) {
			state.build = build;
			// if build is complete (all jobs), we can clean up the branch
			if (/^(passed|failed)$/.test(state.build.state || '')) {
				return deleteBranch(state.repo, state.branch).catch(function(e) {
					/* ignore failure, maybe already deleted */
				});
			}
		});
	};

	// ok, put all the pieces together
	return initState().then(function() {
		if (program.job !== undefined) {
			// watch one particular job
			return watchJob((+program.job));
		}
		// watch all jobs
		return Promise.reduce(state.build.job_ids, function(_,jid,index) {
			return watchJob(index + 1);
		}, null);
	}).then(function() {
		return cleanup();
	}).then(function() {
		if (program.job !== undefined) {
			// return our job status
			return state.job.state;
		}
		// return our build status
		return state.build.state;
	});
};

doit().then(function(status) {
	console.log(json.name + ': '+ status);
	if (status !== 'passed') {
		process.exit(1);
	} else {
		process.exit(0);
	}
}).done();
<|endoftext|>


<commit_before>var isBrowser = (typeof window !== 'undefined');
var Packery = isBrowser ? window.Packery || require('packery') : null;
var imagesloaded = isBrowser ? require('imagesloaded') : null;
var refName = 'packeryContainer';

function PackeryComponent(React) {
    return React.createClass({
        packery: false,

        domChildren: [],

        displayName: 'PackeryComponent',

        propTypes: {
            disableImagesLoaded: React.PropTypes.bool,
            options: React.PropTypes.object
        },

        getDefaultProps: function() {
            return {
                disableImagesLoaded: false,
                options: {},
                className: '',
                elementType: 'div'
            };
        },

        initializePackery: function(force) {
            if (!this.packery || force) {
                this.packery = new Packery(
                    this.refs[refName],
                    this.props.options
                );

                this.domChildren = this.getNewDomChildren();
            }
        },

        getNewDomChildren: function() {
            var node = this.refs[refName];
            var children = this.props.options.itemSelector ? node.querySelectorAll(this.props.options.itemSelector) : node.children;
            return Array.prototype.slice.call(children);
        },

        diffDomChildren: function() {
            var oldChildren = this.domChildren.filter(function(element) {
                /*
                 * take only elements attached to DOM
                 * (aka the parent is the packery container, not null)
                 */
                return !!element.parentNode;
            });

            var newChildren = this.getNewDomChildren();

            var removed = oldChildren.filter(function(oldChild) {
                return !~newChildren.indexOf(oldChild);
            });

            var domDiff = newChildren.filter(function(newChild) {
                return !~oldChildren.indexOf(newChild);
            });

            var beginningIndex = 0;

            // get everything added to the beginning of the DOMNode list
            var prepended = domDiff.filter(function(newChild, i) {
                var prepend = (beginningIndex === newChildren.indexOf(newChild));

                if (prepend) {
                    // increase the index
                    beginningIndex++;
                }

                return prepend;
            });

            // we assume that everything else is appended
            var appended = domDiff.filter(function(el) {
                return prepended.indexOf(el) === -1;
            });

            /*
             * otherwise we reverse it because so we're going through the list picking off the items that
             * have been added at the end of the list. this complex logic is preserved in case it needs to be
             * invoked
             *
             * var endingIndex = newChildren.length - 1;
             *
             * domDiff.reverse().filter(function(newChild, i){
             *     var append = endingIndex == newChildren.indexOf(newChild);
             *
             *     if (append) {
             *         endingIndex--;
             *     }
             *
             *     return append;
             * });
             */

            // get everything added to the end of the DOMNode list
            var moved = [];

            if (removed.length === 0) {
                moved = oldChildren.filter(function(child, index) {
                    return index !== newChildren.indexOf(child);
                });
            }

            this.domChildren = newChildren;

            return {
                old: oldChildren,
                new: newChildren,
                removed: removed,
                appended: appended,
                prepended: prepended,
                moved: moved
            };
        },

        performLayout: function() {
            var diff = this.diffDomChildren();

            if (diff.removed.length > 0) {
                this.packery.remove(diff.removed);
                this.packery.reloadItems();
            }

            if (diff.appended.length > 0) {
                this.packery.appended(diff.appended);
                this.packery.reloadItems();
            }

            if (diff.prepended.length > 0) {
                this.packery.prepended(diff.prepended);
            }

            if (diff.moved.length > 0) {
                this.packery.reloadItems();
            }

            this.packery.layout();
        },

        imagesLoaded: function() {
            if (this.props.disableImagesLoaded) return;

            imagesloaded(
                this.refs[refName],
                function(instance) {
                    this.packery.layout();
                }.bind(this)
            );
        },

        componentDidMount: function() {
            this.initializePackery();
            this.imagesLoaded();
        },

        componentDidUpdate: function() {
            this.performLayout();
            this.imagesLoaded();
        },

        componentWillReceiveProps: function() {
            this._timer = setTimeout(function() {
                this.packery.reloadItems();
                this.isMounted && this.isMounted() && this.forceUpdate();
            }.bind(this), 0);
        },

        componentWillUnmount: function() {
            clearTimeout(this._timer);
        },

        render: function() {
            return React.createElement(this.props.elementType, {
                className: this.props.className,
                ref: refName
            }, this.props.children);
        }
    })
}

module.exports = PackeryComponent;
<commit_msg>added draggabilly support<commit_after>var React = require('react');
var ReactDOM = require('react-dom');

var Packery = require('packery');
var imagesloaded = require('imagesloaded');
var Draggabilly = require('draggabilly');

var refName = 'packeryContainer';

var PackeryClass = React.createClass({

        packery: false,

        domChildren: [],

        displayName: 'PackeryComponent',

        propTypes: {
            disableImagesLoaded: React.PropTypes.bool,
            options: React.PropTypes.object
        },

        getDefaultProps: function() {
            return {
                disableImagesLoaded: false,
                options: {},
                className: '',
                elementType: 'div'
            };
        },

        initializePackery: function(force) {
            if (!this.packery || force) {
                this.packery = new Packery(
                    this.refs[refName],
                    this.props.options
                );

                this.packery.getItemElements().forEach( this.makeEachDraggable );

                this.domChildren = this.getNewDomChildren();
            }
        },

        getNewDomChildren: function() {
            var node = this.refs[refName];
            var children = this.props.options.itemSelector ? node.querySelectorAll(this.props.options.itemSelector) : node.children;
            return Array.prototype.slice.call(children);
        },

        makeEachDraggable( itemElem ) {
          // make element draggable with Draggabilly
          var draggie = new Draggabilly( itemElem );
          // bind Draggabilly events to Packery
          this.packery.bindDraggabillyEvents( draggie );
        },

        diffDomChildren: function() {
            var oldChildren = this.domChildren.filter(function(element) {
                /*
                 * take only elements attached to DOM
                 * (aka the parent is the packery container, not null)
                 */
                return !!element.parentNode;
            });

            var newChildren = this.getNewDomChildren();

            var removed = oldChildren.filter(function(oldChild) {
                return !~newChildren.indexOf(oldChild);
            });

            var domDiff = newChildren.filter(function(newChild) {
                return !~oldChildren.indexOf(newChild);
            });

            var beginningIndex = 0;

            // get everything added to the beginning of the DOMNode list
            var prepended = domDiff.filter(function(newChild, i) {
                var prepend = (beginningIndex === newChildren.indexOf(newChild));

                if (prepend) {
                    // increase the index
                    beginningIndex++;
                }

                return prepend;
            });

            // we assume that everything else is appended
            var appended = domDiff.filter(function(el) {
                return prepended.indexOf(el) === -1;
            });

            /*
             * otherwise we reverse it because so we're going through the list picking off the items that
             * have been added at the end of the list. this complex logic is preserved in case it needs to be
             * invoked
             *
             * var endingIndex = newChildren.length - 1;
             *
             * domDiff.reverse().filter(function(newChild, i){
             *     var append = endingIndex == newChildren.indexOf(newChild);
             *
             *     if (append) {
             *         endingIndex--;
             *     }
             *
             *     return append;
             * });
             */

            // get everything added to the end of the DOMNode list
            var moved = [];

            if (removed.length === 0) {
                moved = oldChildren.filter(function(child, index) {
                    return index !== newChildren.indexOf(child);
                });
            }

            this.domChildren = newChildren;

            return {
                old: oldChildren,
                new: newChildren,
                removed: removed,
                appended: appended,
                prepended: prepended,
                moved: moved
            };
        },

        performLayout: function() {
            var diff = this.diffDomChildren();

            if (diff.removed.length > 0) {
                this.packery.remove(diff.removed);
                this.packery.reloadItems();
            }

            if (diff.appended.length > 0) {
                this.packery.appended(diff.appended);
                diff.appended.forEach( this.makeEachDraggable );
                this.packery.reloadItems();
            }

            if (diff.prepended.length > 0) {
                this.packery.prepended(diff.prepended);
            }

            if (diff.moved.length > 0) {
                this.packery.reloadItems();
            }

            this.packery.layout();
        },

        imagesLoaded: function() {
            if (this.props.disableImagesLoaded) return;

            imagesloaded(
                this.refs[refName],
                function(instance) {
                    this.packery.layout();
                }.bind(this)
            );
        },

        componentDidMount: function() {
            this.initializePackery();
            this.imagesLoaded();
        },

        componentDidUpdate: function() {
            this.performLayout();
            this.imagesLoaded();
        },

        componentWillReceiveProps: function() {
            this._timer = setTimeout(function() {
                this.packery.reloadItems();
                this.isMounted && this.isMounted() && this.forceUpdate();
            }.bind(this), 0);
        },

        componentWillUnmount: function() {
            clearTimeout(this._timer);
        },

        render: function() {
            return React.createElement(this.props.elementType, {
                className: this.props.className,
                ref: refName
            }, this.props.children);
        }
  });

module.exports = PackeryClass;
<|endoftext|>


<commit_before>var $ = require('jquery');

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var Enemy = require("./enemies.js");
var Bluecifer = require('./bluecifer');
var enemy2 = new Enemy();
var enemy = new Enemy();
var bluecifer = new Bluecifer (150, 150, 50, 41);

function newRandomPlane (ctx) {
  var plane = new Enemy(this.x, this.y, this.width, this.height);
  plane.draw(ctx);
}

var enemiesArray = [];

setInterval(function () {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  bluecifer.draw(ctx);
  enemy.draw(ctx);
  enemiesArray.push(enemy);
  enemy2.draw(ctx);
  enemy.moveLeftRandom();
  enemy2.moveLeftRandom();
  bluecifer.gravity();
  for (var i = 0; i < enemiesArray.length; i++) {
    if (enemiesArray[i].x < 0) {
      enemiesArray.splice(0, 2);

    }
  }

  // var x = Math.random();
  // if (x > 0.95) {
  //
  // }
  // Zeus.draw(ctx)
  // Zeus.characterMove() -- gravity enemies.move()

  //if no plane on screen, generate a new plane and move it left
}, 10);

$(document).on('keydown', function (e) {
  var code = e.keyCode || e.which;
  if (code === 32) {
    bluecifer.moveUp();
  } else {
    return false;
  }
});

function createMultiplePlanes () {

}

// Zeus() {
// this.enemiesArray = []
// this.bluecifer = new Bluecifer()
//
// }

// if (bluecifer.collision(enemy)) {
//
// }
//Zeus.draw() {
 //this.enemies.forEach() {
 //enemy.draw()
 //nemy.move()
//}
// this.bluecifer.draw()
//}
<commit_msg>got a plane creator to sort of work<commit_after>var $ = require('jquery');

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var Enemy = require("./enemies.js");
var Bluecifer = require('./bluecifer');
var enemy3 = new Enemy();
var enemy2 = new Enemy();
var enemy = new Enemy();
var bluecifer = new Bluecifer (150, 150, 50, 41);

var enemiesArray = [];

setInterval(function () {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  bluecifer.draw(ctx);
  enemy.draw(ctx);
  enemiesArray.push(enemy);
  enemy2.draw(ctx);
  enemiesArray.push(enemy);
  enemy.moveLeftRandom();
  enemy2.moveLeftRandom();
  bluecifer.gravity();
  for (var i = 0; i < enemiesArray.length; i++) {
    if (enemiesArray[i].x < 0) {
      enemiesArray.splice(0, 2);
    }
  }
  if (enemiesArray[0] === undefined) {
    setTimeout(function () {
      enemy3.draw(ctx);
      enemy3.moveLeftRandom();
      // enemiesArray.push(enemy3);
    }, 500);

  }

  // setTimeout(function(){ alert("Hello"); }, 3000);

  // var x = Math.random();
  // if (x > 0.95) {
  //
  // }
  // Zeus.draw(ctx)
  // Zeus.characterMove() -- gravity enemies.move()

  //if no plane on screen, generate a new plane and move it left
}, 10);

$(document).on('keydown', function (e) {
  var code = e.keyCode || e.which;
  if (code === 32) {
    bluecifer.moveUp();
  } else {
    return false;
  }
});

function createMultiplePlanes () {

}

// Zeus() {
// this.enemiesArray = []
// this.bluecifer = new Bluecifer()
//
// }

// if (bluecifer.collision(enemy)) {
//
// }
//Zeus.draw() {
 //this.enemies.forEach() {
 //enemy.draw()
 //nemy.move()
//}
// this.bluecifer.draw()
//}
<|endoftext|>


<commit_before>/*
 * Vecna
 * https://github.com/eburgos/Vecna
 *
 * Copyright (c) 2015 goliatone
 * Licensed under the MIT license.
 */

'use strict';

var debug = require('debug')('vcn');
var extend = require('gextend');
var _inherit = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var Promise = require('bluebird');
var tildify = require('tildify');
var untildify = require('untildify');
var crypto = require('crypto');
var fs = Promise.promisifyAll(require('fs'));
var tmp = require('tmp');
var awscreds = require('./get-aws-credentials');

var DEFAULTS = {
    autoinitialize: true,
    algorithm: 'aes-256-ctr',
    //bucket needs to be unique across users/orgs
    bucket: 'goliatone.vecna.io',
    groupId: 'secrets',
    extension: 'vcn'
};

function Vecna(config){
    EventEmitter.call(this);
    config = extend({}, this.constructor.DEFAULTS, config);

    if(config.autoinitialize ) this.init(config);
}

_inherit(Vecna, EventEmitter);

Vecna.DEFAULTS = DEFAULTS;

Vecna.prototype.init = function(config){
    if(this.initialized) return;
    this.initialized = true;

    extend(this, this.constructor.DEFAULTS, config);
};

Vecna.prototype.put = function(id, password, filepath) {
    var targetpath = this.getPath(id);

    var self = this;

    return fs.readFileAsync(filepath, 'utf8').then(function(content){
        debug('content', content);

        var VO = {
            content: content,
            path: tildify(filepath),
        };

        content = JSON.stringify(VO);

        var cypher = self.encrypt(content, password);

        var tmpfile = tmp.fileSync();
        debug('tmpfile', tmpfile.name);
        debug('content', cypher);
        return fs.writeFileAsync(tmpfile.name, cypher, 'utf8').then(function(){
            awscreds.knox(self.bucket).then(function(client){
                debug('= here', targetpath);
                return client.putFileAsync(tmpfile.name, targetpath).then(function(res){
                    debug('DONE', res);
                    res.resume();
                }).catch(function(e){
                    console.error('ERROR putFileAsync', e);
                    return e;
                });
            }).catch(function(e){
                //TODO: Make sure we get an error code from credential finder
                console.error('AWS credentials not found');
                debug(e);
                return e;
            });
        }).catch(function(e){
            console.error('ERRR fsWriteAsync', e);
            return e;
        });
    });
};

Vecna.prototype.get = function(id, password, filepath){
    var targetpath = this.getPath(id);

    var self = this;

    return awscreds.knox(self.bucket).then(function(client){
        return client.getFileAsync(targetpath).then(function(res){

            return new Promise(function(resolve, reject){
                if(res.statusCode > 400){
                    return reject(new Error('Failed request: StatusCode ' + res.statusCode));
                }

                res.setEncoding('utf8');
                var data = '';

                res.on('data', function(chunk){
                    data += chunk;
                });

                res.on('end', function(){
                    data = self.decrypt(data, password);
                    debug('data');

                    var VO = JSON.parse(data);
                    VO.path = untildify(VO.path || '');

                    fs.writeFileAsync(filepath, VO.content).then(function(){
                        resolve(VO.content);
                    }).catch(function(e){
                        reject(e);
                    });
                });

                res.on('error', function(err){
                    reject(err);
                });
            });
        }).catch(function(e){
            console.log('ERROR 2', e);
        });
    }).catch(function(e){
        console.log('ERROR', e);
    });
};

Vecna.prototype.list = function(id, password){
    var self = this;

    return awscreds.knox(self.bucket).then(function(client){
        return client.listAsync({ prefix: self.groupId }).then(function(data){
            return data.Contents;
        });
    });
};

Vecna.prototype.del = function(id, password){
    //TODO: do we need a way to secure deletes? or simply by having
    //aws credentials to access the bucket we are cleared?
    var targetpath =  '/' + this.getPath(id);

    return awscreds.knox(this.bucket).then(function(client){
        return client.deleteFileAsync(targetpath).then(function(res){
            if(res.statusCode === 204) {
                console.log(Object.keys(res));
                console.log(res.req.url, res.statusMessage)
                return Promise.reject(new Error('File "'+ targetpath + '" not found. ' + res.statusMessage));
            }

            console.log(res.statusCode);
            console.log('File %s deleted' , targetpath);
            res.resume();
        }).catch(function(err){
            console.error(err);
        });
    });
};

Vecna.prototype.encrypt = function(content, password){
    var cipher = crypto.createCipher( this.algorithm, password);
    var crypted = cipher.update(content,'utf8','hex');
    crypted += cipher.final('hex');
    return crypted;
};

Vecna.prototype.decrypt = function(content, password){
    var decipher = crypto.createDecipher( this.algorithm, password);
    var dec = decipher.update(content, 'hex', 'utf8');
    dec += decipher.final('utf8');
    return dec;
};

Vecna.prototype.getPath = function(id){
    return this.groupId + '/' + id + '.' + this.extension;
};

Vecna.prototype.logger = console;

/**
 * Exports module
 */
module.exports = Vecna;

Vecna.exec = function(command, options){
    if(!command.match(/put|get|del|list/)) Promise.reject(new Error('Command not supported'));

    var config = extract(options, Object.keys(Vecna.DEFAULTS));
    var vcn = new Vecna(config);

    var promise;

    switch (command) {
        case 'put':
            promise = vcn.put(options.id, options.password, options.filepath);
        break;
        case 'get':
            promise = vcn.get(options.id, options.password, options.filepath);
        break;
        case 'del':
            promise = vcn.del(options.id, options.password);
        break;
        case 'list':
            promise = vcn.list(options.id, options.password);
        break;
    }
    return promise;
};
/*
var test = new Vecna({
    bucket: 'goliatone.vecna.io'
});
// test.put('menagerie', 'peperone' , '/Users/goliatone/Development/NODEJS/vecna/package.json').then(function(){
//     console.log('DONE');
// });
//
test.get('menagerie', 'peperone').then(function(c){
    console.log('DONE', JSON.parse(JSON.parse(c).content));
});
*/

function extract(src, keys){
    return keys.reduce(function(out, key){
        if(src.hasOwnProperty(key)) out[key] = src[key];
        return out;
    }, {});
}
<commit_msg>clean up<commit_after>/*
 * Vecna
 * https://github.com/eburgos/Vecna
 *
 * Copyright (c) 2015 goliatone
 * Licensed under the MIT license.
 */

'use strict';

var debug = require('debug')('vcn');
var extend = require('gextend');
var _inherit = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var Promise = require('bluebird');
var tildify = require('tildify');
var untildify = require('untildify');
var crypto = require('crypto');
var fs = Promise.promisifyAll(require('fs'));
var tmp = require('tmp');
var awscreds = require('./get-aws-credentials');

var DEFAULTS = {
    autoinitialize: true,
    algorithm: 'aes-256-ctr',
    //bucket needs to be unique across users/orgs
    bucket: 'goliatone.vecna.io',
    groupId: 'secrets',
    extension: 'vcn'
};

function Vecna(config){
    EventEmitter.call(this);
    config = extend({}, this.constructor.DEFAULTS, config);

    if(config.autoinitialize ) this.init(config);
}

_inherit(Vecna, EventEmitter);

Vecna.DEFAULTS = DEFAULTS;

Vecna.prototype.init = function(config){
    if(this.initialized) return;
    this.initialized = true;

    extend(this, this.constructor.DEFAULTS, config);
};

Vecna.prototype.put = function(id, password, filepath) {
    var targetpath = this.getPath(id);

    var self = this;

    return fs.readFileAsync(filepath, 'utf8').then(function(content){
        debug('content', content);

        var VO = {
            content: content,
            path: tildify(filepath),
        };

        content = JSON.stringify(VO);

        var cypher = self.encrypt(content, password);

        var tmpfile = tmp.fileSync();
        debug('tmpfile', tmpfile.name);
        debug('content', cypher);
        return fs.writeFileAsync(tmpfile.name, cypher, 'utf8').then(function(){
            awscreds.knox(self.bucket).then(function(client){
                debug('= here', targetpath);
                return client.putFileAsync(tmpfile.name, targetpath).then(function(res){
                    debug('DONE', res);
                    res.resume();
                }).catch(function(e){
                    console.error('ERROR putFileAsync', e);
                    return e;
                });
            }).catch(function(e){
                //TODO: Make sure we get an error code from credential finder
                console.error('AWS credentials not found');
                debug(e);
                return e;
            });
        }).catch(function(e){
            console.error('ERRR fsWriteAsync', e);
            return e;
        });
    });
};

Vecna.prototype.get = function(id, password, filepath){
    var targetpath = this.getPath(id);

    var self = this;

    return awscreds.knox(self.bucket).then(function(client){
        return client.getFileAsync(targetpath).then(function(res){

            return new Promise(function(resolve, reject){
                if(res.statusCode > 400){
                    return reject(new Error('Failed request: StatusCode ' + res.statusCode));
                }

                res.setEncoding('utf8');
                var data = '';

                res.on('data', function(chunk){
                    data += chunk;
                });

                res.on('end', function(){
                    data = self.decrypt(data, password);
                    debug('data');

                    var VO = JSON.parse(data);
                    VO.path = untildify(VO.path || '');

                    fs.writeFileAsync(filepath, VO.content).then(function(){
                        resolve(VO.content);
                    }).catch(function(e){
                        reject(e);
                    });
                });

                res.on('error', function(err){
                    reject(err);
                });
            });
        }).catch(function(e){
            console.log('ERROR 2', e);
        });
    }).catch(function(e){
        console.log('ERROR', e);
    });
};

Vecna.prototype.list = function(id, password){
    var self = this;

    return awscreds.knox(self.bucket).then(function(client){
        return client.listAsync({ prefix: self.groupId }).then(function(data){
            return data.Contents;
        });
    });
};

Vecna.prototype.del = function(id, password){
    //TODO: do we need a way to secure deletes? or simply by having
    //aws credentials to access the bucket we are cleared?
    var targetpath =  '/' + this.getPath(id);

    return awscreds.knox(this.bucket).then(function(client){
        return client.deleteFileAsync(targetpath).then(function(res){
            if(res.statusCode === 204) {
                console.log(Object.keys(res));
                console.log(res.req.url, res.statusMessage)
                return Promise.reject(new Error('File "'+ targetpath + '" not found. ' + res.statusMessage));
            }

            console.log(res.statusCode);
            console.log('File %s deleted' , targetpath);
            res.resume();
        }).catch(function(err){
            console.error(err);
        });
    });
};

Vecna.prototype.encrypt = function(content, password){
    var cipher = crypto.createCipher( this.algorithm, password);
    var crypted = cipher.update(content,'utf8','hex');
    crypted += cipher.final('hex');
    return crypted;
};

Vecna.prototype.decrypt = function(content, password){
    var decipher = crypto.createDecipher( this.algorithm, password);
    var dec = decipher.update(content, 'hex', 'utf8');
    dec += decipher.final('utf8');
    return dec;
};

Vecna.prototype.getPath = function(id){
    return this.groupId + '/' + id + '.' + this.extension;
};

Vecna.prototype.logger = console;

/**
 * Exports module
 */
module.exports = Vecna;

Vecna.exec = function(command, options){
    if(!command.match(/put|get|del|list/)) Promise.reject(new Error('Command not supported'));

    var config = extract(options, Object.keys(Vecna.DEFAULTS));
    var vcn = new Vecna(config);

    var promise;

    switch (command) {
        case 'put':
            promise = vcn.put(options.id, options.password, options.filepath);
        break;
        case 'get':
            promise = vcn.get(options.id, options.password, options.filepath);
        break;
        case 'del':
            promise = vcn.del(options.id, options.password);
        break;
        case 'list':
            promise = vcn.list(options.id, options.password);
        break;
    }
    return promise;
};

function extract(src, keys){
    return keys.reduce(function(out, key){
        if(src.hasOwnProperty(key)) out[key] = src[key];
        return out;
    }, {});
}
<|endoftext|>


<commit_before>const Stream = require('stream');
const log = require('gelf-pro');
const CircularJSON = require('circular-json');

class GoodHapiGraylog extends Stream.Writable {
  constructor({ host, port, facility, hostname, adapter = 'udp' }) {
    super({ objectMode: true, decodeStrings: false });
    this.once('finish', () => {
      this.write();
    });
    this.log = log.setConfig({
      fields: { facility },
      adapterName: adapter,
      adapterOptions: {
        host,
        port
      }
    });
  }
  _write(data, encoding, callback) {
    const dataString = CircularJSON.stringify(data);
    this.log.info(dataString);
    callback();
  }
}

module.exports = GoodHapiGraylog;
<commit_msg>add hostname to message fields<commit_after>const Stream = require('stream');
const log = require('gelf-pro');
const CircularJSON = require('circular-json');

class GoodHapiGraylog extends Stream.Writable {
  constructor({ host, port, facility, hostname, adapter = 'udp' }) {
    super({ objectMode: true, decodeStrings: false });
    this.once('finish', () => {
      this.write();
    });
    this.log = log.setConfig({
      fields: { facility, host:hostname },
      adapterName: adapter,
      adapterOptions: {
        host,
        port
      }
    });
  }
  _write(data, encoding, callback) {
    const dataString = CircularJSON.stringify(data);
    this.log.info(dataString);
    callback();
  }
}

module.exports = GoodHapiGraylog;
<|endoftext|>


<commit_before>/**
 * @jsx React.DOM
 */
'use strict';
var Grid = require('./Grid');
var React = require('React');

module.exports = Grid;

var data = [];
for (var i = 0; i < 2000; i++) {
	data.push({
		'key': i,
		'supplier':{'value':'Supplier ' + i, 'editing':true},
		'format': 'fmt ' + i,
		'start':'start',
		'end':'end',
		'price':i });
};

function rows(start, end) {
  return data.slice(start, end);
}

var columns = [

  {
  	idx: 0,
    name: 'Supplier',
    key: 'supplier',
    width: 300,
    locked: true,
  },
  {
  	idx: 1,
    name: 'Format',
    key: 'format',
    width: 350,
  },
  {
  	idx: 2,
    name: 'Start',
    key: 'start',
    width: 250,
  },
  {
  	idx: 3,
    name: 'End',
    key: 'end',
    width: 250,
  },
  {
  	idx: 4,
    name: 'Cost',
    key: 'cost',
    width: 200,
  }
];

var ExcelGrid = React.createClass({
	getInitialState: function() {
		return {
			ActiveCell:{ row:2,cell:2 },
			NoRows:data.length,
			rowHeight:40
		};
	},
	render: function() {
		return (

			<Grid
				columns={columns}
				rows={rows}
				ActiveCell={this.state.ActiveCell}

				length={this.state.NoRows}
				rowHeight={this.state.rowHeight}
    		/>

    	);

	},
	handleGridClick: function(ev) {

	},
	handleGridDoubleClick: function(ev) {

	},
	componentDidMount: function() {
		//handling outside of react
		//reacts keyup is only bound, from what I can tell, if you are focussed on a user input
		//generally, that makes sense (and guess may be more performant)
		//but for our purpose see https://github.com/facebook/react/issues/1795
		window.addEventListener('keyup', this.handleKeyUp);
	},
	componentWillUnmount: function() {
		window.removeEventListener('keyup', this.handleKeyUp);
	},
	handleKeyUp: function(ev) {
		var active = this.state.ActiveCell;
		var change = false;
		if(ev.keyCode == 38) { //UP
			active.row -= 1;
			change=true;
		}
		if(ev.keyCode == 40) { //DOWN
			active.row += 1;
			change=true;
		}
		if(ev.keyCode == 37) { //LEFT
			active.cell -= 1;
			change=true;
		}
		if(ev.keyCode == 39) { //RIGHT
			active.cell += 1;
			change=true;
		}
		if(change) {
			//sense check bounds
			if(active.cell < 0) {active.cell = columns.length;}
			if(active.cell >= columns.length) {active.cell = 0;}
			if(active.row < 0) {active.row = data.length;}
			if(active.row >= data.length) {active.row = 0;}
			this.setState({ActiveCell : active});
		}
	}
});

var renderGrid = function() {
	var grid = ExcelGrid({});
    React.renderComponent(grid,
    	document.getElementById('example'));
};
renderGrid();

//force a global react object, for chrome dev tools if nothing else
window.React = window.React || React;

<commit_msg>use native keyup<commit_after>/**
 * @jsx React.DOM
 */
'use strict';
var Grid = require('./Grid');
var React = require('React');

module.exports = Grid;

var data = [];
for (var i = 0; i < 2000; i++) {
	data.push({
		'key': i,
		'supplier':{'value':'Supplier ' + i, 'editing':true},
		'format': 'fmt ' + i,
		'start':'start',
		'end':'end',
		'price':i });
};

function rows(start, end) {
  return data.slice(start, end);
}

var columns = [

  {
  	idx: 0,
    name: 'Supplier',
    key: 'supplier',
    width: 300,
    locked: true,
  },
  {
  	idx: 1,
    name: 'Format',
    key: 'format',
    width: 350,
  },
  {
  	idx: 2,
    name: 'Start',
    key: 'start',
    width: 250,
  },
  {
  	idx: 3,
    name: 'End',
    key: 'end',
    width: 250,
  },
  {
  	idx: 4,
    name: 'Cost',
    key: 'cost',
    width: 200,
  }
];

var ExcelGrid = React.createClass({
	getInitialState: function() {
		return {
			ActiveCell:{ row:2,cell:2 },
			NoRows:data.length,
			rowHeight:40
		};
	},
	render: function() {
		return (

			<Grid
				columns={columns}
				rows={rows}
				ActiveCell={this.state.ActiveCell}

				length={this.state.NoRows}
				rowHeight={this.state.rowHeight}
    		/>

    	);

	},
	handleGridClick: function(ev) {

	},
	handleGridDoubleClick: function(ev) {

	},
	handleKeyUp: function(ev) {
		var active = this.state.ActiveCell;
		var change = false;
		if(ev.keyCode == 38) { //UP
			active.row -= 1;
			change=true;
		}
		if(ev.keyCode == 40) { //DOWN
			active.row += 1;
			change=true;
		}
		if(ev.keyCode == 37) { //LEFT
			active.cell -= 1;
			change=true;
		}
		if(ev.keyCode == 39) { //RIGHT
			active.cell += 1;
			change=true;
		}
		if(change) {
			//sense check bounds
			if(active.cell < 0) {active.cell = columns.length;}
			if(active.cell >= columns.length) {active.cell = 0;}
			if(active.row < 0) {active.row = data.length;}
			if(active.row >= data.length) {active.row = 0;}
			this.setState({ActiveCell : active});
		}
	}
});

var renderGrid = function() {
	var grid = ExcelGrid({});
    React.renderComponent(grid,
    	document.getElementById('example'));
};
renderGrid();

//force a global react object, for chrome dev tools if nothing else
window.React = window.React || React;
<|endoftext|>