<commit_before>var cp = require('child_process')
var findRoot = require('find-root')
var glob = require('glob')
var Minimatch = require('minimatch').Minimatch
var parallel = require('run-parallel')
var path = require('path')
var split = require('split')
var uniq = require('uniq')

var JSCS_RC = path.join(__dirname, 'rc', '.jscsrc')
var JSCS_REPORTER = path.join(__dirname, 'lib', 'jscs-reporter.js')
var JSCS_REPORTER_VERBOSE = path.join(__dirname, 'lib', 'jscs-reporter-verbose.js')

var ESLINT_RC = path.join(__dirname, 'rc', '.eslintrc')
var ESLINT_REPORTER = path.join(__dirname, 'lib', 'eslint-reporter.js')
var ESLINT_REPORTER_VERBOSE = path.join(__dirname, 'lib', 'eslint-reporter-verbose.js')

var DEFAULT_IGNORE = [
  'node_modules/**',
  '.git/**',
  '**/*.min.js',
  '**/bundle.js',
  'coverage/**'
]

var ERROR_RE = /.*?:\d+:\d+/
var FILE_RE = /(.*?):/
var LINE_RE = /.*?:(\d+)/
var COL_RE = /.*?:\d+:(\d+)/

/**
 * JavaScript Standard Style
 * @param {Object} opts                        options object
 * @param {string|Array.<String>} opts.ignore  files to ignore
 * @param {boolean} opts.bare                  show raw linter output (for debugging)
 * @param {string} opts.cwd                    current working directory
 * @param {Array.<string>} files               files to check
 * @param {boolean} opts.stdin                 check text from stdin instead of filesystem
 * @param {boolean} opts.verbose               show error codes
 */
module.exports = function standard (opts) {
  if (!opts) opts = {}
  var errors = []

  var root
  try {
    root = findRoot(process.cwd())
  } catch (e) {}

  var ignore = [].concat(DEFAULT_IGNORE) // globs to ignore

  if (root) {
    var packageOpts = require(path.join(root, 'package.json')).standard
    if (packageOpts) ignore = ignore.concat(packageOpts.ignore)
  }

  var jscsReporter = opts.verbose ? JSCS_REPORTER_VERBOSE : JSCS_REPORTER
  var jscsArgs = ['--config', JSCS_RC, '--reporter', jscsReporter]

  var eslintReporter = opts.verbose ? ESLINT_REPORTER_VERBOSE : ESLINT_REPORTER
  var eslintArgs = ['--config', ESLINT_RC, '--format', eslintReporter]

  if (opts.verbose) {
    jscsArgs.push('--verbose')
  }

  if ((Array.isArray(opts.files) && opts.files.length > 0) || !opts.stdin) {
    var patterns = (Array.isArray(opts.files) && opts.files.length > 0)
      ? opts.files
      : [ '**/*.js' ]

    // traverse filesystem
    if (opts.ignore) ignore = ignore.concat(opts.ignore)

    ignore = ignore.map(function (pattern) {
      return new Minimatch(pattern)
    })

    parallel(patterns.map(function (pattern) {
      return function (cb) {
        glob(pattern, {
          cwd: opts.cwd || process.cwd(),
          nodir: true
        }, cb)
      }
    }), function (err, results) {
      if (err) return error(err)

      // flatten nested arrays
      var files = results.reduce(function (files, result) {
        result.forEach(function (file) {
          files.push(file)
        })
        return files
      }, [])

      // apply ignore patterns
      files = files.filter(function (file) {
        return !ignore.some(function (mm) {
          return mm.match(file)
        })
      })

      // de-dupe
      files = uniq(files)
      if (files.length > 0) {
        jscsArgs = jscsArgs.concat(files)
        eslintArgs = eslintArgs.concat(files)
        lint()
      }
    })
  } else {
    // stdin
    eslintArgs.push('--stdin')
    lint()
  }

  function lint () {
    findBinPaths(function (err, paths) {
      if (err) return error(err)
      parallel([
        spawn.bind(undefined, paths.jscs, jscsArgs),
        spawn.bind(undefined, paths.eslint, eslintArgs)
      ], function (err, r) {
        if (err) return error(err)
        if (r.some(Boolean)) printErrors()
      })
    })
  }

  function findBinPaths (cb) {
    parallel({
      eslint: findBinPath.bind(undefined, 'eslint'),
      jscs: findBinPath.bind(undefined, 'jscs')
    }, cb)
  }

  function findBinPath (bin, cb) {
    var opts = { cwd: __dirname }
    cp.exec('npm run --silent which-' + bin, opts, function (err, stdout, stderr) {
      if (err) return cb(err)
      cb(null, stdout.toString().replace(/\n/g, ''))
    })
  }

  function spawn (command, args, cb) {
    var child = cp.spawn(command, args)
    child.on('error', cb)
    child.on('close', function (code) {
      cb(null, code)
    })
    if (opts.stdin) process.stdin.pipe(child.stdin)
    stderrPipe(child.stdout)
    stderrPipe(child.stderr)
  }

  function stderrPipe (readable) {
    readable
      .pipe(split())
      .on('data', function (line) {
        if (line === '') return
        errors.push(line)
      })
  }

  function printErrors () {
    if (opts.bare) {
      errors.forEach(function (str) {
        console.error(str)
      })
      return
    }

    console.error('Error: Code style check failed:')
    var unexpectedOutput = []
    var errMap = {}
    errors
      .map(function (str) { // normalize stdin "filename"
        return str.replace(/^(<text>|input)/, 'stdin')
      })
      .filter(function (str) {
        // don't process unexpected/malformed output, just print it at the end
        if (!ERROR_RE.test(str)) {
          unexpectedOutput.push(str)
          return false
        }

        // de-duplicate errors
        if (errMap[str]) return false
        errMap[str] = true
        return true
      })
      .sort(function (a, b) {
        // sort by line number (merges output from all linters)
        var fileA = FILE_RE.exec(a)[1]
        var fileB = FILE_RE.exec(b)[1]

        var lineA = Number(LINE_RE.exec(a)[1])
        var lineB = Number(LINE_RE.exec(b)[1])

        var colA = Number(COL_RE.exec(a)[1])
        var colB = Number(COL_RE.exec(b)[1])

        if (fileA !== fileB) return fileA < fileB ? -1 : 1
        if (lineA !== lineB) return lineA - lineB
        return colA - colB
      })
      .forEach(function (str) {
        console.error('  ' + str) // indent
      })

    if (unexpectedOutput.length) console.error('\nUnexpected Linter Output:')
    unexpectedOutput.forEach(function (str) {
      console.error(str)
    })

    process.exit(1)
  }
}

function error (err) {
  console.error(err.stack || err.message || err)
  process.exit(1)
}
<commit_msg>Improve error message<commit_after>var cp = require('child_process')
var findRoot = require('find-root')
var glob = require('glob')
var Minimatch = require('minimatch').Minimatch
var parallel = require('run-parallel')
var path = require('path')
var split = require('split')
var uniq = require('uniq')

var JSCS_RC = path.join(__dirname, 'rc', '.jscsrc')
var JSCS_REPORTER = path.join(__dirname, 'lib', 'jscs-reporter.js')
var JSCS_REPORTER_VERBOSE = path.join(__dirname, 'lib', 'jscs-reporter-verbose.js')

var ESLINT_RC = path.join(__dirname, 'rc', '.eslintrc')
var ESLINT_REPORTER = path.join(__dirname, 'lib', 'eslint-reporter.js')
var ESLINT_REPORTER_VERBOSE = path.join(__dirname, 'lib', 'eslint-reporter-verbose.js')

var DEFAULT_IGNORE = [
  'node_modules/**',
  '.git/**',
  '**/*.min.js',
  '**/bundle.js',
  'coverage/**'
]

var ERROR_RE = /.*?:\d+:\d+/
var FILE_RE = /(.*?):/
var LINE_RE = /.*?:(\d+)/
var COL_RE = /.*?:\d+:(\d+)/

/**
 * JavaScript Standard Style
 * @param {Object} opts                        options object
 * @param {string|Array.<String>} opts.ignore  files to ignore
 * @param {boolean} opts.bare                  show raw linter output (for debugging)
 * @param {string} opts.cwd                    current working directory
 * @param {Array.<string>} files               files to check
 * @param {boolean} opts.stdin                 check text from stdin instead of filesystem
 * @param {boolean} opts.verbose               show error codes
 */
module.exports = function standard (opts) {
  if (!opts) opts = {}
  var errors = []

  var root
  try {
    root = findRoot(process.cwd())
  } catch (e) {}

  var ignore = [].concat(DEFAULT_IGNORE) // globs to ignore

  if (root) {
    var packageOpts = require(path.join(root, 'package.json')).standard
    if (packageOpts) ignore = ignore.concat(packageOpts.ignore)
  }

  var jscsReporter = opts.verbose ? JSCS_REPORTER_VERBOSE : JSCS_REPORTER
  var jscsArgs = ['--config', JSCS_RC, '--reporter', jscsReporter]

  var eslintReporter = opts.verbose ? ESLINT_REPORTER_VERBOSE : ESLINT_REPORTER
  var eslintArgs = ['--config', ESLINT_RC, '--format', eslintReporter]

  if (opts.verbose) {
    jscsArgs.push('--verbose')
  }

  if ((Array.isArray(opts.files) && opts.files.length > 0) || !opts.stdin) {
    var patterns = (Array.isArray(opts.files) && opts.files.length > 0)
      ? opts.files
      : [ '**/*.js' ]

    // traverse filesystem
    if (opts.ignore) ignore = ignore.concat(opts.ignore)

    ignore = ignore.map(function (pattern) {
      return new Minimatch(pattern)
    })

    parallel(patterns.map(function (pattern) {
      return function (cb) {
        glob(pattern, {
          cwd: opts.cwd || process.cwd(),
          nodir: true
        }, cb)
      }
    }), function (err, results) {
      if (err) return error(err)

      // flatten nested arrays
      var files = results.reduce(function (files, result) {
        result.forEach(function (file) {
          files.push(file)
        })
        return files
      }, [])

      // apply ignore patterns
      files = files.filter(function (file) {
        return !ignore.some(function (mm) {
          return mm.match(file)
        })
      })

      // de-dupe
      files = uniq(files)
      if (files.length > 0) {
        jscsArgs = jscsArgs.concat(files)
        eslintArgs = eslintArgs.concat(files)
        lint()
      }
    })
  } else {
    // stdin
    eslintArgs.push('--stdin')
    lint()
  }

  function lint () {
    findBinPaths(function (err, paths) {
      if (err) return error(err)
      parallel([
        spawn.bind(undefined, paths.jscs, jscsArgs),
        spawn.bind(undefined, paths.eslint, eslintArgs)
      ], function (err, r) {
        if (err) return error(err)
        if (r.some(Boolean)) printErrors()
      })
    })
  }

  function findBinPaths (cb) {
    parallel({
      eslint: findBinPath.bind(undefined, 'eslint'),
      jscs: findBinPath.bind(undefined, 'jscs')
    }, cb)
  }

  function findBinPath (bin, cb) {
    var opts = { cwd: __dirname }
    cp.exec('npm run --silent which-' + bin, opts, function (err, stdout, stderr) {
      if (err) return cb(err)
      cb(null, stdout.toString().replace(/\n/g, ''))
    })
  }

  function spawn (command, args, cb) {
    var child = cp.spawn(command, args)
    child.on('error', cb)
    child.on('close', function (code) {
      cb(null, code)
    })
    if (opts.stdin) process.stdin.pipe(child.stdin)
    stderrPipe(child.stdout)
    stderrPipe(child.stderr)
  }

  function stderrPipe (readable) {
    readable
      .pipe(split())
      .on('data', function (line) {
        if (line === '') return
        errors.push(line)
      })
  }

  function printErrors () {
    if (opts.bare) {
      errors.forEach(function (str) {
        console.error(str)
      })
      return
    }

    console.error('Error: Use JavaScript Standard Style (https://github.com/feross/standard)')
    var unexpectedOutput = []
    var errMap = {}
    errors
      .map(function (str) { // normalize stdin "filename"
        return str.replace(/^(<text>|input)/, 'stdin')
      })
      .filter(function (str) {
        // don't process unexpected/malformed output, just print it at the end
        if (!ERROR_RE.test(str)) {
          unexpectedOutput.push(str)
          return false
        }

        // de-duplicate errors
        if (errMap[str]) return false
        errMap[str] = true
        return true
      })
      .sort(function (a, b) {
        // sort by line number (merges output from all linters)
        var fileA = FILE_RE.exec(a)[1]
        var fileB = FILE_RE.exec(b)[1]

        var lineA = Number(LINE_RE.exec(a)[1])
        var lineB = Number(LINE_RE.exec(b)[1])

        var colA = Number(COL_RE.exec(a)[1])
        var colB = Number(COL_RE.exec(b)[1])

        if (fileA !== fileB) return fileA < fileB ? -1 : 1
        if (lineA !== lineB) return lineA - lineB
        return colA - colB
      })
      .forEach(function (str) {
        console.error('  ' + str) // indent
      })

    if (unexpectedOutput.length) console.error('\nUnexpected Linter Output:')
    unexpectedOutput.forEach(function (str) {
      console.error(str)
    })

    process.exit(1)
  }
}

function error (err) {
  console.error(err.stack || err.message || err)
  process.exit(1)
}
<|endoftext|>


<commit_before>
var JSONListStream = require('json-list-stream')
  , inherits = require('util').inherits
  , querystring = require('querystring')
  , xtend = require('xtend')

module.exports = JSONListResponse

JSONListResponse.Query = Query
JSONListResponse.After = After
JSONListResponse.Paging = Paging

function JSONListResponse(options) {
  JSONListStream.call(this)

  this.query = new Query(options.query, options)
  this.after = new (options.After || After)(this.query.after, options)
  this.base = options.base

  this._count = 0

  this.set('query', this.query)
}
inherits(JSONListResponse, JSONListStream)

JSONListResponse.prototype._transform = function (row, chunk, cb) {
  this._count++

  if (this._count > this.query.limit) {
    return cb()
  }

  this.after.add(row)

  return JSONListStream.prototype._transform.apply(this, arguments)
}

JSONListResponse.prototype._flush = function () {
  this.set('paging', new Paging(this))
  return JSONListStream.prototype._flush.apply(this, arguments)
}

function Query(query, options) {
  if (query instanceof Query) return query
  query = query || {}

  options = options || {}

  var defaultLimit = options.defaultLimit || 1000
    , maxLimit = options.maxLimit || 1000

  this.after = query.after || null
  this.limit = Math.min(Math.max( parseInt(query.limit, 10) || defaultLimit, 1), maxLimit)
}

function After(value, options) {
  this.key = options.sortKey || 'id'
  this.value = value || null
}

After.prototype.add = function (row) {
  var value = row[this.key]
  if (!value) return
  this.value = value
}

After.prototype.toString = function () {
  return String(this.value || '')
}

After.prototype.toJSON = function () {
  return this.toString()
}

function Paging(list) {
  this.after = null
  this.next = null
  this.hasMore = list._count > list.query.limit

  this.after = list.after

  if (list.base) {
    var query = xtend(list.query)
    query.after = this.after.toString()

    this.next = list.base +
                '?' +
                querystring.stringify(query)
  }
}
<commit_msg>only add next link if there is more results<commit_after>
var JSONListStream = require('json-list-stream')
  , inherits = require('util').inherits
  , querystring = require('querystring')
  , xtend = require('xtend')

module.exports = JSONListResponse

JSONListResponse.Query = Query
JSONListResponse.After = After
JSONListResponse.Paging = Paging

function JSONListResponse(options) {
  JSONListStream.call(this)

  this.query = new Query(options.query, options)
  this.after = new (options.After || After)(this.query.after, options)
  this.base = options.base

  this._count = 0

  this.set('query', this.query)
}
inherits(JSONListResponse, JSONListStream)

JSONListResponse.prototype._transform = function (row, chunk, cb) {
  this._count++

  if (this._count > this.query.limit) {
    return cb()
  }

  this.after.add(row)

  return JSONListStream.prototype._transform.apply(this, arguments)
}

JSONListResponse.prototype._flush = function () {
  this.set('paging', new Paging(this))
  return JSONListStream.prototype._flush.apply(this, arguments)
}

function Query(query, options) {
  if (query instanceof Query) return query
  query = query || {}

  options = options || {}

  var defaultLimit = options.defaultLimit || 1000
    , maxLimit = options.maxLimit || 1000

  this.after = query.after || null
  this.limit = Math.min(Math.max( parseInt(query.limit, 10) || defaultLimit, 1), maxLimit)
}

function After(value, options) {
  this.key = options.sortKey || 'id'
  this.value = value || null
}

After.prototype.add = function (row) {
  var value = row[this.key]
  if (!value) return
  this.value = value
}

After.prototype.toString = function () {
  return String(this.value || '')
}

After.prototype.toJSON = function () {
  return this.toString()
}

function Paging(list) {
  this.after = null
  this.next = null
  this.hasMore = list._count > list.query.limit

  this.after = list.after

  if (this.hasMore && list.base) {
    var query = xtend(list.query)
    query.after = this.after.toString()

    this.next = list.base +
                '?' +
                querystring.stringify(query)
  }
}
<|endoftext|>


<commit_before>var path = require('path');

module.exports = {
    book: {
        assets: "./book",
        js: ["plugin.js"],
        css: ["plugin.css"]
    },
    hooks: {
        // After html generation
        "page": function(page) {
            if(this.config.options.generator !== 'website') {
                return page;
            }

            var config = this.options.pluginsConfig["edit-link"] || {};

            if (!config.base) {
                throw "ERROR: 'base' value required to generate 'Edit This Page' link. \nFor help, please refer to - https://github.com/rtCamp/gitbook-plugin-edit-link/blob/master/README.md#usage";
            }

            var label = config.label[this.context.config.language] || config.label || "Edit This Page";

            // add  slash at the end if not present
            var base = config.base;
            if(base.slice(-1) != "/") {
                base = base + "/";
            }

            // relative path to the page
            var newPath = path.relative(this.root, page.rawPath);

            // language, if configured
            var lang = "";
            if(this.context.config.language) {
                lang = this.context.config.language + "/";
            }

            rtEditLink = '  ' + label + '';

            page.sections
                .filter(function(section) {
                    return section.type == 'normal';
                })
                .forEach(function(section) {
                    section.content = rtEditLink + section.content;
                });

            return page;
        }
    }
};
<commit_msg>Fixed error when there was no label set in config<commit_after>var path = require('path');

module.exports = {
    book: {
        assets: "./book",
        js: ["plugin.js"],
        css: ["plugin.css"]
    },
    hooks: {
        // After html generation
        "page": function(page) {
            if(this.config.options.generator !== 'website') {
                return page;
            }

            var config = this.options.pluginsConfig["edit-link"] || {};

            if (!config.base) {
                throw "ERROR: 'base' value required to generate 'Edit This Page' link. \nFor help, please refer to - https://github.com/rtCamp/gitbook-plugin-edit-link/blob/master/README.md#usage";
            }

            var label = (config.label && config.label[this.context.config.language]) || config.label || "Edit This Page";

            // add  slash at the end if not present
            var base = config.base;
            if(base.slice(-1) != "/") {
                base = base + "/";
            }

            // relative path to the page
            var newPath = path.relative(this.root, page.rawPath);

            // language, if configured
            var lang = "";
            if(this.context.config.language) {
                lang = this.context.config.language + "/";
            }

            rtEditLink = '  ' + label + '';

            page.sections
                .filter(function(section) {
                    return section.type == 'normal';
                })
                .forEach(function(section) {
                    section.content = rtEditLink + section.content;
                });

            return page;
        }
    }
};
<|endoftext|>


<commit_before>/*!
 * raw-body
 * Copyright(c) 2013-2014 Jonathan Ong
 * Copyright(c) 2014-2015 Douglas Christopher Wilson
 * MIT Licensed
 */

'use strict'

/**
 * Module dependencies.
 * @private
 */

var bytes = require('bytes')
var iconv = require('iconv-lite')
var unpipe = require('unpipe')

/**
 * Module exports.
 * @public
 */

module.exports = getRawBody

/**
 * Get the decoder for a given encoding.
 *
 * @param {string} encoding
 * @private
 */

function getDecoder(encoding) {
  if (!encoding) return null

  try {
    return iconv.getDecoder(encoding)
  } catch (e) {
    var err = makeError('specified encoding unsupported', 'encoding.unsupported')
    err.status = err.statusCode = 415
    err.encoding = encoding
    throw err
  }
}

/**
 * Get the raw body of a stream (typically HTTP).
 *
 * @param {object} stream
 * @param {object|string|function} [options]
 * @param {function} [callback]
 * @public
 */

function getRawBody(stream, options, callback) {
  var done = callback
  var opts = options || {}

  if (options === true || typeof options === 'string') {
    // short cut for encoding
    opts = {
      encoding: options
    }
  }

  if (typeof options === 'function') {
    done = options
    opts = {}
  }

  // validate callback is a function, if provided
  if (done !== undefined && typeof done !== 'function') {
    throw new TypeError('argument callback must be a function')
  }

  // require the callback without promises
  if (!done && !global.Promise) {
    throw new TypeError('argument callback is required')
  }

  // get encoding
  var encoding = opts.encoding !== true
    ? opts.encoding
    : 'utf-8'

  // convert the limit to an integer
  var limit = bytes.parse(opts.limit)

  // convert the expected length to an integer
  var length = opts.length != null && !isNaN(opts.length)
    ? parseInt(opts.length, 10)
    : null

  if (done) {
    // classic callback style
    return readStream(stream, encoding, length, limit, done)
  }

  return new Promise(function executor(resolve, reject) {
    readStream(stream, encoding, length, limit, function onRead(err, buf) {
      if (err) return reject(err)
      resolve(buf)
    })
  })
}

/**
 * Halt a stream.
 *
 * @param {Object} stream
 * @private
 */

function halt(stream) {
  // unpipe everything from the stream
  unpipe(stream)

  // pause stream
  if (typeof stream.pause === 'function') {
    stream.pause()
  }
}

/**
 * Make a serializable error object.
 *
 * To create serializable errors you must re-set message so
 * that it is enumerable and you must re configure the type
 * property so that is writable and enumerable.
 *
 * @param {string} message
 * @param {string} type
 * @param {object} props
 * @private
 */

function makeError(message, type, props) {
  var error = new Error()

  for (var prop in props) {
    error[prop] = props[prop]
  }

  error.message = message

  Object.defineProperty(error, 'type', {
    value: type,
    enumerable: true,
    writable: true,
    configurable: true
  })

  return error
}

/**
 * Read the data from the stream.
 *
 * @param {object} stream
 * @param {string} encoding
 * @param {number} length
 * @param {number} limit
 * @param {function} callback
 * @public
 */

function readStream(stream, encoding, length, limit, callback) {
  // check the length and limit options.
  // note: we intentionally leave the stream paused,
  // so users should handle the stream themselves.
  if (limit !== null && length !== null && length > limit) {
    var err = makeError('request entity too large', 'entity.too.large', {
      expected: length,
      length: length,
      limit: limit,
      status: 413,
      statusCode: 413
    })

    return process.nextTick(function () {
      done(err)
    })
  }

  // streams1: assert request encoding is buffer.
  // streams2+: assert the stream encoding is buffer.
  //   stream._decoder: streams1
  //   state.encoding: streams2
  //   state.decoder: streams2, specifically < 0.10.6
  var state = stream._readableState
  if (stream._decoder || (state && (state.encoding || state.decoder))) {
    // developer error
    var err = makeError('stream encoding should not be set', 'stream.encoding.set', {
      status: 500,
      statusCode: 500
    })

    return process.nextTick(function () {
      done(err)
    })
  }

  var received = 0
  var decoder

  try {
    decoder = getDecoder(encoding)
  } catch (err) {
    return process.nextTick(function () {
      done(err)
    })
  }

  var buffer = decoder
    ? ''
    : []

  stream.on('aborted', onAborted)
  stream.on('data', onData)
  stream.once('end', onEnd)
  stream.once('error', onEnd)
  stream.once('close', cleanup)

  function done(err) {
    cleanup()

    if (err) {
      // halt the stream on error
      halt(stream)
    }

    callback.apply(this, arguments)
  }

  function onAborted() {
    done(makeError('request aborted', 'request.aborted', {
      code: 'ECONNABORTED',
      expected: length,
      length: length,
      received: received,
      status: 400,
      statusCode: 400
    }))
  }

  function onData(chunk) {
    received += chunk.length
    decoder
      ? buffer += decoder.write(chunk)
      : buffer.push(chunk)

    if (limit !== null && received > limit) {
      done(makeError('request entity too large', 'entity.too.large', {
        limit: limit,
        received: received,
        status: 413,
        statusCode: 413
      }))
    }
  }

  function onEnd(err) {
    if (err) return done(err)

    if (length !== null && received !== length) {
      done(makeError('request size did not match content length', 'request.size.invalid', {
        expected: length,
        length: length,
        received: received,
        status: 400,
        statusCode: 400
      }))
    } else {
      var string = decoder
        ? buffer + (decoder.end() || '')
        : Buffer.concat(buffer)
      cleanup()
      done(null, string)
    }
  }

  function cleanup() {
    received = buffer = null

    stream.removeListener('aborted', onAborted)
    stream.removeListener('data', onData)
    stream.removeListener('end', onEnd)
    stream.removeListener('error', onEnd)
    stream.removeListener('close', cleanup)
  }
}
<commit_msg>Simplify a makeError call<commit_after>/*!
 * raw-body
 * Copyright(c) 2013-2014 Jonathan Ong
 * Copyright(c) 2014-2015 Douglas Christopher Wilson
 * MIT Licensed
 */

'use strict'

/**
 * Module dependencies.
 * @private
 */

var bytes = require('bytes')
var iconv = require('iconv-lite')
var unpipe = require('unpipe')

/**
 * Module exports.
 * @public
 */

module.exports = getRawBody

/**
 * Get the decoder for a given encoding.
 *
 * @param {string} encoding
 * @private
 */

function getDecoder(encoding) {
  if (!encoding) return null

  try {
    return iconv.getDecoder(encoding)
  } catch (e) {
    throw makeError('specified encoding unsupported', 'encoding.unsupported', {
      encoding: encoding,
      status: 415,
      statusCode: 415
    })
  }
}

/**
 * Get the raw body of a stream (typically HTTP).
 *
 * @param {object} stream
 * @param {object|string|function} [options]
 * @param {function} [callback]
 * @public
 */

function getRawBody(stream, options, callback) {
  var done = callback
  var opts = options || {}

  if (options === true || typeof options === 'string') {
    // short cut for encoding
    opts = {
      encoding: options
    }
  }

  if (typeof options === 'function') {
    done = options
    opts = {}
  }

  // validate callback is a function, if provided
  if (done !== undefined && typeof done !== 'function') {
    throw new TypeError('argument callback must be a function')
  }

  // require the callback without promises
  if (!done && !global.Promise) {
    throw new TypeError('argument callback is required')
  }

  // get encoding
  var encoding = opts.encoding !== true
    ? opts.encoding
    : 'utf-8'

  // convert the limit to an integer
  var limit = bytes.parse(opts.limit)

  // convert the expected length to an integer
  var length = opts.length != null && !isNaN(opts.length)
    ? parseInt(opts.length, 10)
    : null

  if (done) {
    // classic callback style
    return readStream(stream, encoding, length, limit, done)
  }

  return new Promise(function executor(resolve, reject) {
    readStream(stream, encoding, length, limit, function onRead(err, buf) {
      if (err) return reject(err)
      resolve(buf)
    })
  })
}

/**
 * Halt a stream.
 *
 * @param {Object} stream
 * @private
 */

function halt(stream) {
  // unpipe everything from the stream
  unpipe(stream)

  // pause stream
  if (typeof stream.pause === 'function') {
    stream.pause()
  }
}

/**
 * Make a serializable error object.
 *
 * To create serializable errors you must re-set message so
 * that it is enumerable and you must re configure the type
 * property so that is writable and enumerable.
 *
 * @param {string} message
 * @param {string} type
 * @param {object} props
 * @private
 */

function makeError(message, type, props) {
  var error = new Error()

  for (var prop in props) {
    error[prop] = props[prop]
  }

  error.message = message

  Object.defineProperty(error, 'type', {
    value: type,
    enumerable: true,
    writable: true,
    configurable: true
  })

  return error
}

/**
 * Read the data from the stream.
 *
 * @param {object} stream
 * @param {string} encoding
 * @param {number} length
 * @param {number} limit
 * @param {function} callback
 * @public
 */

function readStream(stream, encoding, length, limit, callback) {
  // check the length and limit options.
  // note: we intentionally leave the stream paused,
  // so users should handle the stream themselves.
  if (limit !== null && length !== null && length > limit) {
    var err = makeError('request entity too large', 'entity.too.large', {
      expected: length,
      length: length,
      limit: limit,
      status: 413,
      statusCode: 413
    })

    return process.nextTick(function () {
      done(err)
    })
  }

  // streams1: assert request encoding is buffer.
  // streams2+: assert the stream encoding is buffer.
  //   stream._decoder: streams1
  //   state.encoding: streams2
  //   state.decoder: streams2, specifically < 0.10.6
  var state = stream._readableState
  if (stream._decoder || (state && (state.encoding || state.decoder))) {
    // developer error
    var err = makeError('stream encoding should not be set', 'stream.encoding.set', {
      status: 500,
      statusCode: 500
    })

    return process.nextTick(function () {
      done(err)
    })
  }

  var received = 0
  var decoder

  try {
    decoder = getDecoder(encoding)
  } catch (err) {
    return process.nextTick(function () {
      done(err)
    })
  }

  var buffer = decoder
    ? ''
    : []

  stream.on('aborted', onAborted)
  stream.on('data', onData)
  stream.once('end', onEnd)
  stream.once('error', onEnd)
  stream.once('close', cleanup)

  function done(err) {
    cleanup()

    if (err) {
      // halt the stream on error
      halt(stream)
    }

    callback.apply(this, arguments)
  }

  function onAborted() {
    done(makeError('request aborted', 'request.aborted', {
      code: 'ECONNABORTED',
      expected: length,
      length: length,
      received: received,
      status: 400,
      statusCode: 400
    }))
  }

  function onData(chunk) {
    received += chunk.length
    decoder
      ? buffer += decoder.write(chunk)
      : buffer.push(chunk)

    if (limit !== null && received > limit) {
      done(makeError('request entity too large', 'entity.too.large', {
        limit: limit,
        received: received,
        status: 413,
        statusCode: 413
      }))
    }
  }

  function onEnd(err) {
    if (err) return done(err)

    if (length !== null && received !== length) {
      done(makeError('request size did not match content length', 'request.size.invalid', {
        expected: length,
        length: length,
        received: received,
        status: 400,
        statusCode: 400
      }))
    } else {
      var string = decoder
        ? buffer + (decoder.end() || '')
        : Buffer.concat(buffer)
      cleanup()
      done(null, string)
    }
  }

  function cleanup() {
    received = buffer = null

    stream.removeListener('aborted', onAborted)
    stream.removeListener('data', onData)
    stream.removeListener('end', onEnd)
    stream.removeListener('error', onEnd)
    stream.removeListener('close', cleanup)
  }
}
<|endoftext|>


<commit_before>'use strict';

var React = require('react-native');
var {
  View,
  StyleSheet,
  PanResponder,
  Animated,
  TouchableWithoutFeedback,
  Dimensions
} = React;

var screen = Dimensions.get('window');

var styles = StyleSheet.create({

  wrapper: {
    backgroundColor: "white"
  },

  transparent: {
    backgroundColor: 'rgba(0,0,0,0)'
  },

  absolute: {
    position: "absolute",
    top: 0,
    bottom: 0,
    left: 0,
    right: 0
  }

});

var ModalBox = React.createClass({

  propTypes: {
    isOpen: React.PropTypes.bool,
    isDisabled: React.PropTypes.bool,
    swipeToClose: React.PropTypes.bool,
    swipeThreshold: React.PropTypes.number,
    swipeArea: React.PropTypes.number,
    position: React.PropTypes.string,
    backdrop: React.PropTypes.bool,
    backdropOpacity: React.PropTypes.number,
    backdropColor: React.PropTypes.string,
    backdropContent: React.PropTypes.element,

    onClosed: React.PropTypes.func,
    onOpened: React.PropTypes.func,
    onClosingState: React.PropTypes.func,
  },

  getDefaultProps: function () {
    return {
      swipeToClose: true,
      swipeThreshold: 50,
      position: "center",
      backdrop: true,
      backdropOpacity: 0.5,
      backdropColor: "black",
      backdropContent: null
    };
  },

  getInitialState: function () {
    return {
      position: new Animated.Value(screen.height),
      backdropOpacity: new Animated.Value(0),
      isOpen: false,
      isAnimateClose: false,
      isAnimateOpen: false,
      swipeToClose: false,
      height: screen.height,
      width: screen.width
    };
  },

  componentDidMount: function() {
    if (this.props.swipeToClose)
      this.createPanResponder();
  },

  componentWillReceiveProps: function(props) {
    if (typeof props.isOpen == "undefined") return;
    if (props.isOpen)
      this.open();
    else
      this.close();
  },

  /****************** ANIMATIONS **********************/

  /*
   * Open animation for the backdrop, will fade in
   */
  animateBackdropOpen: function() {
    if (this.state.isAnimateBackdrop) {
      this.state.animBackdrop.stop();
      this.state.isAnimateBackdrop = false;
    }

    this.state.isAnimateBackdrop = true;
    this.state.animBackdrop = Animated.timing(
      this.state.backdropOpacity,
      {
        toValue: 1
      }
    );
    this.state.animBackdrop.start(() => {
      this.state.isAnimateBackdrop = false;
    });
  },

  /*
   * Close animation for the backdrop, will fade out
   */
  animateBackdropClose: function() {
    if (this.state.isAnimateBackdrop) {
      this.state.animBackdrop.stop();
      this.state.isAnimateBackdrop = false;
    }

    this.state.isAnimateBackdrop = true;
    this.state.animBackdrop = Animated.timing(
      this.state.backdropOpacity,
      {
        toValue: 0
      }
    );
    this.state.animBackdrop.start(() => {
      this.state.isAnimateBackdrop = false;
    });
  },

  /*
   * Open animation for the modal, will move up
   */
  animateOpen: function() {
    if (this.state.isAnimateClose) {
      this.state.animClose.stop();
      this.state.isAnimateClose = false;
    }

    // Backdrop fadeIn
    if (this.props.backdrop)
      this.animateBackdropOpen();

    // Detecting modal position
    this.state.positionDest = 0;
    if (this.props.position == "bottom") {
      this.state.positionDest = screen.height - this.state.height;
    }
    else if (this.props.position == "center") {
      this.state.positionDest = screen.height / 2 - this.state.height / 2;
    }

    this.state.isAnimateOpen = true;
    this.state.animOpen = Animated.spring(
      this.state.position,
      {
        toValue: this.state.positionDest,
        friction: 8
      }
    );
    this.state.animOpen.start(() => {
      this.state.isAnimateOpen = false;
      this.state.isOpen = true;
      if (this.props.onOpened) this.props.onOpened();
    });
  },

  /*
   * Close animation for the modal, will move down
   */
  animateClose: function() {
    if (this.state.isAnimateOpen) {
      this.state.animOpen.stop();
      this.state.isAnimateOpen = false;
    }

    // Backdrop fadeout
    if (this.props.backdrop)
      this.animateBackdropClose();

    this.state.isAnimateClose = true;
    this.state.animClose = Animated.timing(
      this.state.position,
      {
        toValue: screen.height,
        duration: 400
      }
    );
    this.state.animClose.start(() => {
      this.state.isAnimateClose = false;
      this.state.isOpen = false;
      this.setState({});
      if (this.props.onClosed) this.props.onClosed();
    });
  },

  /*
   * Create the pan responder to detect gesture
   * Only used if swipeToClose is enabled
   */
  createPanResponder: function() {
    var closingState = false;
    var inSwipeArea  = true;

    var onPanRelease = (evt, state) => {
      if (!inSwipeArea) return;
      if (state.dy > this.props.swipeThreshold)
        this.animateClose();
      else
        this.animateOpen();
    };

    var animEvt = Animated.event([null, {customY: this.state.position}]);

    var onPanMove = (evt, state) => {
      var newClosingState = (state.dy > this.props.swipeThreshold) ? true : false;
      if (state.dy < 0) return;
      if (newClosingState != closingState && this.props.onClosingState)
        this.props.onClosingState(newClosingState);
      closingState = newClosingState;
      state.customY = state.dy + this.state.positionDest;

      animEvt(evt, state);
    };

    var onPanStart = (evt, state) => {
      if (this.props.isDisabled || (this.props.swipeArea && (evt.nativeEvent.pageY - this.state.positionDest) > this.props.swipeArea)) {
        inSwipeArea = false;
        return false;
      }
      inSwipeArea = true;
      return true;
    };

    /* Fix 3d touch bug related issue https://github.com/facebook/react-native/issues/3082*/
    var onPanShouldMove = (evt, state) => {
      if (state.dx === 0 || state.dy === 0) {
        return false;
      }
      return inSwipeArea;
    };

    this.state.pan = PanResponder.create({
      onStartShouldSetPanResponder: onPanStart,
      onMoveShouldSetPanResponder: onPanShouldMove,
      onPanResponderMove: onPanMove,
      onPanResponderRelease: onPanRelease,
      onPanResponderTerminate: onPanRelease,
    });
  },

  /*
   * Event called when the modal view layout is calculated
   */
  onViewLayout: function(evt) {
    this.state.height = evt.nativeEvent.layout.height;
    this.state.width = evt.nativeEvent.layout.width;

    if (this.onViewLayoutCalculated) this.onViewLayoutCalculated();
  },

  /*
   * Render the backdrop element
   */
  renderBackdrop: function() {
    var backdrop  = [];
    var size      = {height: screen.height, width: screen.width};

    if (this.props.backdrop) {
      backdrop = (
        <TouchableWithoutFeedback onPress={this.close}>
          <Animated.View style={[styles.absolute, size, {opacity: this.state.backdropOpacity}]}>
            <View style={[styles.absolute, {backgroundColor:this.props.backdropColor, opacity: this.props.backdropOpacity}]}/>
            {this.props.backdropContent || []}
          </Animated.View>
        </TouchableWithoutFeedback>
      );
    }

    return backdrop;
  },

  /*
   * Render the component
   */
  render: function() {
    var visible     = this.state.isOpen || this.state.isAnimateOpen || this.state.isAnimateClose;
    var pan         = this.state.pan ? this.state.pan.panHandlers : {};
    var offsetX     = (screen.width - this.state.width) / 2;
    var backdrop    = this.renderBackdrop();

    if (!visible) return <View/>

    return (
      <View style={[styles.transparent, styles.absolute]} pointerEvents={'box-none'}>
        {backdrop}
        <Animated.View
         onLayout={this.onViewLayout}
         style={[styles.wrapper, {height: screen.height, width: screen.width}, this.props.style, {transform: [{translateY: this.state.position}, {translateX: offsetX}]} ]}
         {...pan}>
          {this.props.children}
        </Animated.View>
      </View>
    );
  },

  /****************** PUBLIC METHODS **********************/

  open: function() {
    if (this.props.isDisabled) return;
    if (!this.state.isAnimateOpen && (!this.state.isOpen || this.state.isAnimateClose)) {
      this.onViewLayoutCalculated = () => {
        this.setState({});
        this.animateOpen();
      };
      this.setState({isAnimateOpen : true});
    }
  },

  close: function() {
    if (this.props.isDisabled) return;
    if (!this.state.isAnimateClose && (this.state.isOpen || this.state.isAnimateOpen)) {
      this.animateClose();
    }
  }

});

module.exports = ModalBox;
<commit_msg>add animationDuration property<commit_after>'use strict';

var React = require('react-native');
var {
  View,
  StyleSheet,
  PanResponder,
  Animated,
  TouchableWithoutFeedback,
  Dimensions,
  Easing
} = React;

var screen = Dimensions.get('window');

var styles = StyleSheet.create({

  wrapper: {
    backgroundColor: "white"
  },

  transparent: {
    backgroundColor: 'rgba(0,0,0,0)'
  },

  absolute: {
    position: "absolute",
    top: 0,
    bottom: 0,
    left: 0,
    right: 0
  }

});

var ModalBox = React.createClass({

  propTypes: {
    isOpen: React.PropTypes.bool,
    isDisabled: React.PropTypes.bool,
    swipeToClose: React.PropTypes.bool,
    swipeThreshold: React.PropTypes.number,
    swipeArea: React.PropTypes.number,
    position: React.PropTypes.string,
    backdrop: React.PropTypes.bool,
    backdropOpacity: React.PropTypes.number,
    backdropColor: React.PropTypes.string,
    backdropContent: React.PropTypes.element,
    animationDuration: React.PropTypes.number,

    onClosed: React.PropTypes.func,
    onOpened: React.PropTypes.func,
    onClosingState: React.PropTypes.func,
  },

  getDefaultProps: function () {
    return {
      swipeToClose: true,
      swipeThreshold: 50,
      position: "center",
      backdrop: true,
      backdropOpacity: 0.5,
      backdropColor: "black",
      backdropContent: null,
      animationDuration: 400
    };
  },

  getInitialState: function () {
    return {
      position: new Animated.Value(screen.height),
      backdropOpacity: new Animated.Value(0),
      isOpen: false,
      isAnimateClose: false,
      isAnimateOpen: false,
      swipeToClose: false,
      height: screen.height,
      width: screen.width
    };
  },

  componentDidMount: function() {
    if (this.props.swipeToClose)
      this.createPanResponder();
  },

  componentWillReceiveProps: function(props) {
    if (typeof props.isOpen == "undefined") return;
    if (props.isOpen)
      this.open();
    else
      this.close();
  },

  /****************** ANIMATIONS **********************/

  /*
   * Open animation for the backdrop, will fade in
   */
  animateBackdropOpen: function() {
    if (this.state.isAnimateBackdrop) {
      this.state.animBackdrop.stop();
      this.state.isAnimateBackdrop = false;
    }

    this.state.isAnimateBackdrop = true;
    this.state.animBackdrop = Animated.timing(
      this.state.backdropOpacity,
      {
        toValue: 1,
        duration: this.props.animationDuration
      }
    );
    this.state.animBackdrop.start(() => {
      this.state.isAnimateBackdrop = false;
    });
  },

  /*
   * Close animation for the backdrop, will fade out
   */
  animateBackdropClose: function() {
    if (this.state.isAnimateBackdrop) {
      this.state.animBackdrop.stop();
      this.state.isAnimateBackdrop = false;
    }

    this.state.isAnimateBackdrop = true;
    this.state.animBackdrop = Animated.timing(
      this.state.backdropOpacity,
      {
        toValue: 0,
        duration: this.props.animationDuration
      }
    );
    this.state.animBackdrop.start(() => {
      this.state.isAnimateBackdrop = false;
    });
  },

  /*
   * Open animation for the modal, will move up
   */
  animateOpen: function() {
    if (this.state.isAnimateClose) {
      this.state.animClose.stop();
      this.state.isAnimateClose = false;
    }

    // Backdrop fadeIn
    if (this.props.backdrop)
      this.animateBackdropOpen();

    // Detecting modal position
    this.state.positionDest = 0;
    if (this.props.position == "bottom") {
      this.state.positionDest = screen.height - this.state.height;
    }
    else if (this.props.position == "center") {
      this.state.positionDest = screen.height / 2 - this.state.height / 2;
    }

    this.state.isAnimateOpen = true;
    this.state.animOpen = Animated.timing(
      this.state.position,
      {
        toValue: this.state.positionDest,
        duration: this.props.animationDuration,
        easing: Easing.elastic(0.8)
      }
    );
    this.state.animOpen.start(() => {
      this.state.isAnimateOpen = false;
      this.state.isOpen = true;
      if (this.props.onOpened) this.props.onOpened();
    });
  },

  /*
   * Close animation for the modal, will move down
   */
  animateClose: function() {
    if (this.state.isAnimateOpen) {
      this.state.animOpen.stop();
      this.state.isAnimateOpen = false;
    }

    // Backdrop fadeout
    if (this.props.backdrop)
      this.animateBackdropClose();

    this.state.isAnimateClose = true;
    this.state.animClose = Animated.timing(
      this.state.position,
      {
        toValue: screen.height,
        duration: this.props.animationDuration
      }
    );
    this.state.animClose.start(() => {
      this.state.isAnimateClose = false;
      this.state.isOpen = false;
      this.setState({});
      if (this.props.onClosed) this.props.onClosed();
    });
  },

  /*
   * Create the pan responder to detect gesture
   * Only used if swipeToClose is enabled
   */
  createPanResponder: function() {
    var closingState = false;
    var inSwipeArea  = true;

    var onPanRelease = (evt, state) => {
      if (!inSwipeArea) return;
      if (state.dy > this.props.swipeThreshold)
        this.animateClose();
      else
        this.animateOpen();
    };

    var animEvt = Animated.event([null, {customY: this.state.position}]);

    var onPanMove = (evt, state) => {
      var newClosingState = (state.dy > this.props.swipeThreshold) ? true : false;
      if (state.dy < 0) return;
      if (newClosingState != closingState && this.props.onClosingState)
        this.props.onClosingState(newClosingState);
      closingState = newClosingState;
      state.customY = state.dy + this.state.positionDest;

      animEvt(evt, state);
    };

    var onPanStart = (evt, state) => {
      if (this.props.isDisabled || (this.props.swipeArea && (evt.nativeEvent.pageY - this.state.positionDest) > this.props.swipeArea)) {
        inSwipeArea = false;
        return false;
      }
      inSwipeArea = true;
      return true;
    };

    /* Fix 3d touch bug related issue https://github.com/facebook/react-native/issues/3082*/
    var onPanShouldMove = (evt, state) => {
      if (state.dx === 0 || state.dy === 0) {
        return false;
      }
      return inSwipeArea;
    };

    this.state.pan = PanResponder.create({
      onStartShouldSetPanResponder: onPanStart,
      onMoveShouldSetPanResponder: onPanShouldMove,
      onPanResponderMove: onPanMove,
      onPanResponderRelease: onPanRelease,
      onPanResponderTerminate: onPanRelease,
    });
  },

  /*
   * Event called when the modal view layout is calculated
   */
  onViewLayout: function(evt) {
    this.state.height = evt.nativeEvent.layout.height;
    this.state.width = evt.nativeEvent.layout.width;

    if (this.onViewLayoutCalculated) this.onViewLayoutCalculated();
  },

  /*
   * Render the backdrop element
   */
  renderBackdrop: function() {
    var backdrop  = [];
    var size      = {height: screen.height, width: screen.width};

    if (this.props.backdrop) {
      backdrop = (
        <TouchableWithoutFeedback onPress={this.close}>
          <Animated.View style={[styles.absolute, size, {opacity: this.state.backdropOpacity}]}>
            <View style={[styles.absolute, {backgroundColor:this.props.backdropColor, opacity: this.props.backdropOpacity}]}/>
            {this.props.backdropContent || []}
          </Animated.View>
        </TouchableWithoutFeedback>
      );
    }

    return backdrop;
  },

  /*
   * Render the component
   */
  render: function() {
    var visible     = this.state.isOpen || this.state.isAnimateOpen || this.state.isAnimateClose;
    var pan         = this.state.pan ? this.state.pan.panHandlers : {};
    var offsetX     = (screen.width - this.state.width) / 2;
    var backdrop    = this.renderBackdrop();

    if (!visible) return <View/>

    return (
      <View style={[styles.transparent, styles.absolute]} pointerEvents={'box-none'}>
        {backdrop}
        <Animated.View
         onLayout={this.onViewLayout}
         style={[styles.wrapper, {height: screen.height, width: screen.width}, this.props.style, {transform: [{translateY: this.state.position}, {translateX: offsetX}]} ]}
         {...pan}>
          {this.props.children}
        </Animated.View>
      </View>
    );
  },

  /****************** PUBLIC METHODS **********************/

  open: function() {
    if (this.props.isDisabled) return;
    if (!this.state.isAnimateOpen && (!this.state.isOpen || this.state.isAnimateClose)) {
      this.onViewLayoutCalculated = () => {
        this.setState({});
        this.animateOpen();
      };
      this.setState({isAnimateOpen : true});
    }
  },

  close: function() {
    if (this.props.isDisabled) return;
    if (!this.state.isAnimateClose && (this.state.isOpen || this.state.isAnimateOpen)) {
      this.animateClose();
    }
  }

});

module.exports = ModalBox;
<|endoftext|>


<commit_before>
/* ==== ==== ==== ==== ==== = */
/* ==== GLOBAL VARIABLES ==== */
/* ==== ==== ==== ==== ==== = */

// ThreeJS
var renderer, scene, camera;
// VR stuff
var controls, effect, manager;

// Debug Interface
var mydebug = true;
var params = { }
var FizzyText = function() {
    this.message = 'Menu to test values faster';
};
var text = new FizzyText();
var gui = new dat.GUI();
gui.add(text, 'message');

// === Pantin VR ===
var player = [];
var path = [];
var lap = -1;

/* ==== ==== ==== ====  */
/* ==== INIT SETUP ==== */
/* ==== ==== ==== ====  */

function init() {
    initThreeJS();
    initVR();

    addSkyBox();

    // === Pantin VR ===

    // Path Initialisation to a Regular Pentagon
    path.push( {position: {x: 0, y:-1, z:0}, action: action_countLap});
    path.push( {position: {x:-1, y: 0, z:0}});
    path.push( {position: {x:-1, y: 1, z:0}});
    path.push( {position: {x: 1, y: 1, z:0}, action: action_moodChecking});
    path.push( {position: {x: 1, y: 0, z:0}});

    // Visual manifestation of the path
    // ...

    // Player initialisation
    player.id = 0;
    player.position = path[0].position;
    player.lookingAt = path[1].position - path[0].position;
    player.speed = 0;
    player.acceleration = 0;
    player.currentNodeInPath = 0;
    player.hasActed = false; // to check if already acted on the current path node.

    // Playground initialisation
    // loading pantin.stl
}

/* ==== ==== ==== ==== == */
/* ==== ANIMATE LOOP ==== */
/* ==== ==== ==== ==== == */

function animate() {

    // Update VR headset position and apply to camera.
    controls.update();

    // === Pantin VR ===

    // Check if we can act on the current path node and haven't already acted
    if(!player.hasActed && path[player.currentNodeInPath].hasOwnProperty('action')) {
        // INTERACTING
        player.hasActed = true;
        path[player.currentNodeInPath].action();

    } else {
        // MOVING
        updatePlayerPosition();
    }

    // === ====== ===

    // Render the scene through the manager.
    manager.render(scene, camera);
    // Kick off next animate call when requested
    requestAnimationFrame(animate);

}

/* ==== ==== ==== ==== */
/* ==== PANTIN VR ==== */
/* ==== ==== ==== ==== */

function updatePlayerPosition() {

    // Move within constrains

    // If changing node, then player.hasActed = false;

}

// ACTION: Counting number of laps
function action_countLap() {
    lap++;

    // Visual feedback
    // ...
}

// ACTION: checking mood, killer7-like decision
function action_moodChecking() {
    // QUESTION: How do you feel today ?

    // Visual feedback
    // ...

    // ANSWERS: Good / Not Good.
    // Impact average speed of user.

    // Visual feedback
    // ...
}

/* ==== ==== ==== === */
/* ==== KICK OFF ==== */
/* ==== ==== ==== === */

init();
animate();

/* ==== ==== ==== ==== ==== */
/* ==== EVENT HANDLING ==== */
/* ==== ==== ==== ==== ==== */

function onKey(event) {
    if (event.keyCode == 90) { // z to reset the sensors
        controls.resetSensor();
	if (mydebug) console.log("sensor resetted");
    }
    if (event.keyCode == 13) { // enter to start
    }
};

function onClick(event) {

};

window.addEventListener('onclick', onClick, true);
window.addEventListener('click', onClick, true);
// does not work on mobile

window.addEventListener('keydown', onKey, true);

// Handle window resizes
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    effect.setSize(window.innerWidth, window.innerHeight);
}

// Gaze interaction
// ...

/* ==== ==== ==== ==== === */
/* ==== INIT SHORTCUT ==== */
/* ==== ==== ==== ==== === */

function initThreeJS() {

    //Setup three.js WebGL renderer
    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);

    // Append the canvas element created by the renderer to document body element.
    document.body.appendChild(renderer.domElement);

    // Create a three.js scene.
    scene = new THREE.Scene();

    // Create a three.js camera.
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.3, 10000);
    //create gaze interaction manager
    scene.add(camera);
}

function initVR() {

    // Apply VR headset positional data to camera.
    controls = new THREE.VRControls(camera);

    // Apply VR stereo rendering to renderer.
    effect = new THREE.VREffect(renderer);
    effect.setSize(window.innerWidth, window.innerHeight);

    // Create a VR manager helper to enter and exit VR mode.
    manager = new WebVRManager(renderer, effect, {
        hideButton: false
    });

}

function addSkyBox() {

    // Also add a repeating grid as a skybox.
    var boxWidth = 10;
    var texture = THREE.ImageUtils.loadTexture( 'textures/box.png');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set(boxWidth, boxWidth);

    var geometry = new THREE.BoxGeometry(boxWidth, boxWidth, boxWidth);
    var material = new THREE.MeshBasicMaterial({
        map: texture,
        color: 0x333333,
        side: THREE.BackSide,
        transparent: true,
        opacity: 0.7
    });

    var skybox = new THREE.Mesh(geometry, material);
    scene.add(skybox);

}<commit_msg>new squeleton with better handling of actions<commit_after>
/* ==== ==== ==== ==== ==== = */
/* ==== GLOBAL VARIABLES ==== */
/* ==== ==== ==== ==== ==== = */

// ThreeJS
var renderer, scene, camera;
// VR stuff
var controls, effect, manager;

// Debug Interface
var mydebug = true;
var params = { }
var FizzyText = function() {
    this.message = 'Menu to test values faster';
};
var text = new FizzyText();
var gui = new dat.GUI();
gui.add(text, 'message');

// === Pantin VR ===
var player = [];
var path = [];
var lap = -1;

/* ==== ==== ==== ====  */
/* ==== INIT SETUP ==== */
/* ==== ==== ==== ====  */

function init() {
    initThreeJS();
    initVR();

    addSkyBox();

    // === Pantin VR ===

    // Path Initialisation to a Regular Pentagon
    // Each elemnt of the path array is a node
    // Each consecutive couple of nodes defines a segment
    path.push( {position: {x: 0, y:-1, z:0}, action: action_countLap});
    path.push( {position: {x:-1, y: 0, z:0}});
    path.push( {position: {x:-1, y: 1, z:0}});
    path.push( {position: {x: 1, y: 1, z:0}, action: action_moodChecking});
    path.push( {position: {x: 1, y: 0, z:0}});

    // Visual manifestation of the path
    // ...

    // Player initialisation
    player.id = 0;
    player.position = path[0].position;
    player.lookingAt = path[1].position - path[0].position;
    player.speed = 0;
    player.acceleration = 0;
    player.currentSegment = 0; // Segment I means between node I and node I+1

    // Playground initialisation
    // loading pantin.stl
}

/* ==== ==== ==== ==== == */
/* ==== ANIMATE LOOP ==== */
/* ==== ==== ==== ==== == */

function animate() {

    // Update VR headset position and apply to camera.
    controls.update();

    // === Pantin VR ===
    var pastSegment = player.currentSegment;
    updatePlayerPosition();

    if(pastSegment != player.currentSegment) {
        // Player just crossed a new node. Maybe there is an action to trigger ?
        var crossedNode = Math.max(pastSegment, player.currentSegment);
        if( path[crossedNode].hasOwnProperty('action') ) {
            path[crossedNode].action();
        }

    }

    // === ====== ===

    // Render the scene through the manager.
    manager.render(scene, camera);
    // Kick off next animate call when requested
    requestAnimationFrame(animate);

}

/* ==== ==== ==== === */
/* ==== KICK OFF ==== */
/* ==== ==== ==== === */

init();
animate();

/* ==== ==== ==== ==== */
/* ==== PANTIN VR ==== */
/* ==== ==== ==== ==== */

function updatePlayerPosition() {

    // Move within constrains

    // If changing node, then player.hasActed = false;

}

// ACTION: Counting number of laps
function action_countLap() {
    lap++;

    // Visual feedback
    // ...
}

// ACTION: checking mood, killer7-like decision
function action_moodChecking() {
    // QUESTION: How do you feel today ?

    // Visual feedback
    // ...

    // ANSWERS: Good / Not Good.
    // Impact average speed of user.

    // Visual feedback
    // ...
}

/* ==== ==== ==== ==== ==== */
/* ==== EVENT HANDLING ==== */
/* ==== ==== ==== ==== ==== */

function onKey(event) {
    if (event.keyCode == 90) { // z to reset the sensors
        controls.resetSensor();
	if (mydebug) console.log("sensor resetted");
    }
    if (event.keyCode == 13) { // enter to start
    }
};

function onClick(event) {

};

window.addEventListener('onclick', onClick, true);
window.addEventListener('click', onClick, true);
// does not work on mobile

window.addEventListener('keydown', onKey, true);

// Handle window resizes
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    effect.setSize(window.innerWidth, window.innerHeight);
}

// Gaze interaction
// ...

/* ==== ==== ==== ==== === */
/* ==== INIT SHORTCUT ==== */
/* ==== ==== ==== ==== === */

function initThreeJS() {

    //Setup three.js WebGL renderer
    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);

    // Append the canvas element created by the renderer to document body element.
    document.body.appendChild(renderer.domElement);

    // Create a three.js scene.
    scene = new THREE.Scene();

    // Create a three.js camera.
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.3, 10000);
    //create gaze interaction manager
    scene.add(camera);
}

function initVR() {

    // Apply VR headset positional data to camera.
    controls = new THREE.VRControls(camera);

    // Apply VR stereo rendering to renderer.
    effect = new THREE.VREffect(renderer);
    effect.setSize(window.innerWidth, window.innerHeight);

    // Create a VR manager helper to enter and exit VR mode.
    manager = new WebVRManager(renderer, effect, {
        hideButton: false
    });

}

function addSkyBox() {

    // Also add a repeating grid as a skybox.
    var boxWidth = 10;
    var texture = THREE.ImageUtils.loadTexture( 'textures/box.png');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set(boxWidth, boxWidth);

    var geometry = new THREE.BoxGeometry(boxWidth, boxWidth, boxWidth);
    var material = new THREE.MeshBasicMaterial({
        map: texture,
        color: 0x333333,
        side: THREE.BackSide,
        transparent: true,
        opacity: 0.7
    });

    var skybox = new THREE.Mesh(geometry, material);
    scene.add(skybox);

}<|endoftext|>


<commit_before>module.exports = function (_Firebase) {
  Firebase = _Firebase
  return Node
}

var debug = require('debug')('fireflower')
var merge = require('merge').recursive
var events = require('events')
var inherits = require('inherits')
var SimplePeer = require('simpler-peer')
var Blacklist = require('./blacklist')
var Firebase = null

var CONNECTION_TIMEOUT = 5000

inherits(Node, events.EventEmitter)

function Node (url, opts) {
  if (!(this instanceof Node)) {
    return new Node(url, opts)
  }

  this.url = url
  this.opts = opts || {}
  this.root = this.opts.root
  this.reportInterval = this.opts.reportInterval
  this.state = 'disconnected'
  this.upstream = null
  this.downstream = {}
  this.blacklist = new Blacklist()

  // firebase refs
  this._ref = new Firebase(this.url)
  this._configRef = this._ref.child('configuration')
  this._requestsRef = this._ref.child('requests')
  this._reports = this._ref.child('reports')

  // set a random id if one was not provided
  this.id = this.opts.id || this._requestsRef.push().key()

  // ensure K
  this.K = this.K || 0

  // use external setTimeout if provided
  this._setTimeout = this.opts.setTimeout || window.setTimeout.bind(window)
  this._clearTimeout = this.opts.clearTimeout || window.clearTimeout.bind(window)

  // bind callbacks
  this._onconfig = this._onconfig.bind(this)
  this._doconnect = this._doconnect.bind(this)
  this._onrequest = this._onrequest.bind(this)
  this._onresponse = this._onresponse.bind(this)
  this._onmaskupdate = this._onmaskupdate.bind(this)
  this._onreportNeeded = this._onreportNeeded.bind(this)

  events.EventEmitter.call(this)
}

Object.defineProperty(Node.prototype, 'K', {
  get: function () {
    return this.opts.K
  },
  set: function (value) {
    if (value === this.opts.K) return
    if (isNaN(value) || ~~value !== value || value < 0) {
      throw new Error('K must be 0 or a positive integer')
    }

    debug(this.id + ' set K to ' + value)
    this.opts.K = value

    var n = 0
    for (var i in this.downstream) {
      if (++n > this.opts.K) {
        this.downstream[i].close()
      }
    }
    this._reviewRequests()
  }
})

Node.prototype.connect = function () {
  if (this.state !== 'disconnected') {
    throw new Error('invalid state for connect')
  }

  this._preventReconnect = false

  // reporting?
  if (this.reportInterval && !this._reportInterval) {
    this._onreportNeeded()
  }

  // change state -> requesting
  debug(this.id + ' requesting connection')
  this.state = 'requesting'
  this.emit('statechange')

  // watch config
  if (!this._watchingConfig) {
    this._configRef.on('value', this._onconfig)
    this._watchingConfig = true
  }

  this._doconnect()

  return this
}

Node.prototype.disconnect = function () {
  this.state = 'disconnected'
  this._preventReconnect = true
  this._watchingConfig = false

  // remove some listeners
  this.removeListener('configure', this._doconnect)
  this._configRef.off('value', this._onconfig)
  this._requestsRef.off('child_added', this._onrequest)

  // stop reporting
  this._clearTimeout(this._reportInterval)
  delete this._reportInterval

  // remove outstanding request / response listener
  if (this._requestRef) {
    this._requestRef.remove()
    this._responsesRef.off('child_added', this._onresponse)
  }

  // close upstream connection
  if (this.upstream) {
    this.upstream.close()
    this.upstream = null
  }

  // close downstream connections
  for (var i in this.downstream) {
    this.downstream[i].close()
  }
  this.downstream = {}

  return this
}

// private api below

Node.prototype._onconfig = function (snapshot) {
  var data = snapshot.val()
  merge(this.opts, data)
  debug(this.id + ' updated configuration')
  this.emit('configure')
}

Node.prototype._doconnect = function () {
  var self = this

  if (this.root) {

    // emit connect but in nextTick
    this._setTimeout(function () {
      // change state -> connected
      debug(self.id + ' connected as root')
      self.state = 'connected'
      self.emit('statechange')

      // emit connect
      self.emit('connect')

      // start responding to requests
      self._requestsRef.on('child_added', self._onrequest)
    })

    return
  }

  // we are not root so publish a connection request
  this._dorequest()
}

Node.prototype._dorequest = function () {
  var self = this

  this._requestRef = this._requestsRef.push({
    id: this.id,
    removal_flag: {
      removed: false
    }
  })

  // make sure no one removes our request until we're connected
  this._requestRef.child('removal_flag').once('child_removed', function () {
    if (self.state === 'requesting') {
      self._responsesRef.off('child_added', self._onresponse)
      self._dorequest()
    }
  })

  // listen for a response
  delete this._responses
  this._responsesRef = this._requestRef.child('responses')
  this._responsesRef.on('child_added', this._onresponse)
}

Node.prototype._onrequest = function (snapshot) {
  if (this.state !== 'connected') {
    return // can't respond to requests unless we are connected
  }

  if (Object.keys(this.downstream).length >= this.opts.K) {
    return // can't respond to requests if we've hit K peers
  }

  var self = this
  var requestRef = snapshot.ref()
  var requestId = snapshot.key()
  var request = snapshot.val()
  var peerId = request.id

  // responders may accidentally recreate requests
  // these won't have an id though and can be removed
  if (!peerId) {
    requestRef.remove()
    return
  }

  // prevent circles
  if (peerId === this._mask) {
    return
  }

  // since we review requests after every connect / disconnect
  // it is possible to see a peer we know about
  var knownPeer = this.downstream[peerId]
  if (knownPeer) {
    if (knownPeer.requestId !== requestId) {
      // if request ids don't match, the peer must have disconnected without us noticing
      knownPeer.close()
    } else {
      return
    }
  }

  debug(this.id + ' saw request by ' + peerId)

  var responseRef = requestRef.child('responses/' + this.id)

  // initiate peer connection
  // we have to do this before actually writing our response because
  // firebase can trigger events in the same tick which could circumvent
  // the K check at the top of this method
  this._connectToPeer(true, peerId, requestId, responseRef)

  // publish response
  responseRef.update({
    id: this.id,
    upstream: {
      id: this.upstream ? this.upstream.id : null
    }
  })

  // watch for request withdrawal
  responseRef.once('child_removed', function () {
    var peer = self.downstream[peerId]
    if (peer && !peer.didConnect) {
      peer.requestWithdrawn = true
      peer.close()
    }
  })
}

Node.prototype._onresponse = function (snapshot) {
  if (this.state !== 'requesting') {
    return
  }

  if (this._responses) {
    this._responses.push(snapshot)
  } else {
    this._responses = [ snapshot ]
    this._setTimeout(this._reviewResponses.bind(this), 250)
  }
}

Node.prototype._reviewResponses = function () {
  this._responsesRef.off('child_added', this._onresponse)

  if (this.state !== 'requesting') {
    delete this._responses
    return
  }

  var candidates = {}

  for (var i in this._responses) {
    var snapshot = this._responses[i]
    var response = snapshot.val()

    if (!response.id || this.blacklist.contains(response.id)) {
      continue
    }

    if (!response.upstream) {
      this._acceptResponse(snapshot)
      return
    }

    candidates[response.id] = {
      snapshot: snapshot,
      upstream: response.upstream.id
    }
  }

  delete this._responses

  for (var i in candidates) {
    if (candidates[candidates[i].upstream]) {
      delete candidates[i]
    }
  }

  var keys = Object.keys(candidates)
  if (keys.length) {
    this._acceptResponse(candidates[keys[0]].snapshot)
  }
}

Node.prototype._acceptResponse = function (snapshot) {
  var responseRef = snapshot.ref()
  var response = snapshot.val()
  var peerId = response.id

  // change state -> connecting (this prevents accepting multiple responses)
  debug(this.id + ' got response from ' + peerId)
  this.state = 'connecting'
  this.emit('statechange')

  // stop watching for request removal
  this._requestRef.child('removal_flag').off()

  // attempt a connection
  this._connectToPeer(false, peerId, null, responseRef)
}

Node.prototype._connectToPeer = function (initiator, peerId, requestId, responseRef) {
  var self = this
  var localSignals = responseRef.child(initiator ? 'responderSignals' : 'requesterSignals')
  var remoteSignals = responseRef.child(initiator ? 'requesterSignals' : 'responderSignals')

  var peer = new SimplePeer({
    initiator: initiator,
    trickle: this.opts.peerConfig ? this.opts.peerConfig.trickle : undefined,
    config: this.opts.peerConfig,
    channelConfig: this.opts.channelConfig
  })

  peer.id = peerId

  if (initiator) {
    this.downstream[peer.id] = peer
    peer.notifications = peer.createDataChannel('notifications')
    peer.requestId = requestId
  } else {
    peer.on('datachannel', function (channel) {
      if (channel.label === 'notifications') {
        peer.notifications = channel
        peer.notifications.on('message', self._onmaskupdate)
      }
    })
  }

  peer.on('connect', this._onpeerConnect.bind(this, peer, remoteSignals))
  peer.on('close', this._onpeerDisconnect.bind(this, peer, remoteSignals))

  peer.on('error', function (err) {
    debug(this.id + ' saw peer connection error', err)
  })

  peer.on('signal', function (signal) {
    if (initiator && self.state !== 'connected') return
    signal = JSON.parse(JSON.stringify(signal))
    localSignals.push(signal)
  })

  remoteSignals.on('child_added', function (snapshot) {
    if (initiator && self.state !== 'connected') return
    var signal = snapshot.val()
    peer.signal(signal)
  })

  // timeout connections
  this._setTimeout(function () {
    if (!peer.didConnect) {
      peer.close()
    }
  }, CONNECTION_TIMEOUT)
}

Node.prototype._onpeerConnect = function (peer, remoteSignals) {
  peer.didConnect = true
  peer.removeAllListeners('connect')
  peer.removeAllListeners('signal')
  remoteSignals.off()

  if (this.downstream[peer.id]) {
    this._ondownstreamConnect(peer)
  } else {
    this._onupstreamConnect(peer)
  }
}

Node.prototype._onpeerDisconnect = function (peer, remoteSignals) {
  peer.removeAllListeners()
  remoteSignals.off()

  if (this.downstream[peer.id]) {
    this._ondownstreamDisconnect(peer)
  } else {
    this._onupstreamDisconnect(peer)
  }
}

Node.prototype._onupstreamConnect = function (peer) {
  // remove our request
  this._requestRef.remove()

  // already got connected by someone else
  if (this.state === 'connected') {
    debug(this.id + ' rejected upstream connection by ' + peer.id)
    peer.close()
    return
  }

  this.upstream = peer

  // change state -> connected
  debug(this.id + ' established upstream connection to ' + peer.id)
  this.state = 'connected'
  this.emit('statechange')
  this.emit('connect', peer)

  // begin responding to requests
  this._reviewRequests()
}

Node.prototype._onupstreamDisconnect = function (peer) {
  // stop responding to new requests
  this._requestsRef.off('child_added', this._onrequest)

  // remove request
  this._requestRef.remove()

  this.upstream = null

  // change state -> disconnected
  if (this.state !== 'disconnected') {
    debug(this.id + ' lost upstream connection to ' + peer.id)
  }

  this.state = 'disconnected'
  this.emit('statechange')

  // emit disconnect if we were connected
  if (peer.didConnect) {
    this.emit('disconnect', peer)
  }

  // attempt to reconnect if we were not disconnected intentionally
  if (!this._preventReconnect) {

    // mask off our descendants
    this._onmaskupdate({ data: this.id })

    // give our mask update a tiny head start
    var self = this
    this._setTimeout(function () {
      if (!self._preventReconnect) {
        self.connect()
      }
    }, 100)
  }
}

Node.prototype._ondownstreamConnect = function (peer) {
  // emit peerconnect
  debug(this.id + ' established downstream connection to ' + peer.id)
  this.emit('peerconnect', peer)

  // stop responding to requests if peers > K
  if (Object.keys(this.downstream).length >= this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
  }

  // make sure downstream has the most up to date mask
  if (this._mask) {
    try {
      peer.notifications.send(this._mask)
    } catch (err) {
      console.warn(err)
    }
  }
}

Node.prototype._ondownstreamDisconnect = function (peer) {
  // remove from lookup
  delete this.downstream[peer.id]

  // emit events and potentially remove stale requests
  if (peer.didConnect) {
    if (this.state !== 'disconnected') {
      debug(this.id + ' lost downstream connection to ' + peer.id)
    }

    this.emit('peerdisconnect', peer)
  } else {
    if (peer.requestWithdrawn) {
      debug(this.id + ' saw request withdrawn by ' + peer.id)
    } else {
      debug(this.id + ' removing stale request by ' + peer.id)
      this._requestsRef.child(peer.requestId).remove()
    }
  }

  this._reviewRequests()
}

Node.prototype._onmaskupdate = function (evt) {
  this._mask = evt.data

  debug(this.id + ' set mask to ' + this._mask)

  // oops we made a circle, fix that
  if (this.downstream[this._mask]) {
    debug(this.id + ' destroying accidental circle back to ' + this._mask)
    this.downstream[this._mask].close()
  }

  for (var i in this.downstream) {
    var notifications = this.downstream[i].notifications
    try {
      notifications.send(this._mask)
    } catch (err) {
      console.warn(err)
    }
  }
}

Node.prototype._onreportNeeded = function () {
  var report = {
    state: this.state,
    upstream: this.upstream ? this.upstream.id : null,
    timestamp: Firebase.ServerValue.TIMESTAMP
  }

  if (this.root) {
    report.root = true
  }

  if (this.reportData) {
    report.data = this.reportData
  }

  this._reports
    .child(this.id)
    .update(report)

  this._reportInterval = this._setTimeout(this._onreportNeeded, this.reportInterval)
}

Node.prototype._reviewRequests = function () {
  if (this.state === 'connected' && Object.keys(this.downstream).length < this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
    this._requestsRef.on('child_added', this._onrequest)
  }
}
<commit_msg>wait for notifications channel open event before connecting downstream peers<commit_after>module.exports = function (_Firebase) {
  Firebase = _Firebase
  return Node
}

var debug = require('debug')('fireflower')
var merge = require('merge').recursive
var events = require('events')
var inherits = require('inherits')
var SimplePeer = require('simpler-peer')
var Blacklist = require('./blacklist')
var Firebase = null

var CONNECTION_TIMEOUT = 5000

inherits(Node, events.EventEmitter)

function Node (url, opts) {
  if (!(this instanceof Node)) {
    return new Node(url, opts)
  }

  this.url = url
  this.opts = opts || {}
  this.root = this.opts.root
  this.reportInterval = this.opts.reportInterval
  this.state = 'disconnected'
  this.upstream = null
  this.downstream = {}
  this.blacklist = new Blacklist()

  // firebase refs
  this._ref = new Firebase(this.url)
  this._configRef = this._ref.child('configuration')
  this._requestsRef = this._ref.child('requests')
  this._reports = this._ref.child('reports')

  // set a random id if one was not provided
  this.id = this.opts.id || this._requestsRef.push().key()

  // ensure K
  this.K = this.K || 0

  // use external setTimeout if provided
  this._setTimeout = this.opts.setTimeout || window.setTimeout.bind(window)
  this._clearTimeout = this.opts.clearTimeout || window.clearTimeout.bind(window)

  // bind callbacks
  this._onconfig = this._onconfig.bind(this)
  this._doconnect = this._doconnect.bind(this)
  this._onrequest = this._onrequest.bind(this)
  this._onresponse = this._onresponse.bind(this)
  this._onmaskupdate = this._onmaskupdate.bind(this)
  this._onreportNeeded = this._onreportNeeded.bind(this)

  events.EventEmitter.call(this)
}

Object.defineProperty(Node.prototype, 'K', {
  get: function () {
    return this.opts.K
  },
  set: function (value) {
    if (value === this.opts.K) return
    if (isNaN(value) || ~~value !== value || value < 0) {
      throw new Error('K must be 0 or a positive integer')
    }

    debug(this.id + ' set K to ' + value)
    this.opts.K = value

    var n = 0
    for (var i in this.downstream) {
      if (++n > this.opts.K) {
        this.downstream[i].close()
      }
    }
    this._reviewRequests()
  }
})

Node.prototype.connect = function () {
  if (this.state !== 'disconnected') {
    throw new Error('invalid state for connect')
  }

  this._preventReconnect = false

  // reporting?
  if (this.reportInterval && !this._reportInterval) {
    this._onreportNeeded()
  }

  // change state -> requesting
  debug(this.id + ' requesting connection')
  this.state = 'requesting'
  this.emit('statechange')

  // watch config
  if (!this._watchingConfig) {
    this._configRef.on('value', this._onconfig)
    this._watchingConfig = true
  }

  this._doconnect()

  return this
}

Node.prototype.disconnect = function () {
  this.state = 'disconnected'
  this._preventReconnect = true
  this._watchingConfig = false

  // remove some listeners
  this.removeListener('configure', this._doconnect)
  this._configRef.off('value', this._onconfig)
  this._requestsRef.off('child_added', this._onrequest)

  // stop reporting
  this._clearTimeout(this._reportInterval)
  delete this._reportInterval

  // remove outstanding request / response listener
  if (this._requestRef) {
    this._requestRef.remove()
    this._responsesRef.off('child_added', this._onresponse)
  }

  // close upstream connection
  if (this.upstream) {
    this.upstream.close()
    this.upstream = null
  }

  // close downstream connections
  for (var i in this.downstream) {
    this.downstream[i].close()
  }
  this.downstream = {}

  return this
}

// private api below

Node.prototype._onconfig = function (snapshot) {
  var data = snapshot.val()
  merge(this.opts, data)
  debug(this.id + ' updated configuration')
  this.emit('configure')
}

Node.prototype._doconnect = function () {
  var self = this

  if (this.root) {

    // emit connect but in nextTick
    this._setTimeout(function () {
      // change state -> connected
      debug(self.id + ' connected as root')
      self.state = 'connected'
      self.emit('statechange')

      // emit connect
      self.emit('connect')

      // start responding to requests
      self._requestsRef.on('child_added', self._onrequest)
    })

    return
  }

  // we are not root so publish a connection request
  this._dorequest()
}

Node.prototype._dorequest = function () {
  var self = this

  this._requestRef = this._requestsRef.push({
    id: this.id,
    removal_flag: {
      removed: false
    }
  })

  // make sure no one removes our request until we're connected
  this._requestRef.child('removal_flag').once('child_removed', function () {
    if (self.state === 'requesting') {
      self._responsesRef.off('child_added', self._onresponse)
      self._dorequest()
    }
  })

  // listen for a response
  delete this._responses
  this._responsesRef = this._requestRef.child('responses')
  this._responsesRef.on('child_added', this._onresponse)
}

Node.prototype._onrequest = function (snapshot) {
  if (this.state !== 'connected') {
    return // can't respond to requests unless we are connected
  }

  if (Object.keys(this.downstream).length >= this.opts.K) {
    return // can't respond to requests if we've hit K peers
  }

  var self = this
  var requestRef = snapshot.ref()
  var requestId = snapshot.key()
  var request = snapshot.val()
  var peerId = request.id

  // responders may accidentally recreate requests
  // these won't have an id though and can be removed
  if (!peerId) {
    requestRef.remove()
    return
  }

  // prevent circles
  if (peerId === this._mask) {
    return
  }

  // since we review requests after every connect / disconnect
  // it is possible to see a peer we know about
  var knownPeer = this.downstream[peerId]
  if (knownPeer) {
    if (knownPeer.requestId !== requestId) {
      // if request ids don't match, the peer must have disconnected without us noticing
      knownPeer.close()
    } else {
      return
    }
  }

  debug(this.id + ' saw request by ' + peerId)

  var responseRef = requestRef.child('responses/' + this.id)

  // initiate peer connection
  // we have to do this before actually writing our response because
  // firebase can trigger events in the same tick which could circumvent
  // the K check at the top of this method
  this._connectToPeer(true, peerId, requestId, responseRef)

  // publish response
  responseRef.update({
    id: this.id,
    upstream: {
      id: this.upstream ? this.upstream.id : null
    }
  })

  // watch for request withdrawal
  responseRef.once('child_removed', function () {
    var peer = self.downstream[peerId]
    if (peer && !peer.didConnect) {
      peer.requestWithdrawn = true
      peer.close()
    }
  })
}

Node.prototype._onresponse = function (snapshot) {
  if (this.state !== 'requesting') {
    return
  }

  if (this._responses) {
    this._responses.push(snapshot)
  } else {
    this._responses = [ snapshot ]
    this._setTimeout(this._reviewResponses.bind(this), 250)
  }
}

Node.prototype._reviewResponses = function () {
  this._responsesRef.off('child_added', this._onresponse)

  if (this.state !== 'requesting') {
    delete this._responses
    return
  }

  var candidates = {}

  for (var i in this._responses) {
    var snapshot = this._responses[i]
    var response = snapshot.val()

    if (!response.id || this.blacklist.contains(response.id)) {
      continue
    }

    if (!response.upstream) {
      this._acceptResponse(snapshot)
      return
    }

    candidates[response.id] = {
      snapshot: snapshot,
      upstream: response.upstream.id
    }
  }

  delete this._responses

  for (var i in candidates) {
    if (candidates[candidates[i].upstream]) {
      delete candidates[i]
    }
  }

  var keys = Object.keys(candidates)
  if (keys.length) {
    this._acceptResponse(candidates[keys[0]].snapshot)
  }
}

Node.prototype._acceptResponse = function (snapshot) {
  var responseRef = snapshot.ref()
  var response = snapshot.val()
  var peerId = response.id

  // change state -> connecting (this prevents accepting multiple responses)
  debug(this.id + ' got response from ' + peerId)
  this.state = 'connecting'
  this.emit('statechange')

  // stop watching for request removal
  this._requestRef.child('removal_flag').off()

  // attempt a connection
  this._connectToPeer(false, peerId, null, responseRef)
}

Node.prototype._connectToPeer = function (initiator, peerId, requestId, responseRef) {
  var self = this
  var localSignals = responseRef.child(initiator ? 'responderSignals' : 'requesterSignals')
  var remoteSignals = responseRef.child(initiator ? 'requesterSignals' : 'responderSignals')

  var peer = new SimplePeer({
    initiator: initiator,
    trickle: this.opts.peerConfig ? this.opts.peerConfig.trickle : undefined,
    config: this.opts.peerConfig,
    channelConfig: this.opts.channelConfig
  })

  peer.id = peerId

  if (initiator) {
    this.downstream[peer.id] = peer
    peer.notifications = peer.createDataChannel('notifications')
    peer.notifications.on('open', this._onnotificationsOpen.bind(this, peer))
    peer.requestId = requestId
  } else {
    peer.on('datachannel', function (channel) {
      if (channel.label === 'notifications') {
        peer.notifications = channel
        peer.notifications.on('message', self._onmaskupdate)
      }
    })
  }

  peer.on('connect', this._onpeerConnect.bind(this, peer, remoteSignals))
  peer.on('close', this._onpeerDisconnect.bind(this, peer, remoteSignals))

  peer.on('error', function (err) {
    debug(this.id + ' saw peer connection error', err)
  })

  peer.on('signal', function (signal) {
    if (initiator && self.state !== 'connected') return
    signal = JSON.parse(JSON.stringify(signal))
    localSignals.push(signal)
  })

  remoteSignals.on('child_added', function (snapshot) {
    if (initiator && self.state !== 'connected') return
    var signal = snapshot.val()
    peer.signal(signal)
  })

  // timeout connections
  this._setTimeout(function () {
    if (!peer.didConnect) {
      peer.close()
    }
  }, CONNECTION_TIMEOUT)
}

Node.prototype._onpeerConnect = function (peer, remoteSignals) {
  peer.didConnect = true
  peer.removeAllListeners('connect')
  peer.removeAllListeners('signal')
  remoteSignals.off()

  if (!this.downstream[peer.id]) {
    this._onupstreamConnect(peer)
  }
}

Node.prototype._onnotificationsOpen = function (peer, evt) {
  this._ondownstreamConnect(peer)
}

Node.prototype._onpeerDisconnect = function (peer, remoteSignals) {
  peer.removeAllListeners()
  remoteSignals.off()

  if (this.downstream[peer.id]) {
    this._ondownstreamDisconnect(peer)
  } else {
    this._onupstreamDisconnect(peer)
  }
}

Node.prototype._onupstreamConnect = function (peer) {
  // remove our request
  this._requestRef.remove()

  // already got connected by someone else
  if (this.state === 'connected') {
    debug(this.id + ' rejected upstream connection by ' + peer.id)
    peer.close()
    return
  }

  this.upstream = peer

  // change state -> connected
  debug(this.id + ' established upstream connection to ' + peer.id)
  this.state = 'connected'
  this.emit('statechange')
  this.emit('connect', peer)

  // begin responding to requests
  this._reviewRequests()
}

Node.prototype._onupstreamDisconnect = function (peer) {
  // stop responding to new requests
  this._requestsRef.off('child_added', this._onrequest)

  // remove request
  this._requestRef.remove()

  this.upstream = null

  // change state -> disconnected
  if (this.state !== 'disconnected') {
    debug(this.id + ' lost upstream connection to ' + peer.id)
  }

  this.state = 'disconnected'
  this.emit('statechange')

  // emit disconnect if we were connected
  if (peer.didConnect) {
    this.emit('disconnect', peer)
  }

  // attempt to reconnect if we were not disconnected intentionally
  if (!this._preventReconnect) {

    // mask off our descendants
    this._onmaskupdate({ data: this.id })

    // give our mask update a tiny head start
    var self = this
    this._setTimeout(function () {
      if (!self._preventReconnect) {
        self.connect()
      }
    }, 100)
  }
}

Node.prototype._ondownstreamConnect = function (peer) {
  // emit peerconnect
  debug(this.id + ' established downstream connection to ' + peer.id)
  this.emit('peerconnect', peer)

  // stop responding to requests if peers > K
  if (Object.keys(this.downstream).length >= this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
  }

  // make sure downstream has the most up to date mask
  if (this._mask) {
    try {
      peer.notifications.send(this._mask)
    } catch (err) {
      console.warn(err)
    }
  }
}

Node.prototype._ondownstreamDisconnect = function (peer) {
  // remove from lookup
  delete this.downstream[peer.id]

  // emit events and potentially remove stale requests
  if (peer.didConnect) {
    if (this.state !== 'disconnected') {
      debug(this.id + ' lost downstream connection to ' + peer.id)
    }

    this.emit('peerdisconnect', peer)
  } else {
    if (peer.requestWithdrawn) {
      debug(this.id + ' saw request withdrawn by ' + peer.id)
    } else {
      debug(this.id + ' removing stale request by ' + peer.id)
      this._requestsRef.child(peer.requestId).remove()
    }
  }

  this._reviewRequests()
}

Node.prototype._onmaskupdate = function (evt) {
  this._mask = evt.data

  debug(this.id + ' set mask to ' + this._mask)

  // oops we made a circle, fix that
  if (this.downstream[this._mask]) {
    debug(this.id + ' destroying accidental circle back to ' + this._mask)
    this.downstream[this._mask].close()
  }

  for (var i in this.downstream) {
    var notifications = this.downstream[i].notifications
    try {
      notifications.send(this._mask)
    } catch (err) {
      console.warn(err)
    }
  }
}

Node.prototype._onreportNeeded = function () {
  var report = {
    state: this.state,
    upstream: this.upstream ? this.upstream.id : null,
    timestamp: Firebase.ServerValue.TIMESTAMP
  }

  if (this.root) {
    report.root = true
  }

  if (this.reportData) {
    report.data = this.reportData
  }

  this._reports
    .child(this.id)
    .update(report)

  this._reportInterval = this._setTimeout(this._onreportNeeded, this.reportInterval)
}

Node.prototype._reviewRequests = function () {
  if (this.state === 'connected' && Object.keys(this.downstream).length < this.opts.K) {
    this._requestsRef.off('child_added', this._onrequest)
    this._requestsRef.on('child_added', this._onrequest)
  }
}
<|endoftext|>


<commit_before>"use strict"

import protochain from 'protochain'

export default serializerr

function serializerr(obj = {}) {
  let chain = protochain(obj)
  .filter(obj => obj !== Object.prototype)
  return [obj]
  .concat(chain)
  .map(item => Object.getOwnPropertyNames(item))
  .reduce((result, names) => {
    names.forEach(name => result[name] = obj[name])
    return result
  }, {})
}

serializerr.serializerr = serializerr
<commit_msg>Ensure explicitly add export for commonjs.<commit_after>import protochain from 'protochain'

function serializerr(obj = {}) {
  let chain = protochain(obj)
  .filter(obj => obj !== Object.prototype)
  return [obj]
  .concat(chain)
  .map(item => Object.getOwnPropertyNames(item))
  .reduce((result, names) => {
    names.forEach(name => result[name] = obj[name])
    return result
  }, {})
}

module.exports = serializerr
serializerr.serializerr = serializerr
export default serializerr
<|endoftext|>


<commit_before>'use strict';

//This file contains then/promise specific extensions to the core promise API

var Promise = require('./core.js')
var asap = require('asap')

module.exports = Promise

/* Static Functions */

function ValuePromise(value) {
  this.then = function (onFulfilled) {
    if (typeof onFulfilled !== 'function') return this
    return new Promise(function (resolve, reject) {
      asap(function () {
        try {
          resolve(onFulfilled(value))
        } catch (ex) {
          reject(ex);
        }
      })
    })
  }
}
ValuePromise.prototype = Object.create(Promise.prototype)

var TRUE = new ValuePromise(true)
var FALSE = new ValuePromise(false)
var NULL = new ValuePromise(null)
var UNDEFINED = new ValuePromise(undefined)
var ZERO = new ValuePromise(0)
var EMPTYSTRING = new ValuePromise('')

Promise.from = Promise.cast = function (value) {
  if (value instanceof Promise) return value

  if (value === null) return NULL
  if (value === undefined) return UNDEFINED
  if (value === true) return TRUE
  if (value === false) return FALSE
  if (value === 0) return ZERO
  if (value === '') return EMPTYSTRING

  if (typeof value === 'object' || typeof value === 'function') {
    try {
      var then = value.then
      if (typeof then === 'function') {
        return new Promise(then.bind(value))
      }
    } catch (ex) {
      return new Promise(function (resolve, reject) {
        reject(ex)
      })
    }
  }

  return new ValuePromise(value)
}
Promise.denodeify = function (fn, argumentCount) {
  argumentCount = argumentCount || Infinity
  return function () {
    var self = this
    var args = Array.prototype.slice.call(arguments)
    return new Promise(function (resolve, reject) {
      while (args.length && args.length > argumentCount) {
        args.pop()
      }
      args.push(function (err, res) {
        if (err) reject(err)
        else resolve(res)
      })
      fn.apply(self, args)
    })
  }
}
Promise.nodeify = function (fn) {
  return function () {
    var args = Array.prototype.slice.call(arguments)
    var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null
    try {
      return fn.apply(this, arguments).nodeify(callback)
    } catch (ex) {
      if (callback === null || typeof callback == 'undefined') {
        return new Promise(function (resolve, reject) { reject(ex) })
      } else {
        asap(function () {
          callback(ex)
        })
      }
    }
  }
}

Promise.all = function () {
  var args = Array.prototype.slice.call(arguments.length === 1 && Array.isArray(arguments[0]) ? arguments[0] : arguments)

  return new Promise(function (resolve, reject) {
    if (args.length === 0) return resolve([])
    var remaining = args.length
    function res(i, val) {
      try {
        if (val && (typeof val === 'object' || typeof val === 'function')) {
          var then = val.then
          if (typeof then === 'function') {
            then.call(val, function (val) { res(i, val) }, reject)
            return
          }
        }
        args[i] = val
        if (--remaining === 0) {
          resolve(args);
        }
      } catch (ex) {
        reject(ex)
      }
    }
    for (var i = 0; i < args.length; i++) {
      res(i, args[i])
    }
  })
}

/* Prototype Methods */

Promise.prototype.done = function (onFulfilled, onRejected) {
  var self = arguments.length ? this.then.apply(this, arguments) : this
  self.then(null, function (err) {
    asap(function () {
      throw err
    })
  })
}

Promise.prototype.nodeify = function (callback) {
  if (callback === null || typeof callback == 'undefined') return this

  this.then(function (value) {
    asap(function () {
      callback(null, value)
    })
  }, function (err) {
    asap(function () {
      callback(err)
    })
  })
}

Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
}

Promise.resolve = function (value) {
  return new Promise(function (resolve) {
    resolve(value);
  });
}

Promise.reject = function (value) {
  return new Promise(function (resolve, reject) {
    reject(value);
  });
}

Promise.race = function (values) {
  return new Promise(function (resolve, reject) {
    values.map(function(value){
      Promise.cast(value).then(resolve, reject);
    })
  });
}
<commit_msg>Checks that a callback passed to Promise.prototype.nodeify is callable before attempting to call<commit_after>'use strict';

//This file contains then/promise specific extensions to the core promise API

var Promise = require('./core.js')
var asap = require('asap')

module.exports = Promise

/* Static Functions */

function ValuePromise(value) {
  this.then = function (onFulfilled) {
    if (typeof onFulfilled !== 'function') return this
    return new Promise(function (resolve, reject) {
      asap(function () {
        try {
          resolve(onFulfilled(value))
        } catch (ex) {
          reject(ex);
        }
      })
    })
  }
}
ValuePromise.prototype = Object.create(Promise.prototype)

var TRUE = new ValuePromise(true)
var FALSE = new ValuePromise(false)
var NULL = new ValuePromise(null)
var UNDEFINED = new ValuePromise(undefined)
var ZERO = new ValuePromise(0)
var EMPTYSTRING = new ValuePromise('')

Promise.from = Promise.cast = function (value) {
  if (value instanceof Promise) return value

  if (value === null) return NULL
  if (value === undefined) return UNDEFINED
  if (value === true) return TRUE
  if (value === false) return FALSE
  if (value === 0) return ZERO
  if (value === '') return EMPTYSTRING

  if (typeof value === 'object' || typeof value === 'function') {
    try {
      var then = value.then
      if (typeof then === 'function') {
        return new Promise(then.bind(value))
      }
    } catch (ex) {
      return new Promise(function (resolve, reject) {
        reject(ex)
      })
    }
  }

  return new ValuePromise(value)
}
Promise.denodeify = function (fn, argumentCount) {
  argumentCount = argumentCount || Infinity
  return function () {
    var self = this
    var args = Array.prototype.slice.call(arguments)
    return new Promise(function (resolve, reject) {
      while (args.length && args.length > argumentCount) {
        args.pop()
      }
      args.push(function (err, res) {
        if (err) reject(err)
        else resolve(res)
      })
      fn.apply(self, args)
    })
  }
}
Promise.nodeify = function (fn) {
  return function () {
    var args = Array.prototype.slice.call(arguments)
    var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null
    try {
      return fn.apply(this, arguments).nodeify(callback)
    } catch (ex) {
      if (callback === null || typeof callback == 'undefined') {
        return new Promise(function (resolve, reject) { reject(ex) })
      } else {
        asap(function () {
          callback(ex)
        })
      }
    }
  }
}

Promise.all = function () {
  var args = Array.prototype.slice.call(arguments.length === 1 && Array.isArray(arguments[0]) ? arguments[0] : arguments)

  return new Promise(function (resolve, reject) {
    if (args.length === 0) return resolve([])
    var remaining = args.length
    function res(i, val) {
      try {
        if (val && (typeof val === 'object' || typeof val === 'function')) {
          var then = val.then
          if (typeof then === 'function') {
            then.call(val, function (val) { res(i, val) }, reject)
            return
          }
        }
        args[i] = val
        if (--remaining === 0) {
          resolve(args);
        }
      } catch (ex) {
        reject(ex)
      }
    }
    for (var i = 0; i < args.length; i++) {
      res(i, args[i])
    }
  })
}

/* Prototype Methods */

Promise.prototype.done = function (onFulfilled, onRejected) {
  var self = arguments.length ? this.then.apply(this, arguments) : this
  self.then(null, function (err) {
    asap(function () {
      throw err
    })
  })
}

Promise.prototype.nodeify = function (callback) {
  if (!(typeof callback == 'function')) return this

  this.then(function (value) {
    asap(function () {
      callback(null, value)
    })
  }, function (err) {
    asap(function () {
      callback(err)
    })
  })
}

Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
}

Promise.resolve = function (value) {
  return new Promise(function (resolve) {
    resolve(value);
  });
}

Promise.reject = function (value) {
  return new Promise(function (resolve, reject) {
    reject(value);
  });
}

Promise.race = function (values) {
  return new Promise(function (resolve, reject) {
    values.map(function(value){
      Promise.cast(value).then(resolve, reject);
    })
  });
}
<|endoftext|>