<commit_before>const config = {
  filtersFormId: 'data-filters-form',
  nameInputId: 'name-input',
  countryCheckSeveritySelectId: 'country-check-severity-select',
  dataTableBodyId: 'data-table-body',
  tableRowClass: 'data-table__row',
  tableCellClass: 'data-table__cell'
}

const dom = (function () {
  'use strict'

  return {
    getElement (id) {
      if (!id) return new Error('getElement: no ID')
      return document.getElementById(id)
    },
    setHTML (elem, content) {
      if (!elem) return new Error('setHTML: no element')
      elem.innerHTML = content
      return elem
    },
    clearHTML (elem) {
      this.setHTML(elem, '')
      return elem
    },
    addEventListener (elem, event, cb) {
      if (!elem) return new Error('addEventListener: no element')

      elem.addEventListener(event, event => {
        if (cb) cb(event)
      })
      return elem
    },
    createElem (tag = 'div', className, text = '') {
      const classes = (className) ? `class="${className}` : ''
      return `<${tag} ${classes}">${text}</${tag}>`
    }
  }
}())

const elements = (function (config, dom) {
  'use strict'

  const messages = {
    elemNotFound: 'No such element',
    noId: 'ID must be specified'
  }

  return {
    _getElem (id) {
      if (!id) throw new Error(messages.noId)
      const elem = dom.getElement(id)
      if (!elem) throw new Error(`${messages.elemNotFound}: ${id}`)
      return elem
    },
    getFiltersForm () {
      return this._getElem(config.filtersFormId)
    },
    getNameInput () {
      return this._getElem(config.nameInputId)
    },
    getCheckSeveritySelect () {
      return this._getElem(config.countryCheckSeveritySelectId)
    },
    getDataTableBody () {
      return this._getElem(config.dataTableBodyId)
    }
  }
}(config, dom))

const filter = (function () {

  const _p = {
    filterStrict (val, field, value) {
      console.info(val[field])
      return val[field] === value
    },
    filterNotStrict (val, field, value) {
      return val[field].toLowerCase().includes(value.toLowerCase())
    }
  }

  const filters = {
    state: {},
    fields: {
      name: {
        name: 'name',
        defaultVal: '',
      },
      severity: {
        name: 'country_check_severity',
        defaultVal: 'all',
        values: {
          all: {
            name: 'all'
          },
          critical: {
            name: 'critical',
            val: '90-CRITICAL'
          },
          unknown: {
            name: 'unknown',
            val: '30-UNKNOWN'
          },
          warning: {
            name: 'warning',
            val: '70-WARNING'
          },
          ok: {
            name: 'ok',
            val: '60-OK'
          }
        }
      }
    },
    filterBy (data, field, value, isStrict) {
      if (!data) throw new Error('filterBy: data shall be passed')
      if (!field) throw new Error('filterBy: field shall be passed')

      return data.filter(v => ((isStrict ? _p.filterStrict : _p.filterNotStrict))(v, field, value))
    },
    filterBySeverity(data, value, isStrict) {
      if (!data) throw new Error('filterBySeverity: data shall be passed')

      const isSeverityFilter = !!(value && value !== this.fields.severity.defaultVal)
      const severityVal = this.fields.severity.values[value].val
      if (isSeverityFilter) {
        data = filter.filterBy(data, filter.fields.severity.name, severityVal, isStrict)
      }
      return data
    },
    getFiltersValues () {
      return {
        // TODO (S.Panfilov) set value manually
        [this.fields.name.name]: this.state[this.fields.name.name],
        [this.fields.severity.name]: this.state[this.fields.severity.name]
      }
    },
    setNameFilter (value) {
      this.state[this.fields.name.name] = value
    },
    setSeverityFilter (value) {
      this.state[this.fields.severity.name] = value
    }
  }

  function init () {
    filters.setNameFilter(null)
    filters.setSeverityFilter(filters.fields.severity.defaultVal)
  }

  init()

  return filters
}())

const sorting = (function () {
  'use strict'

  const sorting = {
    directions: {
      asc: 'ASC',
      desc: 'DESC'
    },
    fields: {
      name: 'name',
      created: 'created',
      modified: 'modified',
    },
    setSorting(field, direction) {
      if (!field) throw new Error('setSorting: field must be set')

      this.state = this.state || {}
      if (field) this.state.field = field
      if (direction) this.state.direction = direction
    },
    getSorting () {
      return this.state
    },
    toggleDirection () {
      if (this.state.direction === this.directions.asc) {
        this.state.direction = this.directions.desc
        return
      }
      this.state.direction = this.directions.asc
    },
    stringSort (a, b, field, direction = 'ASC') {
      let multiplier = 1
      if (direction === this.directions.desc) multiplier = -1

      const v1 = a[field].toLowerCase()
      const v2 = b[field].toLowerCase()

      if (v1 < v2) return (-1 * multiplier)
      if (v1 > v2) return (1 * multiplier)
      return 0
    },
    numberSort (a, b, field, direction = 'ASC') {
      if (direction === this.directions.asc) return a[field] - b[field]
      return b[field] - a[field]
    },
    dateSorting (a, b, field, direction = 'ASC'){
      const d1 = new Date(a).getTime()
      const d2 = new Date(b).getTime()

      return this.numberSort(d1, d2, field, direction)
    },
    sort (data, field, type = 'string') {
      if (!data) throw new Error('sort: no data provided')

      let method
      if (type === 'string') method = this.stringSort
      if (type === 'number') method = this.stringSort

      return data.sort((a, b) => method.call(this, a, b, field, this.getSorting.direction))
    }
  }

  function init () {
    sorting.setSorting(sorting.fields.name, sorting.directions.asc)
  }

  init()

  return sorting
}())

const table = (function (config, dom, elements, sorting) {
  'use strict'

  const tableElem = elements.getDataTableBody()

  function getHumanReadyDate (str) {
    if (!str) throw new Error('getHumanReadyDate: no data string')
    const date = new Date(str)
    let day = date.getUTCDate().toString()
    if (day.length === 1) day = '0' + day
    let month = date.getUTCMonth().toString()
    if (month.length === 1) month = '0' + month
    const year = date.getUTCFullYear().toString()

    return `${day}.${month}.${year}`
  }

  function createTableRow (data) {
    const nameTd = dom.createElem('td', `${config.tableCellClass} -name`, data.name)
    const modifiedTd = dom.createElem('td', `${config.tableCellClass} -modified`, getHumanReadyDate(data.modified))
    const createdTd = dom.createElem('td', `${config.tableCellClass} -created`, getHumanReadyDate(data.created))
    const countryCheckSeverityTd = dom.createElem('td', `${config.tableCellClass} -country-check-severity`, data.country_check_severity)
    return dom.createElem('tr', config.tableRowClass, nameTd + modifiedTd + createdTd + countryCheckSeverityTd)
  }

  return {
    clearData () {
      dom.clearHTML(tableElem)
    },
    prepareHtml (data) {
      return data.reduce((c, v) => {
        c += createTableRow(v)
        return c
      }, '')
    },
    sortData (data) {
      // TODO (S.Panfilov)  field?!!!!!!!!!!!!!!!!!!
      return sorting.sort(data, sorting.getSorting().field, 'string')
    },
    displayData (data) {
      if (!data) throw new Error('displayData: No data')
      const sortedData = this.sortData(data)
      const html = this.prepareHtml(sortedData)

      dom.setHTML(tableElem, html)
      return html
    }
  }
}(config, dom, elements, sorting))

//This is a "pretend" for server work
const fetch = (function (elements, data, filter) {
  'use strict'

  const _p = {
    filterData (data, filters) {
      // TODO (S.Panfilov) implement filtering
      const isNameFilter = (filters.name && filters.name.length > 0)
      if (isNameFilter) data = filter.filterBy(data, filter.fields.name.name, filters.name, false)

      data = filter.filterBySeverity(data, filters[filter.fields.severity.name], true)

      return data
    },
    fetchData (cb) {
      //This is a mock for server work
      //In real life it'sgona be return smt like "fetch(url, options).then(...)"...
      //And no need to handle errors in this 'dream world'
      return setTimeout(() => {
        //data contains our "screenings.json" from task
        if (cb) cb(data)
      }, 0)
    }
  }

  return {
    getScreening (cb, filters) {
      return _p.fetchData(data => {
        const results = data.results
        const filteredResults = _p.filterData(results, filters)
        // TODO (S.Panfilov)  sorting
        return cb(filteredResults)
      })
    },
    onSubmit (event, filters, cb) {
      if (!event) throw new Error('onSubmit: no event provided')
      event.preventDefault()
      event.stopPropagation()

      return this.getScreening(cb, filters)
    },
    _p // dirty hack for tests
  }
}(elements, data, filter))

// eslint-disable-next-line no-unused-vars
const main = (function (elements, dom, fetch, table, filter, sorting) {
  'use strict'

  function onSubmit (event) {
    const filters = filter.getFiltersValues()
    fetch.onSubmit(event, filters, onGetData)
  }

  function onFiltersChange (event) {
    const nameInputElem = elements.getNameInput()
    const severitySelectElem = elements.getCheckSeveritySelect()

    filter.setNameFilter(nameInputElem.value)
    filter.setSeverityFilter(severitySelectElem.value)

    onSubmit(event)
    // fetch.onSubmit(event, filters, onGetData)
  }

  function onGetData (data) {
    table.clearData()
    table.displayData(data)
  }

  return {
    reload (event, sortingBy = 'name') {
      sorting.toggleDirection()
      sorting.setSorting(sortingBy)
      onSubmit(event)
    },
    init () {
      // const formElem = elements.getFiltersForm()
      const nameInputElem = elements.getNameInput()
      const severitySelectElem = elements.getCheckSeveritySelect()

      // dom.addEventListener(formElem, 'submit', event => onSubmit(event))
      dom.addEventListener(nameInputElem, 'change', event => onFiltersChange(event))
      dom.addEventListener(severitySelectElem, 'change', event => onFiltersChange(event))

      fetch.getScreening(onGetData, filter.getFiltersValues())
    }
  }

}(elements, dom, fetch, table, filter, sorting))

// for testing purpose
if (typeof module === 'object' && module.exports) {
  module.exports = {
    main
  }
}

<commit_msg>added reset state (filters)<commit_after>const config = {
  filtersFormId: 'data-filters-form',
  nameInputId: 'name-input',
  countryCheckSeveritySelectId: 'country-check-severity-select',
  dataTableBodyId: 'data-table-body',
  tableRowClass: 'data-table__row',
  tableCellClass: 'data-table__cell'
}

const dom = (function () {
  'use strict'

  return {
    getElement (id) {
      if (!id) return new Error('getElement: no ID')
      return document.getElementById(id)
    },
    setHTML (elem, content) {
      if (!elem) return new Error('setHTML: no element')
      elem.innerHTML = content
      return elem
    },
    clearHTML (elem) {
      this.setHTML(elem, '')
      return elem
    },
    addEventListener (elem, event, cb) {
      if (!elem) return new Error('addEventListener: no element')

      elem.addEventListener(event, event => {
        if (cb) cb(event)
      })
      return elem
    },
    createElem (tag = 'div', className, text = '') {
      const classes = (className) ? `class="${className}` : ''
      return `<${tag} ${classes}">${text}</${tag}>`
    }
  }
}())

const elements = (function (config, dom) {
  'use strict'

  const messages = {
    elemNotFound: 'No such element',
    noId: 'ID must be specified'
  }

  return {
    _getElem (id) {
      if (!id) throw new Error(messages.noId)
      const elem = dom.getElement(id)
      if (!elem) throw new Error(`${messages.elemNotFound}: ${id}`)
      return elem
    },
    getFiltersForm () {
      return this._getElem(config.filtersFormId)
    },
    getNameInput () {
      return this._getElem(config.nameInputId)
    },
    getCheckSeveritySelect () {
      return this._getElem(config.countryCheckSeveritySelectId)
    },
    getDataTableBody () {
      return this._getElem(config.dataTableBodyId)
    }
  }
}(config, dom))

const filter = (function () {

  const _p = {
    filterStrict (val, field, value) {
      return val[field] === value
    },
    filterNotStrict (val, field, value) {
      return val[field].toLowerCase().includes(value.toLowerCase())
    }
  }

  const filters = {
    state: {},
    fields: {
      name: {
        name: 'name',
        defaultVal: '',
      },
      severity: {
        name: 'country_check_severity',
        defaultVal: 'all',
        values: {
          all: {
            name: 'all'
          },
          critical: {
            name: 'critical',
            val: '90-CRITICAL'
          },
          unknown: {
            name: 'unknown',
            val: '30-UNKNOWN'
          },
          warning: {
            name: 'warning',
            val: '70-WARNING'
          },
          ok: {
            name: 'ok',
            val: '60-OK'
          }
        }
      }
    },
    filterBy (data, field, value, isStrict) {
      if (!data) throw new Error('filterBy: data shall be passed')
      if (!field) throw new Error('filterBy: field shall be passed')

      return data.filter(v => ((isStrict ? _p.filterStrict : _p.filterNotStrict))(v, field, value))
    },
    filterBySeverity(data, value, isStrict) {
      if (!data) throw new Error('filterBySeverity: data shall be passed')

      const isSeverityFilter = !!(value && value !== this.fields.severity.defaultVal)
      const severityVal = this.fields.severity.values[value].val
      if (isSeverityFilter) {
        data = filter.filterBy(data, filter.fields.severity.name, severityVal, isStrict)
      }
      return data
    },
    getFiltersValues () {
      return {
        // TODO (S.Panfilov) set value manually
        [this.fields.name.name]: this.state[this.fields.name.name],
        [this.fields.severity.name]: this.state[this.fields.severity.name]
      }
    },
    setNameFilter (value) {
      this.state[this.fields.name.name] = value
    },
    setSeverityFilter (value) {
      this.state[this.fields.severity.name] = value
    },
    resetState () {
      this.setNameFilter(null)
      this.setSeverityFilter(this.fields.severity.defaultVal)
    }
  }

  function init () {
    filters.resetState()
  }

  init()

  return filters
}())

const sorting = (function () {
  'use strict'

  const sorting = {
    directions: {
      asc: 'ASC',
      desc: 'DESC'
    },
    fields: {
      name: 'name',
      created: 'created',
      modified: 'modified',
    },
    setSorting(field, direction) {
      if (!field) throw new Error('setSorting: field must be set')

      this.state = this.state || {}
      if (field) this.state.field = field
      if (direction) this.state.direction = direction
    },
    getSorting () {
      return this.state
    },
    toggleDirection () {
      if (this.state.direction === this.directions.asc) {
        this.state.direction = this.directions.desc
        return
      }
      this.state.direction = this.directions.asc
    },
    stringSort (a, b, field, direction = 'ASC') {
      let multiplier = 1
      if (direction === this.directions.desc) multiplier = -1

      const v1 = a[field].toLowerCase()
      const v2 = b[field].toLowerCase()

      if (v1 < v2) return (-1 * multiplier)
      if (v1 > v2) return (1 * multiplier)
      return 0
    },
    numberSort (a, b, field, direction = 'ASC') {
      if (direction === this.directions.asc) return a[field] - b[field]
      return b[field] - a[field]
    },
    dateSorting (a, b, field, direction = 'ASC'){
      const d1 = new Date(a).getTime()
      const d2 = new Date(b).getTime()

      return this.numberSort(d1, d2, field, direction)
    },
    sort (data, field, type = 'string') {
      if (!data) throw new Error('sort: no data provided')

      let method
      if (type === 'string') method = this.stringSort
      if (type === 'number') method = this.stringSort

      return data.sort((a, b) => method.call(this, a, b, field, this.getSorting.direction))
    }
  }

  function init () {
    sorting.setSorting(sorting.fields.name, sorting.directions.asc)
  }

  init()

  return sorting
}())

const table = (function (config, dom, elements, sorting) {
  'use strict'

  const tableElem = elements.getDataTableBody()

  function getHumanReadyDate (str) {
    if (!str) throw new Error('getHumanReadyDate: no data string')
    const date = new Date(str)
    let day = date.getUTCDate().toString()
    if (day.length === 1) day = '0' + day
    let month = date.getUTCMonth().toString()
    if (month.length === 1) month = '0' + month
    const year = date.getUTCFullYear().toString()

    return `${day}.${month}.${year}`
  }

  function createTableRow (data) {
    const nameTd = dom.createElem('td', `${config.tableCellClass} -name`, data.name)
    const modifiedTd = dom.createElem('td', `${config.tableCellClass} -modified`, getHumanReadyDate(data.modified))
    const createdTd = dom.createElem('td', `${config.tableCellClass} -created`, getHumanReadyDate(data.created))
    const countryCheckSeverityTd = dom.createElem('td', `${config.tableCellClass} -country-check-severity`, data.country_check_severity)
    return dom.createElem('tr', config.tableRowClass, nameTd + modifiedTd + createdTd + countryCheckSeverityTd)
  }

  return {
    clearData () {
      dom.clearHTML(tableElem)
    },
    prepareHtml (data) {
      return data.reduce((c, v) => {
        c += createTableRow(v)
        return c
      }, '')
    },
    sortData (data) {
      return sorting.sort(data, sorting.getSorting().field, 'string')
    },
    displayData (data) {
      if (!data) throw new Error('displayData: No data')
      const sortedData = this.sortData(data)
      const html = this.prepareHtml(sortedData)

      dom.setHTML(tableElem, html)
      return html
    }
  }
}(config, dom, elements, sorting))

//This is a "pretend" for server work
const fetch = (function (elements, data, filter) {
  'use strict'

  const _p = {
    filterData (data, filters) {
      // TODO (S.Panfilov) implement filtering
      const isNameFilter = (filters.name && filters.name.length > 0)
      if (isNameFilter) data = filter.filterBy(data, filter.fields.name.name, filters.name, false)

      data = filter.filterBySeverity(data, filters[filter.fields.severity.name], true)

      return data
    },
    fetchData (cb) {
      //This is a mock for server work
      //In real life it'sgona be return smt like "fetch(url, options).then(...)"...
      //And no need to handle errors in this 'dream world'
      return setTimeout(() => {
        //data contains our "screenings.json" from task
        if (cb) cb(data)
      }, 0)
    }
  }

  return {
    getScreening (cb, filters) {
      return _p.fetchData(data => {
        const results = data.results
        const filteredResults = _p.filterData(results, filters)
        // TODO (S.Panfilov)  sorting
        return cb(filteredResults)
      })
    },
    _p // dirty hack for tests
  }
}(elements, data, filter))

// eslint-disable-next-line no-unused-vars
const main = (function (elements, dom, fetch, table, filter, sorting) {
  'use strict'

  function onSubmit (event) {
    const filters = filter.getFiltersValues()

    event.preventDefault()
    event.stopPropagation()

    fetch.getScreening(onGetData, filters) // TODO (S.Panfilov) filters shouldn't be in fetch
  }

  function onFiltersChange (event) {
    const nameInputElem = elements.getNameInput()
    const severitySelectElem = elements.getCheckSeveritySelect()

    filter.setNameFilter(nameInputElem.value)
    filter.setSeverityFilter(severitySelectElem.value)

    onSubmit(event)
  }

  function onReset (event) {
    filter.resetState()
    const filters = filter.getFiltersValues()
    fetch.getScreening(onGetData, filters)
  }

  function onGetData (data) {
    table.clearData()
    table.displayData(data)
  }

  return {
    reload (event, sortingBy = 'name') {
      sorting.toggleDirection()
      sorting.setSorting(sortingBy)
      onSubmit(event)
    },
    init () {
      const formElem = elements.getFiltersForm()
      const nameInputElem = elements.getNameInput()
      const severitySelectElem = elements.getCheckSeveritySelect()

      // dom.addEventListener(formElem, 'submit', event => onSubmit(event))
      dom.addEventListener(formElem, 'reset', event => onReset(event))
      dom.addEventListener(nameInputElem, 'input', event => onFiltersChange(event))
      dom.addEventListener(severitySelectElem, 'change', event => onFiltersChange(event))

      fetch.getScreening(onGetData, filter.getFiltersValues())
    }
  }

}(elements, dom, fetch, table, filter, sorting))

// for testing purpose
if (typeof module === 'object' && module.exports) {
  module.exports = {
    main
  }
}

<|endoftext|>


<commit_before>// Adapted from http://www.lostdecadegames.com/how-to-make-a-simple-html5-canvas-game/

// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);

// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function() {
  bgReady = true;
}
bgImage.src = "images/background.jpg" // todo

// Prepare molecules
var glucoseReady = false;
var glucoseImage = new Image();
glucoseImage.onload = function () {
  glucoseReady = true;
};
glucoseImage.src = "images/glucose.gif"

var nadReady = false;
var nadImage = new Image();
nadImage.onload = function () {
  nadReady = true;
};
nadImage.src = "images/nad.gif"

// Game objects
var glucose = {
  speed: 256, // movement in pixels/second
  x: 0,
  y: 0
};

var nad = {
  speed: 128, // movement in pixels/second
  x: 0,
  y: 0
};

var points = 0;

// Handle keyboard controls
var keysDown = {};

addEventListener("keydown", function (e) {
  keysDown[e.keyCode] = true;
}, false);

addEventListener("keyup", function (e) {
  delete keysDown[e.keyCode];
}, false);

// Update game objects
var update = function (modifier) {
  if (38 in keysDown) { // Player holding up
    glucose.y -= glucose.speed * modifier
  }
  if (40 in keysDown) { // Player holding down
    glucose.y += glucose.speed * modifier
  }
  if (37 in keysDown) { // Player holding left
    glucose.x -= glucose.speed * modifier
  }
  if (39 in keysDown) { // Player holding right
    glucose.x += glucose.speed * modifier
  }

  // Gain points
  if (
    glucose.x <= (nad.x + 32) &&
    nad.x <= (glucose.x + 32) &&
    glucose.y <= (nad.y + 32) &&
    nad.y <= (glucose.y + 32)
  ) {
    points += 1;
    // switch glucose image?
  }

  if (glucose.y < 0) {
    glucose.y = 0;
  } else if (glucose.y > 480) {
    glucose.y = 480;
  }

  if (glucose.x < 0) {
    glucose.x = 0;
  } else if (glucose.x > 512) {
    glucose.x = 512;
  }

};

// Draw everything
var render = function () {
  if (bgReady) {
    ctx.drawImage(bgImage, 0, 0);
  }

  if (glucoseReady) {
    ctx.drawImage(glucoseImage, glucose.x, glucose.y);
  }

  if (nadReady) {
    ctx.drawImage(nadImage, nad.x, nad.y);
  }

  // Score
  ctx.fillStyle = "rgb(0, 0, 0)";
  ctx.font = "24px Helvetica";
  ctx.textAlign = "left";
  ctx.textBaseline = "top";
  ctx.fillText("Points: " + points, 32, 32);
};

// The main game loop
var main = function () {
  var now = Date.now();
  var delta = now - then;

  update(delta/1000);
  render();

  then = now;

  // Request to do this again ASAP
  requestAnimationFrame(main);
};

// Cross-browser support for requestAnimationFrame
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;

// Play the game
var then = Date.now();
main();
<commit_msg>Move NAD+ randomly, create character array<commit_after>// Adapted from http://www.lostdecadegames.com/how-to-make-a-simple-html5-canvas-game/

// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);

// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function() {
  bgReady = true;
}
bgImage.src = "images/background.png" // todo

// Prepare molecules
var glucoseReady = false;
var glucoseImage = new Image();
glucoseImage.onload = function () {
  glucoseReady = true;
};
glucoseImage.src = "images/glucose.png"

var nadReady = false;
var nadImage = new Image();
nadImage.onload = function () {
  nadReady = true;
};
nadImage.src = "images/nad.gif"

// Game objects
var glucose = {
  speed: 256, // movement in pixels/second
  x: 0,
  y: 0
};

var nad = {
  speed: 128, // movement in pixels/second
  x: 0,
  y: 0
};

var points = 0;
var characters = [glucose, nad];

// Handle keyboard controls
var keysDown = {};

addEventListener("keydown", function (e) {
  keysDown[e.keyCode] = true;
}, false);

addEventListener("keyup", function (e) {
  delete keysDown[e.keyCode];
}, false);

// Update game objects
var update = function (modifier) {
  // move glucose according to keyboard
  if (38 in keysDown) { // Player holding up
    glucose.y -= glucose.speed * modifier
  }
  if (40 in keysDown) { // Player holding down
    glucose.y += glucose.speed * modifier
  }
  if (37 in keysDown) { // Player holding left
    glucose.x -= glucose.speed * modifier
  }
  if (39 in keysDown) { // Player holding right
    glucose.x += glucose.speed * modifier
  }

  // Gain points
  if (
    glucose.x <= (nad.x + 32) &&
    nad.x <= (glucose.x + 32) &&
    glucose.y <= (nad.y + 32) &&
    nad.y <= (glucose.y + 32)
  ) {
    points += 1;
    // switch glucose image?
  }

  // Move NAD+ randomly
  // 2 steps in either direction
  probability = Math.random();
  if (probability >= 0.5) {
    nad.x += 2;
  } else {
    nad.x -= 2;
  }

  probability = Math.random();
  if (probability >= 0.5) {
    nad.y += 2;
  } else {
    nad.y -= 2;
  }

  // don't go out of bounds
  for (int i = 0; i < characters.length; i++) {
    if (characters[i].y < 0) {
      characters[i].y = 0;
    } else if (characters[i].y > 480) {
      characters[i].y = 480;
    }

    if (characters[i].x < 0) {
      characters[i].x = 0;
    } else if (characters[i].x > 512) {
      characters[i].x = 512;
    }
  }

};

// Draw everything
var render = function () {
  if (bgReady) {
    ctx.drawImage(bgImage, 0, 0);
  }

  if (glucoseReady) {
    ctx.drawImage(glucoseImage, glucose.x, glucose.y);
  }

  if (nadReady) {
    ctx.drawImage(nadImage, nad.x, nad.y);
  }

  // Score
  ctx.fillStyle = "rgb(0, 0, 0)";
  ctx.font = "24px Helvetica";
  ctx.textAlign = "left";
  ctx.textBaseline = "top";
  ctx.fillText("Points: " + points, 32, 32);
};

// The main game loop
var main = function () {
  var now = Date.now();
  var delta = now - then;

  update(delta/1000);
  render();

  then = now;

  // Request to do this again ASAP
  requestAnimationFrame(main);
};

// Cross-browser support for requestAnimationFrame
var w = window;
requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;

// Play the game
var then = Date.now();
main();
<|endoftext|>


<commit_before>import React from 'react'

import { Navbar, Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap'

export default class NavigationBar extends React.Component {

  constructor (props) {
    super(props)
  }

  render () {

    var userControl;
    if (!this.props.profile) {
      userControl = <NavItem onClick={this.props.logIn} href="#">Log In</NavItem>
    } else {
      userControl =
        <NavDropdown title={this.props.profile.email} id="basic-nav-dropdown">
          <MenuItem>Settings..</MenuItem>
          <MenuItem onClick={this.props.logOut}>Log Out</MenuItem>
        </NavDropdown>
    }

    return (
      <Navbar inverse>
        <Navbar.Header>
          <Navbar.Brand>
            {this.props.title}
          </Navbar.Brand>
        </Navbar.Header>

          <NavItem href="#">Admin</NavItem>
          <NavItem href="#">Documentation</NavItem>
          {userControl}

      </Navbar>
    )
  }
}
<commit_msg>Navbar layout<commit_after>import React from 'react'

import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon } from 'react-bootstrap'

export default class NavigationBar extends React.Component {

  constructor (props) {
    super(props)
  }

  render () {

    var userControl;
    if (!this.props.profile) {
      userControl = <NavItem onClick={this.props.logIn} href="#">Log In</NavItem>
    } else {
      userControl =
        <NavDropdown title={this.props.profile.email} id="basic-nav-dropdown">
          <MenuItem>Settings..</MenuItem>
          <MenuItem onClick={this.props.logOut}>Log Out</MenuItem>
        </NavDropdown>
    }

    return (
      <Navbar>
        <Navbar.Header>
          <Navbar.Brand>
            {this.props.title}
          </Navbar.Brand>
        </Navbar.Header>

          <NavItem href="http://localhost:9000">Manager</NavItem>
          <NavItem href="http://localhost:9001">Editor</NavItem>
          <NavItem href="#" active>Users</NavItem>

          <NavItem href="#"><Glyphicon glyph="question-sign" /> Guide</NavItem>
          {userControl}

      </Navbar>
    )
  }
}
<|endoftext|>


<commit_before>var chai = require('chai');
var sinon = require('sinon');
var expect = chai.expect;
var Mugshot = require('../lib/mugshot.js');
var path = require('path');

chai.use(require('sinon-chai'));

describe('Mugshot', function() {
  var dummySelector = {
        name: 'path'
      },
      captureItem = {
        name: dummySelector.name,
        selector: 'a dom selector'
      },
      rect = {
        width: 100,
        height: 100,
        top: 50,
        left: 50
      },
      callback = function() {},
      error = new Error('Fatal Error'),
      baseline = new Buffer('bXVnc2hvdA=='),
      screenshot = new Buffer('ZmxvcmVudGlu'),
      diff = new Buffer('anything'),
      rootDirectory = 'visual-tests',
      extension = '.png',
      baselinePath = path.join(rootDirectory, dummySelector.name + extension),
      screenshotPath = path.join(rootDirectory, dummySelector.name + '.new' +
        extension),
      diffPath = path.join(rootDirectory, dummySelector.name + '.diff' +
        extension),
      mugshot, browser,
      FS, differ,
      PNGProcessor;

  beforeEach(function() {
    browser = {
      takeScreenshot: sinon.stub().yields(null, screenshot),
      getBoundingClientRect: sinon.stub()
    };

    FS = {
      exists: sinon.stub(),
      readFile: sinon.stub(),
      writeFile: sinon.stub(),
      mkdir: sinon.stub().yields(null),
      unlink: sinon.stub()
    };

    PNGProcessor = {
      crop: sinon.stub()
    };

    differ = {
      isEqual: sinon.stub(),
      createDiff: sinon.stub()
    };

    var options = {
      differ: differ,
      fs: FS,
      PNGProcessor: PNGProcessor
    };

    mugshot = new Mugshot(browser, options);
  });

  it('should throw an error if no selector is provided', function() {
    expect(mugshot.test.bind(mugshot)).to.throw(Error);
  });

  it('should throw an error if the parameter is not an object', function() {
    expect(mugshot.test.bind(mugshot, function() {}, callback)).to.throw(Error);
  });

  it('should throw an error if the parameter is an array', function() {
    expect(mugshot.test.bind(mugshot, [], callback)).to.throw(Error);
  });

  it('should throw an error if the object has no name property', function() {
    expect(mugshot.test.bind(mugshot, {}, callback)).to.throw(Error);
  });

  it('should throw an error if no callback is provided', function() {
    expect(mugshot.test.bind(mugshot, dummySelector)).to.throw(Error);
  });

  it('should throw an error if the callback param is not a function',
     function() {
    expect(mugshot.test.bind(mugshot, dummySelector, {})).to.throw(Error);
  });

  it('should create the rootDirectory', function() {
    mugshot.test(dummySelector, callback);

    expect(FS.mkdir).to.have.been.calledWith(rootDirectory);
  });

  it('should not throw error if the rootDirectory already exists', function() {
    var error = {code: 'EEXIST'};
    FS.mkdir.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback))
      .to.not.throw(error);
  });

  it('should throw error if mkdir callback receives another error', function() {
    FS.mkdir.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback))
      .to.throw(Error);
  });

  it('should call the browser to take a screenshot', function() {
    mugshot.test(dummySelector, callback);

    expect(browser.takeScreenshot).to.have.been.calledOnce;
  });

  it('should throw an error if the screenshot fails', function() {
    browser.takeScreenshot.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should verify if a baseline already exists', function() {
    mugshot.test(dummySelector, callback);

    expect(FS.exists).to.have.been.calledWith(baselinePath, sinon.match.func);
  });

  it('should write the screenshot on disk if no baseline exists', function() {
    FS.exists.yields(false);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.have.been.calledWith(baselinePath, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the screenshot cannot be written', function() {
    FS.exists.yields(false);
    FS.writeFile.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should not write the screenshot on disk if there is already a baseline',
     function() {
    FS.exists.yields(true);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.not.have.been.called;
  });

  it('should read the baseline from disk if it exists', function() {
    FS.exists.yields(true);

    mugshot.test(dummySelector, callback);

    expect(FS.readFile).to.have.been.calledWith(baselinePath, sinon.match.func);
  });

  it('should throw an error if the baseline cannot be read', function() {
    FS.exists.yields(true);
    FS.readFile.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should not read a baseline from disk if there is none', function() {
    FS.exists.yields(false);

    mugshot.test(dummySelector, callback);

    expect(FS.readFile).to.not.have.been.called;
  });

  it('should call the differ to compare the baseline from the fs with the ' +
     'screenshot from the browser', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);

    mugshot.test(dummySelector, callback);

    expect(differ.isEqual).to.have.been.calledWith(baseline, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the comparison fails', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should not compare if there is no baseline', function() {
    FS.exists.yields(false);
    FS.readFile.yields(null, baseline);

    mugshot.test(dummySelector, callback);

    expect(differ.isEqual).to.not.have.been.called;
  });

  it('should create a diff only if there are differences', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);

    mugshot.test(dummySelector, callback);

    expect(differ.createDiff).to.have.been.calledWith(baseline, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the diff building fails', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should not create a diff if there are no differences', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(differ.createDiff).to.not.have.been.called;
  });

  it('should call the fs to write the diff on disk', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.have.been.calledWith(diffPath, diff,
      sinon.match.func);
  });

  it('should throw an error if the diff cannot be written', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);
    FS.writeFile.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should not call the fs to write the diff on disk if there is none',
     function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.not.have.been.called;
  });

  it('should call the fs to write the screenshot on disk', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.have.been.calledWith(screenshotPath, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the screenshot cannot be written', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);
    FS.writeFile.onSecondCall().yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should try to unlink old diff if the comparison returns true',
     function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(FS.unlink).to.have.been.calledWith(diffPath);
  });

  it('should try to unlink old screenshot if the comparison returns true',
     function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(FS.unlink).to.have.been.calledWith(screenshotPath);
  });

  it('should not try to unlink old diff and screenshot if the comparison ' +
     'returns false', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);

    mugshot.test(dummySelector, callback);

    expect(FS.unlink).to.not.have.been.called;
  });

  it('should not throw an error if the file is not there', function() {
    var error = {code: 'ENOENT'};
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    expect(mugshot.test.bind(mugshot, dummySelector, callback))
      .to.not.throw(error);
  });

  it('should throw an error if the file couldn\'t be unlinked', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);
    FS.unlink.yields(error);

    expect(mugshot.test.bind(mugshot, dummySelector, callback)).to.throw(Error);
  });

  it('should get the bounding rect if a selector is provided', function() {
    mugshot.test(captureItem, callback);

    expect(browser.getBoundingClientRect).to.have.been.calledWith(
      captureItem.selector, sinon.match.func);
  });

  it('should throw an error if the bounding rect couldn\'t be calculated',
     function() {
      browser.getBoundingClientRect.yields(error);

      expect(mugshot.test.bind(mugshot, captureItem, callback)).to.throw(Error);
    });

  it('should not get the bounding rect if no selector is provided', function() {
    mugshot.test(dummySelector, callback);

    expect(browser.getBoundingClientRect).to.not.have.been.called;
  });

  it('should crop the image if a selector is provided', function() {
    browser.getBoundingClientRect.yields(null, rect);

    mugshot.test(captureItem, callback);

    expect(PNGProcessor.crop).to.have.been.calledWith(screenshot, rect,
      sinon.match.func);
  });

  it('should throw an error if the screenshot couldn\'t be cropped',
     function() {
      browser.getBoundingClientRect.yields(error);
      PNGProcessor.crop.yields(error);

      expect(mugshot.test.bind(mugshot, captureItem, callback)).to.throw(Error);
    });

  it('should not crop the image if there is no selector', function() {
    mugshot.test(dummySelector, callback);

    expect(PNGProcessor.crop).to.not.have.been.called;
  });
});
<commit_msg>refactor error tests to check that the error is passed to the callback<commit_after>var chai = require('chai');
var sinon = require('sinon');
var expect = chai.expect;
var Mugshot = require('../lib/mugshot.js');
var path = require('path');

chai.use(require('sinon-chai'));

describe('Mugshot', function() {
  var dummySelector = {
        name: 'path'
      },
      captureItem = {
        name: dummySelector.name,
        selector: 'a dom selector'
      },
      rect = {
        width: 100,
        height: 100,
        top: 50,
        left: 50
      },
      callback,
      error = new Error('Fatal Error'),
      baseline = new Buffer('bXVnc2hvdA=='),
      screenshot = new Buffer('ZmxvcmVudGlu'),
      diff = new Buffer('anything'),
      rootDirectory = 'visual-tests',
      extension = '.png',
      baselinePath = path.join(rootDirectory, dummySelector.name + extension),
      screenshotPath = path.join(rootDirectory, dummySelector.name + '.new' +
        extension),
      diffPath = path.join(rootDirectory, dummySelector.name + '.diff' +
        extension),
      mugshot, browser,
      FS, differ,
      PNGProcessor;

  beforeEach(function() {
    browser = {
      takeScreenshot: sinon.stub().yields(null, screenshot),
      getBoundingClientRect: sinon.stub()
    };

    FS = {
      exists: sinon.stub(),
      readFile: sinon.stub(),
      writeFile: sinon.stub(),
      mkdir: sinon.stub().yields(null),
      unlink: sinon.stub()
    };

    PNGProcessor = {
      crop: sinon.stub()
    };

    differ = {
      isEqual: sinon.stub(),
      createDiff: sinon.stub()
    };

    var options = {
      differ: differ,
      fs: FS,
      PNGProcessor: PNGProcessor
    };

    callback = sinon.spy();

    mugshot = new Mugshot(browser, options);
  });

  it('should throw an error if no selector is provided', function() {
    expect(mugshot.test.bind(mugshot)).to.throw(Error);
  });

  it('should throw an error if the parameter is not an object', function() {
    expect(mugshot.test.bind(mugshot, function() {}, callback)).to.throw(Error);
  });

  it('should throw an error if the parameter is an array', function() {
    expect(mugshot.test.bind(mugshot, [], callback)).to.throw(Error);
  });

  it('should throw an error if the object has no name property', function() {
    expect(mugshot.test.bind(mugshot, {}, callback)).to.throw(Error);
  });

  it('should throw an error if no callback is provided', function() {
    expect(mugshot.test.bind(mugshot, dummySelector)).to.throw(Error);
  });

  it('should throw an error if the callback param is not a function',
     function() {
    expect(mugshot.test.bind(mugshot, dummySelector, {})).to.throw(Error);
  });

  it('should create the rootDirectory', function() {
    mugshot.test(dummySelector, callback);

    expect(FS.mkdir).to.have.been.calledWith(rootDirectory);
  });

  it('should not throw error if the rootDirectory already exists', function() {
    var error = {code: 'EEXIST'};
    FS.mkdir.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.not.called;
  });

  it('should throw error if mkdir callback receives another error', function() {
    FS.mkdir.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should call the browser to take a screenshot', function() {
    mugshot.test(dummySelector, callback);

    expect(browser.takeScreenshot).to.have.been.calledOnce;
  });

  it('should throw an error if the screenshot fails', function() {
    browser.takeScreenshot.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should verify if a baseline already exists', function() {
    mugshot.test(dummySelector, callback);

    expect(FS.exists).to.have.been.calledWith(baselinePath, sinon.match.func);
  });

  it('should write the screenshot on disk if no baseline exists', function() {
    FS.exists.yields(false);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.have.been.calledWith(baselinePath, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the baseline cannot be written', function() {
    FS.exists.yields(false);
    FS.writeFile.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should not write the screenshot on disk if there is already a baseline',
     function() {
    FS.exists.yields(true);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.not.have.been.called;
  });

  it('should read the baseline from disk if it exists', function() {
    FS.exists.yields(true);

    mugshot.test(dummySelector, callback);

    expect(FS.readFile).to.have.been.calledWith(baselinePath, sinon.match.func);
  });

  it('should throw an error if the baseline cannot be read', function() {
    FS.exists.yields(true);
    FS.readFile.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should not read a baseline from disk if there is none', function() {
    FS.exists.yields(false);

    mugshot.test(dummySelector, callback);

    expect(FS.readFile).to.not.have.been.called;
  });

  it('should call the differ to compare the baseline from the fs with the ' +
     'screenshot from the browser', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);

    mugshot.test(dummySelector, callback);

    expect(differ.isEqual).to.have.been.calledWith(baseline, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the comparison fails', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should not compare if there is no baseline', function() {
    FS.exists.yields(false);
    FS.readFile.yields(null, baseline);

    mugshot.test(dummySelector, callback);

    expect(differ.isEqual).to.not.have.been.called;
  });

  it('should create a diff only if there are differences', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);

    mugshot.test(dummySelector, callback);

    expect(differ.createDiff).to.have.been.calledWith(baseline, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the diff building fails', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should not create a diff if there are no differences', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(differ.createDiff).to.not.have.been.called;
  });

  it('should call the fs to write the diff on disk', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.have.been.calledWith(diffPath, diff,
      sinon.match.func);
  });

  it('should throw an error if the diff cannot be written', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);
    FS.writeFile.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should not call the fs to write the diff on disk if there is none',
     function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.not.have.been.called;
  });

  it('should call the fs to write the screenshot on disk', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);

    mugshot.test(dummySelector, callback);

    expect(FS.writeFile).to.have.been.calledWith(screenshotPath, screenshot,
      sinon.match.func);
  });

  it('should throw an error if the screenshot cannot be written', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);
    differ.createDiff.yields(null, diff);
    FS.writeFile.onSecondCall().yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should try to unlink old diff if the comparison returns true',
     function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(FS.unlink).to.have.been.calledWith(diffPath);
  });

  it('should try to unlink old screenshot if the comparison returns true',
     function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);

    mugshot.test(dummySelector, callback);

    expect(FS.unlink).to.have.been.calledWith(screenshotPath);
  });

  it('should not try to unlink old diff and screenshot if the comparison ' +
     'returns false', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, false);

    mugshot.test(dummySelector, callback);

    expect(FS.unlink).to.not.have.been.called;
  });

  it('should not throw an error if the file is not there', function() {
    var error = {code: 'ENOENT'};
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);
    FS.unlink.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.not.called;
  });

  it('should throw an error if the file couldn\'t be unlinked', function() {
    FS.exists.yields(true);
    FS.readFile.yields(null, baseline);
    differ.isEqual.yields(null, true);
    FS.unlink.yields(error);

    mugshot.test(dummySelector, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should get the bounding rect if a selector is provided', function() {
    mugshot.test(captureItem, callback);

    expect(browser.getBoundingClientRect).to.have.been.calledWith(
      captureItem.selector, sinon.match.func);
  });

  it('should throw an error if the bounding rect couldn\'t be calculated',
     function() {
    browser.getBoundingClientRect.yields(error);

    mugshot.test(captureItem, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should not get the bounding rect if no selector is provided', function() {
    mugshot.test(dummySelector, callback);

    expect(browser.getBoundingClientRect).to.not.have.been.called;
  });

  it('should crop the image if a selector is provided', function() {
    browser.getBoundingClientRect.yields(null, rect);

    mugshot.test(captureItem, callback);

    expect(PNGProcessor.crop).to.have.been.calledWith(screenshot, rect,
      sinon.match.func);
  });

  it('should throw an error if the screenshot couldn\'t be cropped',
     function() {
    browser.getBoundingClientRect.yields(error);
    PNGProcessor.crop.yields(error);

    mugshot.test(captureItem, callback);

    expect(callback).to.have.been.calledWithExactly(error);
  });

  it('should not crop the image if there is no selector', function() {
    mugshot.test(dummySelector, callback);

    expect(PNGProcessor.crop).to.not.have.been.called;
  });
});
<|endoftext|>


<commit_before>P = Class.create(P, {

	init: function() {

		this.view.content.className = 'loading';

		this.initToolbar();
		this.draw();

		this.onNotify = this.refresh.bindAsEventListener(this);
		document.observe('chinachu:recorded', this.onNotify);

		return this;
	}
	,
	deinit: function() {

		document.stopObserving('chinachu:recorded', this.onNotify);

		return this;
	}
	,
	refresh: function() {

		this.app.pm.realizeHash(true);

		return this;
	}
	,
	initToolbar: function _initToolbar() {

		this.view.toolbar.add({
			key: 'search',
			ui : new sakura.ui.Button({
				label  : '録画番組検索',
				icon   : './icons/magnifier-zoom.png',
				onClick: this.viewSearchModal.bind(this)
			})
		});

		return this;
	}
	,
	draw: function() {

		this.view.content.className = '';
		this.view.content.update();

		this.grid = new flagrate.Grid({
			multiSelect  : false,
			disableSelect: true,
			pagination   : true,
			fill         : true,
			cols: [
				{
					key  : 'type',
					label: '放送波',
					width: 45,
					align: 'center',
					disableResize: true
				},
				{
					key  : 'channel',
					label: 'チャンネル',
					width: 140
				},
				{
					key  : 'category',
					label: 'ジャンル',
					width: 70,
					align: 'center',
				},
				{
					key  : 'title',
					label: 'タイトル'
				},
				{
					key  : 'datetime',
					label: '放送日時',
					width: 210
				},
				{
					key  : 'duration',
					label: '長さ',
					width: 60
				}
			],
			onClick: function(e, row) {
				window.location.href = '#!/program/view/id=' + row.data.id + '/';
			}.bind(this)
		}).insertTo(this.view.content);

		if (!this.self.query.skip) {
			this.viewSearchModal();
		} else {
			this.drawMain();
		}

		return this;
	}
	,
	drawMain: function() {

		var rows = [];

		var programs = [];

		var program;

		// 正規化方法
		var nf;
		if (global.chinachu.status.feature) {
			nf = global.chinachu.status.feature.normalizationForm;
		}

		// query.title, query.descの正規化をキャッシュ
		var query_title_norm, query_desc_norm;
		if (nf) {
			if (this.self.query.title) {
				query_title_norm = this.self.query.title.normalize(nf);
			}
			if (this.self.query.desc) {
				query_desc_norm = this.self.query.desc.normalize(nf);
			}
		}

		for (var i = 0, l = global.chinachu.recorded.length; i < l; i++) {
			program = global.chinachu.recorded[i];

			if (this.self.query.pgid && this.self.query.pgid !== program.id) continue;
			if (this.self.query.chid && this.self.query.chid !== program.channel.id) continue;
			if (this.self.query.cat && this.self.query.cat !== program.category) continue;
			if (this.self.query.type && this.self.query.type !== program.channel.type) continue;
			if (nf) {
				if (this.self.query.title && program.title.normalize(nf).match(query_title_norm) === null) continue;
				if (this.self.query.desc && (!program.detail || program.detail.normalize(nf).match(query_desc_norm) === null)) continue;
			}
			else {
				if (this.self.query.title && program.title.match(this.self.query.title) === null) continue;
				if (this.self.query.desc && (!program.detail || program.detail.match(this.self.query.desc) === null)) continue;
			}

			if (this.self.query.start || this.self.query.end) {
				var ruleStart = parseInt(this.self.query.start || 0, 10);
				var ruleEnd   = parseInt(this.self.query.end || 24, 10);

				var progStart = new Date(program.start).getHours();
				var progEnd   = new Date(program.end).getHours();

				if (progStart > progEnd) {
					progEnd += 24;
				}

				if (ruleStart > ruleEnd) {
					if ((ruleStart > progStart) && (ruleEnd < progEnd)) continue;
				} else {
					if ((ruleStart > progStart) || (ruleEnd < progEnd)) continue;
				}
			}

			programs.push(program);
		}

		programs.sort(function(a, b) {
			return a.start - b.start;
		});

		programs.each(function(program, i) {

			var row = {
				data: program,
				cell: {
					id: {
						className: 'id',
						sortAlt  : i,
						text     : program.id
					}
				},
				menuItems: [
					{
						label   : 'ルール作成...',
						icon    : './icons/regular-expression.png',
						onSelect: function() {
							new chinachu.ui.CreateRuleByProgram(program.id);
						}
					},
					'------------------------------------------',
					{
						label   : 'ツイート...',
						icon    : 'https://abs.twimg.com/favicons/favicon.ico',
						onSelect: function() {
							var left = (screen.width - 640) / 2;
							var top  = (screen.height - 265) / 2;

							var tweetWindow = window.open(
								'https://twitter.com/share?url=&text=' + encodeURIComponent(chinachu.util.scotify(program)),
								'chinachu-tweet-' + program.id,
								'width=640,height=265,left=' + left + ',top=' + top + ',menubar=no'
							);
						}
					},
					'------------------------------------------',
					{
						label   : 'SCOT形式でコピー...',
						onSelect: function(e) {
							chinachu.ui.copyStr(chinachu.util.scotify(program));
						}
					},
					{
						label   : 'IDをコピー...',
						onSelect: function() {
							chinachu.ui.copyStr(program.id);
						}
					},
					{
						label   : 'タイトルをコピー...',
						onSelect: function() {
							chinachu.ui.copyStr(program.title);
						}
					},
					{
						label   : '説明をコピー...',
						onSelect: function() {
							chinachu.ui.copyStr(program.detail);
						}
					},
					'------------------------------------------',
					{
						label   : '関連サイト',
						icon    : './icons/document-page-next.png',
						onSelect: function() {
							window.open("https://www.google.com/search?btnI=I'm+Feeling+Lucky&q=" + program.title);
						}
					},
					{
						label   : 'Google検索',
						icon    : './icons/ui-search-field.png',
						onSelect: function() {
							window.open("https://www.google.com/search?q=" + program.title);
						}
					},
					{
						label   : 'Wikipedia',
						icon    : './icons/book-open-text-image.png',
						onSelect: function() {
							window.open("https://ja.wikipedia.org/wiki/" + program.title);
						}
					}
				]
			};

			row.cell.type = {
				sortAlt  : program.channel.type,
				className: 'types',
				html     : '' + program.channel.type + ''
			};

			row.cell.category = {
				sortAlt    : program.category,
				className  : 'categories',
				html       : '' + program.category + ''
			};

			row.cell.channel = {
				sortAlt    : program.channel.id,
				text       : program.channel.name,
				attribute  : {
					title: program.channel.id
				}
			};

			var titleHtml = program.flags.invoke('sub', /.+/, '#{0}').join('') + program.title;
			if (typeof program.episode !== 'undefined' && program.episode !== null) {
				titleHtml += '#' + program.episode + '';
			}
			titleHtml += '#' + program.id + '';

			row.cell.title = {
				sortAlt    : program.title + (program.episode || 0).toString(36),
				html       : titleHtml,
				attribute  : {
					title: program.detail
				}
			};

			row.cell.duration = {
				sortAlt    : program.seconds,
				text       : program.seconds / 60 + 'm'
			};

			row.cell.datetime = {
				sortAlt    : program.start,
				text       : chinachu.dateToString(new Date(program.start))
			};

			rows.push(row);
		});

		this.grid.splice(0, null, rows);

		return this;
	}
	,
	viewSearchModal: function() {

		var modal = new flagrate.Modal({
			title  : '録画番組検索',
			buttons: [
				{
					label   : '検索',
					color   : '@pink',
					onSelect: function(e, modal) {
						e.targetButton.disable();

						var result = viewSearchForm.result();

						this.self.query = Object.extend(this.self.query, result);
						this.self.query.skip = 1;

						modal.close();

						window.location.hash = '!/recorded/search/' + Object.toQueryString(this.self.query) + '/';
						//todo
					}.bind(this)
				}
			]
		}).show();

		var viewSearchForm = new Hyperform({
			formWidth  : '100%',
			labelWidth : '100px',
			labelAlign : 'right',
			fields     : [
				{
					key   : 'cat',
					label : 'カテゴリー',
					input : {
						type : 'pulldown',
						items: (function() {
							var array = [];

							[
								'anime', 'information', 'news', 'sports',
								'variety', 'drama', 'music', 'cinema', 'etc'
							].each(function(a) {
								array.push({
									label     : a,
									value     : a,
									isSelected: (this.self.query.cat === a)
								});
							}.bind(this));

							return array;
						}.bind(this))()
					}
				},
				{
					key   : 'title',
					label : 'タイトル',
					input : {
						type : 'text',
						value: this.self.query.title || ''
					}
				},
				{
					key   : 'desc',
					label : '説明',
					input : {
						type : 'text',
						value:  this.self.query.desc || ''
					}
				},
				{
					key   : 'type',
					label : 'タイプ',
					input : {
						type : 'pulldown',
						items: (function() {
							var array = [];

							['GR', 'BS', 'CS', 'EX'].each(function(a) {
								array.push({
									label     : a,
									value     : a,
									isSelected: ((this.self.query.type || []).indexOf(a) !== -1)
								});
							}.bind(this));

							return array;
						}.bind(this))()
					}
				},
				{
					key   : 'start',
					label : '何時から',
					input : {
						type      : 'text',
						width     : 25,
						maxlength : 2,
						appendText: '時',
						value   : this.self.query.start || '',
						isNumber: true
					}
				},
				{
					key   : 'end',
					label : '何時まで',
					input : {
						type      : 'text',
						width     : 25,
						maxlength : 2,
						appendText: '時',
						value     : this.self.query.end || '',
						isNumber  : true
					}
				},
				{
					key   : 'pgid',
					label : 'プログラムID',
					input : {
						type : 'text',
						value:  this.self.query.pgid || ''
					}
				},
				{
					key   : 'chid',
					label : 'チャンネルID',
					input : {
						type : 'text',
						value:  this.self.query.chid || ''
					}
				}
			]
		}).render(modal.content);

		return this;
	}
});
<commit_msg>Add remove program into context menu on search page<commit_after>P = Class.create(P, {

	init: function() {

		this.view.content.className = 'loading';

		this.initToolbar();
		this.draw();

		this.onNotify = this.refresh.bindAsEventListener(this);
		document.observe('chinachu:recorded', this.onNotify);

		return this;
	}
	,
	deinit: function() {

		document.stopObserving('chinachu:recorded', this.onNotify);

		return this;
	}
	,
	refresh: function() {

		this.app.pm.realizeHash(true);

		return this;
	}
	,
	initToolbar: function _initToolbar() {

		this.view.toolbar.add({
			key: 'search',
			ui : new sakura.ui.Button({
				label  : '録画番組検索',
				icon   : './icons/magnifier-zoom.png',
				onClick: this.viewSearchModal.bind(this)
			})
		});

		return this;
	}
	,
	draw: function() {

		this.view.content.className = '';
		this.view.content.update();

		this.grid = new flagrate.Grid({
			multiSelect  : false,
			disableSelect: true,
			pagination   : true,
			fill         : true,
			cols: [
				{
					key  : 'type',
					label: '放送波',
					width: 45,
					align: 'center',
					disableResize: true
				},
				{
					key  : 'channel',
					label: 'チャンネル',
					width: 140
				},
				{
					key  : 'category',
					label: 'ジャンル',
					width: 70,
					align: 'center',
				},
				{
					key  : 'title',
					label: 'タイトル'
				},
				{
					key  : 'datetime',
					label: '放送日時',
					width: 210
				},
				{
					key  : 'duration',
					label: '長さ',
					width: 60
				}
			],
			onClick: function(e, row) {
				window.location.href = '#!/program/view/id=' + row.data.id + '/';
			}.bind(this)
		}).insertTo(this.view.content);

		if (!this.self.query.skip) {
			this.viewSearchModal();
		} else {
			this.drawMain();
		}

		return this;
	}
	,
	drawMain: function() {

		var rows = [];

		var programs = [];

		var program;

		// 正規化方法
		var nf;
		if (global.chinachu.status.feature) {
			nf = global.chinachu.status.feature.normalizationForm;
		}

		// query.title, query.descの正規化をキャッシュ
		var query_title_norm, query_desc_norm;
		if (nf) {
			if (this.self.query.title) {
				query_title_norm = this.self.query.title.normalize(nf);
			}
			if (this.self.query.desc) {
				query_desc_norm = this.self.query.desc.normalize(nf);
			}
		}

		for (var i = 0, l = global.chinachu.recorded.length; i < l; i++) {
			program = global.chinachu.recorded[i];

			if (this.self.query.pgid && this.self.query.pgid !== program.id) continue;
			if (this.self.query.chid && this.self.query.chid !== program.channel.id) continue;
			if (this.self.query.cat && this.self.query.cat !== program.category) continue;
			if (this.self.query.type && this.self.query.type !== program.channel.type) continue;
			if (nf) {
				if (this.self.query.title && program.title.normalize(nf).match(query_title_norm) === null) continue;
				if (this.self.query.desc && (!program.detail || program.detail.normalize(nf).match(query_desc_norm) === null)) continue;
			}
			else {
				if (this.self.query.title && program.title.match(this.self.query.title) === null) continue;
				if (this.self.query.desc && (!program.detail || program.detail.match(this.self.query.desc) === null)) continue;
			}

			if (this.self.query.start || this.self.query.end) {
				var ruleStart = parseInt(this.self.query.start || 0, 10);
				var ruleEnd   = parseInt(this.self.query.end || 24, 10);

				var progStart = new Date(program.start).getHours();
				var progEnd   = new Date(program.end).getHours();

				if (progStart > progEnd) {
					progEnd += 24;
				}

				if (ruleStart > ruleEnd) {
					if ((ruleStart > progStart) && (ruleEnd < progEnd)) continue;
				} else {
					if ((ruleStart > progStart) || (ruleEnd < progEnd)) continue;
				}
			}

			programs.push(program);
		}

		programs.sort(function(a, b) {
			return a.start - b.start;
		});

		programs.each(function(program, i) {

			var row = {
				data: program,
				cell: {
					id: {
						className: 'id',
						sortAlt  : i,
						text     : program.id
					}
				},
				menuItems: [
					{
						label   : '削除...',
						icon    : './icons/cross-script.png',
						onSelect: function() {
							new chinachu.ui.RemoveRecordedProgram(program.id);
						}
					},
					'------------------------------------------',
					{
						label   : 'ルール作成...',
						icon    : './icons/regular-expression.png',
						onSelect: function() {
							new chinachu.ui.CreateRuleByProgram(program.id);
						}
					},
					'------------------------------------------',
					{
						label   : 'ツイート...',
						icon    : 'https://abs.twimg.com/favicons/favicon.ico',
						onSelect: function() {
							var left = (screen.width - 640) / 2;
							var top  = (screen.height - 265) / 2;

							var tweetWindow = window.open(
								'https://twitter.com/share?url=&text=' + encodeURIComponent(chinachu.util.scotify(program)),
								'chinachu-tweet-' + program.id,
								'width=640,height=265,left=' + left + ',top=' + top + ',menubar=no'
							);
						}
					},
					'------------------------------------------',
					{
						label   : 'SCOT形式でコピー...',
						onSelect: function(e) {
							chinachu.ui.copyStr(chinachu.util.scotify(program));
						}
					},
					{
						label   : 'IDをコピー...',
						onSelect: function() {
							chinachu.ui.copyStr(program.id);
						}
					},
					{
						label   : 'タイトルをコピー...',
						onSelect: function() {
							chinachu.ui.copyStr(program.title);
						}
					},
					{
						label   : '説明をコピー...',
						onSelect: function() {
							chinachu.ui.copyStr(program.detail);
						}
					},
					'------------------------------------------',
					{
						label   : '関連サイト',
						icon    : './icons/document-page-next.png',
						onSelect: function() {
							window.open("https://www.google.com/search?btnI=I'm+Feeling+Lucky&q=" + program.title);
						}
					},
					{
						label   : 'Google検索',
						icon    : './icons/ui-search-field.png',
						onSelect: function() {
							window.open("https://www.google.com/search?q=" + program.title);
						}
					},
					{
						label   : 'Wikipedia',
						icon    : './icons/book-open-text-image.png',
						onSelect: function() {
							window.open("https://ja.wikipedia.org/wiki/" + program.title);
						}
					}
				]
			};

			row.cell.type = {
				sortAlt  : program.channel.type,
				className: 'types',
				html     : '' + program.channel.type + ''
			};

			row.cell.category = {
				sortAlt    : program.category,
				className  : 'categories',
				html       : '' + program.category + ''
			};

			row.cell.channel = {
				sortAlt    : program.channel.id,
				text       : program.channel.name,
				attribute  : {
					title: program.channel.id
				}
			};

			var titleHtml = program.flags.invoke('sub', /.+/, '#{0}').join('') + program.title;
			if (typeof program.episode !== 'undefined' && program.episode !== null) {
				titleHtml += '#' + program.episode + '';
			}
			titleHtml += '#' + program.id + '';

			row.cell.title = {
				sortAlt    : program.title + (program.episode || 0).toString(36),
				html       : titleHtml,
				attribute  : {
					title: program.detail
				}
			};

			row.cell.duration = {
				sortAlt    : program.seconds,
				text       : program.seconds / 60 + 'm'
			};

			row.cell.datetime = {
				sortAlt    : program.start,
				text       : chinachu.dateToString(new Date(program.start))
			};

			rows.push(row);
		});

		this.grid.splice(0, null, rows);

		return this;
	}
	,
	viewSearchModal: function() {

		var modal = new flagrate.Modal({
			title  : '録画番組検索',
			buttons: [
				{
					label   : '検索',
					color   : '@pink',
					onSelect: function(e, modal) {
						e.targetButton.disable();

						var result = viewSearchForm.result();

						this.self.query = Object.extend(this.self.query, result);
						this.self.query.skip = 1;

						modal.close();

						window.location.hash = '!/recorded/search/' + Object.toQueryString(this.self.query) + '/';
						//todo
					}.bind(this)
				}
			]
		}).show();

		var viewSearchForm = new Hyperform({
			formWidth  : '100%',
			labelWidth : '100px',
			labelAlign : 'right',
			fields     : [
				{
					key   : 'cat',
					label : 'カテゴリー',
					input : {
						type : 'pulldown',
						items: (function() {
							var array = [];

							[
								'anime', 'information', 'news', 'sports',
								'variety', 'drama', 'music', 'cinema', 'etc'
							].each(function(a) {
								array.push({
									label     : a,
									value     : a,
									isSelected: (this.self.query.cat === a)
								});
							}.bind(this));

							return array;
						}.bind(this))()
					}
				},
				{
					key   : 'title',
					label : 'タイトル',
					input : {
						type : 'text',
						value: this.self.query.title || ''
					}
				},
				{
					key   : 'desc',
					label : '説明',
					input : {
						type : 'text',
						value:  this.self.query.desc || ''
					}
				},
				{
					key   : 'type',
					label : 'タイプ',
					input : {
						type : 'pulldown',
						items: (function() {
							var array = [];

							['GR', 'BS', 'CS', 'EX'].each(function(a) {
								array.push({
									label     : a,
									value     : a,
									isSelected: ((this.self.query.type || []).indexOf(a) !== -1)
								});
							}.bind(this));

							return array;
						}.bind(this))()
					}
				},
				{
					key   : 'start',
					label : '何時から',
					input : {
						type      : 'text',
						width     : 25,
						maxlength : 2,
						appendText: '時',
						value   : this.self.query.start || '',
						isNumber: true
					}
				},
				{
					key   : 'end',
					label : '何時まで',
					input : {
						type      : 'text',
						width     : 25,
						maxlength : 2,
						appendText: '時',
						value     : this.self.query.end || '',
						isNumber  : true
					}
				},
				{
					key   : 'pgid',
					label : 'プログラムID',
					input : {
						type : 'text',
						value:  this.self.query.pgid || ''
					}
				},
				{
					key   : 'chid',
					label : 'チャンネルID',
					input : {
						type : 'text',
						value:  this.self.query.chid || ''
					}
				}
			]
		}).render(modal.content);

		return this;
	}
});
<|endoftext|>


<commit_before>var rc = require("rc");
var log = require("./lib/log").getLogger();

exports.SUCCESS = "SUCCESS";
exports.FAILED = "FAILED";

exports.send = function(event, context, responseStatus, responseData, physicalResourceId, options) {

  // rc mutates defaults, so we define them here
  var DEFAULTS = {
    returnError: true,
    logLevel: "info"
  };

  options = options || {};

  var cfg = rc("cfn_responder",DEFAULTS,options);
  var doneCallback = context.done;
  log.level = cfg.logLevel;

  responseData = responseData || {};
  if (typeof responseData !== 'object') {
    if (cfg.returnError) {
      return doneCallback("'Data' must be a key/pair object");
    }
    else {
      responseData = {};
      log.info("'Data' was set to an empty object because it was not a valid key/pair object");
    }
  }

  var jsonBody = {
    Status: responseStatus,
    Reason: event.Reason || "See the details in CloudWatch Log Stream: " + context.logStreamName,
    StackId: event.StackId,
    RequestId: event.RequestId,
    LogicalResourceId: event.LogicalResourceId,
    Data: responseData
  };

  jsonBody.PhysicalResourceId = physicalResourceId || context.logStreamName;

  // If this lambda function does not have permissions to CloudWatch Logs
  // a PhysicalResourceId is still needed to send to CloudFormation.
  if (event.RequestType === 'Create' && responseStatus === exports.FAILED && !jsonBody.PhysicalResourceId) {
    jsonBody.PhysicalResourceId = exports.FAILED;
  }

  log.debug("jsonBody", {data: jsonBody});
  var responseBody = JSON.stringify(jsonBody);

  var https = require("https");
  var url = require("url");

  var parsedUrl = url.parse(event.ResponseURL);
  var httpOptions = {
    hostname: parsedUrl.hostname,
    port: 443,
    path: parsedUrl.path,
    method: "PUT",
    headers: {
      "content-type": "",
      "content-length": responseBody.length
    }
  };
  log.debug("httpOptions",{data: httpOptions});

  var request = https.request(httpOptions, function(response) {
    if (response.statusCode === 200)
      return doneCallback(null, jsonBody);

    var errMsg = "Failed to communicate to S3: " + response.statusCode;
    if (cfg.returnError) {
      return doneCallback(errMsg,jsonBody);
    }
    else {
      log.error(errMsg);
      return doneCallback(null);
    }
  });

  request.on("error", function(error) {
    var errMsg = "send(..) failed executing https.request(..): " + error;
    if (cfg.returnError) {
      doneCallback(errMsg);
    }
    else {
      log.error(errMsg);
      doneCallback(null);
    }
  });

  request.write(responseBody);
  request.end();
}

<commit_msg>debug output for every callback to lambda<commit_after>var rc = require("rc");
var log = require("./lib/log").getLogger();

exports.SUCCESS = "SUCCESS";
exports.FAILED = "FAILED";

exports.send = function(event, context, responseStatus, responseData, physicalResourceId, options) {

  // rc mutates defaults, so we define them here
  var DEFAULTS = {
    returnError: true,
    logLevel: "info"
  };

  options = options || {};

  var cfg = rc("cfn_responder",DEFAULTS,options);
  log.level = cfg.logLevel;

  var doneCallback = function() {
    log.debug("Finish Lambda callback", {data: arguments});
    context.done.apply(this,arguments);
  };

  responseData = responseData || {};
  if (typeof responseData !== 'object') {
    if (cfg.returnError) {
      return doneCallback("'Data' must be a key/pair object");
    }
    else {
      responseData = {};
      log.info("'Data' was set to an empty object because it was not a valid key/pair object");
    }
  }

  var jsonBody = {
    Status: responseStatus,
    Reason: event.Reason || "See the details in CloudWatch Log Stream: " + context.logStreamName,
    StackId: event.StackId,
    RequestId: event.RequestId,
    LogicalResourceId: event.LogicalResourceId,
    Data: responseData
  };

  jsonBody.PhysicalResourceId = physicalResourceId || context.logStreamName;

  // If this lambda function does not have permissions to CloudWatch Logs
  // a PhysicalResourceId is still needed to send to CloudFormation.
  if (event.RequestType === 'Create' && responseStatus === exports.FAILED && !jsonBody.PhysicalResourceId) {
    jsonBody.PhysicalResourceId = exports.FAILED;
  }

  log.debug("jsonBody", {data: jsonBody});
  var responseBody = JSON.stringify(jsonBody);

  var https = require("https");
  var url = require("url");

  var parsedUrl = url.parse(event.ResponseURL);
  var httpOptions = {
    hostname: parsedUrl.hostname,
    port: 443,
    path: parsedUrl.path,
    method: "PUT",
    headers: {
      "content-type": "",
      "content-length": responseBody.length
    }
  };
  log.debug("httpOptions",{data: httpOptions});

  var request = https.request(httpOptions, function(response) {
    if (response.statusCode === 200)
      return doneCallback(null, jsonBody);

    var errMsg = "Failed to communicate to S3: " + response.statusCode;
    if (cfg.returnError) {
      return doneCallback(errMsg,jsonBody);
    }
    else {
      log.error(errMsg);
      return doneCallback(null);
    }
  });

  request.on("error", function(error) {
    var errMsg = "send(..) failed executing https.request(..): " + error;
    if (cfg.returnError) {
      doneCallback(errMsg);
    }
    else {
      log.error(errMsg);
      doneCallback(null);
    }
  });

  request.write(responseBody);
  request.end();
}

<|endoftext|>


<commit_before>/* global require */
'use strict';
var _ = require('lodash'),
	child_process = require('child_process'),
	gutil = require('gulp-util'),
	PluginError = gutil.PluginError,
	es = require('event-stream'),
	path = require('path'),
	temp = require('temp'),
	fs = require('fs'),
	teamcity = require('./lib/teamcity'),

	PLUGIN_NAME = 'gulp-nunit-runner',
	NUNIT_CONSOLE = 'nunit-console.exe',
	NUNIT_X86_CONSOLE = 'nunit-console-x86.exe',

	runner;

// Main entry point
runner = function gulpNunitRunner(opts) {
	var stream,
		files;
	opts = opts || {};

	files = [];

	stream = es.through(function write(file) {
		if (_.isUndefined(file)) {
			fail(this, 'File may not be null.');
		}

		files.push(file);
		this.emit('data', file);
	}, function end() {
		run(this, files, opts);
	});

	return stream;
};

runner.getExecutable = function (options) {
	var executable,
		consoleRunner;
	consoleRunner = options.platform === 'x86' ? NUNIT_X86_CONSOLE : NUNIT_CONSOLE;
	if (!options.executable) {
		return consoleRunner;
	}
	// trim any existing surrounding quotes and then wrap in ""
	executable = trim(options.executable, '\\s', '"', "'");
	return !path.extname(options.executable) ?
		path.join(executable, consoleRunner) : executable;
};

runner.getArguments = function (options, assemblies) {
	var args = [];

	if (options.options) {
		args = args.concat(parseSwitches(options.options));
	}
	args = args.concat(assemblies);

	return args;
};

function parseSwitches(options) {
	var filtered,
		switches,
		isWin = /^win/.test(process.platform),
	// when running under mono on linux/mac switches must be specified with a - not a /
		switchChar = isWin ? '/' : '-';

	switches = _.map(options, function (val, key) {
		var qualifier;
		if (typeof val === 'boolean') {
			if (val) {
				return (switchChar + key);
			}
			return undefined;
		}
		if (typeof val === 'string') {
			qualifier = val.trim().indexOf(' ') > -1 ? '"' : '';
			return (switchChar + key + ':' + qualifier + val + qualifier);
		}
		if (val instanceof Array) {
			return (switchChar + key + ':' + val.join(','));
		}
	});

	filtered = _.filter(switches, function (val) {
		return !_.isUndefined(val);
	});

	return filtered;
}

function fail(stream, msg) {
	stream.emit('error', new gutil.PluginError(PLUGIN_NAME, msg));
}

function end(stream) {
	stream.emit('end');
}

function run(stream, files, options) {

	var child,
		args,
		exe,
		opts,
		assemblies,
		cleanupTempFiles;

	options.options = options.options || {};

	if (!options.options.result && options.teamcity) {
		temp.track();
		options.options.result = temp.path({suffix: '.xml'});
		cleanupTempFiles = temp.cleanup;
	}

	assemblies = files.map(function (file) {
		return file.path;
	});

	if (assemblies.length === 0) {
		return fail(stream, 'Some assemblies required.'); //<-- See what I did there ;)
	}

	opts = {
		stdio: [null, process.stdout, process.stderr, 'pipe']
	};

	exe = runner.getExecutable(options);
	args = runner.getArguments(options, assemblies);

	child = child_process.spawn(exe, args, opts);

	child.on('error', function (e) {
		fail(stream, e.code === 'ENOENT' ? 'Unable to find \'' + exe + '\'.' : e.message);
	});

	child.on('close', function (code) {
		if (options.teamcity) {
			gutil.log.apply(null, teamcity(fs.readFileSync(options.options.result, 'utf8')));
		}
		if (cleanupTempFiles) {
			cleanupTempFiles();
		}
		if (code !== 0) {
			gutil.log(gutil.colors.red('NUnit tests failed.'));
			fail(stream, 'NUnit tests failed.');
		} else {
			gutil.log(gutil.colors.cyan('NUnit tests passed'));
		}
		return end(stream);
	});
}

function trim() {
	var args = Array.prototype.slice.call(arguments),
		source = args[0],
		replacements = args.slice(1).join(','),
		regex = new RegExp("^[" + replacements + "]+|[" + replacements + "]+$", "g");
	return source.replace(regex, '');
}

module.exports = runner;
<commit_msg>Added check for output file before creating the TeamCity output.<commit_after>/* global require */
'use strict';
var _ = require('lodash'),
	child_process = require('child_process'),
	gutil = require('gulp-util'),
	PluginError = gutil.PluginError,
	es = require('event-stream'),
	path = require('path'),
	temp = require('temp'),
	fs = require('fs'),
	teamcity = require('./lib/teamcity'),

	PLUGIN_NAME = 'gulp-nunit-runner',
	NUNIT_CONSOLE = 'nunit-console.exe',
	NUNIT_X86_CONSOLE = 'nunit-console-x86.exe',

	runner;

// Main entry point
runner = function gulpNunitRunner(opts) {
	var stream,
		files;
	opts = opts || {};

	files = [];

	stream = es.through(function write(file) {
		if (_.isUndefined(file)) {
			fail(this, 'File may not be null.');
		}

		files.push(file);
		this.emit('data', file);
	}, function end() {
		run(this, files, opts);
	});

	return stream;
};

runner.getExecutable = function (options) {
	var executable,
		consoleRunner;
	consoleRunner = options.platform === 'x86' ? NUNIT_X86_CONSOLE : NUNIT_CONSOLE;
	if (!options.executable) {
		return consoleRunner;
	}
	// trim any existing surrounding quotes and then wrap in ""
	executable = trim(options.executable, '\\s', '"', "'");
	return !path.extname(options.executable) ?
		path.join(executable, consoleRunner) : executable;
};

runner.getArguments = function (options, assemblies) {
	var args = [];

	if (options.options) {
		args = args.concat(parseSwitches(options.options));
	}
	args = args.concat(assemblies);

	return args;
};

function parseSwitches(options) {
	var filtered,
		switches,
		isWin = /^win/.test(process.platform),
	// when running under mono on linux/mac switches must be specified with a - not a /
		switchChar = isWin ? '/' : '-';

	switches = _.map(options, function (val, key) {
		var qualifier;
		if (typeof val === 'boolean') {
			if (val) {
				return (switchChar + key);
			}
			return undefined;
		}
		if (typeof val === 'string') {
			qualifier = val.trim().indexOf(' ') > -1 ? '"' : '';
			return (switchChar + key + ':' + qualifier + val + qualifier);
		}
		if (val instanceof Array) {
			return (switchChar + key + ':' + val.join(','));
		}
	});

	filtered = _.filter(switches, function (val) {
		return !_.isUndefined(val);
	});

	return filtered;
}

function fail(stream, msg) {
	stream.emit('error', new gutil.PluginError(PLUGIN_NAME, msg));
}

function end(stream) {
	stream.emit('end');
}

function run(stream, files, options) {

	var child,
		args,
		exe,
		opts,
		assemblies,
		cleanupTempFiles;

	options.options = options.options || {};

	if (!options.options.result && options.teamcity) {
		temp.track();
		options.options.result = temp.path({suffix: '.xml'});
		cleanupTempFiles = temp.cleanup;
	}

	assemblies = files.map(function (file) {
		return file.path;
	});

	if (assemblies.length === 0) {
		return fail(stream, 'Some assemblies required.'); //<-- See what I did there ;)
	}

	opts = {
		stdio: [null, process.stdout, process.stderr, 'pipe']
	};

	exe = runner.getExecutable(options);
	args = runner.getArguments(options, assemblies);

	child = child_process.spawn(exe, args, opts);

	child.on('error', function (e) {
		fail(stream, e.code === 'ENOENT' ? 'Unable to find \'' + exe + '\'.' : e.message);
	});

	child.on('close', function (code) {
		if (options.teamcity) {
			if (fs.existsSync(options.options.result))
				gutil.log.apply(null, teamcity(fs.readFileSync(options.options.result, 'utf8')));
			else fail(stream, 'NUnit output not found: ' + options.options.result);
		}
		if (cleanupTempFiles) {
			cleanupTempFiles();
		}
		if (code !== 0) {
			gutil.log(gutil.colors.red('NUnit tests failed.'));
			fail(stream, 'NUnit tests failed.');
		} else {
			gutil.log(gutil.colors.cyan('NUnit tests passed'));
		}
		return end(stream);
	});
}

function trim() {
	var args = Array.prototype.slice.call(arguments),
		source = args[0],
		replacements = args.slice(1).join(','),
		regex = new RegExp("^[" + replacements + "]+|[" + replacements + "]+$", "g");
	return source.replace(regex, '');
}

module.exports = runner;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from "react"
import { Link } from "react-router"

import cx from "classnames"

import SVGIcon from "../SVGIcon"

export default class Footer extends Component {

  static contextTypes = {
    metadata: PropTypes.object.isRequired,
  }

  render() {
    const { metadata } = this.context
    const i18n = metadata.i18n

    return (

              {i18n.copyright.replace("{*}", new Date().getFullYear())}

            {
              i18n.footerNavigation &&

              {
                [
                  i18n.footerNavigation.map((item) => {
                    return (

                          { item.name }

                    )
                  }),

                      { i18n.forumLabel }

,

                      <SVGIcon
                        className="putainde-Icon"
                        svg={require(`icons/github.svg`)}
                        cleanup
                      />

,

                      <SVGIcon
                        className="putainde-Icon"
                        svg={require(`icons/twitter.svg`)}
                        cleanup
                      />

,

                      <SVGIcon
                        className="putainde-Icon"
                        svg={require(`icons/chat.svg`)}
                        cleanup
                      />

,
                ]
              }

            }

              {i18n.madeWithHeart}

    )
  }
}
<commit_msg>Remove unused Link from react-router in Footer component<commit_after>import React, { Component, PropTypes } from "react"

import cx from "classnames"

import SVGIcon from "../SVGIcon"

export default class Footer extends Component {

  static contextTypes = {
    metadata: PropTypes.object.isRequired,
  }

  render() {
    const { metadata } = this.context
    const i18n = metadata.i18n

    return (

              {i18n.copyright.replace("{*}", new Date().getFullYear())}

            {
              i18n.footerNavigation &&

              {
                [
                  i18n.footerNavigation.map((item) => {
                    return (

                          { item.name }

                    )
                  }),

                      { i18n.forumLabel }

,

                      <SVGIcon
                        className="putainde-Icon"
                        svg={require(`icons/github.svg`)}
                        cleanup
                      />

,

                      <SVGIcon
                        className="putainde-Icon"
                        svg={require(`icons/twitter.svg`)}
                        cleanup
                      />

,

                      <SVGIcon
                        className="putainde-Icon"
                        svg={require(`icons/chat.svg`)}
                        cleanup
                      />

,
                ]
              }

            }

              {i18n.madeWithHeart}

    )
  }
}
<|endoftext|>