<commit_before>module.exports = {
    env: {
        browser: true,
        es2021: true,
        node: true,
    },
    extends: ["airbnb-base", "airbnb-typescript/base", "prettier"],
    parser: "@typescript-eslint/parser",
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
        },
        ecmaVersion: 12,
        sourceType: "module",
        project: ["./tsconfig.json"],
    },
    plugins: ["@typescript-eslint", "prettier", "import", "sort-export-all"],
    rules: {
        // Prettier handles indent, whitespace and empty lines
        "prettier/prettier": 2,

        // Import rules
        "sort-export-all/sort-export-all": [
            "error",
            "asc",
            {
                caseSensitive: false,
                natural: false,
            },
        ],
        "import/extensions": ["error", "never"],
        "import/order": [
            "error",
            {
                alphabetize: {
                    order: "asc",
                    caseInsensitive: true,
                },
                groups: [
                    ["builtin", "external"],
                    "internal",
                    "parent",
                    "sibling",
                    "index",
                ],
                "newlines-between": "always",
            },
        ],
        "import/prefer-default-export": "off",
        "import/no-extraneous-dependencies": "error",
        "sort-imports": [
            "error",
            {
                ignoreCase: true,
                ignoreDeclarationSort: true,
                ignoreMemberSort: false,
                memberSyntaxSortOrder: ["none", "all", "multiple", "single"],
            },
        ],

        // General code-style rules
        "@typescript-eslint/naming-convention": [
            "warn",
            {
                selector: "default",
                format: [
                    "snake_case",
                    "strictCamelCase",
                    "StrictPascalCase",
                    "UPPER_CASE",
                ], // snake_case enabled for Slate types
                // leadingUnderscore is allowed to allow having unused leading function arguments, e.g.
                // const ownPropSelector = (_state, ownProps) => ownProps.id;
                leadingUnderscore: "allow",
                trailingUnderscore: "forbid",
            },
            {
                // This is due to backend sending us snake_case properties
                selector: "property",
                format: [
                    "snake_case",
                    "strictCamelCase",
                    "StrictPascalCase",
                    "UPPER_CASE",
                ],
                leadingUnderscore: "forbid",
                trailingUnderscore: "forbid",
            },
        ],
        "id-blacklist": [
            2,
            "arr",
            "cb",
            "e",
            "el",
            "err",
            "idx",
            "num",
            "str",
            "tmp",
            "val",
        ],
        "no-return-assign": ["error", "except-parens"],
        "no-unused-vars": "off",
        "@typescript-eslint/no-unused-vars": [
            "error",
            { argsIgnorePattern: "^_", ignoreRestSiblings: true },
        ],

        "prefer-destructuring": "warn",
        "no-nested-ternary": "warn",

        // Extra rules
        radix: "off",
    },
};
<commit_msg>Allow exporting { default } with base config<commit_after>module.exports = {
    env: {
        browser: true,
        es2021: true,
        node: true,
    },
    extends: ["airbnb-base", "airbnb-typescript/base", "prettier"],
    parser: "@typescript-eslint/parser",
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
        },
        ecmaVersion: 12,
        sourceType: "module",
        project: ["./tsconfig.json"],
    },
    plugins: ["@typescript-eslint", "prettier", "import", "sort-export-all"],
    rules: {
        // Prettier handles indent, whitespace and empty lines
        "prettier/prettier": 2,

        // Import rules
        "sort-export-all/sort-export-all": [
            "error",
            "asc",
            {
                caseSensitive: false,
                natural: false,
            },
        ],
        "import/extensions": ["error", "never"],
        "import/order": [
            "error",
            {
                alphabetize: {
                    order: "asc",
                    caseInsensitive: true,
                },
                groups: [
                    ["builtin", "external"],
                    "internal",
                    "parent",
                    "sibling",
                    "index",
                ],
                "newlines-between": "always",
            },
        ],
        "import/prefer-default-export": "off",
        "import/no-extraneous-dependencies": "error",
        "sort-imports": [
            "error",
            {
                ignoreCase: true,
                ignoreDeclarationSort: true,
                ignoreMemberSort: false,
                memberSyntaxSortOrder: ["none", "all", "multiple", "single"],
            },
        ],

        // General code-style rules
        "@typescript-eslint/naming-convention": [
            "warn",
            {
                selector: "default",
                format: [
                    "snake_case",
                    "strictCamelCase",
                    "StrictPascalCase",
                    "UPPER_CASE",
                ], // snake_case enabled for Slate types
                // leadingUnderscore is allowed to allow having unused leading function arguments, e.g.
                // const ownPropSelector = (_state, ownProps) => ownProps.id;
                leadingUnderscore: "allow",
                trailingUnderscore: "forbid",
            },
            {
                // This is due to backend sending us snake_case properties
                selector: "property",
                format: [
                    "snake_case",
                    "strictCamelCase",
                    "StrictPascalCase",
                    "UPPER_CASE",
                ],
                leadingUnderscore: "forbid",
                trailingUnderscore: "forbid",
            },
        ],
        "id-blacklist": [
            2,
            "arr",
            "cb",
            "e",
            "el",
            "err",
            "idx",
            "num",
            "str",
            "tmp",
            "val",
        ],
        "no-return-assign": ["error", "except-parens"],
        "no-unused-vars": "off",
        "@typescript-eslint/no-unused-vars": [
            "error",
            { argsIgnorePattern: "^_", ignoreRestSiblings: true },
        ],
        // Allow exporting { default }
        "no-restricted-exports": "warn",

        "prefer-destructuring": "warn",
        "no-nested-ternary": "warn",

        // Extra rules
        radix: "off",
    },
};
<|endoftext|>


<commit_before>
import Profession from '../../definitions/profession';

import * as Foods from '../items/foods';
import * as Weapons from '../items/_weapons';
import * as Bodys from '../items/bodys';
import * as Heads from '../items/heads';
import * as Thresholds from '../../constants/skill-thresholds';
import * as Traits from '../traits/_all';

const archaeologistConfig = {
  hp  : '6d3',
  mp  : '0d0',
  str : '2d2',
  con : '1d4',
  int : '1d2',
  dex : '3d3',
  wis : '1d2',
  cha : '1d1',
  titles: ['Digger',, 'Field Worker',,, 'Investigator',,, 'Exhumer',,, 'Excavator',,, 'Spelunker',,, 'Speleologist',,, 'Collector',,, 'Curator'],
  traits: [Traits.Stealth({ level: 10 }), Traits.Haste({ level: 50 })],
  skillCaps: { bash: Thresholds.Skilled },
  startingItems: [
    { init: () => new Weapons.Bullwhip({ bucName: 'uncursed', enchantment: 2 }) },
    { init: () => new Foods.Ration({ charges: '3d2', bucName: 'uncursed' }) },
    { init: () => new Bodys.LeatherJacket({ bucName: 'uncursed', startIdentified: true }) },
    { init: () => new Heads.Fedora({ bucName: 'uncursed', startIdentified: true }) }
  ]
};

export default class Archaeologist extends Profession {
  constructor() {
    super(archaeologistConfig);
  }
}<commit_msg>more skills for archaeologist<commit_after>
import Profession from '../../definitions/profession';

import * as Foods from '../items/foods';
import * as Weapons from '../items/_weapons';
import * as Bodys from '../items/bodys';
import * as Heads from '../items/heads';
import * as Thresholds from '../../constants/skill-thresholds';
import * as Traits from '../traits/_all';

const archaeologistConfig = {
  hp  : '6d3',
  mp  : '0d0',
  str : '2d2',
  con : '1d4',
  int : '1d2',
  dex : '3d3',
  wis : '1d2',
  cha : '1d1',
  titles: ['Digger',, 'Field Worker',,, 'Investigator',,, 'Exhumer',,, 'Excavator',,, 'Spelunker',,, 'Speleologist',,, 'Collector',,, 'Curator'],
  traits: [Traits.Stealth({ level: 10 }), Traits.Haste({ level: 50 })],
  skillCaps: { bash: Thresholds.Skilled, stab: Thresholds.Basic, slash: Thresholds.Skilled, unarmed: Thresholds.Expert },
  startingItems: [
    { init: () => new Weapons.Bullwhip({ bucName: 'uncursed', enchantment: 2 }) },
    { init: () => new Foods.Ration({ charges: '3d2', bucName: 'uncursed' }) },
    { init: () => new Bodys.LeatherJacket({ bucName: 'uncursed', startIdentified: true }) },
    { init: () => new Heads.Fedora({ bucName: 'uncursed', startIdentified: true }) }
  ]
};

export default class Archaeologist extends Profession {
  constructor() {
    super(archaeologistConfig);
  }
}<|endoftext|>


<commit_before>const zlib = require('zlib')
const downloadCharitiesRouter = require('express').Router()
const log = require('../../../helpers/logger')
const ElasticStream = require('../../../helpers/elasticStream')
const { getAllowedCSVFieldPaths, getFileName, csvHeader } = require('./helpers')
const getParser = require('./parser')
const { s3 } = require('../../../connection')
const { PassThrough } = require('stream')
const config = require('../../../config.json')

const bucket = 'charity-base-uk-downloads'
const folder = 'downloads'
const linkExpiresSeconds = 15*60

// const EventEmitter = require('events')
// EventEmitter.defaultMaxListeners = 20
const numSlices = 30

const BASE_URL = process.env.NODE_ENV === 'production' ? config.prodBaseUrl : 'http://localhost:4000'

// const mergeStreams = streams => {
//   let pass = new PassThrough()
//   let waiting = streams.length
//   for (let stream of streams) {
//     pass = stream.pipe(pass, {end: false})
//     stream.once('end', () => --waiting === 0 && pass.emit('end'))
//   }
//   return pass
// }

const fetch = require('node-fetch')

const getStreamUrl = req => {
  const urlList = req.originalUrl.split('/')
  const streamUrlList = [
    ...urlList.slice(0,urlList.length-1),
    'upload-slice',
    ...urlList.slice(urlList.length-1),
  ]
  return `${BASE_URL}${streamUrlList.join('/')}`
}

const getDownloadCharitiesRouter = (esClient, esIndex) => {

  downloadCharitiesRouter.post('/upload-slice', async function(req, res, next) {
    const { searchParams, fileType, csvFields, key } = req.body
    const myStream = new ElasticStream({
      searchParams,
      client: esClient,
      parser: getParser(fileType, csvFields),
    })

    // TODO: zip before sending
    const gzip = zlib.createGzip()
    if (fileType === 'CSV') {
      gzip.write(csvHeader(csvFields))
    }

    // res.attachment('tempfile')
    // myStream.pipe(gzip).pipe(res)

    try {
      const upload = await s3.upload({
        Body: myStream.pipe(gzip),
        Bucket: bucket,
        Key: key,
      }).promise()
      log.info(`succesfully uploaded key ${key}`)
      log.info(Object.keys(upload))
      return res.status(201).json({ ...upload })
    } catch (e) {
      log.info(`failed to upload key ${key}`)
      log.error(e)
      return res.sendStatus(400)
    }

  })

  downloadCharitiesRouter.post('/', (req, res, next) => {

    const { fileType } = req.body
    const { query, meta } = res.locals.elasticSearch

    const searchParams = sliceId => ({
      index: esIndex,
      size: 8000,
      body: {
        query,
        "slice": {
          "field": "ids.GB-CHC",
          "id": sliceId,
          "max": numSlices,
        },
      },
      scroll: '1m',
      _source: meta._source,
      // sort: meta.sort,
    })

    const csvFields = fileType === 'CSV' && getAllowedCSVFieldPaths(meta._source)

    const fileName = getFileName(req.query, fileType)

    const s3SliceKey = sliceId => `${folder}/slice_${sliceId}_${fileName}`
    const s3CompleteQueryKey = `${folder}/complete_query_${fileName}`

    let complete = false

    const getFileUrl = () => new Promise((resolve, reject) => {
      log.info('checking if upload exists')
      s3.getObject({
        Bucket: bucket,
        Key: s3CompleteQueryKey,
      })
      .promise()
      .then(data => {
        const completeUploadInfo = JSON.parse(data.Body.toString())
        log.info('found complete upload info')
        log.info(completeUploadInfo)
        log.info(Object.keys(completeUploadInfo))
        if (!completeUploadInfo.s3Objects || completeUploadInfo.s3Objects.length === 0) return
        log.info(`found complete query, returning ${completeUploadInfo.s3Objects.length} urls`)
        // TODO: read slice keys from complete_query_ file and return all urls
        // const url = s3
        // .getSignedUrl('getObject', {
        //   Bucket: bucket,
        //   Key: s3KeyComplete(),
        //   Expires: linkExpiresSeconds,
        // })
        // log.info(url)
        const urls = completeUploadInfo.s3Objects.map((x, i) => (
          s3.getSignedUrl('getObject', {
            Bucket: bucket,
            Key: x.Key,
            Expires: linkExpiresSeconds,
          })
        ))
        complete = true
        return resolve(urls)
      })
      .catch(err => {
        if (complete) return
        // TODO: check error type.  it could be something else e.g. s3 auth error
        log.info('no upload found, so lets do it.')
        return
        // return s3
        // .putObject({
        //   Bucket: bucket,
        //   Key: s3KeyProgress,
        //   Body: '',
        // })
        // .promise()
      })
      .then(async function() {
        if (complete) return
        log.info('uploading slices')

        // let uploadId
        // try {
        //   const multiUploadData = await s3.createMultipartUpload({
        //     Bucket: bucket,
        //     Key: s3KeyComplete,
        //   }).promise()
        //   uploadId = multiUploadData.UploadId
        // } catch(err) {
        //   s3.abortMultipartUpload({
        //     Bucket: bucket,
        //     Key: s3KeyComplete,
        //     UploadId: uploadId
        //   }).promise()
        //   return Promise.reject(err)
        // }

        // const slicedStreams = [...new Array(numSlices)].map((x, i) => (
        //   new ElasticStream({
        //     searchParams: searchParams(i),
        //     client: esClient,
        //     parser: getParser(fileType, csvFields),
        //   })
        // ))

        // log.info('successfully created multipart upload. uploading slices.')
        let slicedUploads
        try {
          slicedUploads = await Promise.all([...new Array(numSlices)].map((x, sliceId) => (
            fetch(getStreamUrl(req), {
              method: 'POST',
              body: JSON.stringify({
                searchParams: searchParams(sliceId),
                key: s3SliceKey(sliceId),
                fileType,
                csvFields,
              }),
              headers: {
                Authorization: req.headers.authorization,
                'Content-Type': 'application/json',
              }
            })
            .then(res => {
              log.info(`received status ${res.status} from sliceId ${sliceId}`)
              if (res.status !== 201) return Promise.reject({ message: 'Part of upload failed' })
              return res
            })
            .then(res => res.json())
          )))
        } catch (e) {
          return Promise.reject(e)
        }

        log.info('succesfully uploaded slices. uploading query info to complete file.')

        try {
          const upload = await s3.putObject({
            Bucket: bucket,
            Key: s3CompleteQueryKey,
            Body: JSON.stringify({
              query,
              meta,
              fileType,
              s3Objects: slicedUploads,
            }),
          }).promise()
          log.info('upload complete file')
        } catch (e) {
          log.info('failed to upload complete file')
          return Promise.reject(e)
        }

        log.info('successfully uploaded complete file. returning urls')
        try {

          const urls = slicedUploads.map((x, i) => (
            s3.getSignedUrl('getObject', {
              Bucket: bucket,
              Key: x.Key,
              Expires: linkExpiresSeconds,
            })
          ))
          log.info('signed urls')
          complete = true
          return resolve(urls)

          // const completeUpload = await s3.completeMultipartUpload({
          //   Bucket: bucket,
          //   Key: s3KeyComplete,
          //   UploadId: uploadId,
          //   MultipartUpload: {
          //     Parts: slicedUploads,
          //   },
          //   // RequestPayer: 'requester'
          // }).promise()
          // log.info('completeUpload')
          // log.info(completeUpload)
        } catch (e) {
          log.info('failed to sign urls')
          // s3.abortMultipartUpload({
          //   Bucket: bucket,
          //   Key: s3KeyComplete,
          //   UploadId: uploadId
          // }).promise()
          return Promise.reject(e)
        }

        // const eStream = mergeStreams(slicedStreams)
        // console.log('setting max listeners', slicedStreams[0].setMaxListeners(20))

        // const eStream = mergeStreams(slicedStreams)

        // eStream.on('error', handleError(filePath))

        // const gzip = zlib.createGzip()
        // if (fileType === 'CSV') {
        //   gzip.write(csvHeader(csvFields))
        // }
        // // gzip.on('error', handleError(filePath))

        // const readableGzip = eStream.pipe(gzip)

        // const uploadPromise = s3
        // .upload({
        //   Bucket: bucket,
        //   Key: s3Key,
        //   Body: eStream,
        // })
        // .promise()

        // return uploadPromise
      })
      // .then(data => {
      //   if (complete) return
      //   // log.info('Successfully completed multipart upload!')
      //   // Note: copyObject requires permission: s3:GetObject, s3:PutObject, s3:GetObjectTagging, s3:PutObjectTagging
      //   // return s3.copyObject({
      //   //   Bucket: bucket,
      //   //   CopySource: `${bucket}/${s3Key}`,
      //   //   Key: s3KeyComplete,
      //   // })
      //   // .promise()
      //   return data
      // })
      // .then(data => {
      //   if (complete) return
      //   log.info('file copied. now lets delete the original, but not too bothered if it fails')
      //   s3
      //   .deleteObject({
      //     Bucket: bucket,
      //     Key: s3Key,
      //   })
      //   .promise()

      //   return
      // })
      // .then(() => {
      //   if (complete) return
      //   log.info('returning url of newly uploaded file')
      //   const url = s3
      //   .getSignedUrl('getObject', {
      //     Bucket: bucket,
      //     Key: s3KeyComplete,
      //     Expires: linkExpiresSeconds,
      //   })
      //   log.info(url)
      //   complete = true
      //   return resolve(url)
      // })
      .catch(err => {
        if (complete) return
        log.info('oops something went wrong')
        log.error(err, err.stack)
        complete = true
        return reject({ message: err.message })
      })
    })

    getFileUrl()
    .then(urls => {
      res.json({ urls })
    })
    .catch(err => {
      res.status(400).json({ message: err.message })
    })

  })

  return downloadCharitiesRouter
}

module.exports = getDownloadCharitiesRouter
<commit_msg>refactor(downloads): return stream from slice endpoints & upload single s3 object<commit_after>const zlib = require('zlib')
const downloadCharitiesRouter = require('express').Router()
const log = require('../../../helpers/logger')
const ElasticStream = require('../../../helpers/elasticStream')
const { getAllowedCSVFieldPaths, getFileName, csvHeader } = require('./helpers')
const getParser = require('./parser')
const { s3 } = require('../../../connection')
const { PassThrough } = require('stream')
const config = require('../../../config.json')

const bucket = 'charity-base-uk-downloads'
const folder = 'downloads'
const linkExpiresSeconds = 15*60

const EventEmitter = require('events')
EventEmitter.defaultMaxListeners = 20
const numSlices = process.env.NODE_ENV === 'production' ? 5 : 5
const chunkSize = process.env.NODE_ENV === 'production' ? 10000 : 10000

const BASE_URL = process.env.NODE_ENV === 'production' ? config.prodBaseUrl : 'http://localhost:4000'

// const mergeStreams = streams => {
//   let pass = new PassThrough()
//   let waiting = streams.length
//   for (let stream of streams) {
//     pass = stream.pipe(pass, {end: false})
//     stream.once('end', () => --waiting === 0 && pass.emit('end'))
//   }
//   return pass
// }

const fetch = require('node-fetch')

const getStreamUrl = req => {
  const urlList = req.originalUrl.split('/')
  const streamUrlList = [
    ...urlList.slice(0,urlList.length-1),
    'upload-slice',
    ...urlList.slice(urlList.length-1),
  ]
  return `${BASE_URL}${streamUrlList.join('/')}`
}

const getSignedUrlAsync = params => new Promise((resolve, reject) => {
  s3.getSignedUrl('getObject', params, (err, url) => {
    if (err) return reject(err)
    return resolve(url)
  })
})

const getDownloadCharitiesRouter = (esClient, esIndex) => {

  downloadCharitiesRouter.post('/upload-slice', async function(req, res, next) {
    const { searchParams, fileType, csvFields, key } = req.body
    const myStream = new ElasticStream({
      searchParams,
      client: esClient,
      parser: getParser(fileType, csvFields),
    })

    // TODO: zip before sending
    // const gzip = zlib.createGzip()
    // if (fileType === 'CSV') {
    //   gzip.write(csvHeader(csvFields))
    // }

    res.attachment('tempfile')
    myStream.pipe(res)
    // myStream.pipe(gzip).pipe(res)

    // try {
    //   const upload = await s3.upload({
    //     Body: myStream.pipe(gzip),
    //     Bucket: bucket,
    //     Key: key,
    //   }).promise()
    //   log.info(`succesfully uploaded key ${key}`)
    //   log.info(Object.keys(upload))
    //   return res.status(201).json({ ...upload })
    // } catch (e) {
    //   log.info(`failed to upload key ${key}`)
    //   log.error(e)
    //   return res.sendStatus(400)
    // }

  })

  downloadCharitiesRouter.post('/', (req, res, next) => {

    const { fileType } = req.body
    const { query, meta } = res.locals.elasticSearch

    const searchParams = sliceId => ({
      index: esIndex,
      size: chunkSize,
      body: {
        query,
        "slice": {
          "field": "ids.GB-CHC",
          "id": sliceId,
          "max": numSlices,
        },
        "sort": [
          "_doc"
        ],
      },
      scroll: '1m',
      _source: meta._source,
      // sort: meta.sort,
    })

    const csvFields = fileType === 'CSV' && getAllowedCSVFieldPaths(meta._source)

    const fileName = getFileName(req.query, fileType)

    const s3Key = `${folder}/${fileName}`
    const s3SliceKey = sliceId => `${folder}/slice_${sliceId}_${fileName}`
    const s3CompleteQueryKey = `${folder}/complete_query_${fileName}`

    let complete = false

    const getFileUrl = () => new Promise((resolve, reject) => {
      log.info('checking if upload exists')
      s3.getObject({
        Bucket: bucket,
        Key: s3CompleteQueryKey,
      })
      .promise()
      .then(data => {
        const completeUploadInfo = JSON.parse(data.Body.toString())
        log.info('found complete upload info')
        log.info(completeUploadInfo)
        log.info(Object.keys(completeUploadInfo))
        if (!completeUploadInfo.s3Objects || completeUploadInfo.s3Objects.length === 0) return
        log.info(`found complete query, returning ${completeUploadInfo.s3Objects.length} urls`)
        // TODO: read slice keys from complete_query_ file and return all urls
        // const url = s3
        // .getSignedUrl('getObject', {
        //   Bucket: bucket,
        //   Key: s3KeyComplete(),
        //   Expires: linkExpiresSeconds,
        // })
        // log.info(url)
        const urls = completeUploadInfo.s3Objects.map((x, i) => (
          s3.getSignedUrl('getObject', {
            Bucket: bucket,
            Key: x.Key,
            Expires: linkExpiresSeconds,
          })
        ))
        complete = true
        return resolve(urls)
      })
      .catch(err => {
        if (complete) return
        // TODO: check error type.  it could be something else e.g. s3 auth error
        log.info('no upload found, so lets do it.')
        return
        // return s3
        // .putObject({
        //   Bucket: bucket,
        //   Key: s3KeyProgress,
        //   Body: '',
        // })
        // .promise()
      })
      .then(async function() {
        if (complete) return
        log.info('fetching slice streams')

        // let uploadId
        // try {
        //   const multiUploadData = await s3.createMultipartUpload({
        //     Bucket: bucket,
        //     Key: s3KeyComplete,
        //   }).promise()
        //   uploadId = multiUploadData.UploadId
        // } catch(err) {
        //   s3.abortMultipartUpload({
        //     Bucket: bucket,
        //     Key: s3KeyComplete,
        //     UploadId: uploadId
        //   }).promise()
        //   return Promise.reject(err)
        // }

        // const slicedStreams = [...new Array(numSlices)].map((x, i) => (
        //   new ElasticStream({
        //     searchParams: searchParams(i),
        //     client: esClient,
        //     parser: getParser(fileType, csvFields),
        //   })
        // ))

        // log.info('successfully created multipart upload. uploading slices.')
        let combinedStream = new PassThrough()
        let waitingStreams = numSlices
        let slicedUploads
        try {
          [...new Array(numSlices)].map((x, sliceId) => (
            fetch(getStreamUrl(req), {
              method: 'POST',
              body: JSON.stringify({
                searchParams: searchParams(sliceId),
                key: s3SliceKey(sliceId),
                fileType,
                csvFields,
              }),
              headers: {
                Authorization: req.headers.authorization,
                'Content-Type': 'application/json',
              }
            })
            .then(res => {
              log.info(`received status ${res.status} from sliceId ${sliceId}`)
              if (res.status !== 200) return Promise.reject({ message: 'Part of scrolling failed' })
              const stream = res.body
              combinedStream = stream.pipe(combinedStream, { end: false })
              stream.once('end', () => {
                log.info(`stream ${sliceId} ended`)
                --waitingStreams === 0 && combinedStream.emit('end') // end combined stream once all streams ended
              })
              // return res.body
            })
            // do we need to catch & throw here?
            // .then(res => res.json())
          ))
        } catch (e) {
          return Promise.reject(e)
        }

        log.info('combining streams.')

        // const combinedStream = mergeStreams(slicedUploads)

        const gzip = zlib.createGzip()
        if (fileType === 'CSV') {
          gzip.write(csvHeader(csvFields))
        }
        // gzip.on('error', handleError(filePath))

        log.info('uploading to s3.')

        try {
          const upload = await s3.upload({
            Body: combinedStream.pipe(gzip),
            Bucket: bucket,
            Key: s3Key,
          }).promise()
          log.info(`succesfully uploaded`)
        } catch (e) {
          log.info(`failed to upload key`)
          return Promise.reject(e)
        }

        log.info('creating shareable url.')

        try {
          const url = await getSignedUrlAsync({
            Bucket: bucket,
            Key: 'dummy-file-123', //s3Key,
            Expires: linkExpiresSeconds,
          })
          log.info(url)
          complete = true
          return resolve(url)
        } catch (e) {
          log.info('failed to sign url')
          return Promise.reject(e)
        }

        // const eStream = mergeStreams(slicedStreams)
        // console.log('setting max listeners', slicedStreams[0].setMaxListeners(20))

        // const eStream = mergeStreams(slicedStreams)

        // eStream.on('error', handleError(filePath))

        // const gzip = zlib.createGzip()
        // if (fileType === 'CSV') {
        //   gzip.write(csvHeader(csvFields))
        // }
        // // gzip.on('error', handleError(filePath))

        // const readableGzip = eStream.pipe(gzip)

        // const uploadPromise = s3
        // .upload({
        //   Bucket: bucket,
        //   Key: s3Key,
        //   Body: eStream,
        // })
        // .promise()

        // return uploadPromise
      })
      // .then(data => {
      //   if (complete) return
      //   // log.info('Successfully completed multipart upload!')
      //   // Note: copyObject requires permission: s3:GetObject, s3:PutObject, s3:GetObjectTagging, s3:PutObjectTagging
      //   // return s3.copyObject({
      //   //   Bucket: bucket,
      //   //   CopySource: `${bucket}/${s3Key}`,
      //   //   Key: s3KeyComplete,
      //   // })
      //   // .promise()
      //   return data
      // })
      // .then(data => {
      //   if (complete) return
      //   log.info('file copied. now lets delete the original, but not too bothered if it fails')
      //   s3
      //   .deleteObject({
      //     Bucket: bucket,
      //     Key: s3Key,
      //   })
      //   .promise()

      //   return
      // })
      // .then(() => {
      //   if (complete) return
      //   log.info('returning url of newly uploaded file')
      //   const url = s3
      //   .getSignedUrl('getObject', {
      //     Bucket: bucket,
      //     Key: s3KeyComplete,
      //     Expires: linkExpiresSeconds,
      //   })
      //   log.info(url)
      //   complete = true
      //   return resolve(url)
      // })
      .catch(err => {
        if (complete) return
        log.info('oops something went wrong')
        log.error(err, err.stack)
        complete = true
        return reject({ message: err.message })
      })
    })

    getFileUrl()
    .then(url => {
      res.json({ url })
    })
    .catch(err => {
      res.status(400).json({ message: err.message })
    })

  })

  return downloadCharitiesRouter
}

module.exports = getDownloadCharitiesRouter
<|endoftext|>


<commit_before>class Market {
    constructor() {
        this.apiAdrress = "";
        this.priceInfo = "";
        this.id = null;
    }
    getAdrress() {
        return this.apiAdrress;
    }
    fetchPrices(fctn) {}

    getPriceInfo() {
        return this.priceInfo;
    }

    getId() {
        return this.id;
    }
}

class Bitfinex extends Market {

    constructor() {
        super();
        this.apiAdrress = "https://api.bitfinex.com/v2/candles/trade:15m:tBTCUSD/hist?limit=96";
        this.priceInfo = "Based on Bitfinex 24h timeframe";
        this.id = 0;
    }

    fetchPrices(fctn) {
        var id = this.id;
        $.ajax({
            dataType: "json",
            url: this.getAdrress(),
            success: function(data) {
                fctn(data[95][1], data[0][2], id);
            }
        });
    }

}

class Bitstamp extends Market {

    constructor() {
        super();
        this.apiAdrress = "https://www.bitstamp.net/api/v2/ticker/btcusd/";
        this.priceInfo = "Based on Bistamp daily performance";
        this.id = 1;
    }

    fetchPrices(fctn) {
        var id = this.id;
        $.ajax({
            dataType: "json",
            url: this.getAdrress(),
            success: function(data) {
                fctn(data["low"], data["last"], id);
            }
        });
    }

}<commit_msg>bitstamp open price fix<commit_after>class Market {
    constructor() {
        this.apiAdrress = "";
        this.priceInfo = "";
        this.id = null;
    }
    getAdrress() {
        return this.apiAdrress;
    }
    fetchPrices(fctn) {}

    getPriceInfo() {
        return this.priceInfo;
    }

    getId() {
        return this.id;
    }
}

class Bitfinex extends Market {

    constructor() {
        super();
        this.apiAdrress = "https://api.bitfinex.com/v2/candles/trade:15m:tBTCUSD/hist?limit=96";
        this.priceInfo = "Based on Bitfinex 24h timeframe";
        this.id = 0;
    }

    fetchPrices(fctn) {
        var id = this.id;
        $.ajax({
            dataType: "json",
            url: this.getAdrress(),
            success: function(data) {
                fctn(data[95][1], data[0][2], id);
            }
        });
    }

}

class Bitstamp extends Market {

    constructor() {
        super();
        this.apiAdrress = "https://www.bitstamp.net/api/v2/ticker/btcusd/";
        this.priceInfo = "Based on Bistamp daily performance";
        this.id = 1;
    }

    fetchPrices(fctn) {
        var id = this.id;
        $.ajax({
            dataType: "json",
            url: this.getAdrress(),
            success: function(data) {
                fctn(data["open"], data["last"], id);
            }
        });
    }

}<|endoftext|>


<commit_before>(function () {
  "use strict";
    var request = new XMLHttpRequest();
    request.open('GET', '//35.161.59.228:12345/fakedata', true);
    request.onload = function() {
        if (this.status === 200) {
            var jsonResponse = JSON.parse(this.responseText);
            for (var i = 0; i < jsonResponse.length; i++) {
                var new_word = document.createElement("div");
                new_word.className = "word";
                new_word.setAttribute("_lat", jsonResponse[i].lat);
                new_word.setAttribute("_lng", jsonResponse[i].lon);
                new_word.setAttribute("_pop", jsonResponse[i].pop);
                new_word.setAttribute("_id", jsonResponse[i].mongoID);
                new_word.setAttribute("_latestMessage", jsonResponse[i].latestMessage);
                new_word.innerText = jsonResponse[i].messageHeading;
                new_word.addEventListener("click", loadMessages(new_word.getAttribute("_id")), false);
                document.getElementById("words").appendChild(new_word);
            }
        }
        else {
            alert('Request failed.  Returned status of ' + this.status);
        }
    };

    request.send();

function loadMessages(msgId) {
    document.getElementById('typeBox').disabled = false;
    document.getElementById('send').disabled = false;
    document.getElementById('chat').style.visibility = "visible";

    var request = new XMLHttpRequest();
    var url = '//35.161.59.228:12345/fakedata/' + msgId;
    request.open('GET', url, true);
    request.onload = function() {
        if (this.status === 200) {
            var jsonResponse = JSON.parse(this.responseText);
            for (var i = 0; i < jsonResponse.length; i++) {
                var new_message = document.createElement("h2");
                new_message.className = "message";
                new_word.innerText = jsonResponse[i].msg;
                document.getElementById("chatLog").appendChild(new_message);
            }
        }
        else {
            alert('Request failed.  Returned status of ' + this.status);
        }
    };
    request.send();
}

    //set to true for debugging output
  var debug = false;

  // our current position
  var positionCurrent = {
    lat: null,
    lng: null,
    hng: null
  };

  // elements that ouput our position
  var positionLat = document.getElementById("position-lat");
  var positionLng = document.getElementById("position-lng");
  var positionHng = document.getElementById("position-hng");

  // // debug outputs
  // var debugOrientation = document.getElementById("debug-orientation");
  // var debugOrientationDefault = document.getElementById("debug-orientation-default");
  //
  //
  // // info popup elements, pus buttons that open popups
  // var popup = document.getElementById("popup");
  // var popupContents = document.getElementById("popup-contents");
  // var popupInners = document.querySelectorAll(".popup__inner");
  // var btnsPopup = document.querySelectorAll(".btn-popup");

  // buttons at the bottom of the screen
  // var btnLockOrientation = document.getElementById("btn-lock-orientation");
  // var btnNightmode = document.getElementById("btn-nightmode");
  // var btnMap = document.getElementById("btn-map");
  // var btnInfo = document.getElementById("btn-info");

  // if we have shown the heading unavailable warning yet
  var warningHeadingShown = false;

  // switches keeping track of our current app state
  var isOrientationLockable = false;
  var isOrientationLocked = false;
  var isNightMode = false;

  // the orientation of the device on app load
  var defaultOrientation;

  // browser agnostic orientation
  function getBrowserOrientation() {
    var orientation;
    if (screen.orientation && screen.orientation.type) {
      orientation = screen.orientation.type;
    } else {
      orientation = screen.orientation ||
                    screen.mozOrientation ||
                    screen.msOrientation;
    }

    /*
      'portait-primary':      for (screen width < screen height, e.g. phone, phablet, small tablet)
                                device is in 'normal' orientation
                              for (screen width > screen height, e.g. large tablet, laptop)
                                device has been turned 90deg clockwise from normal

      'portait-secondary':    for (screen width < screen height)
                                device has been turned 180deg from normal
                              for (screen width > screen height)
                                device has been turned 90deg anti-clockwise (or 270deg clockwise) from normal

      'landscape-primary':    for (screen width < screen height)
                                device has been turned 90deg clockwise from normal
                              for (screen width > screen height)
                                device is in 'normal' orientation

      'landscape-secondary':  for (screen width < screen height)
                                device has been turned 90deg anti-clockwise (or 270deg clockwise) from normal
                              for (screen width > screen height)
                                device has been turned 180deg from normal
    */

    return orientation;
  }

    // Converts from degrees to radians.
    Math.radians = function(degrees) {
        return degrees * Math.PI / 180;
    };

    // Converts from radians to degrees.
    Math.degrees = function(radians) {
        return radians * 180 / Math.PI;
    };

    // called on device orientation change
    function onHeadingChange(event) {
        var heading = event.alpha;

        if (typeof event.webkitCompassHeading !== "undefined") {
            heading = event.webkitCompassHeading; //iOS non-standard
        }

        var orientation = getBrowserOrientation();

        if (typeof heading !== "undefined" && heading !== null) { // && typeof orientation !== "undefined") {
            // we have a browser that reports device heading and orientation

            // what adjustment we have to add to rotation to allow for current device orientation
            var adjustment = 0;
            if (defaultOrientation === "landscape") {
                adjustment -= 90;
            }

            if (typeof orientation !== "undefined") {
                var currentOrientation = orientation.split("-");

                if (defaultOrientation !== currentOrientation[0]) {
                    if (defaultOrientation === "landscape") {
                        adjustment -= 270;
                    } else {
                        adjustment -= 90;
                    }
                }

                if (currentOrientation[1] === "secondary") {
                    adjustment -= 180;
                }
            }

            positionCurrent.hng = heading + adjustment;
            var phase = positionCurrent.hng < 0 ? 360 + positionCurrent.hng : positionCurrent.hng;
            positionHng.textContent = (360 - phase | 0) + "°";

            // // apply rotation to compass rose
            // if (typeof rose.style.transform !== "undefined") {
            //     rose.style.transform = "rotateZ(" + positionCurrent.hng + "deg)";
            // } else if (typeof rose.style.webkitTransform !== "undefined") {
            //     rose.style.webkitTransform = "rotateZ(" + positionCurrent.hng + "deg)";
            // }
            var words = document.getElementsByClassName("word");
            var lng1 = Math.radians(positionCurrent.lng);
            var lat1 = Math.radians(positionCurrent.lat);
            for (var i = 0; i < words.length; ++i) {
                var item = words[i];
                var lng2 = Math.radians(item.getAttribute("_lng"));
                var lat2 = Math.radians(item.getAttribute("_lat"));
                var y = Math.sin(lng2-lng1) * Math.cos(lat2);
                var x = Math.cos(lat1)*Math.sin(lat2) -
                    Math.sin(lat1)*Math.cos(lat2)*Math.cos(lng2-lng1);
                var brng = Math.degrees(Math.atan2(y, x));

                var angle = positionCurrent.hng - brng;

                var radius_earth = 6371e3; // metres
                var delta_lat = lat2-lat1;
                var delta_lng = lng2-lng1;

                var a = Math.sin(delta_lat/2) * Math.sin(delta_lat/2) +
                    Math.cos(lat1) * Math.cos(lat2) *
                    Math.sin(delta_lng/2) * Math.sin(delta_lng/2);
                var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

                var distance = radius_earth * c;
                if (distance < 1) {
                    distance = 1;
                }
                item.innerText = parseInt(distance) + "m";

                // Apply corresponding font effects.
                item.style.fontSize = (90 + (item.getAttribute("_pop") * 10)) + "%";
                var isCloseToInteractWith;
                if (distance <= 10) {
                    item.style.color = "white";
                    item.style.fontSizeAdjust = "1.2";
                    isCloseToInteractWith = true;
                } else {
                    item.style.color = "#A9A9A9";
                    item.style.fontSizeAdjust = "1.0";
                    isCloseToInteractWith = false;
                }

                // Apply correct transforms
                if (typeof item.style.transform !== "undefined") {
                    item.style.transform = "rotateZ(" + (-1 * angle) + "deg)";
                    item.style.transformOrigin = "50% 200px";
                    item.style.transform += "translate(0px, " + ((distance / -0.2) - (!isCloseToInteractWith * 50)) + "px)";
                } else if (typeof item.style.webkitTransform !== "undefined") {
                    item.style.webkitTransform = "rotateZ(-" + (-1 * angle) + "deg)";
                    item.style.webkitTransformOrigin = "50% 200px";
                    item.style.webkitTransform += "translate(0px, " + ((distance / -0.2) - (!isCloseToInteractWith * 50))  + "px)";
                }

                //making new topics glow potentialls
                /*
                var prevLatestMessage = item.getAttribute("latestMessage");
                watch(locationUpdate, function(){
                  var newLatestMessage = item.getAttribute("latestMessage");
                  if(prevLatestMessage < newLatestMessage) {
                    item.style.boxShadow = "0 0 10px";
                    prevLatestMessage = newLatestMessage;
                  }
                })
                */
            }

        } else {
            positionHng.textContent = "n/a";
            showHeadingWarning();
        }
    }

    function showHeadingWarning() {
        if (!warningHeadingShown) {
            popupOpen("noorientation");
            warningHeadingShown = true;
        }
    }

  function onFullscreenChange() {
    if (isOrientationLockable && getBrowserFullscreenElement()) {
      if (screen.orientation && screen.orientation.lock) {
        screen.orientation.lock(getBrowserOrientation()).then(function () {
        }).catch(function () {
        });
      }
    } else {
      lockOrientationRequest(false);
    }
  }

  function toggleOrientationLockable(lockable) {
    isOrientationLockable = lockable;
  }

  function checkLockable() {
    if (screen.orientation && screen.orientation.lock) {
      screen.orientation.lock(getBrowserOrientation()).then(function () {
        toggleOrientationLockable(true);
        browserUnlockOrientation();
      }).catch(function (event) {
        if (event.code === 18) { // The page needs to be fullscreen in order to call lockOrientation(), but is lockable
          toggleOrientationLockable(true);
          browserUnlockOrientation(); //needed as chrome was locking orientation (even if not in fullscreen, bug??)
        } else {  // lockOrientation() is not available on this device (or other error)
          toggleOrientationLockable(false);
        }
      });
    } else {
      toggleOrientationLockable(false);
    }
  }

  function lockOrientationRequest(doLock) {
    if (isOrientationLockable) {
      if (doLock) {
        browserRequestFullscreen();
        lockOrientation(true);
      } else {
        browserUnlockOrientation();
        browserExitFullscreen();
        lockOrientation(false);
      }
    }
  }

  function lockOrientation(locked) {
    if (locked) {
      btnLockOrientation.classList.add("active");
    } else {
      btnLockOrientation.classList.remove("active");
    }

    isOrientationLocked = locked;
  }

  function toggleOrientationLock() {
    if (isOrientationLockable) {
      lockOrientationRequest(!isOrientationLocked);
    }
  }

  function locationUpdate(position) {
    positionCurrent.lat = position.coords.latitude;
    positionCurrent.lng = position.coords.longitude;

    positionLat.textContent = positionCurrent.lat;
    positionLng.textContent = positionCurrent.lng;
  }

  function locationUpdateFail(error) {
    positionLat.textContent = "n/a";
    positionLng.textContent = "n/a";
    console.log("location fail: ", error);
  }

    if (screen.width > screen.height) {
        defaultOrientation = "landscape";
    } else {
        defaultOrientation = "portrait";
    }

    document.addEventListener("fullscreenchange", onFullscreenChange);
    document.addEventListener("webkitfullscreenchange", onFullscreenChange);
    document.addEventListener("mozfullscreenchange", onFullscreenChange);
    document.addEventListener("MSFullscreenChange", onFullscreenChange);

  window.addEventListener("deviceorientation", onHeadingChange);

  navigator.geolocation.watchPosition(locationUpdate, locationUpdateFail, {
      enableHighAccuracy: true,
      maximumAge: 0,
      timeout: 30000 //large timeout to accomodate slow GPS lock on some devices
  });
  checkLockable();

}());
<commit_msg>anged<commit_after>(function () {
  "use strict";
    var request = new XMLHttpRequest();
    request.open('GET', '//35.161.59.228:12345/fakedata', true);
    request.onload = function() {
        if (this.status === 200) {
            var jsonResponse = JSON.parse(this.responseText);
            for (var i = 0; i < jsonResponse.length; i++) {
                var new_word = document.createElement("div");
                new_word.className = "word";
                new_word.setAttribute("_lat", jsonResponse[i].lat);
                new_word.setAttribute("_lng", jsonResponse[i].lon);
                new_word.setAttribute("_pop", jsonResponse[i].pop);
                new_word.setAttribute("_id", jsonResponse[i].mongoID);
                new_word.setAttribute("_latestMessage", jsonResponse[i].latestMessage);
                new_word.innerText = jsonResponse[i].messageHeading;
                new_word.addEventListener("click", loadMessages(new_word.getAttribute("_id")), false);
                document.getElementById("words").appendChild(new_word);
            }
        }
        else {
            alert('Request failed.  Returned status of ' + this.status);
        }
    };

    request.send();

function loadMessages(msgId) {
    document.getElementById('typeBox').disabled = false;
    document.getElementById('send').disabled = false;
    document.getElementById('chatLog').style.visibility = "visible";

    var request = new XMLHttpRequest();
    var url = '//35.161.59.228:12345/fakedata/' + msgId;
    request.open('GET', url, true);
    request.onload = function() {
        if (this.status === 200) {
            var jsonResponse = JSON.parse(this.responseText);
            for (var i = 0; i < jsonResponse.length; i++) {
                var new_message = document.createElement("h2");
                new_message.className = "message";
                new_word.innerText = jsonResponse[i].msg;
                document.getElementById("chatLog").appendChild(new_message);
            }
        }
        else {
            alert('Request failed.  Returned status of ' + this.status);
        }
    };
    request.send();
}

    //set to true for debugging output
  var debug = false;

  // our current position
  var positionCurrent = {
    lat: null,
    lng: null,
    hng: null
  };

  // elements that ouput our position
  var positionLat = document.getElementById("position-lat");
  var positionLng = document.getElementById("position-lng");
  var positionHng = document.getElementById("position-hng");

  // // debug outputs
  // var debugOrientation = document.getElementById("debug-orientation");
  // var debugOrientationDefault = document.getElementById("debug-orientation-default");
  //
  //
  // // info popup elements, pus buttons that open popups
  // var popup = document.getElementById("popup");
  // var popupContents = document.getElementById("popup-contents");
  // var popupInners = document.querySelectorAll(".popup__inner");
  // var btnsPopup = document.querySelectorAll(".btn-popup");

  // buttons at the bottom of the screen
  // var btnLockOrientation = document.getElementById("btn-lock-orientation");
  // var btnNightmode = document.getElementById("btn-nightmode");
  // var btnMap = document.getElementById("btn-map");
  // var btnInfo = document.getElementById("btn-info");

  // if we have shown the heading unavailable warning yet
  var warningHeadingShown = false;

  // switches keeping track of our current app state
  var isOrientationLockable = false;
  var isOrientationLocked = false;
  var isNightMode = false;

  // the orientation of the device on app load
  var defaultOrientation;

  // browser agnostic orientation
  function getBrowserOrientation() {
    var orientation;
    if (screen.orientation && screen.orientation.type) {
      orientation = screen.orientation.type;
    } else {
      orientation = screen.orientation ||
                    screen.mozOrientation ||
                    screen.msOrientation;
    }

    /*
      'portait-primary':      for (screen width < screen height, e.g. phone, phablet, small tablet)
                                device is in 'normal' orientation
                              for (screen width > screen height, e.g. large tablet, laptop)
                                device has been turned 90deg clockwise from normal

      'portait-secondary':    for (screen width < screen height)
                                device has been turned 180deg from normal
                              for (screen width > screen height)
                                device has been turned 90deg anti-clockwise (or 270deg clockwise) from normal

      'landscape-primary':    for (screen width < screen height)
                                device has been turned 90deg clockwise from normal
                              for (screen width > screen height)
                                device is in 'normal' orientation

      'landscape-secondary':  for (screen width < screen height)
                                device has been turned 90deg anti-clockwise (or 270deg clockwise) from normal
                              for (screen width > screen height)
                                device has been turned 180deg from normal
    */

    return orientation;
  }

    // Converts from degrees to radians.
    Math.radians = function(degrees) {
        return degrees * Math.PI / 180;
    };

    // Converts from radians to degrees.
    Math.degrees = function(radians) {
        return radians * 180 / Math.PI;
    };

    // called on device orientation change
    function onHeadingChange(event) {
        var heading = event.alpha;

        if (typeof event.webkitCompassHeading !== "undefined") {
            heading = event.webkitCompassHeading; //iOS non-standard
        }

        var orientation = getBrowserOrientation();

        if (typeof heading !== "undefined" && heading !== null) { // && typeof orientation !== "undefined") {
            // we have a browser that reports device heading and orientation

            // what adjustment we have to add to rotation to allow for current device orientation
            var adjustment = 0;
            if (defaultOrientation === "landscape") {
                adjustment -= 90;
            }

            if (typeof orientation !== "undefined") {
                var currentOrientation = orientation.split("-");

                if (defaultOrientation !== currentOrientation[0]) {
                    if (defaultOrientation === "landscape") {
                        adjustment -= 270;
                    } else {
                        adjustment -= 90;
                    }
                }

                if (currentOrientation[1] === "secondary") {
                    adjustment -= 180;
                }
            }

            positionCurrent.hng = heading + adjustment;
            var phase = positionCurrent.hng < 0 ? 360 + positionCurrent.hng : positionCurrent.hng;
            positionHng.textContent = (360 - phase | 0) + "°";

            // // apply rotation to compass rose
            // if (typeof rose.style.transform !== "undefined") {
            //     rose.style.transform = "rotateZ(" + positionCurrent.hng + "deg)";
            // } else if (typeof rose.style.webkitTransform !== "undefined") {
            //     rose.style.webkitTransform = "rotateZ(" + positionCurrent.hng + "deg)";
            // }
            var words = document.getElementsByClassName("word");
            var lng1 = Math.radians(positionCurrent.lng);
            var lat1 = Math.radians(positionCurrent.lat);
            for (var i = 0; i < words.length; ++i) {
                var item = words[i];
                var lng2 = Math.radians(item.getAttribute("_lng"));
                var lat2 = Math.radians(item.getAttribute("_lat"));
                var y = Math.sin(lng2-lng1) * Math.cos(lat2);
                var x = Math.cos(lat1)*Math.sin(lat2) -
                    Math.sin(lat1)*Math.cos(lat2)*Math.cos(lng2-lng1);
                var brng = Math.degrees(Math.atan2(y, x));

                var angle = positionCurrent.hng - brng;

                var radius_earth = 6371e3; // metres
                var delta_lat = lat2-lat1;
                var delta_lng = lng2-lng1;

                var a = Math.sin(delta_lat/2) * Math.sin(delta_lat/2) +
                    Math.cos(lat1) * Math.cos(lat2) *
                    Math.sin(delta_lng/2) * Math.sin(delta_lng/2);
                var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

                var distance = radius_earth * c;
                if (distance < 1) {
                    distance = 1;
                }
                item.innerText = parseInt(distance) + "m";

                // Apply corresponding font effects.
                item.style.fontSize = (90 + (item.getAttribute("_pop") * 10)) + "%";
                var isCloseToInteractWith;
                if (distance <= 10) {
                    item.style.color = "white";
                    item.style.fontSizeAdjust = "1.2";
                    isCloseToInteractWith = true;
                } else {
                    item.style.color = "#A9A9A9";
                    item.style.fontSizeAdjust = "1.0";
                    isCloseToInteractWith = false;
                }

                // Apply correct transforms
                if (typeof item.style.transform !== "undefined") {
                    item.style.transform = "rotateZ(" + (-1 * angle) + "deg)";
                    item.style.transformOrigin = "50% 200px";
                    item.style.transform += "translate(0px, " + ((distance / -0.2) - (!isCloseToInteractWith * 50)) + "px)";
                } else if (typeof item.style.webkitTransform !== "undefined") {
                    item.style.webkitTransform = "rotateZ(-" + (-1 * angle) + "deg)";
                    item.style.webkitTransformOrigin = "50% 200px";
                    item.style.webkitTransform += "translate(0px, " + ((distance / -0.2) - (!isCloseToInteractWith * 50))  + "px)";
                }

                //making new topics glow potentialls
                /*
                var prevLatestMessage = item.getAttribute("latestMessage");
                watch(locationUpdate, function(){
                  var newLatestMessage = item.getAttribute("latestMessage");
                  if(prevLatestMessage < newLatestMessage) {
                    item.style.boxShadow = "0 0 10px";
                    prevLatestMessage = newLatestMessage;
                  }
                })
                */
            }

        } else {
            positionHng.textContent = "n/a";
            showHeadingWarning();
        }
    }

    function showHeadingWarning() {
        if (!warningHeadingShown) {
            popupOpen("noorientation");
            warningHeadingShown = true;
        }
    }

  function onFullscreenChange() {
    if (isOrientationLockable && getBrowserFullscreenElement()) {
      if (screen.orientation && screen.orientation.lock) {
        screen.orientation.lock(getBrowserOrientation()).then(function () {
        }).catch(function () {
        });
      }
    } else {
      lockOrientationRequest(false);
    }
  }

  function toggleOrientationLockable(lockable) {
    isOrientationLockable = lockable;
  }

  function checkLockable() {
    if (screen.orientation && screen.orientation.lock) {
      screen.orientation.lock(getBrowserOrientation()).then(function () {
        toggleOrientationLockable(true);
        browserUnlockOrientation();
      }).catch(function (event) {
        if (event.code === 18) { // The page needs to be fullscreen in order to call lockOrientation(), but is lockable
          toggleOrientationLockable(true);
          browserUnlockOrientation(); //needed as chrome was locking orientation (even if not in fullscreen, bug??)
        } else {  // lockOrientation() is not available on this device (or other error)
          toggleOrientationLockable(false);
        }
      });
    } else {
      toggleOrientationLockable(false);
    }
  }

  function lockOrientationRequest(doLock) {
    if (isOrientationLockable) {
      if (doLock) {
        browserRequestFullscreen();
        lockOrientation(true);
      } else {
        browserUnlockOrientation();
        browserExitFullscreen();
        lockOrientation(false);
      }
    }
  }

  function lockOrientation(locked) {
    if (locked) {
      btnLockOrientation.classList.add("active");
    } else {
      btnLockOrientation.classList.remove("active");
    }

    isOrientationLocked = locked;
  }

  function toggleOrientationLock() {
    if (isOrientationLockable) {
      lockOrientationRequest(!isOrientationLocked);
    }
  }

  function locationUpdate(position) {
    positionCurrent.lat = position.coords.latitude;
    positionCurrent.lng = position.coords.longitude;

    positionLat.textContent = positionCurrent.lat;
    positionLng.textContent = positionCurrent.lng;
  }

  function locationUpdateFail(error) {
    positionLat.textContent = "n/a";
    positionLng.textContent = "n/a";
    console.log("location fail: ", error);
  }

    if (screen.width > screen.height) {
        defaultOrientation = "landscape";
    } else {
        defaultOrientation = "portrait";
    }

    document.addEventListener("fullscreenchange", onFullscreenChange);
    document.addEventListener("webkitfullscreenchange", onFullscreenChange);
    document.addEventListener("mozfullscreenchange", onFullscreenChange);
    document.addEventListener("MSFullscreenChange", onFullscreenChange);

  window.addEventListener("deviceorientation", onHeadingChange);

  navigator.geolocation.watchPosition(locationUpdate, locationUpdateFail, {
      enableHighAccuracy: true,
      maximumAge: 0,
      timeout: 30000 //large timeout to accomodate slow GPS lock on some devices
  });
  checkLockable();

}());
<|endoftext|>


<commit_before>
Elm.Signal = function() {
  var send = function(node, timestep, changed) {
    var kids = node.kids;
    for (var i = kids.length; i--; ) {
      kids[i].recv(timestep, changed, node.id);
    }
  };
  /**
   * @constructor
   */
  function input(base) {
    this.id = Guid.guid();
    this.value = base;
    this.kids = [];
    this.defaultNumberOfKids = 0;
    this.recv = function(timestep, eid, v) {
      var changed = eid === this.id;
      if (changed) { this.value = v; }
      send(this, timestep, changed);
      return changed;
    };
    Dispatcher.inputs.push(this);
  };
  /**
   * @constructor
   */
  function lift(func,args) {
    this.id = Guid.guid();
    this.value = null;
    this.kids = [];
    this.count = 0;
    this.changed = false;

    args.reverse();
    this.recalc = function() {
	var f = func;
	for (var i = args.length; i--; ) {
	    f = f(args[i].value);
	}
	this.value = f;
    };
    this.recalc();

    this.recv = function(timestep, changed, parentID) {
      this.count += 1;
      if (changed) { this.changed = true; }
      if (this.count == args.length) {
	  if (this.changed) { this.recalc() }
	  send(this, timestep, this.changed);
	  this.changed = false;
	  this.count = 0;
      }
    };
    for (var i = args.length; i--; ) {
      args[i].kids.push(this);
    }
  };
  /**
   * @constructor
   */
  function fold(func,base,baseIsFunc,input) {
    this.id = Guid.guid();
    this.value = baseIsFunc ? base(input.value) : base;
    this.kids = [];
    this.recv = function(timestep, changed, parentID) {
      if (changed) { this.value = func(input.value)(this.value); }
      send(this, timestep, changed);
    };
    input.kids.push(this);
  };

  /**
   * @constructor
   */
  function dropIf(pred,base,input) {
    this.id = Guid.guid();
    this.value = pred(input.value) ? base : input.value;
    this.kids = [];
    this.recv = function(timestep, changed, parentID) {
	var chng = changed && !pred(input.value);
	if (chng) { this.value = input.value; }
	send(this, timestep, chng);
    };
    input.kids.push(this);
  };

  /**
   * @constructor
   */
  function dropRepeats(input) {
      this.id = Guid.guid();
      this.value = input.value;
      this.kids = [];
      this.recv = function(timestep, changed, parentID) {
	  var chng = changed && !Value.eq(this.value,input.value);
	  if (chng) { this.value = input.value; }
	  send(this, timestep, chng);
      };
      input.kids.push(this);
  };

  var dropWhen = function(s1) { return function(b) { return function(s2) {
    var pairs = new lift(function(x){return function(y){return [x,y];};},[s1,s2]);
    var dropped = new dropIf(function(p){return p[0];},[true,b],pairs);
    return new lift(function(p){return p[1];},[dropped]); }; };
  };

  /**
   * @constructor
   */
  function timeIndex(pair,s) {
      this.id = Guid.guid();
      var t = (new window.Date).getTime();
      this.value = pair ? Value.Tuple(t,s.value) : t;
      this.kids = [];
      this.recv = function(timestep, changed, parentID) {
	  if (changed) this.value = pair ? Value.Tuple(timestep, s.value) : timestep;
	  send(this, timestep, changed);
      };
      s.kids.push(this);
  }

  /**
   * @constructor
   */
  function sampleOn(s1,s2) {
    this.id = Guid.guid();
    this.value = s2.value;
    this.kids = [];
    this.count = 0;
    this.changed = false;

    this.recv = function(timestep, changed, parentID) {
	if (parentID === s1.id) this.changed = changed;
	this.count += 1;
	if (this.count == 2) {
	    if (this.changed) { this.value = s2.value; }
	    send(this, timestep, this.changed);
	    this.count = 0;
	    this.changed = false;
	}
    };
    s1.kids.push(this);
    s2.kids.push(this);
  };

  function delay(t) { return function(s) {
      var delayed = new input(s.value);
      var firstEvent = true;
      function update(v) {
	  if (firstEvent) return;
	  setTimeout(function() { Dispatcher.notify(delayed.id,v); },t);
      }
      function first(a) { return function(b) { return a; }; }
      var out = new sampleOn(delayed,new lift(first, [delayed, new lift(update,[s])]));
      firstEvent = false;
      return out;
    };
  }

  /**
   * @constructor
   */
  function merge(s1,s2) {
    this.id = Guid.guid();
    this.value = s1.value;
    this.kids = [];
    this.next = null;
    this.count = 0;
    this.changed = false;

    this.recv = function(timestep, changed, parentID) {
      this.count += 1;
      if (changed) {
	  this.changed = true;
	  if (parentID == s2.id && this.next === null) { this.next = s2.value; }
	  if (parentID == s1.id) { this.next = s1.value; }
      }

      if (this.count == 2) {
	  if (this.changed) {
	      this.value = this.next;
	      this.next = null;
	  }
	  send(this, timestep, this.changed);
	  this.changed = false;
	  this.count = 0;
      }
    };
    s1.kids.push(this);
    s2.kids.push(this);
  };

  function merges(ss) {
      function mrg(x) { return function(y) { return new merge(x,y); }; }
      return Elm.List.foldl1(mrg)(ss);
  }

  function mergeEither(s1) { return function(s2) {
      function f(s) { return function(x) { return [s,x]; }; }
      return new merge(new lift(f,[s1]), new lift(f,[s2]));
    };
  }

  function average(sampleSize) { return function(s) {
    var sample = new Array(sampleSize);
    var i = sampleSize;
    while (i--) sample[i] = 0;
    i = 0;
    var full = false;
    var total = 0;
    function f(n) {
	total += n - sample[i];
	sample[i] = n;
	var avg = total / Math.max(1, full ? sampleSize : i);
	if (++i == sampleSize) { full = true; i = 0; }
	return avg;
    }
    return new lift(f, [s]);
   };
  }

  return {
    constant : function(v) { return new input(v); },
    lift  : function(f){return function(e){return new lift(f,[e]);};},
    lift2 : function(f) {
	      return function(e1) { return function(e2) {
	      return new lift(f, [e1,e2]); };};},
    lift3 : function(f) {
	      return function(e1) { return function(e2) {
	      return function(e3){return new lift(f,[e1,e2,e3]);};};};},
    lift4 : function(f) {
	      return function(e1) { return function(e2) {
	      return function(e3) { return function(e4) {
	      return new lift(f, [e1,e2,e3,e4]);};};};};},
    lift5 : function(f) {
	      return function(e1) { return function(e2) {
	      return function(e3) { return function(e4) {
	      return function(e5) {
	      return new lift(f, [e1,e2,e3,e4,e5]);};};};};};},
    lift6 : function(f) { return function(e1) { return function(e2) {
              return function(e3) { return function(e4) {
              return function(e5) { return function(e6) {
	      return new lift(f, [e1,e2,e3,e4,e5,e6]); };};};};};};},
    lift7 : function(f) { return function(e1) { return function(e2) {
              return function(e3) { return function(e4) {
              return function(e5) { return function(e6) {
              return function(e7) {
              return new lift(f, [e1,e2,e3,e4,e5,e6,e7]);};};};};};};};},
    lift8 : function(f) { return function(e1) { return function(e2) {
              return function(e3) { return function(e4) {
              return function(e5) { return function(e6) {
              return function(e7) { return function(e8) {
              return new lift(f, [e1,e2,e3,e4,e5,e6,e7,e8]);};};};};};};};};},
    foldp : function(f) { return function(b) { return function(e) {
		  return new fold(f,b,false,e); }; }; },
    foldp$ : function(f) { return function(b) { return function(e) {
		  return new fold(f,b,true,e); }; }; },
    foldp1 : function(f) { return function(e) {
	      return new fold(f,function(x){return x;},true,e); }; },
    delay : delay,
    merge : function(s1) { return function(s2) { return new merge(s1,s2)}; },
    merges : merges,
    mergeEither : mergeEither,
    average : average,
    count : function(sig) {
	  var incr = function(_){return function(c){return c+1;};};
	  return new fold(incr,0,false,sig) },
    countIf : function(pred) { return function(sig) {
	      var incr = function(x){return function(c){return pred(x) ? c+1 : c;};};
	      return new fold(incr,0,false,sig) }; },
    keepIf : function(pred) { return function(base) { return function(sig) {
		  return new dropIf(function(x) { return !pred(x)},base,sig); }; }; },
    dropIf : function(pred) { return function(base) { return function(sig) {
		  return new dropIf(pred,base,sig); }; }; },
    keepWhen : function(s) { return dropWhen(new lift(function(b){return !b;},[s])); },
    dropWhen : dropWhen,
    dropRepeats : function(s) { return new dropRepeats(s);},
    sampleOn : function(s1){return function(s2){return new sampleOn(s1,s2);};},
    timestamp : function(s) { return new timeIndex(true, s); },
    timeOf : function(s) { return new timeIndex(false, s); }
  };
}();<commit_msg>Fix silly error with `mergeEither`.<commit_after>
Elm.Signal = function() {
  var send = function(node, timestep, changed) {
    var kids = node.kids;
    for (var i = kids.length; i--; ) {
      kids[i].recv(timestep, changed, node.id);
    }
  };
  /**
   * @constructor
   */
  function input(base) {
    this.id = Guid.guid();
    this.value = base;
    this.kids = [];
    this.defaultNumberOfKids = 0;
    this.recv = function(timestep, eid, v) {
      var changed = eid === this.id;
      if (changed) { this.value = v; }
      send(this, timestep, changed);
      return changed;
    };
    Dispatcher.inputs.push(this);
  };
  /**
   * @constructor
   */
  function lift(func,args) {
    this.id = Guid.guid();
    this.value = null;
    this.kids = [];
    this.count = 0;
    this.changed = false;

    args.reverse();
    this.recalc = function() {
	var f = func;
	for (var i = args.length; i--; ) {
	    f = f(args[i].value);
	}
	this.value = f;
    };
    this.recalc();

    this.recv = function(timestep, changed, parentID) {
      this.count += 1;
      if (changed) { this.changed = true; }
      if (this.count == args.length) {
	  if (this.changed) { this.recalc() }
	  send(this, timestep, this.changed);
	  this.changed = false;
	  this.count = 0;
      }
    };
    for (var i = args.length; i--; ) {
      args[i].kids.push(this);
    }
  };
  /**
   * @constructor
   */
  function fold(func,base,baseIsFunc,input) {
    this.id = Guid.guid();
    this.value = baseIsFunc ? base(input.value) : base;
    this.kids = [];
    this.recv = function(timestep, changed, parentID) {
      if (changed) { this.value = func(input.value)(this.value); }
      send(this, timestep, changed);
    };
    input.kids.push(this);
  };

  /**
   * @constructor
   */
  function dropIf(pred,base,input) {
    this.id = Guid.guid();
    this.value = pred(input.value) ? base : input.value;
    this.kids = [];
    this.recv = function(timestep, changed, parentID) {
	var chng = changed && !pred(input.value);
	if (chng) { this.value = input.value; }
	send(this, timestep, chng);
    };
    input.kids.push(this);
  };

  /**
   * @constructor
   */
  function dropRepeats(input) {
      this.id = Guid.guid();
      this.value = input.value;
      this.kids = [];
      this.recv = function(timestep, changed, parentID) {
	  var chng = changed && !Value.eq(this.value,input.value);
	  if (chng) { this.value = input.value; }
	  send(this, timestep, chng);
      };
      input.kids.push(this);
  };

  var dropWhen = function(s1) { return function(b) { return function(s2) {
    var pairs = new lift(function(x){return function(y){return [x,y];};},[s1,s2]);
    var dropped = new dropIf(function(p){return p[0];},[true,b],pairs);
    return new lift(function(p){return p[1];},[dropped]); }; };
  };

  /**
   * @constructor
   */
  function timeIndex(pair,s) {
      this.id = Guid.guid();
      var t = (new window.Date).getTime();
      this.value = pair ? Value.Tuple(t,s.value) : t;
      this.kids = [];
      this.recv = function(timestep, changed, parentID) {
	  if (changed) this.value = pair ? Value.Tuple(timestep, s.value) : timestep;
	  send(this, timestep, changed);
      };
      s.kids.push(this);
  }

  /**
   * @constructor
   */
  function sampleOn(s1,s2) {
    this.id = Guid.guid();
    this.value = s2.value;
    this.kids = [];
    this.count = 0;
    this.changed = false;

    this.recv = function(timestep, changed, parentID) {
	if (parentID === s1.id) this.changed = changed;
	this.count += 1;
	if (this.count == 2) {
	    if (this.changed) { this.value = s2.value; }
	    send(this, timestep, this.changed);
	    this.count = 0;
	    this.changed = false;
	}
    };
    s1.kids.push(this);
    s2.kids.push(this);
  };

  function delay(t) { return function(s) {
      var delayed = new input(s.value);
      var firstEvent = true;
      function update(v) {
	  if (firstEvent) return;
	  setTimeout(function() { Dispatcher.notify(delayed.id,v); },t);
      }
      function first(a) { return function(b) { return a; }; }
      var out = new sampleOn(delayed,new lift(first, [delayed, new lift(update,[s])]));
      firstEvent = false;
      return out;
    };
  }

  /**
   * @constructor
   */
  function merge(s1,s2) {
    this.id = Guid.guid();
    this.value = s1.value;
    this.kids = [];
    this.next = null;
    this.count = 0;
    this.changed = false;

    this.recv = function(timestep, changed, parentID) {
      this.count += 1;
      if (changed) {
	  this.changed = true;
	  if (parentID == s2.id && this.next === null) { this.next = s2.value; }
	  if (parentID == s1.id) { this.next = s1.value; }
      }

      if (this.count == 2) {
	  if (this.changed) {
	      this.value = this.next;
	      this.next = null;
	  }
	  send(this, timestep, this.changed);
	  this.changed = false;
	  this.count = 0;
      }
    };
    s1.kids.push(this);
    s2.kids.push(this);
  };

  function merges(ss) {
      function mrg(x) { return function(y) { return new merge(x,y); }; }
      return Elm.List.foldl1(mrg)(ss);
  }

  function mergeEither(s1) { return function(s2) {
      function f(s) { return function(x) { return [s,x]; }; }
      return new merge(new lift(f('Left'),[s1]), new lift(f('Right'),[s2]));
    };
  }

  function average(sampleSize) { return function(s) {
    var sample = new Array(sampleSize);
    var i = sampleSize;
    while (i--) sample[i] = 0;
    i = 0;
    var full = false;
    var total = 0;
    function f(n) {
	total += n - sample[i];
	sample[i] = n;
	var avg = total / Math.max(1, full ? sampleSize : i);
	if (++i == sampleSize) { full = true; i = 0; }
	return avg;
    }
    return new lift(f, [s]);
   };
  }

  return {
    constant : function(v) { return new input(v); },
    lift  : function(f){return function(e){return new lift(f,[e]);};},
    lift2 : function(f) {
	      return function(e1) { return function(e2) {
	      return new lift(f, [e1,e2]); };};},
    lift3 : function(f) {
	      return function(e1) { return function(e2) {
	      return function(e3){return new lift(f,[e1,e2,e3]);};};};},
    lift4 : function(f) {
	      return function(e1) { return function(e2) {
	      return function(e3) { return function(e4) {
	      return new lift(f, [e1,e2,e3,e4]);};};};};},
    lift5 : function(f) {
	      return function(e1) { return function(e2) {
	      return function(e3) { return function(e4) {
	      return function(e5) {
	      return new lift(f, [e1,e2,e3,e4,e5]);};};};};};},
    lift6 : function(f) { return function(e1) { return function(e2) {
              return function(e3) { return function(e4) {
              return function(e5) { return function(e6) {
	      return new lift(f, [e1,e2,e3,e4,e5,e6]); };};};};};};},
    lift7 : function(f) { return function(e1) { return function(e2) {
              return function(e3) { return function(e4) {
              return function(e5) { return function(e6) {
              return function(e7) {
              return new lift(f, [e1,e2,e3,e4,e5,e6,e7]);};};};};};};};},
    lift8 : function(f) { return function(e1) { return function(e2) {
              return function(e3) { return function(e4) {
              return function(e5) { return function(e6) {
              return function(e7) { return function(e8) {
              return new lift(f, [e1,e2,e3,e4,e5,e6,e7,e8]);};};};};};};};};},
    foldp : function(f) { return function(b) { return function(e) {
		  return new fold(f,b,false,e); }; }; },
    foldp$ : function(f) { return function(b) { return function(e) {
		  return new fold(f,b,true,e); }; }; },
    foldp1 : function(f) { return function(e) {
	      return new fold(f,function(x){return x;},true,e); }; },
    delay : delay,
    merge : function(s1) { return function(s2) { return new merge(s1,s2)}; },
    merges : merges,
    mergeEither : mergeEither,
    average : average,
    count : function(sig) {
	  var incr = function(_){return function(c){return c+1;};};
	  return new fold(incr,0,false,sig) },
    countIf : function(pred) { return function(sig) {
	      var incr = function(x){return function(c){return pred(x) ? c+1 : c;};};
	      return new fold(incr,0,false,sig) }; },
    keepIf : function(pred) { return function(base) { return function(sig) {
		  return new dropIf(function(x) { return !pred(x)},base,sig); }; }; },
    dropIf : function(pred) { return function(base) { return function(sig) {
		  return new dropIf(pred,base,sig); }; }; },
    keepWhen : function(s) { return dropWhen(new lift(function(b){return !b;},[s])); },
    dropWhen : dropWhen,
    dropRepeats : function(s) { return new dropRepeats(s);},
    sampleOn : function(s1){return function(s2){return new sampleOn(s1,s2);};},
    timestamp : function(s) { return new timeIndex(true, s); },
    timeOf : function(s) { return new timeIndex(false, s); }
  };
}();<|endoftext|>


<commit_before>import React, { Component } from "react";
import { Container, Row, Col, Card } from "reactstrap";
import gql from "graphql-tag.macro";
import { graphql, withApollo } from "react-apollo";
import SubscriptionHelper from "helpers/subscriptionHelper";

import "./setConfig.scss";

class SetsPicker extends Component {
  static trainingSteps = [
    {
      selector: ".set-picker",
      content: (

          This is the set picker. Sets connect stations to clients, making it
          easy to immediately set all of the clients to the correct station for
          the flight. Click on the simulator name for the set you want to
          activate.

      )
    }
  ];
  applyClientSet = (
    { id },
    { id: simulatorId, templateId, stationSet: { id: stationSetId } },
    { id: flightId }
  ) => {
    const mutation = gql`
      mutation ApplyClientSet(
        $id: ID!
        $flightId: ID!
        $simulatorId: ID!
        $templateId: ID!
        $stationSetId: ID!
      ) {
        applyClientSet(
          id: $id
          flightId: $flightId
          simulatorId: $simulatorId
          templateId: $templateId
          stationSetId: $stationSetId
        )
      }
    `;
    const variables = {
      id,
      simulatorId,
      templateId,
      flightId,
      stationSetId
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  render() {
    if (
      this.props.data.loading ||
      !this.props.data.flights ||
      !this.props.data.sets
    ) {
      return null;
    }
    const { flights, sets } = this.props.data;
    const flight = flights && flights[0];
    return (
      <Container className="set-picker">
        <SubscriptionHelper
          subscribe={() =>
            this.props.data.subscribeToMore({
              document: FLIGHTS_SUB,
              variables: {
                flightId: this.props.match.params.flightId
              },
              updateQuery: (previousResult, { subscriptionData }) => {
                return Object.assign({}, previousResult, {
                  flights: subscriptionData.data.flightsUpdate
                });
              }
            })
          }
        />

Sets

        <Row className="justify-content-md-center">
          {flight &&
            flight.simulators.map(s => (
              <Col key={s.id} sm={4}>

                  {s.name} - {s.stationSet && s.stationSet.name}

                <Card style={{ maxHeight: "10vh", overflowY: "auto" }}>
                  {sets
                    .filter(se =>
                      se.clients.find(
                        c =>
                          c.simulatorId === s.simulatorId &&
                          c.stationSet.id === s.stationSet.id
                      )
                    )
                    .map(set => (

 this.applyClientSet(set, s, flight)}
                        key={`${s.id}-${set.id}}`}
                      >
                        {set.name}

                    ))}
                </Card>
              </Col>
            ))}
        </Row>
      </Container>
    );
  }
}

const FLIGHTS_SUB = gql`
  subscription FlightsSub($flightId: ID) {
    flightsUpdate(id: $flightId) {
      id
      name
      simulators {
        id
        name
        templateId
        stationSet {
          id
          name
        }
      }
    }
  }
`;

const SETS_QUERY = gql`
  query Sets($flightId: ID) {
    flights(id: $flightId) {
      id
      name
      simulators {
        id
        templateId
        name
        stationSet {
          id
          name
        }
      }
    }
    sets {
      id
      name
      clients {
        id
        client {
          id
        }
        simulator {
          id
          name
        }
        stationSet {
          id
          name
        }
        station
      }
    }
  }
`;

export default graphql(SETS_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: {
      flightId: ownProps.match.params.flightId
    }
  })
})(withApollo(SetsPicker));
<commit_msg>fix(Sets): Fixes an issue where a misconfigured set can cause a client crash. Closes #2284<commit_after>import React, { Component } from "react";
import { Container, Row, Col, Card } from "reactstrap";
import gql from "graphql-tag.macro";
import { graphql, withApollo } from "react-apollo";
import SubscriptionHelper from "helpers/subscriptionHelper";

import "./setConfig.scss";

class SetsPicker extends Component {
  static trainingSteps = [
    {
      selector: ".set-picker",
      content: (

          This is the set picker. Sets connect stations to clients, making it
          easy to immediately set all of the clients to the correct station for
          the flight. Click on the simulator name for the set you want to
          activate.

      )
    }
  ];
  applyClientSet = (
    { id },
    { id: simulatorId, templateId, stationSet: { id: stationSetId } },
    { id: flightId }
  ) => {
    const mutation = gql`
      mutation ApplyClientSet(
        $id: ID!
        $flightId: ID!
        $simulatorId: ID!
        $templateId: ID!
        $stationSetId: ID!
      ) {
        applyClientSet(
          id: $id
          flightId: $flightId
          simulatorId: $simulatorId
          templateId: $templateId
          stationSetId: $stationSetId
        )
      }
    `;
    const variables = {
      id,
      simulatorId,
      templateId,
      flightId,
      stationSetId
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  render() {
    if (
      this.props.data.loading ||
      !this.props.data.flights ||
      !this.props.data.sets
    ) {
      return null;
    }
    const { flights, sets } = this.props.data;
    const flight = flights && flights[0];
    return (
      <Container className="set-picker">
        <SubscriptionHelper
          subscribe={() =>
            this.props.data.subscribeToMore({
              document: FLIGHTS_SUB,
              variables: {
                flightId: this.props.match.params.flightId
              },
              updateQuery: (previousResult, { subscriptionData }) => {
                return Object.assign({}, previousResult, {
                  flights: subscriptionData.data.flightsUpdate
                });
              }
            })
          }
        />

Sets

        <Row className="justify-content-md-center">
          {flight &&
            flight.simulators.map(s => (
              <Col key={s.id} sm={4}>

                  {s.name} - {s.stationSet && s.stationSet.name}

                <Card style={{ maxHeight: "10vh", overflowY: "auto" }}>
                  {sets
                    .filter(se =>
                      se.clients.find(
                        c =>
                          c.stationSet &&
                          c.simulatorId === s.simulatorId &&
                          c.stationSet.id === s.stationSet.id
                      )
                    )
                    .map(set => (

 this.applyClientSet(set, s, flight)}
                        key={`${s.id}-${set.id}}`}
                      >
                        {set.name}

                    ))}
                </Card>
              </Col>
            ))}
        </Row>
      </Container>
    );
  }
}

const FLIGHTS_SUB = gql`
  subscription FlightsSub($flightId: ID) {
    flightsUpdate(id: $flightId) {
      id
      name
      simulators {
        id
        name
        templateId
        stationSet {
          id
          name
        }
      }
    }
  }
`;

const SETS_QUERY = gql`
  query Sets($flightId: ID) {
    flights(id: $flightId) {
      id
      name
      simulators {
        id
        templateId
        name
        stationSet {
          id
          name
        }
      }
    }
    sets {
      id
      name
      clients {
        id
        client {
          id
        }
        simulator {
          id
          name
        }
        stationSet {
          id
          name
        }
        station
      }
    }
  }
`;

export default graphql(SETS_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: {
      flightId: ownProps.match.params.flightId
    }
  })
})(withApollo(SetsPicker));
<|endoftext|>