<commit_before>import _ from 'lodash'
import crypto from 'crypto'
import dialogflow from 'dialogflow'

import Provider from './base'

export default class DialogflowProvider extends Provider {
  constructor(config) {
    super({ ...config, name: 'dialogflow', entityKey: '@dialogflow' })

    this.projectId = this.config.googleProjectId

    this.agentClient = new dialogflow.AgentsClient()
    this.sessionClient = new dialogflow.SessionsClient()
  }

  _getSessionId(event) {
    let shortUserId = _.get(event, 'user.id') || ''
    if (shortUserId.length > 36) {
      shortUserId = crypto
        .createHash('md5')
        .update(shortUserId)
        .digest('hex')
    }
    return shortUserId
  }

  _resolveEntity(field) {
    const entity = field[field.kind]

    if (field.kind === 'stringValue' || field.kind === 'numberValue') {
      return entity
    } else if (field.kind === 'listValue') {
      return entity.values.map(v => this._resolveEntity(v))
    } else if (field.kind === 'structValue') {
      return _.mapValues(entity.fields, f => this._resolveEntity(f))
    } else {
      throw new Error('Not supported')
    }
  }

  /*******
  Public API
  *******/

  async init() {
    const [agent] = await this.agentClient.getAgent({ parent: this.agentClient.projectPath(this.projectId) })
    this.agent = agent
  }

  async sync() {
    throw new Error('Not implemented')
  }

  async checkSyncNeeded() {
    return false // Not implemented yet
  }

  async extract(event) {
    const request = {
      session: this.sessionClient.sessionPath(this.projectId, this._getSessionId(event)),
      queryInput: {
        text: {
          text: event.text,
          languageCode: this.agent.defaultLanguageCode
        }
      }
    }
    const detection = await this.sessionClient.detectIntent(request)
    const { queryResult } = detection[0]
    const intent = {
      name: queryResult.intent.displayName,
      confidence: queryResult.intentDetectionConfidence,
      provider: 'dialogflow'
    }
    const entities = _.map(queryResult.parameters.fields, (v, k) => ({ name: k, value: this._resolveEntity(v) }))

    return {
      intent,
      intents: [intent],
      entities: entities.map(entity => ({
        name: entity.name, // usually the entity name, but can be modified
        type: entity.name, // when parameter name modified dialogflow doesn't give the original entity name
        value: entity.value,
        original: null,
        confidence: null,
        position: null,
        provider: 'dialogflow'
      }))
    }
  }

  async getCustomEntities() {
    return [] // Not implemented yet
  }
}
<commit_msg>fix(nlu): dialogflow isn't required (resolve #860)<commit_after>import _ from 'lodash'
import crypto from 'crypto'

import Provider from './base'

export default class DialogflowProvider extends Provider {
  constructor(config) {
    super({ ...config, name: 'dialogflow', entityKey: '@dialogflow' })

    this.projectId = this.config.googleProjectId

    // TODO: get rid of eval once we drop webpack for node-part (needed to overcome webpack compilation)
    const dialogflow = eval("require('dialogflow')") // eslint-disable-line no-eval

    this.agentClient = new dialogflow.AgentsClient()
    this.sessionClient = new dialogflow.SessionsClient()
  }

  _getSessionId(event) {
    let shortUserId = _.get(event, 'user.id') || ''
    if (shortUserId.length > 36) {
      shortUserId = crypto
        .createHash('md5')
        .update(shortUserId)
        .digest('hex')
    }
    return shortUserId
  }

  _resolveEntity(field) {
    const entity = field[field.kind]

    if (field.kind === 'stringValue' || field.kind === 'numberValue') {
      return entity
    } else if (field.kind === 'listValue') {
      return entity.values.map(v => this._resolveEntity(v))
    } else if (field.kind === 'structValue') {
      return _.mapValues(entity.fields, f => this._resolveEntity(f))
    } else {
      throw new Error('Not supported')
    }
  }

  /*******
  Public API
  *******/

  async init() {
    const [agent] = await this.agentClient.getAgent({ parent: this.agentClient.projectPath(this.projectId) })
    this.agent = agent
  }

  async sync() {
    throw new Error('Not implemented')
  }

  async checkSyncNeeded() {
    return false // Not implemented yet
  }

  async extract(event) {
    const request = {
      session: this.sessionClient.sessionPath(this.projectId, this._getSessionId(event)),
      queryInput: {
        text: {
          text: event.text,
          languageCode: this.agent.defaultLanguageCode
        }
      }
    }
    const detection = await this.sessionClient.detectIntent(request)
    const { queryResult } = detection[0]
    const intent = {
      name: queryResult.intent.displayName,
      confidence: queryResult.intentDetectionConfidence,
      provider: 'dialogflow'
    }
    const entities = _.map(queryResult.parameters.fields, (v, k) => ({ name: k, value: this._resolveEntity(v) }))

    return {
      intent,
      intents: [intent],
      entities: entities.map(entity => ({
        name: entity.name, // usually the entity name, but can be modified
        type: entity.name, // when parameter name modified dialogflow doesn't give the original entity name
        value: entity.value,
        original: null,
        confidence: null,
        position: null,
        provider: 'dialogflow'
      }))
    }
  }

  async getCustomEntities() {
    return [] // Not implemented yet
  }
}
<|endoftext|>


<commit_before>/**
 * Script to parse and write data for a single season
 * This is supposed to be called in child processes via the main parse script
 */

const fs = require('fs-extra')
const path = require('path')
const utils = require('./utils')
const fct = require('flusight-csv-tools')
const moize = require('moize')

// Directory with CSVs
const DATA_DIR = './data'

// Directory for output JSONs
const OUT_DIR = './src/assets/data'

// Look for seasons in the data directory
const SEASONS = utils.getSubDirectories(DATA_DIR)

// Take input season from command line argument
const SEASON_ID = parseInt(process.argv[2])
const SEASON = `${SEASON_ID}-${SEASON_ID + 1}`
const MODELS_DIR = utils.getSubDirectories(path.join(DATA_DIR, SEASON))

/**
 * Return season name for writing files
 */
function getSeasonName () {
  if (SEASONS.indexOf(SEASON) === SEASONS.length - 1) {
    return 'latest'
  } else {
    return SEASON
  }
}

async function writeSeasonFile (data) {
  await utils.writeJSON(path.join(OUT_DIR, `season-${getSeasonName()}.json`), data)
}

async function writeScoresFile (data) {
  await utils.writeJSON(path.join(OUT_DIR, `scores-${getSeasonName()}.json`), data)
}

async function writeDistsFile (data, regionId) {
  let distsDir = path.join(OUT_DIR, 'distributions')
  await fs.ensureDir(distsDir)

  let outputFile
  if (regionId === 'nat') {
    // We only use latest identifier in the latest AND nat data
    outputFile = path.join(distsDir, `season-${getSeasonName()}-nat.json`)
  } else {
    outputFile = path.join(distsDir, `season-${SEASON}-${regionId}.json`)
  }

  await utils.writeJSON(outputFile, data)
}

/**
 * Return region data with nulls filled in for missing epiweeks
 */
function parseRegionActual (seasonData, regionId) {
  let regionSubset = seasonData[regionId]

  let epiweeks = fct.utils.epiweek.seasonEpiweeks(SEASON_ID)
  return epiweeks.map(ew => {
    let ewData = regionSubset.find(({ epiweek }) => epiweek === ew)
    // Rename keys to match the expectations of flusight
    // and extend by filling in missing epiweeks
    if (ewData) {
      return {
        week: ewData.epiweek,
        actual: ewData.wili,
        lagData: ewData.lagData.map(({ lag, wili }) => { return { lag, value: wili } })
      }
    } else {
      return {
        week: ew,
        actual: null,
        lagData: []
      }
    }
  })
}

/**
 * Return fct csv object for given specifications using the cache
 */
const getCsv = moize(function (modelPath, epiweek) {
  let modelId = path.basename(modelPath)
  return new fct.Csv(path.join(modelPath, epiweek + '.csv'), epiweek, modelId)
})

/**
 * Return csv score using cache
 */
const getCsvScore = moize(async function (csv) {
  return await fct.score.score(csv)
}, { isPromise: true })

/**
 * Return formatted point data for the region using the cvs object
 */
function parsePointData (csv, regionId) {
  let seasonEpiweeks = fct.utils.epiweek.seasonEpiweeks(SEASON_ID)

  function getTargetData (target) {
    let cis = [90, 50]
    let point = csv.getPoint(target, regionId)
    let ranges = cis.map(c => csv.getConfidenceRange(target, regionId, c))
    let low = ranges.map(r => r[0])
    let high = ranges.map(r => r[1])

    if (['peak-wk', 'onset-wk'].indexOf(target) > -1) {
      // Return indices for time based targets
      point = seasonEpiweeks.indexOf(point)
      high = high.map(d => seasonEpiweeks.indexOf(d))
      low = low.map(d => seasonEpiweeks.indexOf(d))
    }

    return { point, low, high }
  }

  return {
    series: ['1-ahead', '2-ahead', '3-ahead', '4-ahead'].map(getTargetData),
    peakValue: getTargetData('peak'),
    peakTime: getTargetData('peak-wk'),
    onsetTime: getTargetData('onset-wk')
  }
}

/**
 * Return bin data for the given region using the csv object
 */
function parseBinData (csv, regionId) {
  function getTargetData (target) {
    let bins = csv.getBins(target, regionId)

    // There are different types of bins that we need to consider
    if (bins.length === 131) {
      // These are regular, new style, wili bins with last one being
      // [13, 100] which we skip
      return { bins: fct.utils.bins.sliceSumBins(bins.slice(0, -1), 5).map(b => b[2]) }
    } else if (bins.length === 27) {
      // These are old style wili bins with last one being [13, 100] which
      // we skip
      return { bins: bins.slice(0, -1).map(b => b[2]) }
    } else if (target === 'peak-wk') {
      return { bins: bins.map(b => b[2]) }
    } else if (target === 'onset-wk') {
      // We skip the none bin
      return { bins: bins.slice(0, -1).map(b => b[2]) }
    } else {
      throw new Error(`Unknown bin size ${bins.length} in parseBinData for ${target}, ${regionId}`)
    }
  }

  return {
    series: ['1-ahead', '2-ahead', '3-ahead', '4-ahead'].map(getTargetData),
    peakValue: getTargetData('peak'),
    peakTime: getTargetData('peak-wk'),
    onsetTime: getTargetData('onset-wk')
  }
}

/**
 * Return formatted data from the csv for a region
 * Each value (point, bin, score) is in the following structure
 * - series: [{}, {}, {}, {}]
 * - peakValue: {}
 * - peakTime: {}
 * - onsetTime: {}
 */
async function parseCsv (csv, regionId) {
  return {
    pointData: parsePointData(csv, regionId),
    binData: parseBinData(csv, regionId),
    scoreData: (await getCsvScore(csv))[regionId]
  }
}

/**
 * Return formatted data for the complete model with given region
 */
async function parseModelDir (modelPath, regionId) {
  let modelId = path.basename(modelPath)
  let availableEpiweeks = utils.getWeekFiles(modelPath)
  let modelMeta = utils.getModelMeta(modelPath)

  let pointPredictions = []
  let binPredictions = []
  let scores = []

  for (let epiweek of fct.utils.epiweek.seasonEpiweeks(SEASON_ID)) {
    if (availableEpiweeks.indexOf(epiweek) === -1) {
      // Prediction not available for this week, return null
      pointPredictions.push(null)
      binPredictions.push(null)
    } else {
      let { pointData, binData, scoreData } = await parseCsv(getCsv(modelPath, epiweek), regionId)
      pointPredictions.push(pointData)
      binPredictions.push(binData)
      scores.push(scoreData)
    }
  }

  return {
    pointData: {
      id: modelId,
      meta: modelMeta,
      predictions: pointPredictions
    },
    distsData: {
      id: modelId,
      predictions: binPredictions
    },
    scoresData: {
      id: modelId,
      scores: utils.aggregateScores(scores)
    }
  }
}

/**
 * Generate data files for the provided seasonData and using the
 * submission files in dataDir
 */
async function generateFiles (seasonData) {
  // Output to be written in file season-{season}.json
  let seasonOut = {
    seasonId: SEASON, // NOTE: This id is full xxxx-yyyy type id
    regions: []
  }

  // Output to be written in file scores-{season}.json
  let scoresOut = {
    seasonId: SEASON,
    regions: []
  }

  // Output to be written in file distributions/season-{season}-{region}.json
  let distsOut = []

  let regionPointData, regionDistsData, regionScoresData

  for (let regionId of fct.meta.regionIds) {
    regionPointData = []
    regionDistsData = []
    regionScoresData = []

    for (let model of MODELS_DIR) {
      let modelPath = path.join(DATA_DIR, SEASON, model)
      let { pointData, distsData, scoresData } = await parseModelDir(modelPath, regionId)
      regionPointData.push(pointData)
      regionDistsData.push(distsData)
      regionScoresData.push(scoresData)
    }

    seasonOut.regions.push({
      id: regionId,
      actual: parseRegionActual(seasonData, regionId),
      models: regionPointData,
      baseline: await fct.truth.getBaseline(regionId, SEASON_ID)
    })

    distsOut.push({
      seasonId: SEASON,
      regionId: regionId,
      models: regionDistsData
    })

    scoresOut.regions.push({
      id: regionId,
      models: regionScoresData
    })
  }

  await Promise.all([
    writeSeasonFile(seasonOut),
    writeScoresFile(scoresOut),
    ...distsOut.map(d => writeDistsFile(d, d.regionId))
  ])
  console.log(` Data files for season ${SEASON} written.`)
}

// Entry point
fct.truth.getSeasonDataAllLags(SEASON_ID)
  .then(sd => generateFiles(sd))
  .then(() => { console.log('All done') })
  .catch(e => {
    console.log(e)
    process.exit(1)
  })
<commit_msg>add logging to stop Travis stalling issue<commit_after>/**
 * Script to parse and write data for a single season
 * This is supposed to be called in child processes via the main parse script
 */

const fs = require('fs-extra')
const path = require('path')
const utils = require('./utils')
const fct = require('flusight-csv-tools')
const moize = require('moize')

// Directory with CSVs
const DATA_DIR = './data'

// Directory for output JSONs
const OUT_DIR = './src/assets/data'

// Look for seasons in the data directory
const SEASONS = utils.getSubDirectories(DATA_DIR)

// Take input season from command line argument
const SEASON_ID = parseInt(process.argv[2])
const SEASON = `${SEASON_ID}-${SEASON_ID + 1}`
const MODELS_DIR = utils.getSubDirectories(path.join(DATA_DIR, SEASON))

/**
 * Return season name for writing files
 */
function getSeasonName () {
  if (SEASONS.indexOf(SEASON) === SEASONS.length - 1) {
    return 'latest'
  } else {
    return SEASON
  }
}

async function writeSeasonFile (data) {
  await utils.writeJSON(path.join(OUT_DIR, `season-${getSeasonName()}.json`), data)
}

async function writeScoresFile (data) {
  await utils.writeJSON(path.join(OUT_DIR, `scores-${getSeasonName()}.json`), data)
}

async function writeDistsFile (data, regionId) {
  let distsDir = path.join(OUT_DIR, 'distributions')
  await fs.ensureDir(distsDir)

  let outputFile
  if (regionId === 'nat') {
    // We only use latest identifier in the latest AND nat data
    outputFile = path.join(distsDir, `season-${getSeasonName()}-nat.json`)
  } else {
    outputFile = path.join(distsDir, `season-${SEASON}-${regionId}.json`)
  }

  await utils.writeJSON(outputFile, data)
}

/**
 * Return region data with nulls filled in for missing epiweeks
 */
function parseRegionActual (seasonData, regionId) {
  let regionSubset = seasonData[regionId]

  let epiweeks = fct.utils.epiweek.seasonEpiweeks(SEASON_ID)
  return epiweeks.map(ew => {
    let ewData = regionSubset.find(({ epiweek }) => epiweek === ew)
    // Rename keys to match the expectations of flusight
    // and extend by filling in missing epiweeks
    if (ewData) {
      return {
        week: ewData.epiweek,
        actual: ewData.wili,
        lagData: ewData.lagData.map(({ lag, wili }) => { return { lag, value: wili } })
      }
    } else {
      return {
        week: ew,
        actual: null,
        lagData: []
      }
    }
  })
}

/**
 * Return fct csv object for given specifications using the cache
 */
const getCsv = moize(function (modelPath, epiweek) {
  let modelId = path.basename(modelPath)
  console.log(path.join(modelPath, epiweek + '.csv'))
  return new fct.Csv(path.join(modelPath, epiweek + '.csv'), epiweek, modelId)
})

/**
 * Return csv score using cache
 */
const getCsvScore = moize(async function (csv) {
  return await fct.score.score(csv)
}, { isPromise: true })

/**
 * Return formatted point data for the region using the cvs object
 */
function parsePointData (csv, regionId) {
  let seasonEpiweeks = fct.utils.epiweek.seasonEpiweeks(SEASON_ID)

  function getTargetData (target) {
    let cis = [90, 50]
    let point = csv.getPoint(target, regionId)
    let ranges = cis.map(c => csv.getConfidenceRange(target, regionId, c))
    let low = ranges.map(r => r[0])
    let high = ranges.map(r => r[1])

    if (['peak-wk', 'onset-wk'].indexOf(target) > -1) {
      // Return indices for time based targets
      point = seasonEpiweeks.indexOf(point)
      high = high.map(d => seasonEpiweeks.indexOf(d))
      low = low.map(d => seasonEpiweeks.indexOf(d))
    }

    return { point, low, high }
  }

  return {
    series: ['1-ahead', '2-ahead', '3-ahead', '4-ahead'].map(getTargetData),
    peakValue: getTargetData('peak'),
    peakTime: getTargetData('peak-wk'),
    onsetTime: getTargetData('onset-wk')
  }
}

/**
 * Return bin data for the given region using the csv object
 */
function parseBinData (csv, regionId) {
  function getTargetData (target) {
    let bins = csv.getBins(target, regionId)

    // There are different types of bins that we need to consider
    if (bins.length === 131) {
      // These are regular, new style, wili bins with last one being
      // [13, 100] which we skip
      return { bins: fct.utils.bins.sliceSumBins(bins.slice(0, -1), 5).map(b => b[2]) }
    } else if (bins.length === 27) {
      // These are old style wili bins with last one being [13, 100] which
      // we skip
      return { bins: bins.slice(0, -1).map(b => b[2]) }
    } else if (target === 'peak-wk') {
      return { bins: bins.map(b => b[2]) }
    } else if (target === 'onset-wk') {
      // We skip the none bin
      return { bins: bins.slice(0, -1).map(b => b[2]) }
    } else {
      throw new Error(`Unknown bin size ${bins.length} in parseBinData for ${target}, ${regionId}`)
    }
  }

  return {
    series: ['1-ahead', '2-ahead', '3-ahead', '4-ahead'].map(getTargetData),
    peakValue: getTargetData('peak'),
    peakTime: getTargetData('peak-wk'),
    onsetTime: getTargetData('onset-wk')
  }
}

/**
 * Return formatted data from the csv for a region
 * Each value (point, bin, score) is in the following structure
 * - series: [{}, {}, {}, {}]
 * - peakValue: {}
 * - peakTime: {}
 * - onsetTime: {}
 */
async function parseCsv (csv, regionId) {
  return {
    pointData: parsePointData(csv, regionId),
    binData: parseBinData(csv, regionId),
    scoreData: (await getCsvScore(csv))[regionId]
  }
}

/**
 * Return formatted data for the complete model with given region
 */
async function parseModelDir (modelPath, regionId) {
  let modelId = path.basename(modelPath)
  let availableEpiweeks = utils.getWeekFiles(modelPath)
  let modelMeta = utils.getModelMeta(modelPath)

  let pointPredictions = []
  let binPredictions = []
  let scores = []

  for (let epiweek of fct.utils.epiweek.seasonEpiweeks(SEASON_ID)) {
    if (availableEpiweeks.indexOf(epiweek) === -1) {
      // Prediction not available for this week, return null
      pointPredictions.push(null)
      binPredictions.push(null)
    } else {
      let { pointData, binData, scoreData } = await parseCsv(getCsv(modelPath, epiweek), regionId)
      pointPredictions.push(pointData)
      binPredictions.push(binData)
      scores.push(scoreData)
    }
  }

  return {
    pointData: {
      id: modelId,
      meta: modelMeta,
      predictions: pointPredictions
    },
    distsData: {
      id: modelId,
      predictions: binPredictions
    },
    scoresData: {
      id: modelId,
      scores: utils.aggregateScores(scores)
    }
  }
}

/**
 * Generate data files for the provided seasonData and using the
 * submission files in dataDir
 */
async function generateFiles (seasonData) {
  // Output to be written in file season-{season}.json
  let seasonOut = {
    seasonId: SEASON, // NOTE: This id is full xxxx-yyyy type id
    regions: []
  }

  // Output to be written in file scores-{season}.json
  let scoresOut = {
    seasonId: SEASON,
    regions: []
  }

  // Output to be written in file distributions/season-{season}-{region}.json
  let distsOut = []

  let regionPointData, regionDistsData, regionScoresData

  for (let regionId of fct.meta.regionIds) {
    regionPointData = []
    regionDistsData = []
    regionScoresData = []

    for (let model of MODELS_DIR) {
      let modelPath = path.join(DATA_DIR, SEASON, model)
      let { pointData, distsData, scoresData } = await parseModelDir(modelPath, regionId)
      regionPointData.push(pointData)
      regionDistsData.push(distsData)
      regionScoresData.push(scoresData)
    }

    seasonOut.regions.push({
      id: regionId,
      actual: parseRegionActual(seasonData, regionId),
      models: regionPointData,
      baseline: await fct.truth.getBaseline(regionId, SEASON_ID)
    })

    distsOut.push({
      seasonId: SEASON,
      regionId: regionId,
      models: regionDistsData
    })

    scoresOut.regions.push({
      id: regionId,
      models: regionScoresData
    })
  }

  await Promise.all([
    writeSeasonFile(seasonOut),
    writeScoresFile(scoresOut),
    ...distsOut.map(d => writeDistsFile(d, d.regionId))
  ])
  console.log(` Data files for season ${SEASON} written.`)
}

// Entry point
fct.truth.getSeasonDataAllLags(SEASON_ID)
  .then(sd => generateFiles(sd))
  .then(() => { console.log('All done') })
  .catch(e => {
    console.log(e)
    process.exit(1)
  })
<|endoftext|>


<commit_before>const WorkspaceConverter = require('../../app/utilities/WorkspaceConverter');
import { WORKSPACE_ID } from '../../app/constants';

describe('conversion of empty workspace', ()=>{
  const defaultWorkspace = {
    componentCounter: 0,
    activeComponent: '0',
    components: {
      workspace: {
        id: 'workspace',
        children: [],
        events: {},
        props: {
          style: {
            width: '602.40625px',
            height: '617.75px'
          }
        }
      }
    },
    state: {},
    methods: '/*Anything you type in here will be appended to App.js as a \n method. you can then attach them as event handlers, logic handlers, etc. \n make sure to append an "@" symbol following the closing bracket of your method!*/',
    methodNames: []
  };
  it('will ', () => {
    const wc = new WorkspaceConverter(defaultWorkspace);
    let result = wc.convert();
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with two components', ()=>{
  const flatWorkspace = {
    componentCounter: 2,
    activeComponent: '0',
    components: {
      '0': {
        id: 0,
        name: 'BlackBox',
        children: [],
        parentID: 'workspace',
        props: {
          style: {
            position: 'absolute',
            height: '100px',
            width: '100px',
            display: 'inline-block',
            backgroundColor: 'black',
            overflow: 'auto'
          }
        },
        events: {
          onClick: '()=>{console.log("test")}'
        }
      },
      '1': {
        id: 1,
        name: 'BlueBox',
        children: [],
        parentID: 'workspace',
        props: {
          style: {
            position: 'absolute',
            height: '100px',
            width: '100px',
            display: 'inline-block',
            backgroundColor: 'blue',
            overflow: 'auto'
          }
        },
        events: {}
      },
      workspace: {
        id: 'workspace',
        children: [
          0,
          1
        ],
        events: {},
        props: {
          style: {
            width: '602.40625px',
            height: '617.75px'
          }
        }
      }
    },
    state: {},
    methods: '/*Anything you type in here will be appended to App.js as a \n method. you can then attach them as event handlers, logic handlers, etc. \n make sure to append an "@" symbol following the closing bracket of your method!*/',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(flatWorkspace);
    let result = wc.convert(flatWorkspace);
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with one nested components', ()=>{
  const flatWorkspace = {
    componentCounter: 2,
    activeComponent: '0',
    components: {
      '0': {
        id: 0,
        name: 'BlackBox',
        children: [ 1 ],
        parentID: 'workspace',
        props: {
          style: {
            position: 'absolute',
            height: '100px',
            width: '100px',
            display: 'inline-block',
            backgroundColor: 'black',
            overflow: 'auto'
          }
        },
        events: {}
      },
      '1': {
        id: 1,
        name: 'BlueBox',
        children: [],
        parentID: 'workspace',
        props: {
          style: {
            position: 'absolute',
            height: '100px',
            width: '100px',
            display: 'inline-block',
            backgroundColor: 'blue',
            overflow: 'auto'
          }
        },
        events: {}
      },
      workspace: {
        id: 'workspace',
        children: [ 0 ],
        events: {},
        props: {
          style: {
            width: '602.40625px',
            height: '617.75px'
          }
        }
      }
    },
    state: {},
    methods: '/*Anything you type in here will be appended to App.js as a \n method. you can then attach them as event handlers, logic handlers, etc. \n make sure to append an "@" symbol following the closing bracket of your method!*/',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(flatWorkspace);
    let result = wc.convert(flatWorkspace);
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with one nested components and the child has events', ()=>{
  const flatWorkspace = {
    componentCounter: 2,
    activeComponent: '0',
    components: {
      '0': {
        id: 0,
        name: 'BlackBox',
        children: [ 1 ],
        parentID: 'workspace',
        props: {
          style: {
            position: 'absolute',
            height: '100px',
            width: '100px',
            display: 'inline-block',
            backgroundColor: 'black',
            overflow: 'auto'
          }
        },
        events: {
          onClick: '()=>{console.log("test")}'
        }
      },
      '1': {
        id: 1,
        name: 'BlueBox',
        children: [],
        parentID: 'workspace',
        props: {
          style: {
            position: 'absolute',
            height: '100px',
            width: '100px',
            display: 'inline-block',
            backgroundColor: 'blue',
            overflow: 'auto'
          }
        },
        events: {
          onClick: '()=>{console.log("hello")'
        }
      },
      workspace: {
        id: 'workspace',
        children: [ 0 ],
        events: {},
        props: {
          style: {
            width: '602.40625px',
            height: '617.75px'
          }
        }
      }
    },
    state: {},
    methods: '/*Anything you type in here will be appended to App.js as a \n method. you can then attach them as event handlers, logic handlers, etc. \n make sure to append an "@" symbol following the closing bracket of your method!*/',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(flatWorkspace);
    let result = wc.convert(flatWorkspace);
    expect(result).toMatchSnapshot();
  });
});<commit_msg>Add tests for event, method and props<commit_after>const WorkspaceConverter = require('../../app/utilities/WorkspaceConverter');
import { WORKSPACE_ID } from '../../app/constants';

describe('conversion of empty workspace', ()=>{
  const defaultWorkspace = {
    componentCounter: 0,
    activeComponent: 'workspace',
    components: {
      workspace: {
        id: 'workspace',
        children: [],
        events: {},
        props: {
          style: {
            width: '1558px',
            height: '0px'
          }
        }
      }
    },
    state: {},
    methods: '',
    methodNames: []
  };
  it('will generate state', () => {
    const wc = new WorkspaceConverter(defaultWorkspace);
    let result = wc.convert();
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with two components', ()=>{
  const flatWorkspace = {
    componentCounter: 2,
    activeComponent: '0',
    components: {
      '0': {
        events: {},
        children: [],
        props: {
          style: {
            width: '100px',
            height: '100px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'black',
            border: '1px solid lightgreen',
            left: 176,
            top: 35
          }
        },
        name: 'BlackBox',
        parentID: 'workspace',
        id: 0
      },
      '1': {
        events: {},
        children: [],
        props: {
          style: {
            width: '50px',
            height: '73px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'blue',
            left: 0,
            top: 0
          }
        },
        name: 'BlueBox',
        parentID: 'workspace',
        id: 1
      },
      workspace: {
        id: 'workspace',
        children: [
          0,
          1
        ],
        events: {},
        props: {
          style: {
            width: '447px',
            height: '638.921875px'
          }
        }
      }
    },
    state: {},
    methods: '',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(flatWorkspace);
    let result = wc.convert(flatWorkspace);
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with one nested component', ()=>{
  const nestedWorkspace = {
    componentCounter: 2,
    activeComponent: '0',
    components: {
      '0': {
        events: {},
        children: [ 1 ],
        props: {
          style: {
            width: '100px',
            height: '100px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'black',
            border: '1px solid lightgreen',
            left: 176,
            top: 35
          }
        },
        name: 'BlackBox',
        parentID: 'workspace',
        id: 0
      },
      '1': {
        events: {},
        children: [],
        props: {
          style: {
            width: '50px',
            height: '73px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'blue',
            left: 0,
            top: 0
          }
        },
        name: 'BlueBox',
        parentID: 0,
        id: 1
      },
      workspace: {
        id: 'workspace',
        children: [ 0 ],
        events: {},
        props: {
          style: {
            width: '447px',
            height: '638.921875px'
          }
        }
      }
    },
    state: {},
    methods: '',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(nestedWorkspace);
    let result = wc.convert();
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with one nested component and the child has event', ()=>{
  const nestedWorkspace = {
    componentCounter: 2,
    activeComponent: '1',
    components: {
      '0': {
        events: {},
        children: [
          1
        ],
        props: {
          style: {
            width: '197px',
            height: '202px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'black'
          }
        },
        name: 'BlackBox',
        parentID: 'workspace',
        id: 0
      },
      '1': {
        events: {
          onClick: '()=>console.log(\'hello\')'
        },
        children: [],
        props: {
          style: {
            width: '100px',
            height: '100px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'blue',
            left: 0,
            top: 0,
            border: '1px solid lightgreen'
          }
        },
        name: 'BlueBox',
        parentID: 0,
        id: 1
      },
      workspace: {
        id: 'workspace',
        children: [
          0
        ],
        events: {},
        props: {
          style: {
            width: '1558px',
            height: '0px'
          }
        }
      }
    },
    state: {},
    methods: '',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(nestedWorkspace);
    let result = wc.convert();
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with one nested component and the child has event attached to method', ()=>{
  const nestedWorkspaceMethod = {
    componentCounter: 2,
    activeComponent: '1',
    components: {
      '0': {
        events: {},
        children: [
          1
        ],
        props: {
          style: {
            width: '100px',
            height: '100px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'black'
          }
        },
        name: 'BlackBox',
        parentID: 'workspace',
        id: 0
      },
      '1': {
        events: {
          onClick: '()=>hello()'
        },
        children: [],
        props: {
          style: {
            width: '53px',
            height: '51px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'blue',
            border: '1px solid lightgreen',
            left: 0,
            top: 0
          }
        },
        name: 'BlueBox',
        parentID: 0,
        id: 1
      },
      workspace: {
        id: 'workspace',
        children: [
          0
        ],
        events: {},
        props: {
          style: {
            width: '1558px',
            height: '0px'
          }
        }
      }
    },
    state: {},
    methods: 'hello(){\n    console.log(\'hello\')\n}@\n',
    methodNames: [
      'hello'
    ]
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(nestedWorkspaceMethod);
    let result = wc.convert();
    expect(result).toMatchSnapshot();
  });
});

describe('conversion of workspace with one nested component and the child has prop', ()=>{
  const nestedWorkspaceProp = {
    componentCounter: 2,
    activeComponent: '1',
    components: {
      '0': {
        events: {},
        children: [
          1
        ],
        props: {
          style: {
            width: '100px',
            height: '100px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'black'
          }
        },
        name: 'BlackBox',
        parentID: 'workspace',
        id: 0
      },
      '1': {
        events: {},
        children: [],
        props: {
          style: {
            width: '50px',
            height: '56px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'blue',
            border: '1px solid lightgreen',
            left: 0,
            top: 0
          },
          key: '1'
        },
        name: 'BlueBox',
        parentID: 0,
        id: 1
      },
      workspace: {
        id: 'workspace',
        children: [
          0
        ],
        events: {},
        props: {
          style: {
            width: '587.40625px',
            height: '638.921875px'
          }
        }
      }
    },
    state: {},
    methods: '',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(nestedWorkspaceProp);
    let result = wc.convert();
    expect(result).toMatchSnapshot();
  });
});
describe('conversion of workspace with one nested component and the child has event handler associated to prop', ()=>{
  const nestedWorkspaceProp = {
    componentCounter: 2,
    activeComponent: '1',
    components: {
      '0': {
        events: {},
        children: [
          1
        ],
        props: {
          style: {
            width: '100px',
            height: '100px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'black'
          }
        },
        name: 'BlackBox',
        parentID: 'workspace',
        id: 0
      },
      '1': {
        events: {
          onClick: '()=>console.log(\'name\', name)'
        },
        children: [],
        props: {
          style: {
            width: '68px',
            height: '55px',
            position: 'absolute',
            display: 'inline-block',
            backgroundColor: 'blue',
            left: 0,
            top: 0,
            border: '1px solid lightgreen'
          },
          name: '\'jeff\''
        },
        name: 'BlueBox',
        parentID: 0,
        id: 1
      },
      workspace: {
        id: 'workspace',
        children: [
          0
        ],
        events: {},
        props: {
          style: {
            width: '587.40625px',
            height: '638.921875px'
          }
        }
      }
    },
    state: {},
    methods: '',
    methodNames: []
  };
  it('will generate 3 exportable texts', () => {
    const wc = new WorkspaceConverter(nestedWorkspaceProp);
    let result = wc.convert();
    expect(result).toMatchSnapshot();
  });
});<|endoftext|>


<commit_before>var gulp = require('gulp'),
    paths = require('../paths.js'),
    uglify = require('gulp-uglify');

gulp.task('build', ['concat'], function() {
    return gulp.src([paths.dist + '**/*.js'])
    .pipe(uglify())
    .pipe(gulp.dest(paths.dist));
});<commit_msg>fix gulp task build<commit_after>var gulp = require('gulp'),
    paths = require('../paths.js'),
    uglify = require('gulp-uglify');

gulp.task('build', ['browserify'], function() {
    return gulp.src([paths.dist + '**/*.js'])
    .pipe(uglify())
    .pipe(gulp.dest(paths.dist));
});<|endoftext|>


<commit_before>module.exports = function(config, specificOptions) {
  config.set({
    frameworks: ['jasmine'],
    autoWatch: true,
    logLevel: config.LOG_INFO,
    logColors: true,
    browsers: ['Chrome'],
    browserDisconnectTimeout: 10000,
    browserDisconnectTolerance: 2,

    // config for Travis CI
    sauceLabs: {
      testName: specificOptions.testName || 'AngularJS',
      startConnect: false,
      tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
    },

    // BrowserStack config for Travis CI
    browserStack: {
      startTunnel: false,
      project: 'AngularJS',
      name: specificOptions.testName,
      build: process.env.TRAVIS_BUILD_NUMBER,
      timeout: 600 // 10min
    },

    // For more browsers on Sauce Labs see:
    // https://saucelabs.com/docs/platforms/webdriver
    customLaunchers: {
      'SL_Chrome': {
        base: 'SauceLabs',
        browserName: 'chrome'
      },
      'SL_Firefox': {
        base: 'SauceLabs',
        browserName: 'firefox'
      },
      'SL_Safari': {
        base: 'SauceLabs',
        browserName: 'safari',
        platform: 'Mac 10.8',
        version: '6'
      },
      'SL_IE_8': {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 7',
        version: '8'
      },
      'SL_IE_9': {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 2008',
        version: '9'
      },
      'SL_IE_10': {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 2012',
        version: '10'
      },

      'BS_Chrome': {
        base: 'BrowserStack',
        browser: 'chrome',
        os: 'OS X',
        os_version: 'Mountain Lion'
      },
      'BS_Safari': {
        base: 'BrowserStack',
        browser: 'safari',
        os: 'OS X',
        os_version: 'Mountain Lion'
      },
      'BS_Firefox': {
        base: 'BrowserStack',
        browser: 'firefox',
        os: 'Windows',
        os_version: '8'
      },
      'BS_IE_8': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '8.0',
        os: 'Windows',
        os_version: '7'
      },
      'BS_IE_9': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '9.0',
        os: 'Windows',
        os_version: '7'
      },
      'BS_IE_10': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '10.0',
        os: 'Windows',
        os_version: '8'
      },
      'BS_IE_11': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '11.0',
        os: 'Windows',
        os_version: '8.1'
      }
    }
  });

  if (process.env.TRAVIS) {
    config.logLevel = config.LOG_DEBUG;
    config.transports = ['websocket', 'xhr-polling'];
    config.browserStack.build = 'TRAVIS ' + process.env.TRAVIS_BUILD_ID;

    // Debug logging into a file, that we print out at the end of the build.
    config.loggers.push({
      type: 'file',
      filename: process.env.LOGS_DIR + '/' + (specificOptions.logFile || 'karma.log')
    });
  }

  // Terrible hack to workaround inflexibility of log4js:
  // - ignore web-server's 404 warnings,
  // - ignore DEBUG logs (on Travis), we log them into a file instead.
  var IGNORED_404 = [
    '/favicon.ico',
    '/%7B%7BtestUrl%7D%7D',
    '/someSanitizedUrl',
    '/{{testUrl}}'
  ];
  var log4js = require('./node_modules/karma/node_modules/log4js');
  var layouts = require('./node_modules/karma/node_modules/log4js/lib/layouts');
  var originalConfigure = log4js.configure;
  log4js.configure = function(log4jsConfig) {
    var consoleAppender = log4jsConfig.appenders.shift();
    var originalResult = originalConfigure.call(log4js, log4jsConfig);
    var layout = layouts.layout(consoleAppender.layout.type, consoleAppender.layout);

    log4js.addAppender(function(log) {
      var msg = log.data[0];

      // ignore web-server's 404s
      if (log.categoryName === 'web-server' && log.level.levelStr === config.LOG_WARN &&
          IGNORED_404.some(function(ignoredLog) {return msg.indexOf(ignoredLog) !== -1})) {
        return;
      }

      // on Travis, ignore DEBUG statements
      if (process.env.TRAVIS && log.level.levelStr === config.LOG_DEBUG) {
        return;
      }

      console.log(layout(log));
    });

    return originalResult;
  };
};
<commit_msg>chore(travis): report both build number and id to BS<commit_after>module.exports = function(config, specificOptions) {
  config.set({
    frameworks: ['jasmine'],
    autoWatch: true,
    logLevel: config.LOG_INFO,
    logColors: true,
    browsers: ['Chrome'],
    browserDisconnectTimeout: 10000,
    browserDisconnectTolerance: 2,

    // config for Travis CI
    sauceLabs: {
      testName: specificOptions.testName || 'AngularJS',
      startConnect: false,
      tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
    },

    // BrowserStack config for Travis CI
    browserStack: {
      startTunnel: false,
      project: 'AngularJS',
      name: specificOptions.testName,
      build: process.env.TRAVIS_BUILD_NUMBER,
      timeout: 600 // 10min
    },

    // For more browsers on Sauce Labs see:
    // https://saucelabs.com/docs/platforms/webdriver
    customLaunchers: {
      'SL_Chrome': {
        base: 'SauceLabs',
        browserName: 'chrome'
      },
      'SL_Firefox': {
        base: 'SauceLabs',
        browserName: 'firefox'
      },
      'SL_Safari': {
        base: 'SauceLabs',
        browserName: 'safari',
        platform: 'Mac 10.8',
        version: '6'
      },
      'SL_IE_8': {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 7',
        version: '8'
      },
      'SL_IE_9': {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 2008',
        version: '9'
      },
      'SL_IE_10': {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 2012',
        version: '10'
      },

      'BS_Chrome': {
        base: 'BrowserStack',
        browser: 'chrome',
        os: 'OS X',
        os_version: 'Mountain Lion'
      },
      'BS_Safari': {
        base: 'BrowserStack',
        browser: 'safari',
        os: 'OS X',
        os_version: 'Mountain Lion'
      },
      'BS_Firefox': {
        base: 'BrowserStack',
        browser: 'firefox',
        os: 'Windows',
        os_version: '8'
      },
      'BS_IE_8': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '8.0',
        os: 'Windows',
        os_version: '7'
      },
      'BS_IE_9': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '9.0',
        os: 'Windows',
        os_version: '7'
      },
      'BS_IE_10': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '10.0',
        os: 'Windows',
        os_version: '8'
      },
      'BS_IE_11': {
        base: 'BrowserStack',
        browser: 'ie',
        browser_version: '11.0',
        os: 'Windows',
        os_version: '8.1'
      }
    }
  });

  if (process.env.TRAVIS) {
    config.logLevel = config.LOG_DEBUG;
    config.transports = ['websocket', 'xhr-polling'];
    config.browserStack.build = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';

    // Debug logging into a file, that we print out at the end of the build.
    config.loggers.push({
      type: 'file',
      filename: process.env.LOGS_DIR + '/' + (specificOptions.logFile || 'karma.log')
    });
  }

  // Terrible hack to workaround inflexibility of log4js:
  // - ignore web-server's 404 warnings,
  // - ignore DEBUG logs (on Travis), we log them into a file instead.
  var IGNORED_404 = [
    '/favicon.ico',
    '/%7B%7BtestUrl%7D%7D',
    '/someSanitizedUrl',
    '/{{testUrl}}'
  ];
  var log4js = require('./node_modules/karma/node_modules/log4js');
  var layouts = require('./node_modules/karma/node_modules/log4js/lib/layouts');
  var originalConfigure = log4js.configure;
  log4js.configure = function(log4jsConfig) {
    var consoleAppender = log4jsConfig.appenders.shift();
    var originalResult = originalConfigure.call(log4js, log4jsConfig);
    var layout = layouts.layout(consoleAppender.layout.type, consoleAppender.layout);

    log4js.addAppender(function(log) {
      var msg = log.data[0];

      // ignore web-server's 404s
      if (log.categoryName === 'web-server' && log.level.levelStr === config.LOG_WARN &&
          IGNORED_404.some(function(ignoredLog) {return msg.indexOf(ignoredLog) !== -1})) {
        return;
      }

      // on Travis, ignore DEBUG statements
      if (process.env.TRAVIS && log.level.levelStr === config.LOG_DEBUG) {
        return;
      }

      console.log(layout(log));
    });

    return originalResult;
  };
};
<|endoftext|>


<commit_before>import Ember from 'ember';
var assert = Ember.assert;
var $ = Ember.$;

// Do a little dance with Ember to create a function that can render
// components for the given application.
export function getRenderComponentFor(application) {
  var container = application.__container__;
  var componentLookup = container.lookup('component-lookup:main');

  return function renderComponent(name, attributes, element) {
    var component = componentLookup.lookupFactory(name, container);
    assert(`ember-islands could not find a component named "${name}" in your Ember appliction.`, component);
    component.create(attributes).replaceIn(element);
  };
}

function componentAttributes(element) {
  var attrs;
  var attrsJSON = element.getAttribute('data-attrs');

  if (attrsJSON) {
    attrs = JSON.parse(attrsJSON);
  } else {
    attrs = {};
  }

  attrs.innerContent = element.innerHTML;
  return attrs;
}

export default function renderComponents(application) {
  var renderComponent = getRenderComponentFor(application);

  $('[data-component]').each(function() {
    var name = this.getAttribute('data-component');
    var attrs = componentAttributes(this);
    renderComponent(name, attrs, this);
  });
}
<commit_msg>Add fix for `replaceIn`<commit_after>import Ember from 'ember';
var assert = Ember.assert;
var $ = Ember.$;

// Do a little dance with Ember to create a function that can render
// components for the given application.
export function getRenderComponentFor(application) {
  var container = application.__container__;
  var componentLookup = container.lookup('component-lookup:main');

  return function renderComponent(name, attributes, element) {
    var component = componentLookup.lookupFactory(name, container);
    assert(`ember-islands could not find a component named "${name}" in your Ember appliction.`, component);

    // Temporary fix for bug in `replaceIn`
    $(element).empty();
    component.create(attributes).replaceIn(element);
  };
}

function componentAttributes(element) {
  var attrs;
  var attrsJSON = element.getAttribute('data-attrs');

  if (attrsJSON) {
    attrs = JSON.parse(attrsJSON);
  } else {
    attrs = {};
  }

  attrs.innerContent = element.innerHTML;
  return attrs;
}

export default function renderComponents(application) {
  var renderComponent = getRenderComponentFor(application);

  $('[data-component]').each(function() {
    var name = this.getAttribute('data-component');
    var attrs = componentAttributes(this);
    renderComponent(name, attrs, this);
  });
}
<|endoftext|>


<commit_before>import React from 'react'
import { shallow } from 'enzyme'

import Message from './message'
import Wrapper from './message-wrapper'

describe('fyndiq-component-message MessageWrapper', () => {
  beforeEach(() => {
    Wrapper.instance = null
  })

  it('should keep an instance of the running wrapper', () => {
    shallow(<Wrapper />)
    expect(Wrapper.instance).not.toBe(null)
  })

  it('should warn the user if there are 2 instances of wrapper', () => {
    console.warn = jest.fn()
    shallow(<Wrapper />)
    shallow(<Wrapper />)
    expect(console.warn).toHaveBeenCalled()
  })

  it('should remove messages internally', () => {
    const component = shallow(<Wrapper />)
    Wrapper.addMessage(<Message>New message</Message>)
    component.update()
    component.find(Message).simulate('close')
    component.update()
    expect(component.find(Message).exists()).toBe(false)
  })

  describe('static addMessage', () => {
    it('should have a static addMessage function', () => {
      const component = shallow(<Wrapper />)
      Wrapper.addMessage(<Message>New message</Message>)
      component.update()
      expect(component.find(Message).exists()).toBe(true)
    })

    it('should throw if there is no instance of Wrapper', () => {
      expect(() => Wrapper.addMessage(<Message>New message</Message>)).toThrow()
    })

    it('should have the proper structure', () => {
      Math.random = jest.fn(() => 'RANDOM-ID')
      const component = shallow(<Wrapper />)
      Wrapper.addMessage(<Message>New message 1</Message>)
      Wrapper.addMessage(<Message>New message 2</Message>)
      component.update()
      expect(component).toMatchSnapshot()
    })
  })
})
<commit_msg>:white_check_mark: Add test for empty message wrapper<commit_after>import React from 'react'
import { shallow } from 'enzyme'

import Message from './message'
import Wrapper from './message-wrapper'

describe('fyndiq-component-message MessageWrapper', () => {
  beforeEach(() => {
    Wrapper.instance = null
  })

  it('should keep an instance of the running wrapper', () => {
    shallow(<Wrapper />)
    expect(Wrapper.instance).not.toBe(null)
  })

  it('should warn the user if there are 2 instances of wrapper', () => {
    console.warn = jest.fn()
    shallow(<Wrapper />)
    shallow(<Wrapper />)
    expect(console.warn).toHaveBeenCalled()
  })

  it('should remove messages internally', () => {
    const component = shallow(<Wrapper />)
    Wrapper.addMessage(<Message>New message</Message>)
    component.update()
    component.find(Message).simulate('close')
    component.update()
    expect(component.find(Message).exists()).toBe(false)
  })

  it('should not render anything if there is no message', () => {
    expect(shallow(<Wrapper />).isEmptyRender()).toBe(true)
  })

  describe('static addMessage', () => {
    it('should have a static addMessage function', () => {
      const component = shallow(<Wrapper />)
      Wrapper.addMessage(<Message>New message</Message>)
      component.update()
      expect(component.find(Message).exists()).toBe(true)
    })

    it('should throw if there is no instance of Wrapper', () => {
      expect(() => Wrapper.addMessage(<Message>New message</Message>)).toThrow()
    })

    it('should have the proper structure', () => {
      Math.random = jest.fn(() => 'RANDOM-ID')
      const component = shallow(<Wrapper />)
      Wrapper.addMessage(<Message>New message 1</Message>)
      Wrapper.addMessage(<Message>New message 2</Message>)
      component.update()
      expect(component).toMatchSnapshot()
    })
  })
})
<|endoftext|>


<commit_before>var theCard = "";
var specialChars = "";
var cornerText = "";
var hasCrypto = false;

window.onload = init;

function BoundaryException(value, desiredOperation) {
   this.value = value;
   this.message = "is to big for" + desiredOperation;
   this.toString = function() {
      return this.value + this.message
   };
}

/*
 * jQueryUI functions
 */

$(function() {
  $( "#generateTable" )
    .button()
    .click(function() {
    fillTheCard();});
  $( "#printCard" )
    .button()
    .click(function() {
    window.print();});
  $( "#tabs" )
    .tabs( {heightStyle: "auto"} );
  $( "#de, #en" ).click(function() {
    lang = $(this).attr("id");
    translateView();
    });
  $( "#noCryptoWarning" ).dialog({
     modal: true,
	 autoOpen: false,
     buttons: {
     Ok: function() {
         $( this ).dialog( "close" );
       }
      }
    });
  });

/*
 * Translations, thanks to Sebastian Fuchs for describing the usage of
 * data attributes as an translation tool.
 *
 * http://foxable.net/2014/04/client-seitige-mehrsprachigkeit-mit-jquery/
 *
 */

var lang="de"; // current language

// translation data
var langData = {
    de: {
        PASSWORDCARD: "Passwort-Karte",
	SHORT_DESCRIPTION: "Diese Anwendung erzeugt eine Karte für Passwörter.",
	THE_CARD: "Die Karte",
	SETTINGS: "Einstellungen",
	SPECIAL_CHARS: "Sonderzeichen:",
	CORNER_TEXT: "Eckentext:",
	GENERATE_CARD: "Karte erzeugen",
	PRINT_CARD: "Karte drucken",
	NO_CRYPTO_WARNING: "Ihr Browser unterstützt keine kryptografisch starken Zufallszahlen.",
	EXPLANATIONS_TEXT: "Lorem ipsum ...",
	EXPLANATION: "Erläuterung"
    },
    en: {
        PASSWORDCARD: "Passwordcard",
	SHORT_DESCRIPTION: "This application generates a card for passwords.",
	THE_CARD: "The card",
	SETTINGS: "Settings",
	SPECIAL_CHARS: "Special chars:",
	CORNER_TEXT: "Corner text:",
	GENERATE_CARD: "Generate card",
	PRINT_CARD: "Print card",
    NO_CRYPTO_WARNING: "Your browser doesn't support cryptographically strong random numbers.",
	EXPLANATIONS_TEXT: "Lorem ipsum ...",
	EXPLANATION: "Explanation"
    }
};

// returns the translation for the given
// language key (depending on the current language)
function translate(key) {
    if (langData.hasOwnProperty(lang)) {
        if (langData[lang].hasOwnProperty(key)) {
            return langData[lang][key];
        } else {
            console.log("Unknown language key: " + key);
            return "Undefined";
        }
    } else {
        console.log("Unknown language: " + lang);
        return "Undefined";
    }
};

// translate all multi-lingual DOM elements
function translateView() {
    $(".totranslate").each(function() {
        var key = $(this).attr("data-totranslate");

        $(this).text(translate(key));
    });
};

function initializeTheCard() {

  // create header:
  var singleRow = "";

  singleRow = document.createElement("tr");
  singleRow.innerHTML =
  '
' +
  '	' +
  'ABC	' +
  'DEF	' +
  'GHI	' +
  'JKL	' +
  'MNO	' +
  'PQR	' +
  'STU	' +
  'VWX	' +
  'YZ.	' +
  '	';
  singleRow.setAttribute("class", "tableHead");
  theCard.appendChild(singleRow);
}

function initializeSettings() {
  specialChars = document.getElementById("specialChars");
  cornerText = document.getElementById("cornerText");
  specialChars.value = "+-#.,!§$%&/()=?;:_'*";
  cornerText.value = "TST";
}

function checkCryptoSupport() {
 hasCrypto = window.crypto ? true : false;
 if (hasCrypto == false) {
   $( "#noCryptoWarning" ).dialog( "open" );
 }
}

function init() {

  checkCryptoSupport();
  theCard = document.getElementById("theCard");
  initializeSettings();
  translateView();
  initializeTheCard();
  fillTheCard();
}

function fillTheCard() {
  /*
   * this function fills the card with random contents.
   * As a first step, we get rid off any contents left from
   * former runs.
   *
   * The headerline is left untouched.
   */

  // get all the rows and count them:
  var rowsOfTheCard = document.getElementsByClassName("generatedRow");
  var countRows = rowsOfTheCard.length;

  for (var i = 0; i < countRows; i++) {
    rowsOfTheCard[0].parentNode.removeChild(rowsOfTheCard[0]);

  }

  // adjust the corner text:
  var cornerCell = document.getElementById("cornerTextOnCard");
  cornerCell.innerHTML = cornerText.value;

  /* the first cell in each row is a prefix number, simply counted
   * from 1 to 9.
   */

  var prefix = "";

  /*
   * and now: generate 9 rows, each starting with a new prefix.
   * The "createSingleRow" returns one new row of cells with
   * 10 entries.
   */
  for (var i=1; i < 10; i++) {
    prefix = i.toString();
    theCard.appendChild(createSingleRow(prefix));
  }
}

function createSingleRow(prefix) {
  /*
   * this function generates a single row filled with random character
   * triples.
   *
   * The first cell contains the prefix given.
   */

  var singleRow = document.createElement("tr");
  var prefixCell = document.createElement("td");

  prefixCell.innerHTML = '' + prefix + '	';
  prefixCell.setAttribute("class", "singleCell");

  singleRow.appendChild(prefixCell);

  for (var i = 0; i < 9; i++) {
    singleRow.appendChild(createSingleCell());
  }

  singleRow.setAttribute("class", "generatedRow");
  return singleRow;
}

function createSingleCell() {
  var singleCell = document.createElement("td");

  singleCell.innerHTML = '' + giveTriple() + '	';
  singleCell.setAttribute("class", "singleCell");

  return singleCell;

}

function giveTriple() {
  /*
   * initialization of the output string: start with an empty string
   */
    var outputString = "";

  /* there is an urn which contains all four types of characters:
    U for uppercase letters
    l for lowercase letters
    0 for digits and
    # for special characters
   */

  var urn = "Ul0#";

  /*
   * we choose one of the four types in the urn at random:
   */
  var toDraw = giveRandomPosition(4);

  /*
   * we delete the choosen type from the urn:
   */
  urn = deleteNthChar(urn, toDraw);

  /*
   * the urn now contains three types of characters, and we
   * choose one of them:
   */

  toDraw = giveRandomPosition(3);

  /*
   * we put choosen character type into a new variable "typeKey"
   */
  var typeKey = "";
  typeKey = urn.slice(toDraw, toDraw+1);

  /*
   * we generate one character of the choosen type and put it
   * into the output:
   */

  outputString = outputString + giveCharOfGivenType(typeKey);

  /*
   * we delete the choosen type from the urn, the urn now contains two types
   */
  urn = deleteNthChar(urn, toDraw);

  /*
   * again, we choose one of them at random:
   */

  toDraw = giveRandomPosition(2);

  /*
   * one character of the choosen type into the output string:
   */
  typeKey = urn.slice(toDraw, toDraw+1);
  outputString = outputString + giveCharOfGivenType(typeKey);

  /*
   * we delete the choosen one, the urn has one left
   */

  urn = deleteNthChar(urn, toDraw);

  /*
   * nothing to choose, we use the last type of character to finish
   * the output string
   */

  typeKey = urn;
  outputString = outputString + giveCharOfGivenType(typeKey);

  return outputString;
}

function giveRandomPosition(upTo) {
  /*
   * returns some random integer up to (but not including) "upTo"
   */
  if (upTo > 255) {
    throw new BoundaryException(upTo, "random position generation");
  }

  if (hasCrypto) {
    var randomArray = new Uint8Array(1);
    //TODO replace by Math.Random for older browsers
    window.crypto.getRandomValues(randomArray);

    return Math.floor((randomArray[0] / 256 ) * upTo);
  }
  else {
    return Math.floor(Math.random() * upTo);
  }
}

function giveCharOfGivenType(givenType) {
  /*
   * returns one random character of the given type:
   * U - uppercase letter
   * l - lowercase letter
   * 0 - single digit
   * # - special character
   */

   var charToReturn = "";

   switch (givenType) {
    case "U":
      charToReturn = giveUpperCaseLetter();
      break;
    case "l":
      charToReturn = giveLowerCaseLetter();
      break;
    case "0":
      charToReturn = giveDigit();
      break;
    case "#":
      charToReturn = giveSpecialChar();
      break;
    default:
      charToReturn = "";
      break;
  }
  return charToReturn;

}

/*
 * the next four functions return a random character, each function
 * chooses the character at random out of its own set of characters.
 */

function giveUpperCaseLetter() {
  var upperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  return upperCaseLetters.charAt(giveRandomPosition(26));
}

function giveLowerCaseLetter() {
  var lowerCaseLetters = "abcdefghijklmnopqrstuvwxyz";
  return lowerCaseLetters.charAt(giveRandomPosition(26));
}

function giveDigit() {
  var digits = "0123456789";
  return digits.charAt(giveRandomPosition(10));
}

function giveSpecialChar() {
  var specialCharsVal = specialChars.value;
  return specialCharsVal.charAt(giveRandomPosition(specialCharsVal.length));
}

function deleteNthChar(inputString, position) {
  /*
   * deletes the n-th character of the input string and
   * returns the (now one character shorter) string.
   *
   * n is counted from 0
   */
  if (position > inputString.length) {
    throw new BoundaryException(position, "deleting char at position");
   }

  var outputString = "";
  outputString = inputString.slice(0, position);
  outputString = outputString + inputString.slice(position+1);
  return outputString;
}

<commit_msg>made script conforming to JSLint<commit_after>/*jslint browser: true*/
/*globals $, jQuery, alert, Uint8Array*/
/*
 * password card generator
 * Peter Brockfeld, 2014-11-06
 */

var theCard = "",
    specialChars = "",
    cornerText = "",
    hasCrypto = false,
    /*
     * Translations, thanks to Sebastian Fuchs for describing the usage of
     * data attributes as an translation tool.
     *
     * http://foxable.net/2014/04/client-seitige-mehrsprachigkeit-mit-jquery/
     *
     */
    lang = "de", // current language

    // translation data
    langData = {
        de: {
            PASSWORDCARD: "Passwort-Karte",
            SHORT_DESCRIPTION: "Diese Anwendung erzeugt eine Karte für Passwörter.",
            THE_CARD: "Die Karte",
            SETTINGS: "Einstellungen",
            SPECIAL_CHARS: "Sonderzeichen:",
            CORNER_TEXT: "Eckentext:",
            GENERATE_CARD: "Karte erzeugen",
            PRINT_CARD: "Karte drucken",
            NO_CRYPTO_WARNING: "Ihr Browser unterstützt keine kryptografisch starken Zufallszahlen.",
            EXPLANATIONS_TEXT: "Die Anwendung erzeugt eine kreditkartengroße Passwortkarte nach den Ideen des Artikels 'Passwort aus Papier - Kennwörter mit Zettel und Stift verwalten' in der c't 18/2014, S. 92. Weitere Erläuterungen finden sich in der README.md-Datei.",
            EXPLANATION: "Erläuterung"
        },
        en: {
            PASSWORDCARD: "Passwordcard",
            SHORT_DESCRIPTION: "This application generates a card for passwords.",
            THE_CARD: "The card",
            SETTINGS: "Settings",
            SPECIAL_CHARS: "Special chars:",
            CORNER_TEXT: "Corner text:",
            GENERATE_CARD: "Generate card",
            PRINT_CARD: "Print card",
            NO_CRYPTO_WARNING: "Your browser doesn't support cryptographically strong random numbers.",
            EXPLANATIONS_TEXT: "This application generates a credit card sized card for passwords. The idea is from an article in the german magazine c't, issue 18/2014, p. 92. Some further explanations could be found in README.md",
            EXPLANATION: "Explanation"
        }
    };

function BoundaryException(value, desiredOperation) {
    "use strict";
    this.value = value;
    this.message = "is to big for" + desiredOperation;
    this.toString = function () {
        return this.value + this.message;
    };
}

// returns the translation for the given
// language key (depending on the current language)
function translate(key) {
    "use strict";
    if (langData.hasOwnProperty(lang)) {
        if (langData[lang].hasOwnProperty(key)) {
            return langData[lang][key];
        } else {
            return "Undefined";
        }
    } else {
        return "Undefined";
    }
}

// translate all multi-lingual DOM elements
function translateView() {
    "use strict";
    $(".totranslate").each(function () {
        var key = $(this).attr("data-totranslate");

        $(this).text(translate(key));
    });
}

function giveRandomPosition(upTo) {
    "use strict";
    /*
     * returns some random integer up to (but not including) "upTo"
     */
    if (upTo > 255) {
        throw new BoundaryException(upTo, "random position generation");
    }

    if (hasCrypto) {
        var randomArray = new Uint8Array(1);
        //TODO replace by Math.Random for older browsers
        window.crypto.getRandomValues(randomArray);

        return Math.floor((randomArray[0] / 256) * upTo);
    } else {
        return Math.floor(Math.random() * upTo);
    }
}

function giveUpperCaseLetter() {
    "use strict";
    var upperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    return upperCaseLetters.charAt(giveRandomPosition(26));
}

function giveLowerCaseLetter() {
    "use strict";
    var lowerCaseLetters = "abcdefghijklmnopqrstuvwxyz";
    return lowerCaseLetters.charAt(giveRandomPosition(26));
}

function giveDigit() {
    "use strict";
    var digits = "0123456789";
    return digits.charAt(giveRandomPosition(10));
}

function giveSpecialChar() {
    "use strict";
    var specialCharsVal = specialChars.value;
    return specialCharsVal.charAt(giveRandomPosition(specialCharsVal.length));
}

function giveCharOfGivenType(givenType) {
    "use strict";
    /*
     * returns one random character of the given type:
     * U - uppercase letter
     * l - lowercase letter
     * 0 - single digit
     * # - special character
     */

    var charToReturn = "";

    switch (givenType) {
    case "U":
        charToReturn = giveUpperCaseLetter();
        break;
    case "l":
        charToReturn = giveLowerCaseLetter();
        break;
    case "0":
        charToReturn = giveDigit();
        break;
    case "#":
        charToReturn = giveSpecialChar();
        break;
    default:
        charToReturn = "";
        break;
    }
    return charToReturn;

}

/*
 * the next four functions return a random character, each function
 * chooses the character at random out of its own set of characters.
 */

function deleteNthChar(inputString, position) {
    "use strict";
    /*
     * deletes the n-th character of the input string and
     * returns the (now one character shorter) string.
     *
     * n is counted from 0
     */
    if (position > inputString.length) {
        throw new BoundaryException(position, "deleting char at position");
    }

    var outputString = "";
    outputString = inputString.slice(0, position);
    outputString = outputString + inputString.slice(position + 1);
    return outputString;
}

function giveTriple() {
    "use strict";
    /*
     * initialization of the output string: start with an empty string
     */
    var outputString = "",
        /* there is an urn which contains all four types of characters:
    U for uppercase letters
    l for lowercase letters
    0 for digits and
    # for special characters
   */
        urn = "Ul0#",
        /*
         * we choose one of the four types in the urn at random:
         */
        toDraw = giveRandomPosition(4),
        typeKey = "";

    /*
     * we delete the choosen type from the urn:
     */
    urn = deleteNthChar(urn, toDraw);

    /*
     * the urn now contains three types of characters, and we
     * choose one of them:
     */

    toDraw = giveRandomPosition(3);

    /*
     * we put choosen character type into a new variable "typeKey"
     */

    typeKey = urn.slice(toDraw, toDraw + 1);

    /*
     * we generate one character of the choosen type and put it
     * into the output:
     */

    outputString = outputString + giveCharOfGivenType(typeKey);

    /*
     * we delete the choosen type from the urn, the urn now contains two types
     */
    urn = deleteNthChar(urn, toDraw);

    /*
     * again, we choose one of them at random:
     */

    toDraw = giveRandomPosition(2);

    /*
     * one character of the choosen type into the output string:
     */
    typeKey = urn.slice(toDraw, toDraw + 1);
    outputString = outputString + giveCharOfGivenType(typeKey);

    /*
     * we delete the choosen one, the urn has one left
     */

    urn = deleteNthChar(urn, toDraw);

    /*
     * nothing to choose, we use the last type of character to finish
     * the output string
     */

    typeKey = urn;
    outputString = outputString + giveCharOfGivenType(typeKey);

    return outputString;
}

function initializeTheCard() {
    "use strict";
    // create header:
    var singleRow = "";

    singleRow = document.createElement("tr");
    singleRow.innerHTML =
        '
' +
        '	' +
        'ABC	' +
        'DEF	' +
        'GHI	' +
        'JKL	' +
        'MNO	' +
        'PQR	' +
        'STU	' +
        'VWX	' +
        'YZ.	' +
        '	';
    singleRow.setAttribute("class", "tableHead");
    theCard.appendChild(singleRow);
}

function initializeSettings() {
    "use strict";
    specialChars = document.getElementById("specialChars");
    cornerText = document.getElementById("cornerText");
    specialChars.value = "+-#.,!§$%&/()=?;:_'*";
    cornerText.value = "TST";
}

function checkCryptoSupport() {
    "use strict";
    hasCrypto = window.crypto ? true : false;
    if (hasCrypto === false) {
        $("#noCryptoWarning").dialog("open");
    }
}

function createSingleCell() {
    "use strict";
    var singleCell = document.createElement("td");

    singleCell.innerHTML = '' + giveTriple() + '	';
    singleCell.setAttribute("class", "singleCell");

    return singleCell;

}

function createSingleRow(prefix) {
    "use strict";
    /*
     * this function generates a single row filled with random character
     * triples.
     *
     * The first cell contains the prefix given.
     */

    var singleRow = document.createElement("tr"),
        prefixCell = document.createElement("td"),
        i = 0;

    prefixCell.innerHTML = '' + prefix + '	';
    prefixCell.setAttribute("class", "singleCell");

    singleRow.appendChild(prefixCell);

    for (i = 0; i < 9; i = i + 1) {
        singleRow.appendChild(createSingleCell());
    }

    singleRow.setAttribute("class", "generatedRow");
    return singleRow;
}

function fillTheCard() {
    "use strict";
    /*
     * this function fills the card with random contents.
     * As a first step, we get rid off any contents left from
     * former runs.
     *
     * The headerline is left untouched.
     */

    // get all the rows and count them:
    var rowsOfTheCard = document.getElementsByClassName("generatedRow"),
        countRows = rowsOfTheCard.length,
        i = 0,
        cornerCell = document.getElementById("cornerTextOnCard"),
        prefix = "";

    for (i = 0; i < countRows; i = i + 1) {
        rowsOfTheCard[0].parentNode.removeChild(rowsOfTheCard[0]);

    }

    // adjust the corner text:

    cornerCell.innerHTML = cornerText.value;

    /* the first cell in each row is a prefix number, simply counted
     * from 1 to 9.
     */

    /*
     * and now: generate 9 rows, each starting with a new prefix.
     * The "createSingleRow" returns one new row of cells with
     * 10 entries.
     */
    for (i = 1; i < 10; i = i + 1) {
        prefix = i.toString();
        theCard.appendChild(createSingleRow(prefix));
    }
}

/*
 * jQueryUI functions
 */

$(function () {
    "use strict";
    $("#generateTable")
        .button()
        .click(function () {
            fillTheCard();
        });
    $("#printCard")
        .button()
        .click(function () {
            window.print();
        });
    $("#tabs")
        .tabs({
            heightStyle: "auto"
        });
    $("#de, #en").click(function () {
        lang = $(this).attr("id");
        translateView();
    });
    $("#noCryptoWarning").dialog({
        modal: true,
        autoOpen: false,
        buttons: {
            Ok: function () {
                $(this).dialog("close");
            }
        }
    });
});

function init() {
    "use strict";
    checkCryptoSupport();
    theCard = document.getElementById("theCard");
    initializeSettings();
    translateView();
    initializeTheCard();
    fillTheCard();
}

window.onload = init;<|endoftext|>


<commit_before>$(document).on('can_embed_loaded', function(){
	$(theForm).submit(function (event) {
    commanderData();
  });
});

validatePhone = function (num) {
  console.log('Validating \'' + num + '\' as phone number');
  num = num.replace(/[^0-9]/g, '')
  if (num.substring(0, 1) != '1') num = '1' + num;
  if (num.length == 11) return num;
  return false;
}

commanderData = function () {
  thisPhone = validatePhone($(phoneField).val());
  thisLocation = $(zipField).val();
  console.log('Phone is ' + thisPhone);
  console.log('ZIP is ' + thisLocation);
  if (!thisLocation) console.log('No location.');
  if (thisPhone) {
    //For now, comment out the state-by-state campaign function.  Make a prettier way of doing this later on.
    //geoCheck(thisLocation);
    var callData = {
        campaignId: '7',
        userPhone: thisPhone,
        userLocation: thisLocation,
      }
      console.log('Assembled call data');
      console.log(callData);
      makeTheCall(callData);
  } else {
  console.log('No phone number.');
  }

}

handleCallResponse = function (res) {
  console.log(res);
  if (res.status == '200') {
    console.log('Got response 200 OK');
    targetHTML = res.responseJSON.script;
  } else {
    console.log('Got response ' + res.status + ' ' + res.statusText);
    targetHTML = 'There was an error with your call (' + res.status + ' ' + res.statusText + ').  Please try again.'
  }

  $(document).on('can_embed_submitted',function(){
    replaceThankYou(targetHTML)
  });

}

makeTheCall = function (data) {
  if (data) {
    $.ajax({
      method: 'get',
      data: data,
      url: 'https://advocacycommons.callpower.org/call/create',
      dataType: 'json',
      complete: function (res) {
        handleCallResponse(res);
      }
    });
  }
}
<commit_msg>Fix indents<commit_after>$(document).on('can_embed_loaded', function(){
	$(theForm).submit(function (event) {
    commanderData();
  });
});

validatePhone = function (num) {
  console.log('Validating \'' + num + '\' as phone number');
  num = num.replace(/[^0-9]/g, '')
  if (num.substring(0, 1) != '1') num = '1' + num;
  if (num.length == 11) return num;
  return false;
}

commanderData = function () {
  thisPhone = validatePhone($(phoneField).val());
  thisLocation = $(zipField).val();
  console.log('Phone is ' + thisPhone);
  console.log('ZIP is ' + thisLocation);
  if (!thisLocation) console.log('No location.');
  if (thisPhone) {
    //For now, comment out the state-by-state campaign function.  Make a prettier way of doing this later on.
    //geoCheck(thisLocation);
    var callData = {
        campaignId: '7',
        userPhone: thisPhone,
        userLocation: thisLocation,
      }
		console.log('Assembled call data');
		console.log(callData);
		makeTheCall(callData);
  } else {
		console.log('No phone number.');
  }

}

handleCallResponse = function (res) {
  console.log(res);
  if (res.status == '200') {
    console.log('Got response 200 OK');
    targetHTML = res.responseJSON.script;
  } else {
    console.log('Got response ' + res.status + ' ' + res.statusText);
    targetHTML = 'There was an error with your call (' + res.status + ' ' + res.statusText + ').  Please try again.'
  }

  $(document).on('can_embed_submitted',function(){
    replaceThankYou(targetHTML)
  });

}

makeTheCall = function (data) {
  if (data) {
    $.ajax({
      method: 'get',
      data: data,
      url: 'https://advocacycommons.callpower.org/call/create',
      dataType: 'json',
      complete: function (res) {
        handleCallResponse(res);
      }
    });
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * 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.
 */

/**
 * @fileoverview Definitions for W3C's Navigation Timing specification.
 *
 * Created from
 * http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
 *
 * @externs
 */

/** @constructor */
function PerformanceTiming() {}
/** @type {number} */ PerformanceTiming.prototype.navigationStart;
/** @type {number} */ PerformanceTiming.prototype.unloadEventStart;
/** @type {number} */ PerformanceTiming.prototype.unloadEventEnd;
/** @type {number} */ PerformanceTiming.prototype.redirectStart;
/** @type {number} */ PerformanceTiming.prototype.redirectEnd;
/** @type {number} */ PerformanceTiming.prototype.fetchStart;
/** @type {number} */ PerformanceTiming.prototype.domainLookupStart;
/** @type {number} */ PerformanceTiming.prototype.domainLookupEnd;
/** @type {number} */ PerformanceTiming.prototype.connectStart;
/** @type {number} */ PerformanceTiming.prototype.connectEnd;
/** @type {number} */ PerformanceTiming.prototype.secureConnectionStart;
/** @type {number} */ PerformanceTiming.prototype.requestStart;
/** @type {number} */ PerformanceTiming.prototype.responseStart;
/** @type {number} */ PerformanceTiming.prototype.responseEnd;
/** @type {number} */ PerformanceTiming.prototype.domLoading;
/** @type {number} */ PerformanceTiming.prototype.domInteractive;
/** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventStart;
/** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventEnd;
/** @type {number} */ PerformanceTiming.prototype.domComplete;
/** @type {number} */ PerformanceTiming.prototype.loadEventStart;
/** @type {number} */ PerformanceTiming.prototype.loadEventEnd;

/** @constructor */
function PerformanceNavigation() {}
/** @type {number} */ PerformanceNavigation.prototype.TYPE_NAVIGATE = 0;
/** @type {number} */ PerformanceNavigation.prototype.TYPE_RELOAD = 1;
/** @type {number} */ PerformanceNavigation.prototype.TYPE_BACK_FORWARD = 2;
/** @type {number} */ PerformanceNavigation.prototype.TYPE_RESERVED = 255;
/** @type {number} */ PerformanceNavigation.prototype.type;
/** @type {number} */ PerformanceNavigation.prototype.redirectCount;

/** @constructor */
function Performance() {}
/** @type {PerformanceTiming} */ Performance.prototype.timing;
/** @type {PerformanceNavigation} */ Performance.prototype.navigation;

/** @type {Performance} */
Window.prototype.performance;
<commit_msg><commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * 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.
 */

/**
 * @fileoverview Definitions for W3C's Navigation Timing specification.
 *
 * Created from
 * http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
 *
 * @externs
 */

/** @constructor */
function PerformanceTiming() {}
/** @type {number} */ PerformanceTiming.prototype.navigationStart;
/** @type {number} */ PerformanceTiming.prototype.unloadEventStart;
/** @type {number} */ PerformanceTiming.prototype.unloadEventEnd;
/** @type {number} */ PerformanceTiming.prototype.redirectStart;
/** @type {number} */ PerformanceTiming.prototype.redirectEnd;
/** @type {number} */ PerformanceTiming.prototype.fetchStart;
/** @type {number} */ PerformanceTiming.prototype.domainLookupStart;
/** @type {number} */ PerformanceTiming.prototype.domainLookupEnd;
/** @type {number} */ PerformanceTiming.prototype.connectStart;
/** @type {number} */ PerformanceTiming.prototype.connectEnd;
/** @type {number} */ PerformanceTiming.prototype.secureConnectionStart;
/** @type {number} */ PerformanceTiming.prototype.requestStart;
/** @type {number} */ PerformanceTiming.prototype.responseStart;
/** @type {number} */ PerformanceTiming.prototype.responseEnd;
/** @type {number} */ PerformanceTiming.prototype.domLoading;
/** @type {number} */ PerformanceTiming.prototype.domInteractive;
/** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventStart;
/** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventEnd;
/** @type {number} */ PerformanceTiming.prototype.domComplete;
/** @type {number} */ PerformanceTiming.prototype.loadEventStart;
/** @type {number} */ PerformanceTiming.prototype.loadEventEnd;

/** @constructor */
function PerformanceNavigation() {}
/** @type {number} */ PerformanceNavigation.prototype.TYPE_NAVIGATE = 0;
/** @type {number} */ PerformanceNavigation.prototype.TYPE_RELOAD = 1;
/** @type {number} */ PerformanceNavigation.prototype.TYPE_BACK_FORWARD = 2;
/** @type {number} */ PerformanceNavigation.prototype.TYPE_RESERVED = 255;
/** @type {number} */ PerformanceNavigation.prototype.type;
/** @type {number} */ PerformanceNavigation.prototype.redirectCount;

// Only available in Webkit, and only with the --enable-memory-info flag.
/** @constructor */
function PerformanceMemory() {}
/** @type {number} */ PerformanceMemory.prototype.jsHeapSizeLimit;
/** @type {number} */ PerformanceMemory.prototype.totalJSHeapSize;
/** @type {number} */ PerformanceMemory.prototype.usedJSHeapSize;

/** @constructor */
function Performance() {}
/** @type {PerformanceTiming} */ Performance.prototype.timing;
/** @type {PerformanceNavigation} */ Performance.prototype.navigation;

// Only available in Webkit, and only with the --enable-memory-info flag.
/** @type {PerformanceMemory} */ Performance.prototype.memory;

/** @type {Performance} */
Window.prototype.performance;
<|endoftext|>