<commit_before>var boom = require('boom')
var braveHapi = require('../brave-hapi')
var braveJoi = require('../brave-joi')
var Joi = require('joi')
var Readable = require('stream').Readable
var underscore = require('underscore')
var url = require('url')
var uuid = require('uuid')

var v1 = {}

/*
   GET /v1/reports/file/{reportId}
 */

v1.getFile = {
  handler: (runtime) => {
    return async function (request, reply) {
      var file, reader, writer
      var debug = braveHapi.debug(module, request)
      var reportId = request.params.reportId

      file = await runtime.db.file(reportId, 'r')
      if (!file) return reply(boom.notFound('no such report: ' + reportId))

      reader = runtime.db.source({ filename: reportId })
      reader.on('error', (err) => {
        debug('getFile error', err)
        reply(boom.badImplementation('Sic transit gloria mundi: ' + reportId))
      }).on('open', () => {
        debug('getFile open', underscore.pick(file, [ 'contentType', 'metadata' ]))
        writer = reply(new Readable().wrap(reader))
        if (file.contentType) {
          console.log('contentType=' + file.contentType)
          writer = writer.type(file.contentType)
        }
        underscore.keys(file.metadata || {}).forEach((header) => {
          console.log('header= ' + header + ': ' + file.metadata[header])
          writer = writer.header(header, file.metadata[header])
        })
      })
    }
  },

  description: 'Gets a report file',
  tags: [ 'api' ],

  validate:
    { params: { reportId: Joi.string().guid().required().description('the report identifier') } }
}

v1.publisher = {}
v1.publishers = {}

/*
   GET /v1/reports/publisher/{publisher}/contributions
   GET /v1/reports/publishers/contributions
 */

v1.publisher.contributions = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-contributions',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 request.params, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about contributions to a publisher',
  tags: [ 'api' ],

  validate: {
    params: { publisher: braveJoi.string().publisher().required().description('the publisher identity') },
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } }
}

v1.publishers.contributions = {
  handler: (runtime) => {
    return async function (request, reply) {
      var amount = request.query.amount
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var currency = request.query.currency
      var days = request.query.days
      var age = 0
      var rate = runtime.wallet.rates[currency.toUpperCase()]
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var threshold = 0
      var debug = braveHapi.debug(module, request)

      if ((amount) && (rate)) threshold = Math.floor((amount / rate) * 1e8)
      if (days > 0) age = underscore.now() - (days * 86400 * 1000)

      await runtime.queue.send(debug, 'report-publishers-contributions',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 { age: age, threshold: threshold }, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about contributions to publishers',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report'),
      authorized: Joi.boolean().optional().description('filter on authorization status'),
      amount: Joi.number().integer().min(0).optional().description('the minimum amount in fiat currency'),
      currency: braveJoi.string().currencyCode().optional().default('USD').description('the fiat currency'),
      days: Joi.number().integer().min(1).optional().description('the minimal age of the contributions')
    } }
}

/*
   GET /v1/reports/publisher/{publisher}/settlements
   GET /v1/reports/publishers/settlements
 */

v1.publisher.settlements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-settlements',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 request.params, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about settlements to a publisher',
  tags: [ 'api' ],

  validate: {
    params: { publisher: braveJoi.string().publisher().required().description('the publisher identity') },
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } }
}

v1.publishers.settlements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-settlements',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about settlements to publishers',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } }
}

/*
   GET /v1/reports/publisher/{publisher}/statements
   GET /v1/reports/publishers/statements/{hash}
 */

v1.publisher.statements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-statements',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 request.params, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns statements for a publisher',
  tags: [ 'api' ],

  validate: {
    params: { publisher: braveJoi.string().publisher().required().description('the publisher identity') },
    query: { summary: Joi.boolean().optional().default(true).description('summarize report') }
  }
}

v1.publishers.statements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var hash = request.params.hash
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-statements',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 { hash: hash }, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns statements for publishers',
  tags: [ 'api' ],

  validate: {
    params: { hash: Joi.string().hex().required().description('transaction hash') },
    query: {
      rollup: Joi.boolean().optional().default(true).description('include all settlements for associated publishers'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    }
  }
}

/*
   GET /v1/reports/publishers/status
 */

v1.publishers.status = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-status',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger', 'QA' ],
    mode: 'required'
  },

  description: 'Returns information about publisher status',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the response'),
      elide: Joi.boolean().optional().default(true).description('elide contact information'),
      summary: Joi.boolean().optional().default(true).description('summarize report'),
      verified: Joi.boolean().optional().description('filter on verification status')
    } },

  response:
    { schema: Joi.object().keys().unknown(true) }
}

/*
   GET /v1/reports/surveyors
 */

v1.surveyors = {}

v1.surveyors.contributions = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-surveyors-contributions',
                             underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                 request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about contribution activity',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } },

  response:
    { schema: Joi.object().keys().unknown(true) }
}

module.exports.routes = [
  braveHapi.routes.async().path('/v1/reports/file/{reportId}').config(v1.getFile),
  braveHapi.routes.async().path('/v1/reports/publisher/{publisher}/contributions').config(v1.publisher.contributions),
  braveHapi.routes.async().path('/v1/reports/publishers/contributions').config(v1.publishers.contributions),
  braveHapi.routes.async().path('/v1/reports/publisher/{publisher}/settlements').config(v1.publisher.settlements),
  braveHapi.routes.async().path('/v1/reports/publishers/settlements').config(v1.publishers.settlements),
  braveHapi.routes.async().path('/v1/reports/publisher/{publisher}/statements').config(v1.publisher.statements),
  braveHapi.routes.async().path('/v1/reports/publishers/statements/{hash}').config(v1.publishers.statements),
  braveHapi.routes.async().path('/v1/reports/publishers/status').config(v1.publishers.status),
  braveHapi.routes.async().path('/v1/reports/surveyors/contributions').config(v1.surveyors.contributions)
]

module.exports.initialize = async function (debug, runtime) {
  await runtime.queue.create('report-publishers-contributions')
  await runtime.queue.create('report-publishers-status')
  await runtime.queue.create('report-surveyors-contributions')
}
<commit_msg>Add /v2/reports/publishers/status<commit_after>var boom = require('boom')
var braveHapi = require('../brave-hapi')
var braveJoi = require('../brave-joi')
var Joi = require('joi')
var Readable = require('stream').Readable
var underscore = require('underscore')
var url = require('url')
var uuid = require('uuid')

var v1 = {}
var v2 = {}

/*
   GET /v1/reports/file/{reportId}
 */

v1.getFile = {
  handler: (runtime) => {
    return async function (request, reply) {
      var file, reader, writer
      var debug = braveHapi.debug(module, request)
      var reportId = request.params.reportId

      file = await runtime.db.file(reportId, 'r')
      if (!file) return reply(boom.notFound('no such report: ' + reportId))

      reader = runtime.db.source({ filename: reportId })
      reader.on('error', (err) => {
        debug('getFile error', err)
        reply(boom.badImplementation('Sic transit gloria mundi: ' + reportId))
      }).on('open', () => {
        debug('getFile open', underscore.pick(file, [ 'contentType', 'metadata' ]))
        writer = reply(new Readable().wrap(reader))
        if (file.contentType) {
          console.log('contentType=' + file.contentType)
          writer = writer.type(file.contentType)
        }
        underscore.keys(file.metadata || {}).forEach((header) => {
          console.log('header= ' + header + ': ' + file.metadata[header])
          writer = writer.header(header, file.metadata[header])
        })
      })
    }
  },

  description: 'Gets a report file',
  tags: [ 'api' ],

  validate:
    { params: { reportId: Joi.string().guid().required().description('the report identifier') } }
}

v1.publisher = {}
v1.publishers = {}
v2.publishers = {}

/*
   GET /v1/reports/publisher/{publisher}/contributions
   GET /v1/reports/publishers/contributions
 */

v1.publisher.contributions = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-contributions',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   request.params, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about contributions to a publisher',
  tags: [ 'api' ],

  validate: {
    params: { publisher: braveJoi.string().publisher().required().description('the publisher identity') },
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } }
}

v1.publishers.contributions = {
  handler: (runtime) => {
    return async function (request, reply) {
      var amount = request.query.amount
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var currency = request.query.currency
      var days = request.query.days
      var age = 0
      var rate = runtime.wallet.rates[currency.toUpperCase()]
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var threshold = 0
      var debug = braveHapi.debug(module, request)

      if ((amount) && (rate)) threshold = Math.floor((amount / rate) * 1e8)
      if (days > 0) age = underscore.now() - (days * 86400 * 1000)

      await runtime.queue.send(debug, 'report-publishers-contributions',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   { age: age, threshold: threshold }, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about contributions to publishers',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report'),
      authorized: Joi.boolean().optional().description('filter on authorization status'),
      amount: Joi.number().integer().min(0).optional().description('the minimum amount in fiat currency'),
      currency: braveJoi.string().currencyCode().optional().default('USD').description('the fiat currency'),
      days: Joi.number().integer().min(1).optional().description('the minimal age of the contributions')
    } }
}

/*
   GET /v1/reports/publisher/{publisher}/settlements
   GET /v1/reports/publishers/settlements
 */

v1.publisher.settlements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-settlements',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   request.params, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about settlements to a publisher',
  tags: [ 'api' ],

  validate: {
    params: { publisher: braveJoi.string().publisher().required().description('the publisher identity') },
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } }
}

v1.publishers.settlements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-settlements',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about settlements to publishers',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } }
}

/*
   GET /v1/reports/publisher/{publisher}/statements
   GET /v1/reports/publishers/statements/{hash}
 */

v1.publisher.statements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-statements',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   request.params, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns statements for a publisher',
  tags: [ 'api' ],

  validate: {
    params: { publisher: braveJoi.string().publisher().required().description('the publisher identity') },
    query: { summary: Joi.boolean().optional().default(true).description('summarize report') }
  }
}

v1.publishers.statements = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var hash = request.params.hash
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-statements',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   { hash: hash }, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns statements for publishers',
  tags: [ 'api' ],

  validate: {
    params: { hash: Joi.string().hex().required().description('transaction hash') },
    query: {
      rollup: Joi.boolean().optional().default(true).description('include all settlements for associated publishers'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    }
  }
}

/*
   GET /v1/reports/publishers/status
   GET /v2/reports/publishers/status
 */

v1.publishers.status = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-status',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger', 'QA' ],
    mode: 'required'
  },

  description: 'Returns information about publisher status',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the response'),
      elide: Joi.boolean().optional().default(true).description('elide contact information'),
      summary: Joi.boolean().optional().default(true).description('summarize report'),
      verified: Joi.boolean().optional().description('filter on verification status')
    } },

  response:
    { schema: Joi.object().keys().unknown(true) }
}

v2.publishers.status = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = 'automation'
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-publishers-status',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   { elide: true }, request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'simple',
    mode: 'required'
  },

  description: 'Returns information about publisher status (for automation)',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the response'),
      summary: Joi.boolean().optional().default(true).description('summarize report'),
      verified: Joi.boolean().optional().description('filter on verification status'),
      access_token: Joi.string().guid().optional()
    } },

  response:
    { schema: Joi.object().keys().unknown(true) }
}

/*
   GET /v1/reports/surveyors
 */

v1.surveyors = {}

v1.surveyors.contributions = {
  handler: (runtime) => {
    return async function (request, reply) {
      var authority = request.auth.credentials.provider + ':' + request.auth.credentials.profile.username
      var reportId = uuid.v4().toLowerCase()
      var reportURL = url.format(underscore.defaults({ pathname: '/v1/reports/file/' + reportId }, runtime.config.server))
      var debug = braveHapi.debug(module, request)

      await runtime.queue.send(debug, 'report-surveyors-contributions',
                               underscore.defaults({ reportId: reportId, reportURL: reportURL, authority: authority },
                                                   request.query))
      reply({ reportURL: reportURL })
    }
  },

  auth: {
    strategy: 'session',
    scope: [ 'ledger' ],
    mode: 'required'
  },

  description: 'Returns information about contribution activity',
  tags: [ 'api' ],

  validate: {
    query: {
      format: Joi.string().valid('json', 'csv').optional().default('csv').description('the format of the report'),
      summary: Joi.boolean().optional().default(true).description('summarize report')
    } },

  response:
    { schema: Joi.object().keys().unknown(true) }
}

module.exports.routes = [
  braveHapi.routes.async().path('/v1/reports/file/{reportId}').config(v1.getFile),
  braveHapi.routes.async().path('/v1/reports/publisher/{publisher}/contributions').config(v1.publisher.contributions),
  braveHapi.routes.async().path('/v1/reports/publishers/contributions').config(v1.publishers.contributions),
  braveHapi.routes.async().path('/v1/reports/publisher/{publisher}/settlements').config(v1.publisher.settlements),
  braveHapi.routes.async().path('/v1/reports/publishers/settlements').config(v1.publishers.settlements),
  braveHapi.routes.async().path('/v1/reports/publisher/{publisher}/statements').config(v1.publisher.statements),
  braveHapi.routes.async().path('/v1/reports/publishers/statements/{hash}').config(v1.publishers.statements),
  braveHapi.routes.async().path('/v1/reports/publishers/status').config(v1.publishers.status),
  braveHapi.routes.async().path('/v2/reports/publishers/status').config(v2.publishers.status),
  braveHapi.routes.async().path('/v1/reports/surveyors/contributions').config(v1.surveyors.contributions)
]

module.exports.initialize = async function (debug, runtime) {
  await runtime.queue.create('report-publishers-contributions')
  await runtime.queue.create('report-publishers-status')
  await runtime.queue.create('report-surveyors-contributions')
}
<|endoftext|>


<commit_before>//
// # Browbeat
//

(function () {
  // "Constants" for keys used in `localStorage`.
  var HEARTBEAT_KEY      = '_browbeat_heartbeat';
  var ELECTION_KEY       = '_browbeat_election';
  var ELECTION_START_KEY = '_browbeat_election_start';
  var CURRENT_KEY        = '_browbeat_currentMaster';
  var MSG_PREFIX         = '_browbeat_msg';
  var KEY_PREFIX         = '_browbeat_';

  //
  // ## Constructor
  //
  // Creates a new browbeat instance. Creating more then one Browbeat instance
  // per window is not recommended since storage events are not triggered on
  // the tab that initiated the change in `localStorage`. Hence the message
  // bus is broken.
  //
  // * **options**, an object of options. Available options are the properties
  //   assigned to `this` below.
  //
  var Browbeat = function Browbeat(options) {
    if (!this instanceof Browbeat) { return new Browbeat(options); }

    options = options || {};

    // How long to wait for a heartbeat before initiating a new election.
    // The actual heartbeat will be half of this value.
    this.heartbeatTTL = 2000;
    // For how long will the election be running?
    this.electionTime = 2000;
    // Set to `true` to recieve debug output.
    this.debug        = false;
    // Maximum number of messages to garbage collect on each run.
    this.gcLimit      = 100;

    for (var i in options) {
      if (typeof this[i] !== 'undefined') this[i] = options[i];
    }

    this.id              = Math.random() * 1000;
    this.store           = window.localStorage || false;
    this.isMaster        = false;
    this.sanityTimer     = null;
    this.heartbeatTimer  = null;
    this.gcTimer         = null;
    this.listeners       = {};
    this.heartbeatOffset = Math.random() * 10 + 500;

    this.init();
  };

  //
  // ## Log
  //
  // Debug function for console.logging.
  //
  Browbeat.prototype.log = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    args.unshift('[Browbeat]');
    if (this.debug) console.log.apply(console, args);
  };

  //
  // ## Init
  //
  // Initializes the current Browbeat instance. Setups up event listeners and
  // checks the current state and acts accordingly.
  //
  Browbeat.prototype.init = function browbeatInit() {
    this.log('ID:', this.id);
    // No store means no support, make it the master
    if (!this.store) {
      return this.becomeMaster();
    }

    // Hook up storage event listener
    var self = this;
    function handler(event) { self.storageEvent(event); }
    if (window.addEventListener) {
      window.addEventListener('storage', handler, false);
    }
    else {
      window.attachEventListener('storage', handler);
    }

    // Check for ongoing election.
    var now = (new Date()).getTime();
    var lastHearbeat = this.store.getItem(HEARTBEAT_KEY) || 0;
    var election = this.store.getItem(ELECTION_KEY);
    var started = this.store.getItem(ELECTION_START_KEY);
    if (election && (now - started) < this.electionTime) {
      this.log('Ongoing election, casting vote');
      return this.castVote();
    }
    // Check for heartbeat, if fresh, become slave.
    else if (now - lastHearbeat < this.heartbeatTTL) {
      this.log('Found fresh heartbeat');
      return this.becomeSlave();
    }
    // Start election.
    else {
      return this.startElection();
    }
  };

  //
  // ## Handle Storage Event
  //
  // The storage event is used as a message bus between all open tabs. Thus
  // this method acts as kind of a message dispatcher.
  //
  Browbeat.prototype.storageEvent = function browbeatEventHandler(event) {
    var key = event.key;
    if (key.indexOf(KEY_PREFIX) !== 0) {
      return;
    }

    // Handle election events.
    if (key === ELECTION_KEY) {
      // No previous value means a new election was initiated, cast our vote.
      if (event.oldValue === null) {
        clearTimeout(this.heartbeatTimer);
        clearTimeout(this.sanityTimer);
        return this.castVote();
      }
    }

    if (key === CURRENT_KEY) {
      if (event.newValue === this.id.toString()) {
        return this.becomeMaster();
      }
      else {
        return this.becomeSlave();
      }
    }

    // Handle heartbeat events. Check for dead masters.
    if (!this.isMaster && key === HEARTBEAT_KEY) {
      var self = this;
      clearTimeout(this.heartbeatTimer);
      this.heartbeatTimer = setTimeout(function () {
        self.startElection();
      }, this.heartbeatTTL + this.heartbeatOffset);
      return;
    }

    if (key.indexOf(MSG_PREFIX) === 0) {
      var data = JSON.parse(event.newValue);
      switch (data.message) {
        case 'master':
          if (this.isMaster) { this.emit('master', data.data); }
          break;
        case 'slave':
          if (!this.isMaster) { this.emit('slave', data.data); }
          break;
        case 'broadcast':
          this.emit('broadcast', data.data);
          break;
        default:
          this.emit(data.message, data.data);
          break;
      }
      return;
    }
  };

  // -------------------------------------------------------------------------

  //
  // ## Become Master
  //
  // Becomes the master window. Initiate heartbeat and emit event.
  //
  Browbeat.prototype.becomeMaster = function browbeatElected() {
    this.log('Became master');
    var self = this;
    this.isMaster = true;
    this.emit('wonElection');

    if (this.store) {
      this.heartbeatTimer = setInterval(function heartbeat() {
        self.store.setItem(HEARTBEAT_KEY, (new Date()).getTime());
      }, this.heartbeatTTL / 2);

      // Garbage collect messages older then 2 seconds
      this.gcTimer = setInterval(function garbageCollect() {
        var now = (new Date()).getTime();
        var len = self.store.length;
        for (var i = len; i >= 0; i--) {
          if (i > self.gcLimit) break;
          var key = self.store.key(i);
          if (key && key.indexOf(MSG_PREFIX) === 0) {
            var parts = key.split('~');
            if (now - parseInt(parts[1], 10) > 2000) {
              self.store.removeItem(key);
              console.log('collected', key);
            }
          }
        }
      }, Math.random() * 20000);
    }
  };

  //
  // ## Resign
  //
  // Resigns presidency and let the other windows initiate a new election.
  // Assigns a new ID to avoid the same outcome after a vote.
  //
  Browbeat.prototype.resign = function browbeatResign() {
    if (!this.master) return;
    this.id = Math.random() * 1000;
    this.isMaster = false;
    this.emit('resigned');
    clearInterval(this.heartbeatTimer);
    this.becomeSlave();
  };

  //
  // ## Become Slave
  //
  // Did not win election. Monitor heartbeat and react to dead master.
  //
  Browbeat.prototype.becomeSlave = function browbeatBecomeSlave() {
    this.log('Became slave');
    this.isMaster = false;
    this.emit('lostElection');
    var self = this;
    clearTimeout(this.heartbeatTimer);
    this.heartbeatTimer = setTimeout(function () {
      self.startElection();
    }, this.heartbeatTTL + this.heartbeatOffset);
  };

  //
  // ## Cast Vote
  //
  // Register as a candidate in the election.
  //
  Browbeat.prototype.castVote = function browbeatVote() {
    clearTimeout(this.sanityTimer);
    this.log('Casting vote');
    this.emit('voting');
    var votes = this.store.getItem(ELECTION_KEY);
    votes = votes ? votes.split(',') : [];
    votes.push(this.id);
    this.store.setItem(ELECTION_KEY, votes);

    // Sometimes the initiating window will disappear before the election is
    // completed. To avoid a stalemate add a sanity check here.
    var self = this;
    this.sanity = setTimeout(function () {
      if (!self.store.getItem(CURRENT_KEY)) {
        self.startElection();
      }
    }, this.electionTime + this.heartbeatOffset);
  };

  //
  // ## Start Election
  //
  // Initiates a new election by writing to the localStorage. Since storage
  // events are not emitted to the window that initiated the event this method
  // also casts a vote.
  //
  Browbeat.prototype.startElection = function browbeatStartElection() {
    this.log('Initiating election');
    this.emit('electionInitiated');

    var self = this;
    this.store.removeItem(CURRENT_KEY);
    this.store.removeItem(HEARTBEAT_KEY);
    this.castVote();
    this.store.setItem(ELECTION_START_KEY, (new Date()).getTime());
    setTimeout(function endElection() {
      self.emit('electionConcluded');
      var candidates = self.store.getItem(ELECTION_KEY);
      candidates = candidates ? candidates.split(',') : [self.id];
      var winner = Math.max.apply(Math, candidates);
      self.store.setItem(CURRENT_KEY, winner);
      self.store.removeItem(ELECTION_KEY);
      self.store.removeItem(ELECTION_START_KEY);
      if (winner === self.id) {
        self.becomeMaster();
      }
    }, this.electionTime);
  };

  // -------------------------------------------------------------------------

  //
  // ## On Event
  //
  // Custom event emitter functionality. Attach a handler to the given event.
  //
  Browbeat.prototype.on = function browbeatEventOn(e, handler) {
    if (!this.listeners[e]) {
      this.listeners[e] = [];
    }

    this.listeners[e].push(handler);
  };

  //
  // ## Emit Event
  //
  // Emits an event to the registered listeners.
  //
  Browbeat.prototype.emit = function browbeatEventEmit(e, data) {
    if (!this.listeners[e]) return;
    data = data || {};
    data.eventName = e;
    for (var i in this.listeners[e]) {
      this.listeners[e][i](data);
    }
  };

  //
  // ## Remove Event Listener
  //
  // Removes the given event listener from the given event. The function
  // supplied here must be the exact same function supplied to `on()`.
  //
  Browbeat.prototype.off = function browbeatEventOff(e, handler) {
    if (!this.listeners[e]) {
      this.listeners[e] = [];
    }

    for (var i in this.listeners[e]) {
      if (this.listeners[e][i] === handler) {
        this.listeners[e].splice(i, 1);
      }
    }
  };

  // -------------------------------------------------------------------------

  //
  // ## Broadcast
  //
  // Broadcast a message to _all_ windows, including the sender.
  //
  Browbeat.prototype.broadcast = function browbeatBroadcast(message) {
    this.emit('broadcast', message);
    this.sendMessage('broadcast', message);
  };

  //
  // ## Message Master
  //
  // Sends a message to the master only.
  //
  Browbeat.prototype.messageMaster = function browbeatMsgMaster(message) {
    if (this.isMaster) {
      this.emit('master', message);
    }
    else {
      this.sendMessage('master', message);
    }
  };

  //
  // ## Message Slaves
  //
  // Sends a message to the slaves only.
  //
  Browbeat.prototype.messageSlaves = function browbeatMsSlaves(message) {
    if (!this.isMaster) this.emit('slave', message);
    this.sendMessage('slave', message);
  };

  //
  // ## Send Message
  //
  // Sends a message on the "bus" to other tabs. The message is written to the
  // `localStorage`. The message will be garbage collected by the master at
  // some point.
  //
  Browbeat.prototype.sendMessage = function browbeatSend(message, data) {
    var msg = {
      message: message,
      data: data,
      timestamp: (new Date()).getTime()
    };

    var key = MSG_PREFIX + '~' + msg.timestamp + '~' + Math.random();
    this.store.setItem(key, JSON.stringify(msg));
    this.emit('sentMessage', [message, data]);
  };

  // -------------------------------------------------------------------------

  //
  // ## Export Module
  //
  // Try to be a good citizen in whatever environment we find ourselves.
  //
  (function () {
    var hasDefine = typeof define === 'function' && define.amd;
    var hasExport = typeof exports !== 'undefined';

    if (hasDefine) {
      define('Browbeat', Browbeat);
    }
    else if (hasExport) {
      exports = Browbeat;
    }
    else {
      window.Browbeat = Browbeat;
    }
  }());
}());

<commit_msg>Don't waste cycles.<commit_after>//
// # Browbeat
//

(function () {
  // "Constants" for keys used in `localStorage`.
  var HEARTBEAT_KEY      = '_browbeat_heartbeat';
  var ELECTION_KEY       = '_browbeat_election';
  var ELECTION_START_KEY = '_browbeat_election_start';
  var CURRENT_KEY        = '_browbeat_currentMaster';
  var MSG_PREFIX         = '_browbeat_msg';
  var KEY_PREFIX         = '_browbeat_';

  //
  // ## Constructor
  //
  // Creates a new browbeat instance. Creating more then one Browbeat instance
  // per window is not recommended since storage events are not triggered on
  // the tab that initiated the change in `localStorage`. Hence the message
  // bus is broken.
  //
  // * **options**, an object of options. Available options are the properties
  //   assigned to `this` below.
  //
  var Browbeat = function Browbeat(options) {
    if (!this instanceof Browbeat) { return new Browbeat(options); }

    options = options || {};

    // How long to wait for a heartbeat before initiating a new election.
    // The actual heartbeat will be half of this value.
    this.heartbeatTTL = 2000;
    // For how long will the election be running?
    this.electionTime = 2000;
    // Set to `true` to recieve debug output.
    this.debug        = false;
    // Maximum number of messages to garbage collect on each run.
    this.gcLimit      = 100;

    for (var i in options) {
      if (typeof this[i] !== 'undefined') this[i] = options[i];
    }

    this.id              = Math.random() * 1000;
    this.store           = window.localStorage || false;
    this.isMaster        = false;
    this.sanityTimer     = null;
    this.heartbeatTimer  = null;
    this.gcTimer         = null;
    this.listeners       = {};
    this.heartbeatOffset = Math.random() * 10 + 500;

    this.init();
  };

  //
  // ## Log
  //
  // Debug function for console.logging.
  //
  Browbeat.prototype.log = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    args.unshift('[Browbeat]');
    if (this.debug) console.log.apply(console, args);
  };

  //
  // ## Init
  //
  // Initializes the current Browbeat instance. Setups up event listeners and
  // checks the current state and acts accordingly.
  //
  Browbeat.prototype.init = function browbeatInit() {
    this.log('ID:', this.id);
    // No store means no support, make it the master
    if (!this.store) {
      return this.becomeMaster();
    }

    // Hook up storage event listener
    var self = this;
    function handler(event) { self.storageEvent(event); }
    if (window.addEventListener) {
      window.addEventListener('storage', handler, false);
    }
    else {
      window.attachEventListener('storage', handler);
    }

    // Check for ongoing election.
    var now = (new Date()).getTime();
    var lastHearbeat = this.store.getItem(HEARTBEAT_KEY) || 0;
    var election = this.store.getItem(ELECTION_KEY);
    var started = this.store.getItem(ELECTION_START_KEY);
    if (election && (now - started) < this.electionTime) {
      this.log('Ongoing election, casting vote');
      return this.castVote();
    }
    // Check for heartbeat, if fresh, become slave.
    else if (now - lastHearbeat < this.heartbeatTTL) {
      this.log('Found fresh heartbeat');
      return this.becomeSlave();
    }
    // Start election.
    else {
      return this.startElection();
    }
  };

  //
  // ## Handle Storage Event
  //
  // The storage event is used as a message bus between all open tabs. Thus
  // this method acts as kind of a message dispatcher.
  //
  Browbeat.prototype.storageEvent = function browbeatEventHandler(event) {
    var key = event.key;
    if (key.indexOf(KEY_PREFIX) !== 0) {
      return;
    }

    // Handle election events.
    if (key === ELECTION_KEY) {
      // No previous value means a new election was initiated, cast our vote.
      if (event.oldValue === null) {
        clearTimeout(this.heartbeatTimer);
        clearTimeout(this.sanityTimer);
        return this.castVote();
      }
    }

    if (key === CURRENT_KEY) {
      if (event.newValue === this.id.toString()) {
        return this.becomeMaster();
      }
      else {
        return this.becomeSlave();
      }
    }

    // Handle heartbeat events. Check for dead masters.
    if (!this.isMaster && key === HEARTBEAT_KEY) {
      var self = this;
      clearTimeout(this.heartbeatTimer);
      this.heartbeatTimer = setTimeout(function () {
        self.startElection();
      }, this.heartbeatTTL + this.heartbeatOffset);
      return;
    }

    if (key.indexOf(MSG_PREFIX) === 0) {
      var data = JSON.parse(event.newValue);
      switch (data.message) {
        case 'master':
          if (this.isMaster) { this.emit('master', data.data); }
          break;
        case 'slave':
          if (!this.isMaster) { this.emit('slave', data.data); }
          break;
        case 'broadcast':
          this.emit('broadcast', data.data);
          break;
        default:
          this.emit(data.message, data.data);
          break;
      }
      return;
    }
  };

  // -------------------------------------------------------------------------

  //
  // ## Become Master
  //
  // Becomes the master window. Initiate heartbeat and emit event.
  //
  Browbeat.prototype.becomeMaster = function browbeatElected() {
    this.log('Became master');
    var self = this;
    this.isMaster = true;
    this.emit('wonElection');

    if (this.store) {
      this.heartbeatTimer = setInterval(function heartbeat() {
        self.store.setItem(HEARTBEAT_KEY, (new Date()).getTime());
      }, this.heartbeatTTL / 2);

      // Garbage collect messages older then 2 seconds
      this.gcTimer = setInterval(function garbageCollect() {
        var now = (new Date()).getTime();
        var len = self.store.length;
        for (var i = len; i >= 0; i--) {
          if (i > self.gcLimit) break;
          var key = self.store.key(i);
          if (key && key.indexOf(MSG_PREFIX) === 0) {
            var parts = key.split('~');
            if (now - parseInt(parts[1], 10) > 2000) {
              self.store.removeItem(key);
              console.log('collected', key);
            }
          }
        }
      }, Math.random() * 20000);
    }
  };

  //
  // ## Resign
  //
  // Resigns presidency and let the other windows initiate a new election.
  // Assigns a new ID to avoid the same outcome after a vote.
  //
  Browbeat.prototype.resign = function browbeatResign() {
    if (!this.master) return;
    this.id = Math.random() * 1000;
    this.isMaster = false;
    this.emit('resigned');
    clearInterval(this.heartbeatTimer);
    this.becomeSlave();
  };

  //
  // ## Become Slave
  //
  // Did not win election. Monitor heartbeat and react to dead master.
  //
  Browbeat.prototype.becomeSlave = function browbeatBecomeSlave() {
    this.log('Became slave');
    this.isMaster = false;
    this.emit('lostElection');
    var self = this;
    clearTimeout(this.heartbeatTimer);
    this.heartbeatTimer = setTimeout(function () {
      self.startElection();
    }, this.heartbeatTTL + this.heartbeatOffset);
  };

  //
  // ## Cast Vote
  //
  // Register as a candidate in the election.
  //
  Browbeat.prototype.castVote = function browbeatVote() {
    clearTimeout(this.sanityTimer);
    this.log('Casting vote');
    this.emit('voting');
    var votes = this.store.getItem(ELECTION_KEY);
    votes = votes ? votes.split(',') : [];
    votes.push(this.id);
    this.store.setItem(ELECTION_KEY, votes);

    // Sometimes the initiating window will disappear before the election is
    // completed. To avoid a stalemate add a sanity check here.
    var self = this;
    this.sanity = setTimeout(function () {
      if (!self.store.getItem(CURRENT_KEY)) {
        self.startElection();
      }
    }, this.electionTime + this.heartbeatOffset);
  };

  //
  // ## Start Election
  //
  // Initiates a new election by writing to the localStorage. Since storage
  // events are not emitted to the window that initiated the event this method
  // also casts a vote.
  //
  Browbeat.prototype.startElection = function browbeatStartElection() {
    this.log('Initiating election');
    this.emit('electionInitiated');

    var self = this;
    this.store.removeItem(CURRENT_KEY);
    this.store.removeItem(HEARTBEAT_KEY);
    this.castVote();
    this.store.setItem(ELECTION_START_KEY, (new Date()).getTime());
    setTimeout(function endElection() {
      self.emit('electionConcluded');
      var candidates = self.store.getItem(ELECTION_KEY);
      candidates = candidates ? candidates.split(',') : [self.id];
      var winner = Math.max.apply(Math, candidates);
      self.store.setItem(CURRENT_KEY, winner);
      self.store.removeItem(ELECTION_KEY);
      self.store.removeItem(ELECTION_START_KEY);
      if (winner === self.id) {
        self.becomeMaster();
      }
    }, this.electionTime);
  };

  // -------------------------------------------------------------------------

  //
  // ## On Event
  //
  // Custom event emitter functionality. Attach a handler to the given event.
  //
  Browbeat.prototype.on = function browbeatEventOn(e, handler) {
    if (!this.listeners[e]) {
      this.listeners[e] = [];
    }

    this.listeners[e].push(handler);
  };

  //
  // ## Emit Event
  //
  // Emits an event to the registered listeners.
  //
  Browbeat.prototype.emit = function browbeatEventEmit(e, data) {
    if (!this.listeners[e]) return;
    data = data || {};
    data.eventName = e;
    for (var i in this.listeners[e]) {
      this.listeners[e][i](data);
    }
  };

  //
  // ## Remove Event Listener
  //
  // Removes the given event listener from the given event. The function
  // supplied here must be the exact same function supplied to `on()`.
  //
  Browbeat.prototype.off = function browbeatEventOff(e, handler) {
    if (!this.listeners[e]) {
      this.listeners[e] = [];
    }

    for (var i in this.listeners[e]) {
      if (this.listeners[e][i] === handler) {
        this.listeners[e].splice(i, 1);
        break;
      }
    }
  };

  // -------------------------------------------------------------------------

  //
  // ## Broadcast
  //
  // Broadcast a message to _all_ windows, including the sender.
  //
  Browbeat.prototype.broadcast = function browbeatBroadcast(message) {
    this.emit('broadcast', message);
    this.sendMessage('broadcast', message);
  };

  //
  // ## Message Master
  //
  // Sends a message to the master only.
  //
  Browbeat.prototype.messageMaster = function browbeatMsgMaster(message) {
    if (this.isMaster) {
      this.emit('master', message);
    }
    else {
      this.sendMessage('master', message);
    }
  };

  //
  // ## Message Slaves
  //
  // Sends a message to the slaves only.
  //
  Browbeat.prototype.messageSlaves = function browbeatMsSlaves(message) {
    if (!this.isMaster) this.emit('slave', message);
    this.sendMessage('slave', message);
  };

  //
  // ## Send Message
  //
  // Sends a message on the "bus" to other tabs. The message is written to the
  // `localStorage`. The message will be garbage collected by the master at
  // some point.
  //
  Browbeat.prototype.sendMessage = function browbeatSend(message, data) {
    var msg = {
      message: message,
      data: data,
      timestamp: (new Date()).getTime()
    };

    var key = MSG_PREFIX + '~' + msg.timestamp + '~' + Math.random();
    this.store.setItem(key, JSON.stringify(msg));
    this.emit('sentMessage', [message, data]);
  };

  // -------------------------------------------------------------------------

  //
  // ## Export Module
  //
  // Try to be a good citizen in whatever environment we find ourselves.
  //
  (function () {
    var hasDefine = typeof define === 'function' && define.amd;
    var hasExport = typeof exports !== 'undefined';

    if (hasDefine) {
      define('Browbeat', Browbeat);
    }
    else if (hasExport) {
      exports = Browbeat;
    }
    else {
      window.Browbeat = Browbeat;
    }
  }());
}());

<|endoftext|>


<commit_before>// import Inferno from '../..';
import getRecycledFragment from './getRecycledFragment';
import updateFragment from './updateFragment';
import attachFragmentList from './attachFragmentList';
import fragmentValueTypes from '../enum/fragmentValueTypes';
import insertFragment from './insertFragment';
import bind from '../util/bind';
import templateTypes from '../enum/templateTypes';
import templateCreateElement from '../template/createElement';
import domTemplate from '../template/dom';
import virtualTemplate from '../template/virtual';
import attachComponent from './attachComponent';

function attachFragment(context, fragment, parentDom, component, nextFragment, replace) {
	let { template } = fragment,
		templateKey = template.key,
		mountCallbacks = [];

	if (context.shouldRecycle === true) {
	    let recycledFragment = getRecycledFragment(templateKey);
	    if (recycledFragment != null) {
	        updateFragment(context, recycledFragment, fragment, parentDom, component);
	        insertFragment(context, fragment, parentDom, fragment.dom, nextFragment, replace);
	        return;
	    }
	}

	//there are different things we need to check for now
	const templateToUse = context.useVirtual ? virtualTemplate : domTemplate;
	switch (template.type) {
		case templateTypes.TEMPLATE_API:
			template(fragment, templateToUse);
			break;
		case templateTypes.FUNCTIONAL_API:
			const createElement = bind(fragment, templateCreateElement(templateToUse));
			let params = [createElement], length = (fragment.templateValue != null && 1)
				|| (fragment.templateValues && fragment.templateValues.length) || 0;

			//create our pointers, for example 0,1,2,3,4,5 as params to pass through
			for(let i = 0; i < length; i++) {
				params.push({pointer: i});
			}
			fragment.dom = template.apply(null, params);
			break;
		default:
			template(fragment);
			break;
	}
	//if this fragment has a single value, we attach only that value
	if ( fragment.templateValue ) {
		switch ( fragment.templateType ) {
			case fragmentValueTypes.LIST:
				attachFragmentList( context, fragment.templateValue, fragment.templateElement );
				break;
			case fragmentValueTypes.FRAGMENT:
			case fragmentValueTypes.LIST_REPLACE:
				attachFragment( context, fragment.templateValue, fragment.templateElement, component );
				break;
			case fragmentValueTypes.FRAGMENT_REPLACE:
				attachFragment( context, fragment.templateValue, parentDom, fragment.templateElement, true );
				fragment.templateElement = fragment.templateValue.dom.parentNode;
				break;
			case fragmentValueTypes.ATTR_REF:
				fragment.templateValue.element = fragment.templateElement;
				break;
			case fragmentValueTypes.COMPONENT_REPLACE:
				var replace = true;

				if(fragment.dom.nodeName === '#text') {
					fragment.dom = document.createDocumentFragment();
					replace = false;
				}
				const {newElement, component, mountCallback} = attachComponent(fragment.templateElement, fragment.templateValue, fragment.templateComponent, fragment.dom, replace);
				fragment.templateElement = newElement;
				fragment.templateComponent = component;
				mountCallbacks.push(mountCallback);
				break;
			case fragmentValueTypes.COMPONENT:
				debugger;
				break;
		}
	} else if ( fragment.templateValues ) {
		//if the fragment has multiple values, we must loop through them all and attach them
		//pulling this block of code out into its own function caused strange things to happen
		//with performance. it was faster in Gecko but far slower in v8
		for ( let i = 0, length = fragment.templateValues.length; i < length; i++ ) {
			let element = fragment.templateElements[i],
				value = fragment.templateValues[i];

			switch ( fragment.templateTypes[i] ) {
				case fragmentValueTypes.LIST:
					attachFragmentList( context, value, element );
					break;
				case fragmentValueTypes.LIST_REPLACE:
					const nodeList = document.createDocumentFragment(),
						placeholderNode = fragment.templateElements[i],
						parentElem = placeholderNode.parentNode;

					attachFragmentList( context, value, nodeList );
					parentElem.replaceChild( nodeList, placeholderNode );
					fragment.templateElements[i] = parentElem;
					break;
				case fragmentValueTypes.FRAGMENT:
					attachFragment( context, fragment.templateValues[i], fragment.templateElements[i], component );
					break;
				case fragmentValueTypes.FRAGMENT_REPLACE:
					attachFragment( context, value, parentDom, component, element, true );
					fragment.templateElements[i] = value.dom.parentNode;
					break;
				case fragmentValueTypes.ATTR_REF:
					fragment.templateValues[i].element = fragment.templateElements[i];
					break;
				case fragmentValueTypes.COMPONENT_REPLACE:
					const {newElement, component, mountCallback} = attachComponent(element, value, fragment.templateComponents[i], fragment.dom);
					fragment.templateElements[i] = newElement;
					fragment.templateComponents[i] = component;
					mountCallbacks.push(mountCallback);
					break;
				case fragmentValueTypes.COMPONENT:
					debugger;
					break;
			}
		}
	}

	insertFragment(context, fragment, parentDom, fragment.dom, nextFragment, replace );
	//now fire all the component mountCallback functions so they know this fragment has been added
	for(let i = 0; i < mountCallbacks.length; i++) {
		mountCallbacks[i]();
	}
}

export default attachFragment;
<commit_msg>more eslint<commit_after>import getRecycledFragment from './getRecycledFragment';
import updateFragment from './updateFragment';
import attachFragmentList from './attachFragmentList';
import fragmentValueTypes from '../enum/fragmentValueTypes';
import insertFragment from './insertFragment';
import bind from '../util/bind';
import templateTypes from '../enum/templateTypes';
import templateCreateElement from '../template/createElement';
import domTemplate from '../template/dom';
import virtualTemplate from '../template/virtual';
import attachComponent from './attachComponent';

function attachFragment(context, fragment, parentDom, component, nextFragment, replace) {
    let {
        template
    } = fragment;
    let templateKey = template.key;
    let mountCallbacks = [];

    if (context.shouldRecycle === true) {
		 let recycledFragment = getRecycledFragment(templateKey);
        if (recycledFragment != null) {
            updateFragment(context, recycledFragment, fragment, parentDom, component);
            insertFragment(context, fragment, parentDom, fragment.dom, nextFragment, replace);
            return;
        }
    }

    //there are different things we need to check for now
    const templateToUse = context.useVirtual ? virtualTemplate : domTemplate;
    switch (template.type) {
        case templateTypes.TEMPLATE_API:
            template(fragment, templateToUse);
            break;
        case templateTypes.FUNCTIONAL_API:
            const createElement = bind(fragment, templateCreateElement(templateToUse));
            let params = [createElement],
                length = (fragment.templateValue != null && 1) || (fragment.templateValues && fragment.templateValues.length) || 0;

            //create our pointers, for example 0,1,2,3,4,5 as params to pass through
            for (let i = 0; i < length; i++) {
                params.push({
                    pointer: i
                });
            }
            fragment.dom = template.apply(null, params);
            break;
        default:
            template(fragment);
            break;
    }
    //if this fragment has a single value, we attach only that value
    if (fragment.templateValue) {
        switch (fragment.templateType) {
            case fragmentValueTypes.LIST:
                attachFragmentList(context, fragment.templateValue, fragment.templateElement);
                break;
            case fragmentValueTypes.FRAGMENT:
            case fragmentValueTypes.LIST_REPLACE:
                attachFragment(context, fragment.templateValue, fragment.templateElement, component);
                break;
            case fragmentValueTypes.FRAGMENT_REPLACE:
                attachFragment(context, fragment.templateValue, parentDom, fragment.templateElement, true);
                fragment.templateElement = fragment.templateValue.dom.parentNode;
                break;
            case fragmentValueTypes.ATTR_REF:
                fragment.templateValue.element = fragment.templateElement;
                break;
            case fragmentValueTypes.COMPONENT_REPLACE:
                var replace = true;

                if (fragment.dom.nodeName === '#text') {
                    fragment.dom = document.createDocumentFragment();
                    replace = false;
                }
                const {
                    newElement, component, mountCallback
                } = attachComponent(fragment.templateElement, fragment.templateValue, fragment.templateComponent, fragment.dom, replace);
                fragment.templateElement = newElement;
                fragment.templateComponent = component;
                mountCallbacks.push(mountCallback);
                break;
            case fragmentValueTypes.COMPONENT:
                debugger;
                break;
        }
    } else if (fragment.templateValues) {
        //if the fragment has multiple values, we must loop through them all and attach them
        //pulling this block of code out into its own function caused strange things to happen
        //with performance. it was faster in Gecko but far slower in v8
        for (let i = 0, length = fragment.templateValues.length; i < length; i++) {
            let element = fragment.templateElements[i],
                value = fragment.templateValues[i];

            switch (fragment.templateTypes[i]) {
                case fragmentValueTypes.LIST:
                    attachFragmentList(context, value, element);
                    break;
                case fragmentValueTypes.LIST_REPLACE:
                    const nodeList = document.createDocumentFragment(),
                        placeholderNode = fragment.templateElements[i],
                        parentElem = placeholderNode.parentNode;

                    attachFragmentList(context, value, nodeList);
                    parentElem.replaceChild(nodeList, placeholderNode);
                    fragment.templateElements[i] = parentElem;
                    break;
                case fragmentValueTypes.FRAGMENT:
                    attachFragment(context, fragment.templateValues[i], fragment.templateElements[i], component);
                    break;
                case fragmentValueTypes.FRAGMENT_REPLACE:
                    attachFragment(context, value, parentDom, component, element, true);
                    fragment.templateElements[i] = value.dom.parentNode;
                    break;
                case fragmentValueTypes.ATTR_REF:
                    fragment.templateValues[i].element = fragment.templateElements[i];
                    break;
                case fragmentValueTypes.COMPONENT_REPLACE:
                    const {
                        newElement, component, mountCallback
                    } = attachComponent(element, value, fragment.templateComponents[i], fragment.dom);
                    fragment.templateElements[i] = newElement;
                    fragment.templateComponents[i] = component;
                    mountCallbacks.push(mountCallback);
                    break;
                case fragmentValueTypes.COMPONENT:
                    debugger;
                    break;
            }
        }
    }

    insertFragment(context, fragment, parentDom, fragment.dom, nextFragment, replace);
    //now fire all the component mountCallback functions so they know this fragment has been added
    for (let i = 0; i < mountCallbacks.length; i++) {
        mountCallbacks[i]();
    }
}

export default attachFragment;<|endoftext|>


<commit_before>// http://res.cloudinary.com/demo/image/upload/f_auto,q_auto,w_250,h_250,c_fit/sample.jpg
const defaultState = 'f_auto,q_auto,fl_lossy';

export default (url, { mode, maxWidth, effect, maxHeight, border, width, height, cropX, cropY, quality, blur, retina, crop: crop0 } = {}, crop) => {
  if (!crop) crop = crop0;
  if (!mode) mode = 'fill';

  // RETINA
  // if (retina && width) width *= 2; geht so nicht, da cloudinary einen Fehler wirft, wenn die angefragt Größe > als die tatsächlich ist
  // if (retina && height) height *= 2;
  if (retina && maxWidth && maxWidth * 2 < width) maxWidth *= 2;
  if (retina && maxHeight && maxHeight * 2 < height) maxHeight *= 2;

  if (!url) return url;
  if (crop) {
    width = crop[0];
    height = crop[1];
    cropX = crop[2];
    cropY = crop[3];
  }
  if (url.indexOf('http://res.cloudinary.com/') === 0) {
    url = url.split('ttp://res.cloudinary.com/').join('ttps://res.cloudinary.com/');
  }
  if (url.indexOf('https://res.cloudinary.com/') !== 0) return url;
  let part = defaultState;
  if (cropX !== undefined && cropY !== undefined) {
    part = `x_${cropX},y_${cropY},w_${width},h_${height},c_crop/${part}`;
  } else if (width && height) {
    part = `w_${width},h_${height},c_${mode}/${part}`;
  }

  if (maxWidth || maxHeight) {
    if (maxWidth) part += `,w_${maxWidth}`;
    if (maxHeight) part += `,h_${maxHeight}`;
    part += `,c_${mode}`;
  }
  if (quality) {
    part += `,q_${quality}`;
  }
  if (blur) {
    part += `,e_blur:${blur}`;
  }
  if (part === defaultState) {
    part += ',q_75';
  }
  if (border) {
    Object.keys(border).map((key) => {
      part = `bo_${border[key]}px_solid_${key}/${part}`;
    });
  }
  if (effect) {
    part = `e_${effect}/${part}`;
  }

  return url.replace('/upload/', `/upload/${part}/`);
};
<commit_msg>fix error with image-comp (doesn’t load image in full size)<commit_after>// http://res.cloudinary.com/demo/image/upload/f_auto,q_auto,w_250,h_250,c_fit/sample.jpg
const defaultState = 'f_auto,q_auto,fl_lossy';

export default (url, { mode, maxWidth, effect, maxHeight, border, width, height, cropX, cropY, quality, blur, retina, crop: crop0 } = {}, crop) => {
  if (!crop) crop = crop0;
  if (!mode) mode = 'fill';

  // RETINA
  // if (retina && width) width *= 2; geht so nicht, da cloudinary einen Fehler wirft, wenn die angefragt Größe > als die tatsächlich ist
  // if (retina && height) height *= 2;
  if (retina && maxWidth && maxWidth * 2 < width) maxWidth *= 2;
  if (retina && maxHeight && maxHeight * 2 < height) maxHeight *= 2;

  if (!url) return url;
  if (crop) {
    width = crop[0];
    height = crop[1];
    cropX = crop[2];
    cropY = crop[3];
  }
  if (url.indexOf('http://res.cloudinary.com/') === 0) {
    url = url.split('ttp://res.cloudinary.com/').join('ttps://res.cloudinary.com/');
  }
  if (url.indexOf('https://res.cloudinary.com/') !== 0) return url;
  let part = defaultState;
  if (cropX !== undefined && cropY !== undefined) {
    part = `x_${cropX},y_${cropY},w_${width},h_${height},c_crop/${part}`;
  } else if (width && height) {
    // part = `w_${width},h_${height},c_${mode}/${part}`;
    // beni, solltest du das hier ändern wollen, dann sag mir Bescheid. Mit dieser Zeile wird eine URL wie folgt erzeugt:
    // https://res.cloudinary.com/dhsf4vjjc/image/upload/w_2896,h_1944,c_fill/f_auto,q_auto,fl_lossy,w_960,c_fill/v1490025279/p0xdfvo73u7pj6oudtf4.tiff
    // d.h. die Bilder werden mit voller Dimension geladen, egal wie groß sie eigentlich sein sollten!
  }

  if (maxWidth || maxHeight) {
    if (maxWidth) part += `,w_${maxWidth}`;
    if (maxHeight) part += `,h_${maxHeight}`;
    part += `,c_${mode}`;
  }
  if (quality) {
    part += `,q_${quality}`;
  }
  if (blur) {
    part += `,e_blur:${blur}`;
  }
  if (part === defaultState) {
    part += ',q_75';
  }
  if (border) {
    Object.keys(border).map((key) => {
      part = `bo_${border[key]}px_solid_${key}/${part}`;
    });
  }
  if (effect) {
    part = `e_${effect}/${part}`;
  }

  return url.replace('/upload/', `/upload/${part}/`);
};
<|endoftext|>


<commit_before>// @flow
/**
 * Machine: contains all mutable state.
 * @module machine
 *
 * The machine should be in charge of managing the animation frames, but it
 * doesn't know anything about what to do in those frames, that's up to the
 * Animator.
 */

import type { VoidFn, Machine } from './flow-types'

// TODO: use utils
const noop: VoidFn = () => {};

/**
 * Creates a machine that will continue to run until the caller tells it to stop.
 */
export const PerpetualMachineFactory = (
  requestAnimationFrame: (fn: VoidFn) => void,
  cancelAnimationFrame: (frame: number) => void,
) => (
  onComplete?: VoidFn = noop,
  onFrame?: VoidFn = noop,
): Machine => {

  let frame: ?number = null;
  let machineIsStopped: boolean = false;

  const jobs: Array<VoidFn> = [];

  const runIteration: VoidFn = () => {
    if (machineIsStopped) return;

    onFrame();
    frame = requestAnimationFrame(() => {
      jobs.forEach(job => job());
      runIteration();
    });
  }

  const machine: Machine = {
    isStopped: () => machineIsStopped,

    do: (job: VoidFn) => {
      jobs.push(job);
      return machine;
    },

    run: () => {
      runIteration();
      return machine;
    },

    stop: () => {
      if (frame) {
        cancelAnimationFrame(frame);
        frame = null;
      }
      machineIsStopped = true;
      onComplete();
    }

  };

  return machine;
};

/**
 * Creates a machine that will stop running after the specified duration.
 */
export const TimedMachineFactory = (
  requestAnimationFrame: (fn: VoidFn) => void,
  cancelAnimationFrame: (frame: number) => void,
) => (
  duration: number,
  onComplete?: VoidFn = noop,
  onFrame?: VoidFn = noop,
): Machine => {

  let startTime: number = 0;

  const _onFrame: VoidFn = () => {
    const elapsedTime: number = Date.now() - startTime;
    onFrame();
    if (elapsedTime >= duration) {
      machine.stop();
    }
  }

  const factory = PerpetualMachineFactory(requestAnimationFrame, cancelAnimationFrame);
  const machine: Machine = factory(onComplete, _onFrame);

  const _run = machine.run;
  machine.run = () => {
    startTime = Date.now();
    return _run();
  }

  return machine;
};
<commit_msg>:art: Move `frame` clearing<commit_after>// @flow
/**
 * Machine: contains all mutable state.
 * @module machine
 *
 * The machine should be in charge of managing the animation frames, but it
 * doesn't know anything about what to do in those frames, that's up to the
 * Animator.
 */

import type { VoidFn, Machine } from './flow-types'

// TODO: use utils
const noop: VoidFn = () => {};

/**
 * Creates a machine that will continue to run until the caller tells it to stop.
 */
export const PerpetualMachineFactory = (
  requestAnimationFrame: (fn: VoidFn) => void,
  cancelAnimationFrame: (frame: number) => void,
) => (
  onComplete?: VoidFn = noop,
  onFrame?: VoidFn = noop,
): Machine => {

  let frame: ?number = null;
  let machineIsStopped: boolean = false;

  const jobs: Array<VoidFn> = [];

  const runIteration: VoidFn = () => {
    if (machineIsStopped) return;

    onFrame();
    frame = requestAnimationFrame(() => {
      jobs.forEach(job => job());
      runIteration();
    });
  }

  const machine: Machine = {
    isStopped: () => machineIsStopped,

    do: (job: VoidFn) => {
      jobs.push(job);
      return machine;
    },

    run: () => {
      runIteration();
      return machine;
    },

    stop: () => {
      if (frame) {
        cancelAnimationFrame(frame);
      }
      frame = null;
      machineIsStopped = true;
      onComplete();
    }

  };

  return machine;
};

/**
 * Creates a machine that will stop running after the specified duration.
 */
export const TimedMachineFactory = (
  requestAnimationFrame: (fn: VoidFn) => void,
  cancelAnimationFrame: (frame: number) => void,
) => (
  duration: number,
  onComplete?: VoidFn = noop,
  onFrame?: VoidFn = noop,
): Machine => {

  let startTime: number = 0;

  const _onFrame: VoidFn = () => {
    const elapsedTime: number = Date.now() - startTime;
    onFrame();
    if (elapsedTime >= duration) {
      machine.stop();
    }
  }

  const factory = PerpetualMachineFactory(requestAnimationFrame, cancelAnimationFrame);
  const machine: Machine = factory(onComplete, _onFrame);

  const _run = machine.run;
  machine.run = () => {
    startTime = Date.now();
    return _run();
  }

  return machine;
};
<|endoftext|>


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

var del = require('del');
var fs = require('fs');
var gulp = require('gulp-help')(require('gulp'));
var gzipSize = require('gzip-size');
var prettyBytes = require('pretty-bytes');
var table = require('text-table');
var through = require('through2');
var util = require('gulp-util');

var tempFolderName = '__size-temp';

var MIN_FILE_SIZE_POS = 0;
var GZIP_POS = 1;
var FILENAME_POS = 2;

// normalized table headers
var tableHeaders = [
  ['max', 'min', 'gzip', 'file'],
  ['---', '---', '---', '---'],
];

var tableOptions = {
  align: ['r', 'r', 'r', 'l'],
  hsep: '   |   ',
};

/**
 * Returns a number greater than -1 if item is found within the array, else
 * returns -1.
 *
 * @param {!Array<!Array>} rows
 * @param {!fuction(string)} predicate
 * @return {number}
 */
function findMaxIndexByFilename(rows, predicate) {
  for (var i = 0; i < rows.length; i++) {
    var curRow = rows[i];
    var curFilename = curRow[FILENAME_POS];
    if (predicate(curFilename)) {
      return i;
    }
  }
  return -1;
}

/**
 * Mutates the rows in place and merges the minified file entry as well
 * as its unminified file entry counterpart.
 * @param {!Array<!Array>} rows
 * @param {string} minFilename
 * @param {string} maxFilename
 * @param {boolean} mergeNames
 */
function normalizeRow(rows, minFilename, maxFilename, mergeNames) {
  var minIndex = findMaxIndexByFilename(rows, function(filename) {
    return filename == minFilename;
  });
  var maxIndex = findMaxIndexByFilename(rows, function(filename) {
    return filename == maxFilename;
  });

  if (maxIndex != -1 && minIndex != -1) {
    if (mergeNames) {
      rows[minIndex][FILENAME_POS] += ' / ' + rows[maxIndex][FILENAME_POS];
    }
    rows[minIndex].unshift(rows[maxIndex][MIN_FILE_SIZE_POS]);
    rows.splice(maxIndex, 1);
  }
}

/**
 * Call normalizeRow on the core file, integration file and all extensions.
 * @param {!Array<!Array>} rows
 * @return {!Array<!Array>}
 */
function normalizeRows(rows) {
  // normalize amp.js
  normalizeRow(rows, 'v0.js', 'amp.js', true);

  // normalize integration.js
  normalizeRow(rows, 'current-min/f.js', 'current/integration.js', true);

  // normalize alp.js
  normalizeRow(rows, 'alp.js', 'alp.max.js', true);

  // normalize amp-shadow.js
  normalizeRow(rows, 'shadow-v0.js', 'amp-shadow.js', true);

  normalizeRow(rows, 'amp4ads-v0.js', 'amp-inabox.js', true);

  normalizeRow(rows, 'amp4ads-host-v0.js', 'amp-inabox-host.js', true);

  // normalize sw.js
  normalizeRow(rows, 'sw.js', 'sw.max.js', true);
  normalizeRow(rows, 'sw-kill.js', 'sw-kill.max.js', true);

  // normalize extensions
  var curName = null;
  var i = rows.length;
  // we are mutating in place... kind of icky but this will do fow now.
  while (i--) {
    curName = rows[i][FILENAME_POS];
    if (/^v0/.test(curName)) {
      normalizeExtension(rows, curName);
    }
  }
  return rows;
}

/**
 * Finds the counterpart entry of the extension file, wether it be
 * the unminified or the minified counterpart.
 * @param {!Array<!Array>} rows
 * @param {string} filename
 */
function normalizeExtension(rows, filename) {
  var isMax = /\.max\.js$/.test(filename);
  var counterpartName = filename.replace(/(v0\/.*?)(\.max)?(\.js)$/,
      function(full, grp1, grp2, grp3) {
        if (isMax) {
          return grp1 + grp3;
        }
        return full;
      });

  if (isMax) {
    normalizeRow(rows, counterpartName, filename, false);
  } else {
    normalizeRow(rows, filename, counterpartName, false);
  }
}

/**
 * Through2 transform function - Tracks the original size and the gzipped size
 * of the file content in the rows array. Passes the file and/or err to the
 * callback when finished.
 * @param {!Array<!Array<string>>} rows array to store content size information
 * @param {!File} file File to process
 * @param {string} enc Encoding (not used)
 * @param {function(?Error, !File)} cb Callback function
 */
function onFileThrough(rows, file, enc, cb) {
  if (file.isNull()) {
    cb(null, file);
    return;
  }

  if (file.isStream()) {
    cb(new util.PluginError('size', 'Stream not supported'));
    return;
  }

  rows.push([
    prettyBytes(file.contents.length),
    prettyBytes(gzipSize.sync(file.contents)),
    file.relative,
  ]);

  cb(null, file);
}

/**
 * Through2 flush function - combines headers with the rows and generates
 * a text-table of the content size information to log to the console and the
 * test/size.txt logfile.
 *
 * @param {!Array<!Array<string>>} rows array of content size information
 * @param {function()} cb Callback function
 */
function onFileThroughEnd(rows, cb) {
  rows = normalizeRows(rows);
  rows.unshift.apply(rows, tableHeaders);
  var tbl = table(rows, tableOptions);
  console/*OK*/.log(tbl);
  fs.writeFileSync('test/size.txt', tbl);
  cb();
}

/**
 * Setup through2 to capture size information using the above transform and
 * flush functions on a stream
 * @return {!Stream} a Writable Stream
 */
function sizer() {
  var rows = [];
  return through.obj(
      onFileThrough.bind(null, rows),
      onFileThroughEnd.bind(null, rows)
  );
}

/**
 * Pipe the distributable js files through the sizer to get a record of
 * the content size before and after gzip and cleanup any temporary file
 * output from the process.
 */
function sizeTask() {
  gulp.src([
      'dist/**/*.js',
      '!dist/**/*-latest.js',
      '!dist/**/*check-types.js',
      'dist.3p/{current,current-min}/**/*.js',
    ])
    .pipe(sizer())
    .pipe(gulp.dest(tempFolderName))
    .on('end', del.bind(null, [tempFolderName]));
}

gulp.task('size', 'Runs a report on artifact size', sizeTask);
<commit_msg>normalize ampcontext-lib (#6524)<commit_after>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var del = require('del');
var fs = require('fs');
var gulp = require('gulp-help')(require('gulp'));
var gzipSize = require('gzip-size');
var prettyBytes = require('pretty-bytes');
var table = require('text-table');
var through = require('through2');
var util = require('gulp-util');

var tempFolderName = '__size-temp';

var MIN_FILE_SIZE_POS = 0;
var GZIP_POS = 1;
var FILENAME_POS = 2;

// normalized table headers
var tableHeaders = [
  ['max', 'min', 'gzip', 'file'],
  ['---', '---', '---', '---'],
];

var tableOptions = {
  align: ['r', 'r', 'r', 'l'],
  hsep: '   |   ',
};

/**
 * Returns a number greater than -1 if item is found within the array, else
 * returns -1.
 *
 * @param {!Array<!Array>} rows
 * @param {!fuction(string)} predicate
 * @return {number}
 */
function findMaxIndexByFilename(rows, predicate) {
  for (var i = 0; i < rows.length; i++) {
    var curRow = rows[i];
    var curFilename = curRow[FILENAME_POS];
    if (predicate(curFilename)) {
      return i;
    }
  }
  return -1;
}

/**
 * Mutates the rows in place and merges the minified file entry as well
 * as its unminified file entry counterpart.
 * @param {!Array<!Array>} rows
 * @param {string} minFilename
 * @param {string} maxFilename
 * @param {boolean} mergeNames
 */
function normalizeRow(rows, minFilename, maxFilename, mergeNames) {
  var minIndex = findMaxIndexByFilename(rows, function(filename) {
    return filename == minFilename;
  });
  var maxIndex = findMaxIndexByFilename(rows, function(filename) {
    return filename == maxFilename;
  });

  if (maxIndex != -1 && minIndex != -1) {
    if (mergeNames) {
      rows[minIndex][FILENAME_POS] += ' / ' + rows[maxIndex][FILENAME_POS];
    }
    rows[minIndex].unshift(rows[maxIndex][MIN_FILE_SIZE_POS]);
    rows.splice(maxIndex, 1);
  }
}

/**
 * Call normalizeRow on the core file, integration file and all extensions.
 * @param {!Array<!Array>} rows
 * @return {!Array<!Array>}
 */
function normalizeRows(rows) {
  // normalize amp.js
  normalizeRow(rows, 'v0.js', 'amp.js', true);

  // normalize integration.js
  normalizeRow(rows, 'current-min/f.js', 'current/integration.js', true);

  normalizeRow(rows, 'current-min/ampcontext-lib.js',
      'current/ampcontext-lib.js', true);

  // normalize alp.js
  normalizeRow(rows, 'alp.js', 'alp.max.js', true);

  // normalize amp-shadow.js
  normalizeRow(rows, 'shadow-v0.js', 'amp-shadow.js', true);

  normalizeRow(rows, 'amp4ads-v0.js', 'amp-inabox.js', true);

  normalizeRow(rows, 'amp4ads-host-v0.js', 'amp-inabox-host.js', true);

  // normalize sw.js
  normalizeRow(rows, 'sw.js', 'sw.max.js', true);
  normalizeRow(rows, 'sw-kill.js', 'sw-kill.max.js', true);

  // normalize extensions
  var curName = null;
  var i = rows.length;
  // we are mutating in place... kind of icky but this will do fow now.
  while (i--) {
    curName = rows[i][FILENAME_POS];
    if (/^v0/.test(curName)) {
      normalizeExtension(rows, curName);
    }
  }
  return rows;
}

/**
 * Finds the counterpart entry of the extension file, wether it be
 * the unminified or the minified counterpart.
 * @param {!Array<!Array>} rows
 * @param {string} filename
 */
function normalizeExtension(rows, filename) {
  var isMax = /\.max\.js$/.test(filename);
  var counterpartName = filename.replace(/(v0\/.*?)(\.max)?(\.js)$/,
      function(full, grp1, grp2, grp3) {
        if (isMax) {
          return grp1 + grp3;
        }
        return full;
      });

  if (isMax) {
    normalizeRow(rows, counterpartName, filename, false);
  } else {
    normalizeRow(rows, filename, counterpartName, false);
  }
}

/**
 * Through2 transform function - Tracks the original size and the gzipped size
 * of the file content in the rows array. Passes the file and/or err to the
 * callback when finished.
 * @param {!Array<!Array<string>>} rows array to store content size information
 * @param {!File} file File to process
 * @param {string} enc Encoding (not used)
 * @param {function(?Error, !File)} cb Callback function
 */
function onFileThrough(rows, file, enc, cb) {
  if (file.isNull()) {
    cb(null, file);
    return;
  }

  if (file.isStream()) {
    cb(new util.PluginError('size', 'Stream not supported'));
    return;
  }

  rows.push([
    prettyBytes(file.contents.length),
    prettyBytes(gzipSize.sync(file.contents)),
    file.relative,
  ]);

  cb(null, file);
}

/**
 * Through2 flush function - combines headers with the rows and generates
 * a text-table of the content size information to log to the console and the
 * test/size.txt logfile.
 *
 * @param {!Array<!Array<string>>} rows array of content size information
 * @param {function()} cb Callback function
 */
function onFileThroughEnd(rows, cb) {
  rows = normalizeRows(rows);
  rows.unshift.apply(rows, tableHeaders);
  var tbl = table(rows, tableOptions);
  console/*OK*/.log(tbl);
  fs.writeFileSync('test/size.txt', tbl);
  cb();
}

/**
 * Setup through2 to capture size information using the above transform and
 * flush functions on a stream
 * @return {!Stream} a Writable Stream
 */
function sizer() {
  var rows = [];
  return through.obj(
      onFileThrough.bind(null, rows),
      onFileThroughEnd.bind(null, rows)
  );
}

/**
 * Pipe the distributable js files through the sizer to get a record of
 * the content size before and after gzip and cleanup any temporary file
 * output from the process.
 */
function sizeTask() {
  gulp.src([
      'dist/**/*.js',
      '!dist/**/*-latest.js',
      '!dist/**/*check-types.js',
      'dist.3p/{current,current-min}/**/*.js',
    ])
    .pipe(sizer())
    .pipe(gulp.dest(tempFolderName))
    .on('end', del.bind(null, [tempFolderName]));
}

gulp.task('size', 'Runs a report on artifact size', sizeTask);
<|endoftext|>


<commit_before><commit_msg>Updated Dashboard.js<commit_after>var React = require('react');
var Row = require('react-bootstrap/lib/Row');
var Col = require('react-bootstrap/lib/Col');
var Owl = require('./Owl');
var HealthBar = require('./HealthBar');

module.exports = React.createClass({
  render: function() {
    return (
            <Row>
                <Col xs={12}>
                    <Row>
                        <Owl name={this.props.username} message={this.props.message} />
                    </Row>
                    <Row>
                        <HealthBar now={this.props.health} />
                    </Row>
                </Col>
            </Row>
		);
  }
});

//<!--
//Usage:
//<Dashboard name="username" message="this is a message" health='42' />
//-->
<|endoftext|>