<commit_before>#!/usr/bin/env node

const { join, dirname, basename } = require('path')
const { promises: fs } = require('fs')
const { safeDump } = require('js-yaml')
const clone = require('clone')
const { fileExists } = require('./utils')

const sourceFolder = process.argv[2]
const destFolder = process.argv[3]

if (!sourceFolder || !destFolder) {
  throw new Error(`Missing parameters sourceFolder and destFolder.

  Expected command:
    ${process.argv[0]} ${process.argv[1]} <sourceFolder> <destFolder>
`)
}

async function main () {
  const releasesFile = join(sourceFolder, 'releases.json')
  const releaseFileContent = await fs.readFile(releasesFile, 'utf-8')
  const releases = JSON.parse(releaseFileContent)
  const masterRelease = releases.find(r => r.name === 'master')
  await createDocSources(releases)
  await createEcosystemDataFile(masterRelease.dest)
  console.log('All releases processed successfully')
}

async function createDocSources (releases) {
  const latestRelease = releases.find(r => r.name === 'latest')
  const tocByRelease = await Promise.all(releases.map(getTOCForRelease))
  const indexedToc = releases.reduce((acc, curr, i) => {
    acc[curr.docsPath] = tocByRelease[i]
    return acc
  }, {})
  const versions = releases.map(r => r.docsPath)
  await createDocsDataFile(join(destFolder, 'data', 'docs.yml'), { versions, toc: indexedToc, releases })
  await processDocFiles(indexedToc, latestRelease)
  await createIndexFiles(releases)
}

async function extractTOCFromFile (file, release) {
  const fileContent = await fs.readFile(file, 'utf8')

  // searches for the beginning of the ToC in the file (between '## Documentation' and '\n\n')
  const lines = fileContent.split('## Documentation')[1].split('\n\n')[0].split('\n').filter(Boolean)
  // every line is a ToC entry
  const re = /master\/docs\/([a-zA-Z-0-9]+\.md)">(.+)<\/b>/
  const toc = lines.map((line) => {
    const match = re.exec(line)
    const fileName = match[1]
    const name = match[2]
    const sourceFile = join(dirname(file), 'docs', fileName)
    const destinationFile = join(destFolder, 'content', 'docs', release.docsPath, fileName)
    const slug = basename(sourceFile, '.md')
    const link = `/docs/${release.docsPath}/${slug}`

    return {
      fileName,
      name,
      sourceFile,
      destinationFile,
      slug,
      link,
      fullVersion: release.fullVersion,
      docsPath: release.docsPath,
      label: release.label
    }
  })

  return toc
}

async function getTOCForRelease (release) {
  const folder = join(sourceFolder, release.dest)
  const files = await fs.readdir(folder)
  const subfolder = files.find(file => file.match(/^fastify-/))
  const indexFile = join(folder, subfolder, 'README.md')

  return extractTOCFromFile(indexFile, release)
}

function createDocsDataFile (destination, docsInfo) {
  const toDump = clone(docsInfo)
  // remove sourceFile and destinationFile keys from toc
  Object.keys(toDump.toc).forEach((version) => {
    toDump.toc[version].forEach((entry, i) => {
      delete toDump.toc[version][i].sourceFile
      delete toDump.toc[version][i].destinationFile
    })
  })

  return fs.writeFile(destination, safeDump(toDump), 'utf8')
}

async function processDocFiles (docs, latestRelease) {
  // merge all docs into a single array adding the version as a key in every object
  const docsArray = Object.keys(docs).reduce((acc, version) => {
    const curr = docs[version]
    return acc.concat(curr.map((item) => {
      item.version = version
      return item
    }))
  }, [])

  for (const item of docsArray) {
    const dir = dirname(item.destinationFile)
    const hasDir = await fileExists(dir)
    if (!hasDir) {
      await fs.mkdir(dir)
    }

    const buffer = await fs.readFile(item.sourceFile, 'utf8')

    let content = buffer.toString()

    // removes doc header from github
    content = content.replace(/
Fastify<\/h1>\n/, '')

    // remap links
    content = remapLinks(content, item)

    // adds frontmatter
    content =
`---
title: ${item.name}
layout: docs_page.html
path: ${item.link}
version: ${item.version}
fullVersion: ${item.fullVersion}
label: ${item.label}
docsPath: ${item.docsPath}
${item.version === 'latest' ? `canonical: "${item.link.replace(/latest/, latestRelease.label)}"` : ''}
${item.version === 'master' ? `github_url: https://github.com/fastify/fastify/blob/master/docs/${item.fileName}` : ''}
---
${content}`

    await fs.writeFile(item.destinationFile, content, 'utf8')
    console.log(`Created doc page ${item.destinationFile}`)
  }
}

function remapLinks (content, item) {
  /*
    Links remapping rules:
    /https:\/\/github.com\/fastify\/fastify\/blob\/master\/docs/ -> /docs/[VERSION]
    [XXXX](Plugins.md) -> [XXXX](/docs/[VERSION]/Plugins)
    [XXXX](/docs/VVVV/Ecosystem.md) -> [XXXX](/ecosystem)
    [XXXX](/docs/VVVV/YYYY.md) -> [XXXX](/docs/VVVV/YYYY)
    [XXXX]('./YYYY' "ZZZZ") -> [XXXX]('/docs/[VERSION]/YYYY' "ZZZZ")
    [XXXX](./YYYY "ZZZZ") -> [XXXX]('/docs/[VERSION]/YYYY' "ZZZZ")
    href="https://github.com/fastify/fastify/blob/master/docs/YYYY.md -> href="/docs/[VERSION]/YYYY
  */
  const ecosystemLinkRx = /\(\/docs\/[\w\d.-]+\/Ecosystem\.md\)/gi
  const docInternalLinkRx = /\(\/docs\/[\w\d.-]+\/[\w\d-]+(.md)/gi
  const pluginsLink = /\(Plugins.md\)/gi
  const relativeLinksWithLabel = /\('?(\.\/)([\w\d.-]+)(.md)'?\s+"([\w\d.-]+)"\)/gi
  const hrefAbsoluteLinks = /href="https:\/\/github\.com\/fastify\/fastify\/blob\/master\/docs\/([\w\d.-]+)\.md/gi
  return content
    .replace(hrefAbsoluteLinks, (match, p1) => `href="/docs/${item.version}/${p1}`)
    .replace(/https:\/\/github.com\/fastify\/fastify\/blob\/master\/docs/g, `/docs/${item.version}`)
    .replace(ecosystemLinkRx, (match) => '(/ecosystem)')
    .replace(pluginsLink, (match) => `(/docs/${item.version}/Plugins)`)
    .replace(relativeLinksWithLabel, (match, ...parts) => `(/docs/${item.version}/${parts[1]} "${parts[3]}")`)
    .replace(docInternalLinkRx, (match, p1) => match.replace(p1, ''))
}

async function createVersionIndexFile (release) {
  const content = `---
title: Documentation - ${release.name}
layout: docs_version_index.html
path: /docs/${release.docsPath}
version: ${release.name}
fullVersion: ${release.fullVersion}
label: ${release.label}
docsPath: ${release.docsPath}
${release.name === 'latest' ? `canonical: "/docs/${release.label}"` : ''}
github_url: "https://github.com/fastify/website/blob/master/src/website/layouts/docs_version_index.html"
---`

  const dest = join(destFolder, 'content', 'docs', release.docsPath, 'index.md')
  await fs.writeFile(dest, content, 'utf8')
  console.log(`Created doc index page ${dest}`)
}

async function createIndexFiles (releases) {
  // create docs index

  const docsIndexContent = `---
title: Documentation
layout: docs_index.html
path: /docs
github_url: "https://github.com/fastify/website/blob/master/src/website/layouts/docs_index.html"
---`

  const dest = join(destFolder, 'content', 'docs', 'index.md')
  await fs.writeFile(dest, docsIndexContent, 'utf8')
  console.log(`Created docs index at ${dest}`)

  for (const release of releases) {
    await createVersionIndexFile(release)
  }
}

const extractPlugins = (pluginContent) => {
  const lines = pluginContent
    .split('\n')
    .filter(Boolean) // remove empty lines

  // if a line doesn't start with "-" merge it back with the previous item
  const mergedLines = lines.reduce((acc, curr) => {
    if (curr[0] === '-') {
      acc.push(curr)
    } else {
      acc[acc.length - 1] += ' ' + curr
    }
    return acc
  }, [])
  const re = /\[`([-a-zA-Z0-9]+)`\]\(([^)]+)\)(\s*(.+))?/
  const plugins = mergedLines.map((line) => {
    const match = re.exec(line)

    const name = match[1]
    const url = match[2]
    const description = match[3] ? match[3].trim() : ''

    return { name, url, description }
  })
  return plugins
}

async function extractEcosystemFromFile (file) {
  const data = await fs.readFile(file, 'utf8')

  const content = data.toString()
  const corePluginsContent = content
    .split('#### [Core](#core)\n')[1]
    .split('#### [Community](#community)')[0]

  const communityPluginsContent = content
    .split('#### [Core](#core)\n')[1]
    .split('#### [Community](#community)')[1]

  const plugins = {
    corePlugins: extractPlugins(corePluginsContent),
    communityPlugins: extractPlugins(communityPluginsContent)
  }

  return ({ plugins })
}

async function createEcosystemDataFile (masterReleaseDownloadPath) {
  const versionFolder = join(sourceFolder, masterReleaseDownloadPath)
  const destination = join(destFolder, 'data', 'ecosystem.yml')
  const files = await fs.readdir(versionFolder)
  const subfolder = files.find(file => file.match(/^fastify-/))
  const ecosystemFile = join(versionFolder, subfolder, 'docs', 'Ecosystem.md')

  const ecosystem = await extractEcosystemFromFile(ecosystemFile)
  await fs.writeFile(destination, safeDump(ecosystem), 'utf8')
  console.log(`Ecosystem file written: ${destination}`)
}

main().catch((err) => {
  console.error(err)
  process.exit(1)
})
<commit_msg>Fix: supports relative paths in ToC entries (#168)<commit_after>#!/usr/bin/env node

const { join, dirname, basename } = require('path')
const { promises: fs } = require('fs')
const { safeDump } = require('js-yaml')
const clone = require('clone')
const { fileExists } = require('./utils')

const sourceFolder = process.argv[2]
const destFolder = process.argv[3]

if (!sourceFolder || !destFolder) {
  throw new Error(`Missing parameters sourceFolder and destFolder.

  Expected command:
    ${process.argv[0]} ${process.argv[1]} <sourceFolder> <destFolder>
`)
}

async function main () {
  const releasesFile = join(sourceFolder, 'releases.json')
  const releaseFileContent = await fs.readFile(releasesFile, 'utf-8')
  const releases = JSON.parse(releaseFileContent)
  const masterRelease = releases.find(r => r.name === 'master')
  await createDocSources(releases)
  await createEcosystemDataFile(masterRelease.dest)
  console.log('All releases processed successfully')
}

async function createDocSources (releases) {
  const latestRelease = releases.find(r => r.name === 'latest')
  const tocByRelease = await Promise.all(releases.map(getTOCForRelease))
  const indexedToc = releases.reduce((acc, curr, i) => {
    acc[curr.docsPath] = tocByRelease[i]
    return acc
  }, {})
  const versions = releases.map(r => r.docsPath)
  await createDocsDataFile(join(destFolder, 'data', 'docs.yml'), { versions, toc: indexedToc, releases })
  await processDocFiles(indexedToc, latestRelease)
  await createIndexFiles(releases)
}

async function extractTOCFromFile (file, release) {
  const fileContent = await fs.readFile(file, 'utf8')

  // searches for the beginning of the ToC in the file (between '## Documentation' and '\n\n')
  const lines = fileContent.split('## Documentation')[1].split('\n\n')[0].split('\n').filter(Boolean)
  // every line is a ToC entry
  const re = /(master|.)\/docs\/([a-zA-Z-0-9]+\.md)">(.+)<\/b>/
  const toc = lines.map((line) => {
    const match = re.exec(line)
    const fileName = match[2]
    const name = match[3]
    const sourceFile = join(dirname(file), 'docs', fileName)
    const destinationFile = join(destFolder, 'content', 'docs', release.docsPath, fileName)
    const slug = basename(sourceFile, '.md')
    const link = `/docs/${release.docsPath}/${slug}`

    return {
      fileName,
      name,
      sourceFile,
      destinationFile,
      slug,
      link,
      fullVersion: release.fullVersion,
      docsPath: release.docsPath,
      label: release.label
    }
  })

  return toc
}

async function getTOCForRelease (release) {
  const folder = join(sourceFolder, release.dest)
  const files = await fs.readdir(folder)
  const subfolder = files.find(file => file.match(/^fastify-/))
  const indexFile = join(folder, subfolder, 'README.md')

  return extractTOCFromFile(indexFile, release)
}

function createDocsDataFile (destination, docsInfo) {
  const toDump = clone(docsInfo)
  // remove sourceFile and destinationFile keys from toc
  Object.keys(toDump.toc).forEach((version) => {
    toDump.toc[version].forEach((entry, i) => {
      delete toDump.toc[version][i].sourceFile
      delete toDump.toc[version][i].destinationFile
    })
  })

  return fs.writeFile(destination, safeDump(toDump), 'utf8')
}

async function processDocFiles (docs, latestRelease) {
  // merge all docs into a single array adding the version as a key in every object
  const docsArray = Object.keys(docs).reduce((acc, version) => {
    const curr = docs[version]
    return acc.concat(curr.map((item) => {
      item.version = version
      return item
    }))
  }, [])

  for (const item of docsArray) {
    const dir = dirname(item.destinationFile)
    const hasDir = await fileExists(dir)
    if (!hasDir) {
      await fs.mkdir(dir)
    }

    const buffer = await fs.readFile(item.sourceFile, 'utf8')

    let content = buffer.toString()

    // removes doc header from github
    content = content.replace(/
Fastify<\/h1>\n/, '')

    // remap links
    content = remapLinks(content, item)

    // adds frontmatter
    content =
`---
title: ${item.name}
layout: docs_page.html
path: ${item.link}
version: ${item.version}
fullVersion: ${item.fullVersion}
label: ${item.label}
docsPath: ${item.docsPath}
${item.version === 'latest' ? `canonical: "${item.link.replace(/latest/, latestRelease.label)}"` : ''}
${item.version === 'master' ? `github_url: https://github.com/fastify/fastify/blob/master/docs/${item.fileName}` : ''}
---
${content}`

    await fs.writeFile(item.destinationFile, content, 'utf8')
    console.log(`Created doc page ${item.destinationFile}`)
  }
}

function remapLinks (content, item) {
  /*
    Links remapping rules:
    /https:\/\/github.com\/fastify\/fastify\/blob\/master\/docs/ -> /docs/[VERSION]
    [XXXX](Plugins.md) -> [XXXX](/docs/[VERSION]/Plugins)
    [XXXX](/docs/VVVV/Ecosystem.md) -> [XXXX](/ecosystem)
    [XXXX](/docs/VVVV/YYYY.md) -> [XXXX](/docs/VVVV/YYYY)
    [XXXX]('./YYYY' "ZZZZ") -> [XXXX]('/docs/[VERSION]/YYYY' "ZZZZ")
    [XXXX](./YYYY "ZZZZ") -> [XXXX]('/docs/[VERSION]/YYYY' "ZZZZ")
    href="https://github.com/fastify/fastify/blob/master/docs/YYYY.md -> href="/docs/[VERSION]/YYYY
  */
  const ecosystemLinkRx = /\(\/docs\/[\w\d.-]+\/Ecosystem\.md\)/gi
  const docInternalLinkRx = /\(\/docs\/[\w\d.-]+\/[\w\d-]+(.md)/gi
  const pluginsLink = /\(Plugins.md\)/gi
  const relativeLinksWithLabel = /\('?(\.\/)([\w\d.-]+)(.md)'?\s+"([\w\d.-]+)"\)/gi
  const hrefAbsoluteLinks = /href="https:\/\/github\.com\/fastify\/fastify\/blob\/master\/docs\/([\w\d.-]+)\.md/gi
  return content
    .replace(hrefAbsoluteLinks, (match, p1) => `href="/docs/${item.version}/${p1}`)
    .replace(/https:\/\/github.com\/fastify\/fastify\/blob\/master\/docs/g, `/docs/${item.version}`)
    .replace(ecosystemLinkRx, (match) => '(/ecosystem)')
    .replace(pluginsLink, (match) => `(/docs/${item.version}/Plugins)`)
    .replace(relativeLinksWithLabel, (match, ...parts) => `(/docs/${item.version}/${parts[1]} "${parts[3]}")`)
    .replace(docInternalLinkRx, (match, p1) => match.replace(p1, ''))
}

async function createVersionIndexFile (release) {
  const content = `---
title: Documentation - ${release.name}
layout: docs_version_index.html
path: /docs/${release.docsPath}
version: ${release.name}
fullVersion: ${release.fullVersion}
label: ${release.label}
docsPath: ${release.docsPath}
${release.name === 'latest' ? `canonical: "/docs/${release.label}"` : ''}
github_url: "https://github.com/fastify/website/blob/master/src/website/layouts/docs_version_index.html"
---`

  const dest = join(destFolder, 'content', 'docs', release.docsPath, 'index.md')
  await fs.writeFile(dest, content, 'utf8')
  console.log(`Created doc index page ${dest}`)
}

async function createIndexFiles (releases) {
  // create docs index

  const docsIndexContent = `---
title: Documentation
layout: docs_index.html
path: /docs
github_url: "https://github.com/fastify/website/blob/master/src/website/layouts/docs_index.html"
---`

  const dest = join(destFolder, 'content', 'docs', 'index.md')
  await fs.writeFile(dest, docsIndexContent, 'utf8')
  console.log(`Created docs index at ${dest}`)

  for (const release of releases) {
    await createVersionIndexFile(release)
  }
}

const extractPlugins = (pluginContent) => {
  const lines = pluginContent
    .split('\n')
    .filter(Boolean) // remove empty lines

  // if a line doesn't start with "-" merge it back with the previous item
  const mergedLines = lines.reduce((acc, curr) => {
    if (curr[0] === '-') {
      acc.push(curr)
    } else {
      acc[acc.length - 1] += ' ' + curr
    }
    return acc
  }, [])
  const re = /\[`([-a-zA-Z0-9]+)`\]\(([^)]+)\)(\s*(.+))?/
  const plugins = mergedLines.map((line) => {
    const match = re.exec(line)

    const name = match[1]
    const url = match[2]
    const description = match[3] ? match[3].trim() : ''

    return { name, url, description }
  })
  return plugins
}

async function extractEcosystemFromFile (file) {
  const data = await fs.readFile(file, 'utf8')

  const content = data.toString()
  const corePluginsContent = content
    .split('#### [Core](#core)\n')[1]
    .split('#### [Community](#community)')[0]

  const communityPluginsContent = content
    .split('#### [Core](#core)\n')[1]
    .split('#### [Community](#community)')[1]

  const plugins = {
    corePlugins: extractPlugins(corePluginsContent),
    communityPlugins: extractPlugins(communityPluginsContent)
  }

  return ({ plugins })
}

async function createEcosystemDataFile (masterReleaseDownloadPath) {
  const versionFolder = join(sourceFolder, masterReleaseDownloadPath)
  const destination = join(destFolder, 'data', 'ecosystem.yml')
  const files = await fs.readdir(versionFolder)
  const subfolder = files.find(file => file.match(/^fastify-/))
  const ecosystemFile = join(versionFolder, subfolder, 'docs', 'Ecosystem.md')

  const ecosystem = await extractEcosystemFromFile(ecosystemFile)
  await fs.writeFile(destination, safeDump(ecosystem), 'utf8')
  console.log(`Ecosystem file written: ${destination}`)
}

main().catch((err) => {
  console.error(err)
  process.exit(1)
})
<|endoftext|>


<commit_before>import shortid from 'shortid';
import { forEach, find, now } from 'lodash';
import { toJS } from 'immutable';
import { logger } from '../helpers/vendor';
import createStore from '../store';
import { addEntity, removeEntity, assignTeam, leaveTeam, advanceTime } from '../actions/game';
import { createEntity } from '../factories/entity';
import { createMap } from '../factories/map';
import { DATA_PATH, GAME_TICK_RATE, GAME_SYNC_RATE } from '../constants';

// TODO: Separate game logic and generic game session logic into two different classes.

class Session {
  /**
   * Creates a new game session.
   * @param io
   */
  constructor(io) {
    this._io = io;
    this._id = shortid.generate();
    this._store = createStore();
    this._gameData = this.loadGameData();
    this._isRunning = false;
    this._lastTickAt = null;
    this._entities = [];
    this._packetSequence = 0;
    this._lastSyncAt = null;

    // Notify each client every time the state is changed.
    this._unsubscribeFromStore = this._store.subscribe(this.handleStateChange.bind(this));

    logger.info(`session.create (session_id: ${this._id})`);
  }

  /**
   * Loads the game data for this game session.
   * @returns {Object}
   */
  loadGameData() {
    return {
      assets: require(`${DATA_PATH}/assets.json`),
      config: require(`${DATA_PATH}/config.json`),
      ui: require(`${DATA_PATH}/ui.json`),
      map: createMap('castle', this._store.dispatch)
    };
  }

  /**
   * Starts this session.
   */
  start() {
    this._isRunning = true;

    setInterval(this.tick.bind(this), 1000 / GAME_TICK_RATE);

    logger.info(`session.start (session_id: ${this._id})`);
  }

  /**
   * Called each time this session is updated.
   */
  tick() {
    let timeNow, timeElapsed;

    if (this._isRunning) {
      timeNow = now();
      timeElapsed = this._lastTickAt ? timeNow - this._lastTickAt : 0;

      this._store.dispatch(advanceTime(timeElapsed));

      this.update();

      this._lastTickAt = timeNow;
    }
  }

  /**
   * Called each time this sessions should be updated.
   */
  update() {
    const gameState = this.gameState;

    this.updateEntities(gameState);
  }

  /**
   * Ends this session.
   */
  end() {
    this._unsubscribeFromStore();
  }

  /**
   * Updates each entity in this session.
   */
  updateEntities(gameState) {
    let removedEntityIds = this.getEntityIds();

    forEach(gameState.entities, props => {
      let entity = find(this._entities, entity => entity.id == props.id);

      // Create the entity if it does not exist.
      if (!entity) {
        entity = createEntity(this, props);
        this.addEntity(entity);
      }

      entity.update(props, this._store.dispatch);

      // Remove updated entities from the list of entities to be removed.
      removedEntityIds = removedEntityIds.filter(id => id !== props.id);
    });

    // Destroy entities that have been removed.
    this.destroyEntities(removedEntityIds);
  }

  /**
   * Adds an entity to this session's entity pool.
   * @param {Entity} entity
   */
  addEntity(entity) {
    this._entities.push(entity);
  }

  /**
   * Returns a list containing the id of every entity in this session.
   * @returns {Array}
   */
  getEntityIds() {
    let ids = [];

    forEach(this._entities, entity => {
      ids.push(entity.id);
    });

    return ids;
  }

  /**
   * Destroys a set of entities from this session's entity pool.
   * @param {Array} ids
   */
  destroyEntities(ids) {
    this._entities = this._entities.filter(entity => {
      let isRemoved = ids.indexOf(entity.id) !== -1;

      if (isRemoved) {
        entity.destroy();
      }

      return !isRemoved;
    });
  }

  /**
   * Adds a player to this session.
   * @param {Object} props
   */
  addPlayer(props) {
    this._store.dispatch(addEntity(props));
    this._store.dispatch(assignTeam(props.id));
  }

  /**
   * Removes a player from this session.
   * @param {string} id
   */
  removePlayer(id) {
    this._store.dispatch(leaveTeam(id));
    this._store.dispatch(removeEntity(id));
  }

  /**
   * Called each time the state of this session changes.
   */
  handleStateChange() {
    if (this._isRunning && this.shouldSendState()) {
      this._io.to(this.channel).emit('set_state', this.gameState, this._packetSequence++);
      this._lastSyncAt = now();
    }
  }

  /**
   * Called by clients to dispatch actions received from the browser.
   * @param {Object} action
   */
  handleClientAction(action) {
    this._store.dispatch(action);
  }

  /**
   *
   * @returns {boolean}
   */
  shouldSendState() {
    return !this._lastSyncAt || now() - this._lastSyncAt > 1000 / GAME_SYNC_RATE;
  }

  /**
   * Returns the id of this session.
   * @returns {string}
   */
  get id() {
    return this._id;
  }

  /**
   * Returns the socket room for this session.
   * @returns {string}
   */
  get channel() {
    return `session/${this._id}`;
  }

  /**
   * Returns the game data for this session.
   * @returns {Object}
   */
  get gameData() {
    return this._gameData;
  }

  /**
   * Returns the current state of this session.
   * @returns {Object}
   */
  get gameState() {
    return this._store.getState().game.toJS();
  }
}

export default Session;
<commit_msg>Refactor game session<commit_after>import shortid from 'shortid';
import { forEach, find, now } from 'lodash';
import { toJS } from 'immutable';
import { logger } from '../helpers/vendor';
import createStore from '../store';
import { addEntity, removeEntity, assignTeam, leaveTeam, advanceTime } from '../actions/game';
import { createEntity } from '../factories/entity';
import { createMap } from '../factories/map';
import { DATA_PATH, GAME_TICK_RATE, GAME_SYNC_RATE } from '../constants';

// TODO: Separate game logic and generic game session logic into two different classes.

class Session {
  /**
   * Creates a new game session.
   * @param io
   */
  constructor(io) {
    this._io = io;
    this._id = shortid.generate();
    this._store = createStore();
    this._gameData = this.loadGameData();
    this._isRunning = false;
    this._lastTickAt = null;
    this._entities = [];
    this._packetSequence = 0;
    this._lastSyncAt = null;

    // Notify each client every time the state is changed.
    this._unsubscribeFromStore = this._store.subscribe(this.handleStateChange.bind(this));

    logger.info(`session.create (session_id: ${this._id})`);
  }

  /**
   * Loads the game data for this game session.
   * @returns {Object}
   */
  loadGameData() {
    return {
      assets: require(`${DATA_PATH}/assets.json`),
      config: require(`${DATA_PATH}/config.json`),
      ui: require(`${DATA_PATH}/ui.json`),
      map: createMap('castle', this._store.dispatch)
    };
  }

  /**
   * Starts this session.
   */
  start() {
    this._isRunning = true;

    setInterval(this.tick.bind(this), 1000 / GAME_TICK_RATE);

    logger.info(`session.start (session_id: ${this._id})`);
  }

  /**
   * Called each time this session is updated.
   */
  tick() {
    let timeNow, timeElapsed;

    if (this._isRunning) {
      timeNow = now();
      timeElapsed = this._lastTickAt ? timeNow - this._lastTickAt : 0;

      this._store.dispatch(advanceTime(timeElapsed));

      this.update();

      this._lastTickAt = timeNow;
    }
  }

  /**
   * Called each time this sessions should be updated.
   */
  update() {
    const gameState = this.gameState;

    this.updateEntities(gameState);
  }

  /**
   * Ends this session.
   */
  end() {
    this._unsubscribeFromStore();
  }

  /**
   * Updates each entity in this session.
   */
  updateEntities(gameState) {
    let removedEntityIds = this.getEntityIds();

    forEach(gameState.entities, props => {
      let entity = find(this._entities, entity => entity.id == props.id);

      // Create the entity if it does not exist.
      if (!entity) {
        entity = createEntity(this, props);
        this.addEntity(entity);
      }

      entity.update(props, this._store.dispatch);

      // Remove updated entities from the list of entities to be removed.
      removedEntityIds = removedEntityIds.filter(id => id !== props.id);
    });

    // Destroy entities that have been removed.
    this.destroyEntities(removedEntityIds);
  }

  /**
   * Adds an entity to this session's entity pool.
   * @param {Entity} entity
   */
  addEntity(entity) {
    this._entities.push(entity);
  }

  /**
   * Returns a list containing the id of every entity in this session.
   * @returns {Array}
   */
  getEntityIds() {
    let ids = [];

    forEach(this._entities, entity => {
      ids.push(entity.id);
    });

    return ids;
  }

  /**
   * Destroys a set of entities from this session's entity pool.
   * @param {Array} ids
   */
  destroyEntities(ids) {
    this._entities = this._entities.filter(entity => {
      let isRemoved = ids.indexOf(entity.id) !== -1;

      if (isRemoved) {
        entity.destroy();
      }

      return !isRemoved;
    });
  }

  /**
   * Adds a player to this session.
   * @param {Object} props
   */
  addPlayer(props) {
    this._store.dispatch(addEntity(props));
    this._store.dispatch(assignTeam(props.id));
  }

  /**
   * Removes a player from this session.
   * @param {string} id
   */
  removePlayer(id) {
    this._store.dispatch(leaveTeam(id));
    this._store.dispatch(removeEntity(id));
  }

  /**
   * Called by clients to dispatch actions received from the browser.
   * @param {Object} action
   */
  handleClientAction(action) {
    this._store.dispatch(action);
  }

  /**
   * Called each time the state of this session changes.
   */
  handleStateChange() {
    if (this._isRunning && this.shouldSendState()) {
      this._io.to(this.channel).emit('set_state', this.gameState, this._packetSequence++);
      this._lastSyncAt = now();
    }
  }

  /**
   *
   * @returns {boolean}
   */
  shouldSendState() {
    return !this._lastSyncAt || (now() - this._lastSyncAt) > (1000 / GAME_SYNC_RATE);
  }

  /**
   * Returns the id of this session.
   * @returns {string}
   */
  get id() {
    return this._id;
  }

  /**
   * Returns the socket room for this session.
   * @returns {string}
   */
  get channel() {
    return `session/${this._id}`;
  }

  /**
   * Returns the game data for this session.
   * @returns {Object}
   */
  get gameData() {
    return this._gameData;
  }

  /**
   * Returns the current state of this session.
   * @returns {Object}
   */
  get gameState() {
    return this._store.getState().game.toJS();
  }
}

export default Session;
<|endoftext|>


<commit_before>/**
 * Created by matthias on 17.11.16.
 */

var listTop = {};
var allGbEntries;
var vsrUrl = "";

window.onload = function () {
    // init. variables
    listTop =document.getElementsByTagName("ul")[0];
    vsrUrl = document.getElementsByTagName("form")[0].action;

    // event listener
    document.getElementsByTagName("button")[0].addEventListener("click", function (event) {
            event.preventDefault(); // prevent new load?!
            SubmitEntry();
    });

    // onLoad event
    var xHttp = new XMLHttpRequest();

    xHttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            // parse response from JSON in js object
            renderGbAtStart(JSON.parse(this.responseText));
        }
    };
    xHttp.open("GET", vsrUrl, true);
    xHttp.send();
}

function renderGbAtStart(gbResponse) {
    allGbEntries = gbResponse;

    // process the js object
    for (var i = 0; i < allGbEntries.length; i++) {
        var tmp = allGbEntries[i];
        var newItem = createGbEntry(tmp.id, tmp.name, tmp.text);
        listTop.insertBefore(newItem, listTop.childNodes[0]);
    }
}

function createGbEntry(id, name, text) {
    var liEntry = document.createElement("li");
    var divName = document.createElement("div");
    var divText = document.createElement("div");
    var xHref = document.createElement("a");

    divName.innerHTML = String(name);
    divName.style.fontWeight = "bold";
    divText.innerHTML = String(text);
    xHref.innerHTML = "(X)";
    xHref.href = "#";
    xHref.alt = "Delete entry";
    xHref.addEventListener("click", function () {
        removeElement(liEntry, String(id));
    });

    liEntry.appendChild(divName);
    liEntry.appendChild(divText);
    liEntry.appendChild(xHref);
    return liEntry;
}

function removeElement(liEntry, id) {
    // delete entry from remote gb
    var idPara = "id=" + id;
    var xHttpR = new XMLHttpRequest();
    xHttpR.open("DELETE", vsrUrl + "?" + idPara, true);
    xHttpR.send();

    // remove childs ("local")
    while (liEntry.firstChild) {
        liEntry.removeChild(liEntry.firstChild);
    }
    // after that remove the parent node (li)
    listTop.removeChild(liEntry);
}

function SubmitEntry() {
    var nameV = encodeURIComponent(document.getElementById("name").value);
    var textV = encodeURIComponent(document.getElementById("text").value);
    var toSend = "name=" + nameV + "&text=" + textV;

    // error cases
    if (nameV === "") {
        alert("Please provide a name.");
        return;
    }
    if (textV === "") {
        alert("Please provide some text.");
        return;
    }

    var xHttp = new XMLHttpRequest();
    xHttp.open("POST", vsrUrl, true);
    xHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    xHttp.onreadystatechange = function () {
        var resp;
        if (this.readyState == 4 && this.status == 200) {

            resp = JSON.parse(this.responseText);

            // append Entry
            var newItem = createGbEntry(resp.id, nameV, textV);
            listTop.insertBefore(newItem, listTop.childNodes[0]);
        }
    };

    xHttp.send(toSend);
}
<commit_msg>Da habe ich doch glatt die Struktur des responseText missachtet.<commit_after>/**
 * Created by matthias on 17.11.16.
 */

var listTop = {};
var allGbEntries;
var vsrUrl = "";

window.onload = function () {
    // init. variables
    listTop =document.getElementsByTagName("ul")[0];
    vsrUrl = document.getElementsByTagName("form")[0].action;

    // event listener
    document.getElementsByTagName("button")[0].addEventListener("click", function (event) {
            event.preventDefault(); // prevent new load?!
            SubmitEntry();
    });

    // onLoad event
    var xHttp = new XMLHttpRequest();

    xHttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            // parse response from JSON in js object
            renderGbAtStart(JSON.parse(this.responseText));
        }
    };
    xHttp.open("GET", vsrUrl, true);
    xHttp.send();
}

function renderGbAtStart(gbResponse) {
    allGbEntries = gbResponse;

    // process the js object
    for (var i = 0; i < allGbEntries.length; i++) {
        var tmp = allGbEntries[i];
        var newItem = createGbEntry(tmp.id, tmp.name, tmp.text);
        listTop.insertBefore(newItem, listTop.childNodes[0]);
    }
}

function createGbEntry(id, name, text) {
    var liEntry = document.createElement("li");
    var divName = document.createElement("div");
    var divText = document.createElement("div");
    var xHref = document.createElement("a");

    divName.innerHTML = name;
    divName.style.fontWeight = "bold";
    divText.innerHTML = text;
    xHref.innerHTML = "(X)";
    xHref.href = "#";
    xHref.alt = "Delete entry";
    xHref.addEventListener("click", function () {
        removeElement(liEntry, id);
    });

    liEntry.appendChild(divName);
    liEntry.appendChild(divText);
    liEntry.appendChild(xHref);
    return liEntry;
}

function removeElement(liEntry, id) {
    // delete entry from remote gb
    var idPara = "id=" + id;
    var xHttpR = new XMLHttpRequest();
    xHttpR.open("DELETE", vsrUrl + "?" + idPara, true);
    xHttpR.send();

    // remove childs ("local")
    while (liEntry.firstChild) {
        liEntry.removeChild(liEntry.firstChild);
    }
    // after that remove the parent node (li)
    listTop.removeChild(liEntry);
}

function SubmitEntry() {
    var name = document.getElementById("name").value;
    var text = document.getElementById("text").value;
    var nameC = encodeURIComponent(name);
    var textC = encodeURIComponent(text);
    var toSend = "name=" + nameC + "&text=" + textC;

    // error cases
    if (nameC === "") {
        alert("Please provide a name.");
        return;
    }
    if (textC === "") {
        alert("Please provide some text.");
        return;
    }

    var xHttp = new XMLHttpRequest();
    xHttp.open("POST", vsrUrl, true);
    xHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    xHttp.onreadystatechange = function () {
        var resp;
        if (this.readyState == 4 && this.status == 200) {

            resp = JSON.parse(this.responseText);

            // append Entry - answer type: Object { message: "Successful", entry: Object }
            var newItem = createGbEntry(resp.entry.id, resp.entry.name, resp.entry.text);
            listTop.insertBefore(newItem, listTop.childNodes[0]);
        }
    };

    xHttp.send(toSend);
}
<|endoftext|>


<commit_before>// 2-dimensional transformation matrix

'use strict';

var _ = require('lodash');

var bezier = require('../util/bezier');
var math = require('../util/math');

var Group = require('../objects/group');
var Path = require('../objects/path');
var Point = require('../objects/point');

var MOVETO  = bezier.MOVETO;
var LINETO  = bezier.LINETO;
var QUADTO  = bezier.QUADTO;
var CURVETO = bezier.CURVETO;
var CLOSE   = bezier.CLOSE;

// A geometric transformation in Euclidean space (i.e. 2D)
// that preserves collinearity and ratio of distance between points.
// Linear transformations include rotation, translation, scaling, shear.
var Transform = function (m) {
    if (m !== undefined) {
        this.m = m;
    } else {
        this.m = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Identity matrix.
    }
};

Transform.IDENTITY = new Transform();

Transform.identity = function () {
    return new Transform();
};

// Returns the 3x3 matrix multiplication of A and B.
// Note that scale(), translate(), rotate() work with premultiplication,
// e.g. the matrix A followed by B = BA and not AB.
Transform._mmult = function (a, b) {
    if (a.m !== undefined) { a = a.m; }
    if (b.m !== undefined) { b = b.m; }

    return new Transform([
        a[0] * b[0] + a[1] * b[3],
        a[0] * b[1] + a[1] * b[4], 0,
        a[3] * b[0] + a[4] * b[3],
        a[3] * b[1] + a[4] * b[4], 0,
        a[6] * b[0] + a[7] * b[3] + b[6],
        a[6] * b[1] + a[7] * b[4] + b[7], 1
    ]);
};

Transform.prototype.prepend = function (matrix) {
    return Transform._mmult(this.m, matrix.m);
};

Transform.prototype.append = function (matrix) {
    return Transform._mmult(matrix.m, this.m);
};

Transform.prototype.inverse = function () {
    var m = this.m,
        d = m[0] * m[4] - m[1] * m[3];
    return new Transform([
        m[4] / d,
        -m[1] / d, 0,
        -m[3] / d,
        m[0] / d, 0,
        (m[3] * m[7] - m[4] * m[6]) / d,
        -(m[0] * m[7] - m[1] * m[6]) / d, 1
    ]);
};

Transform.prototype.scale = function (x, y) {
    if (y === undefined) { y = x; }
    return Transform._mmult([x, 0, 0, 0, y, 0, 0, 0, 1], this.m);
};

Transform.prototype.translate = function (x, y) {
    return Transform._mmult([1, 0, 0, 0, 1, 0, x, y, 1], this.m);
};

Transform.prototype.rotate = function (angle) {
    var c = Math.cos(math.radians(angle)),
        s = Math.sin(math.radians(angle));
    return Transform._mmult([c, s, 0, -s, c, 0, 0, 0, 1], this.m);
};

Transform.prototype.skew = function (x, y) {
    var kx = Math.PI * x / 180.0,
        ky = Math.PI * y / 180.0;
    return Transform._mmult([1, Math.tan(ky), 0, -Math.tan(kx), 1, 0, 0, 0, 1], this.m);
};

// Returns the new coordinates of the given point (x,y) after transformation.
Transform.prototype.transformPoint = function (point) {
    var x = point.x,
        y = point.y,
        m = this.m;
    return new Point(
        x * m[0] + y * m[3] + m[6],
        x * m[1] + y * m[4] + m[7]
    );
};

Transform.prototype.transformPoints = function (points) {
    var _this = this;
    return _.map(points, function (pt) {
        return _this.transformPoint(pt);
    });
};

Transform.prototype.transformPath = function (path) {
    var _this = this,
        point,
        ctrl1,
        ctrl2,
        commands = _.map(path.commands, function (cmd) {
            if (cmd.type === MOVETO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                return { type: MOVETO, x: point.x, y: point.y };
            } else if (cmd.type === LINETO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                return { type: LINETO, x: point.x, y: point.y };
            } else if (cmd.type === QUADTO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                ctrl1 = _this.transformPoint({x: cmd.x1, y: cmd.y1});
                return { type: QUADTO, x1: ctrl1.x, y1: ctrl1.y, x: point.x, y: point.y };
            } else if (cmd.type === CURVETO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                ctrl1 = _this.transformPoint({x: cmd.x1, y: cmd.y1});
                ctrl2 = _this.transformPoint({x: cmd.x2, y: cmd.y2});
                return { type: CURVETO, x1: ctrl1.x, y1: ctrl1.y, x2: ctrl2.x, y2: ctrl2.y, x: point.x, y: point.y };
            } else if (cmd.type === CLOSE) {
                return cmd;
            } else {
                throw new Error('Unknown command type ' + cmd);
            }
        });
    return new Path(commands, path.fill, path.stroke, path.strokeWidth);
};

Transform.prototype.transformText = function (text) {
    var t = text.clone();
    t.transform = this.append(t.transform);
    return t;
};

Transform.prototype.transformGroup = function (group) {
    var _this = this,
        shapes = _.map(group.shapes, function (shape) {
            return _this.transformShape(shape);
        });
    return new Group(shapes);
};

Transform.prototype.transformShape = function (shape) {
    var fn;
    if (shape.shapes) {
        fn = this.transformGroup;
    } else if (shape.text) {
        fn = this.transformText;
    } else if (shape.x !== undefined && shape.y !== undefined) {
        fn = this.transformPoint;
    } else if (Array.isArray(shape) && shape.length > 0 && shape[0].x !== undefined && shape[0].y !== undefined) {
        fn = this.transformPoints;
    } else {
        fn = this.transformPath;
    }
    return fn.call(this, shape);
};

module.exports = Transform;
<commit_msg>transformShape can handle an array of different shapes.<commit_after>// 2-dimensional transformation matrix

'use strict';

var _ = require('lodash');

var bezier = require('../util/bezier');
var math = require('../util/math');

var Group = require('../objects/group');
var Path = require('../objects/path');
var Point = require('../objects/point');

var MOVETO  = bezier.MOVETO;
var LINETO  = bezier.LINETO;
var QUADTO  = bezier.QUADTO;
var CURVETO = bezier.CURVETO;
var CLOSE   = bezier.CLOSE;

// A geometric transformation in Euclidean space (i.e. 2D)
// that preserves collinearity and ratio of distance between points.
// Linear transformations include rotation, translation, scaling, shear.
var Transform = function (m) {
    if (m !== undefined) {
        this.m = m;
    } else {
        this.m = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Identity matrix.
    }
};

Transform.IDENTITY = new Transform();

Transform.identity = function () {
    return new Transform();
};

// Returns the 3x3 matrix multiplication of A and B.
// Note that scale(), translate(), rotate() work with premultiplication,
// e.g. the matrix A followed by B = BA and not AB.
Transform._mmult = function (a, b) {
    if (a.m !== undefined) { a = a.m; }
    if (b.m !== undefined) { b = b.m; }

    return new Transform([
        a[0] * b[0] + a[1] * b[3],
        a[0] * b[1] + a[1] * b[4], 0,
        a[3] * b[0] + a[4] * b[3],
        a[3] * b[1] + a[4] * b[4], 0,
        a[6] * b[0] + a[7] * b[3] + b[6],
        a[6] * b[1] + a[7] * b[4] + b[7], 1
    ]);
};

Transform.prototype.prepend = function (matrix) {
    return Transform._mmult(this.m, matrix.m);
};

Transform.prototype.append = function (matrix) {
    return Transform._mmult(matrix.m, this.m);
};

Transform.prototype.inverse = function () {
    var m = this.m,
        d = m[0] * m[4] - m[1] * m[3];
    return new Transform([
        m[4] / d,
        -m[1] / d, 0,
        -m[3] / d,
        m[0] / d, 0,
        (m[3] * m[7] - m[4] * m[6]) / d,
        -(m[0] * m[7] - m[1] * m[6]) / d, 1
    ]);
};

Transform.prototype.scale = function (x, y) {
    if (y === undefined) { y = x; }
    return Transform._mmult([x, 0, 0, 0, y, 0, 0, 0, 1], this.m);
};

Transform.prototype.translate = function (x, y) {
    return Transform._mmult([1, 0, 0, 0, 1, 0, x, y, 1], this.m);
};

Transform.prototype.rotate = function (angle) {
    var c = Math.cos(math.radians(angle)),
        s = Math.sin(math.radians(angle));
    return Transform._mmult([c, s, 0, -s, c, 0, 0, 0, 1], this.m);
};

Transform.prototype.skew = function (x, y) {
    var kx = Math.PI * x / 180.0,
        ky = Math.PI * y / 180.0;
    return Transform._mmult([1, Math.tan(ky), 0, -Math.tan(kx), 1, 0, 0, 0, 1], this.m);
};

// Returns the new coordinates of the given point (x,y) after transformation.
Transform.prototype.transformPoint = function (point) {
    var x = point.x,
        y = point.y,
        m = this.m;
    return new Point(
        x * m[0] + y * m[3] + m[6],
        x * m[1] + y * m[4] + m[7]
    );
};

Transform.prototype.transformPoints = function (points) {
    var _this = this;
    return _.map(points, function (pt) {
        return _this.transformPoint(pt);
    });
};

Transform.prototype.transformPath = function (path) {
    var _this = this,
        point,
        ctrl1,
        ctrl2,
        commands = _.map(path.commands, function (cmd) {
            if (cmd.type === MOVETO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                return { type: MOVETO, x: point.x, y: point.y };
            } else if (cmd.type === LINETO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                return { type: LINETO, x: point.x, y: point.y };
            } else if (cmd.type === QUADTO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                ctrl1 = _this.transformPoint({x: cmd.x1, y: cmd.y1});
                return { type: QUADTO, x1: ctrl1.x, y1: ctrl1.y, x: point.x, y: point.y };
            } else if (cmd.type === CURVETO) {
                point = _this.transformPoint({x: cmd.x, y: cmd.y});
                ctrl1 = _this.transformPoint({x: cmd.x1, y: cmd.y1});
                ctrl2 = _this.transformPoint({x: cmd.x2, y: cmd.y2});
                return { type: CURVETO, x1: ctrl1.x, y1: ctrl1.y, x2: ctrl2.x, y2: ctrl2.y, x: point.x, y: point.y };
            } else if (cmd.type === CLOSE) {
                return cmd;
            } else {
                throw new Error('Unknown command type ' + cmd);
            }
        });
    return new Path(commands, path.fill, path.stroke, path.strokeWidth);
};

Transform.prototype.transformText = function (text) {
    var t = text.clone();
    t.transform = this.append(t.transform);
    return t;
};

Transform.prototype.transformGroup = function (group) {
    var _this = this,
        shapes = _.map(group.shapes, function (shape) {
            return _this.transformShape(shape);
        });
    return new Group(shapes);
};

Transform.prototype.transformShape = function (shape) {
    var fn;
    if (shape.shapes) {
        fn = this.transformGroup;
    } else if (shape.text) {
        fn = this.transformText;
    } else if (shape.x !== undefined && shape.y !== undefined) {
        fn = this.transformPoint;
    } else if (Array.isArray(shape) && shape.length > 0) {
        if (shape[0].x !== undefined && shape[0].y !== undefined) {
            fn = this.transformPoints;
        } else {
            var l = [];
            for (var i = 0; i < shape.length; i += 1) {
                l.push(this.transformShape(shape[i]));
            }
            return l;
        }
    } else {
        fn = this.transformPath;
    }
    return fn.call(this, shape);
};

module.exports = Transform;
<|endoftext|>


<commit_before>// import config from './config';
import messages from '../client/messages';

const initialLocale = 'en';

export default {
  $pendingActions: {},
  auth: {
    form: {
      fields: {
        email: '',
        password: ''
      },
      error: null
    }
  },
  i18n: {
    formats: {},
    locales: initialLocale,
    messages: messages[initialLocale]
  },
  newTodo: {
    title: ''
  },
  todos: [
    {id: 1, title: 'consider ‘stop doing’ app'},
    {id: 2, title: 'relax'}
  ],
  products: [],
  user: {
    authData: null
  },
  search: {
  }
};
<commit_msg>Init total properly<commit_after>// import config from './config';
import messages from '../client/messages';

const initialLocale = 'en';

export default {
  $pendingActions: {},
  auth: {
    form: {
      fields: {
        email: '',
        password: ''
      },
      error: null
    }
  },
  i18n: {
    formats: {},
    locales: initialLocale,
    messages: messages[initialLocale]
  },
  newTodo: {
    title: ''
  },
  todos: [
    {id: 1, title: 'consider ‘stop doing’ app'},
    {id: 2, title: 'relax'}
  ],
  products: [],
  total: 0,
  user: {
    authData: null
  },
  search: {
  }
};
<|endoftext|>


<commit_before>'use strict'

const expect = require('code').expect
const Lab = require('lab')
const lab = exports.lab = Lab.script()
const Boom = require('boom')
var proxyquire = require('proxyquire')
var utils = require('./../../utils')

var authorizeMock = {}
var authRoutes = proxyquire('./../../../routes/public/authorization', { './../../lib/authorizeOps': () => authorizeMock })
var server = proxyquire('./../../../wiring-hapi', { './routes/public/authorization': authRoutes })

lab.experiment('Authorization', () => {
  lab.test('check authorization should return access true for allowed', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      process.nextTick(() => {
        cb(null, {access: true})
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/check/1/action_a/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal({ access: true })

      done()
    })
  })

  lab.test('check authorization should return access false for denied', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      process.nextTick(() => {
        cb(null, {access: false})
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/check/1/action_a/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal({ access: false })

      done()
    })
  })

  lab.test('check authorization should return 500 for error case', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      process.nextTick(() => {
        cb(Boom.badImplementation())
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/check/1/action_a/1/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(500)
      expect(result).to.be.undefined

      done()
    })
  })

  lab.test('list authorizations should return actions allowed for the user', (done) => {
    const actionListStub = {
      actions: [
        'action a',
        'action b'
      ]
    }

    authorizeMock.listAuthorizations = (params, cb) => {
      process.nextTick(() => {
        cb(null, actionListStub)
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/list/1/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal(actionListStub)

      done()
    })
  })

  lab.test('list authorizations should return 500 for error case', (done) => {
    authorizeMock.listAuthorizations = (params, cb) => {
      process.nextTick(() => {
        cb(Boom.badImplementation())
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/list/1/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(500)
      expect(result).to.be.undefined

      done()
    })
  })

  lab.test('list authorizations should return actions allowed for the user when using an URI', (done) => {
    const actionListStub = {
      actions: [
        'action a',
        'action b'
      ]
    }

    authorizeMock.listAuthorizations = (params, cb) => {
      process.nextTick(() => {
        cb(null, actionListStub)
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/list/1/my/resource/uri'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal(actionListStub)

      done()
    })
  })

  lab.test('check authorization should return access true for allowed on URI resource', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      expect(params.userId).to.equal(1)
      expect(params.action).to.equal('action_a')
      expect(params.resource).to.equal('/my/resource/uri')

      process.nextTick(() => {
        cb(null, {access: true})
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/check/1/action_a//my/resource/uri'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal({ access: true })

      done()
    })
  })
})
<commit_msg>fixed failing tests after access endpoint change<commit_after>'use strict'

const expect = require('code').expect
const Lab = require('lab')
const lab = exports.lab = Lab.script()
const Boom = require('boom')
var proxyquire = require('proxyquire')
var utils = require('./../../utils')

var authorizeMock = {}
var authRoutes = proxyquire('./../../../routes/public/authorization', { './../../lib/authorizeOps': () => authorizeMock })
var server = proxyquire('./../../../wiring-hapi', { './routes/public/authorization': authRoutes })

lab.experiment('Authorization', () => {
  lab.test('check authorization should return access true for allowed', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      process.nextTick(() => {
        cb(null, {access: true})
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/access/1/action_a/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal({ access: true })

      done()
    })
  })

  lab.test('check authorization should return access false for denied', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      process.nextTick(() => {
        cb(null, {access: false})
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/access/1/action_a/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal({ access: false })

      done()
    })
  })

  lab.test('check authorization should return 500 for error case', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      process.nextTick(() => {
        cb(Boom.badImplementation())
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/access/1/action_a/1/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(500)
      expect(result).to.be.undefined

      done()
    })
  })

  lab.test('list authorizations should return actions allowed for the user', (done) => {
    const actionListStub = {
      actions: [
        'action a',
        'action b'
      ]
    }

    authorizeMock.listAuthorizations = (params, cb) => {
      process.nextTick(() => {
        cb(null, actionListStub)
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/list/1/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal(actionListStub)

      done()
    })
  })

  lab.test('list authorizations should return 500 for error case', (done) => {
    authorizeMock.listAuthorizations = (params, cb) => {
      process.nextTick(() => {
        cb(Boom.badImplementation())
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/list/1/resource_a'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(500)
      expect(result).to.be.undefined

      done()
    })
  })

  lab.test('list authorizations should return actions allowed for the user when using an URI', (done) => {
    const actionListStub = {
      actions: [
        'action a',
        'action b'
      ]
    }

    authorizeMock.listAuthorizations = (params, cb) => {
      process.nextTick(() => {
        cb(null, actionListStub)
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/list/1/my/resource/uri'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal(actionListStub)

      done()
    })
  })

  lab.test('check authorization should return access true for allowed on URI resource', (done) => {
    authorizeMock.isUserAuthorized = (params, cb) => {
      expect(params.userId).to.equal(1)
      expect(params.action).to.equal('action_a')
      expect(params.resource).to.equal('/my/resource/uri')

      process.nextTick(() => {
        cb(null, {access: true})
      })
    }

    const options = utils.requestOptions({
      method: 'GET',
      url: '/authorization/access/1/action_a//my/resource/uri'
    })

    server.inject(options, (response) => {
      const result = response.result

      expect(response.statusCode).to.equal(200)
      expect(result).to.equal({ access: true })

      done()
    })
  })
})
<|endoftext|>


<commit_before>const router = require('express').Router()
const {indexView} = require('./route_helpers/view')
const {AlbumsAndNewestReviews} = require('./route_helpers/album-review-helpers')

router.get('/', (request, response, next) => {
  AlbumsAndNewestReviews()
  .then(albumsAndReviews => {
    response.status(200).render('index', {view: indexView(request, albumsAndReviews)})
  })
})

router.use('/', require('./domains/users'))
router.use('/albums', require('./domains/albums'))
router.use('/reviews', require('./domains/reviews'))

module.exports = router
<commit_msg>indent<commit_after>const router = require('express').Router()
const {indexView} = require('./route_helpers/view')
const {AlbumsAndNewestReviews} = require('./route_helpers/album-review-helpers')

router.get('/', (request, response, next) => {
  AlbumsAndNewestReviews().then(albumsAndReviews => {
    response.status(200).render('index', {view: indexView(request, albumsAndReviews)})
  })
})

router.use('/', require('./domains/users'))
router.use('/albums', require('./domains/albums'))
router.use('/reviews', require('./domains/reviews'))

module.exports = router
<|endoftext|>


<commit_before>var
  KuzzleRole = require('./kuzzleRole'),
  KuzzleProfile = require('./kuzzleProfile'),
  KuzzleUser = require('./kuzzleUser');

/**
 * Kuzzle security constructor
 *
 * @param kuzzle
 * @returns {KuzzleSecurity}
 * @constructor
 */
function KuzzleSecurity(kuzzle) {

  Object.defineProperty(this, 'kuzzle', {
    value: kuzzle
  });

  Object.defineProperty(this, 'buildQueryArgs', {
    value: function (action) {
      return {
        controller: 'security',
        action: action
      };
    }
  });

  if (this.kuzzle.bluebird) {
    return this.kuzzle.bluebird.promisifyAll(this, {
      suffix: 'Promise',
      filter: function (name, func, target, passes) {
        var blacklist = [];

        return passes && blacklist.indexOf(name) === -1;
      }
    });
  }

  return this;
}

/**
 * @param {string} name
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.fetchRole = function (name, cb) {

};
/**
 *
 * @param {object} filters
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchRoles = function (filters, options, cb) {
  var
    query,
    self = this;

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  self.kuzzle.callbackRequired('KuzzleSecurity.searchRoles', cb);
  self.kuzzle.query(this.buildQueryArgs('searchRoles'), {body: filters}, options, function (error, result) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = result.result.hits.map(function (doc) {
      return new KuzzleRole(this, doc._id, doc._source);
    });

    cb(null, { total: result.result.total, documents: documents });
  });

  return this;
};

/**
 *
 * @param {object} role
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createRole = function (role, cb) {

};

/**
 * @param {string} name
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.fetchProfile = function (name, hydrate, cb) {

};
/**
 *
 * @param {object} filters
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchProfiles = function (filters, hydrate, cb) {

};

/**
 *
 * @param {object} profile
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createProfile = function (profile, cb) {

};

/**
 * @param {string} name
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.fetchUser = function (name, hydrate, cb) {

};
/**
 *
 * @param {object} filters
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchUsers = function (filters, hydrate, cb) {

};

/**
 *
 * @param {object} profile
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createUser = function (profile, cb) {

};

module.exports = KuzzleSecurity;<commit_msg>add comment in searchRoles<commit_after>var
  KuzzleRole = require('./kuzzleRole'),
  KuzzleProfile = require('./kuzzleProfile'),
  KuzzleUser = require('./kuzzleUser');

/**
 * Kuzzle security constructor
 *
 * @param kuzzle
 * @returns {KuzzleSecurity}
 * @constructor
 */
function KuzzleSecurity(kuzzle) {

  Object.defineProperty(this, 'kuzzle', {
    value: kuzzle
  });

  Object.defineProperty(this, 'buildQueryArgs', {
    value: function (action) {
      return {
        controller: 'security',
        action: action
      };
    }
  });

  if (this.kuzzle.bluebird) {
    return this.kuzzle.bluebird.promisifyAll(this, {
      suffix: 'Promise',
      filter: function (name, func, target, passes) {
        var blacklist = [];

        return passes && blacklist.indexOf(name) === -1;
      }
    });
  }

  return this;
}

/**
 * @param {string} name
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.fetchRole = function (name, cb) {

};
/**
 * Executes a search on roles according to a filter
 *
 * /!\ There is a small delay between role creation and their existence in our persistent search layer,
 * usually a couple of seconds.
 * That means that a role that was just been created won’t be returned by this function.
 *
 * @param {Object} filters - this object can contains an array `indexes` with a list of index id, a integer `from` and a integer `size`
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchRoles = function (filters, options, cb) {
  var
    self = this;

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  self.kuzzle.callbackRequired('KuzzleSecurity.searchRoles', cb);

  self.kuzzle.query(this.buildQueryArgs('searchRoles'), {body: filters}, options, function (error, result) {
    var documents;

    if (error) {
      return cb(error);
    }

    documents = result.result.hits.map(function (doc) {
      return new KuzzleRole(self, doc._id, doc._source);
    });

    cb(null, { total: result.result.total, documents: documents });
  });

  return this;
};

/**
 *
 * @param {object} role
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createRole = function (role, cb) {

};

/**
 * @param {string} name
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.fetchProfile = function (name, hydrate, cb) {

};
/**
 *
 * @param {object} filters
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchProfiles = function (filters, hydrate, cb) {

};

/**
 *
 * @param {object} profile
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createProfile = function (profile, cb) {

};

/**
 * @param {string} name
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.fetchUser = function (name, hydrate, cb) {

};
/**
 *
 * @param {object} filters
 * @param {Boolean} hydrate
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.searchUsers = function (filters, hydrate, cb) {

};

/**
 *
 * @param {object} profile
 * @param {responseCallback} [cb] - returns Kuzzle's response
 *
 * @returns {Object} this
 */
KuzzleSecurity.prototype.createUser = function (profile, cb) {

};

module.exports = KuzzleSecurity;<|endoftext|>


<commit_before>const manifestHelpers = require("./manifestHelpers");
const path = require("path");

const urlToParts = (url, lower) => {
  if (lower) return url.toLowerCase().replace(/^\/+/, "").replace(/\/+$/, "").split("/");
  return url.replace(/^\/+/, "").replace(/\/+$/, "").split("/");
};

const getSource = (url) => {
  let parts = urlToParts(url, true);
  if (parts[0] === "status") {
    parts = parts.slice(1);
  }
  if (!parts.length || (parts.length === 1 && parts[0] === '')) {
    return "live";
  }
  if (parts[0] === "local") return "local";
  else if (parts[0] === "staging") return "staging";
  else if (parts[0] === "community") return "github";
  return "live";
};

const collectDatasets = (source) => {
  if (source === "local" && global.LOCAL_MANIFEST) {
    return {available: global.LOCAL_MANIFEST, source};
  } else if (source === "live" && global.LIVE_MANIFEST) {
    return {available: global.LIVE_MANIFEST, source};
  } else if (source === "staging" && global.STAGING_MANIFEST) {
    return {available: global.STAGING_MANIFEST, source};
  }
  return {available: undefined, source};
};

const guessTreeName = (parts) => {
  const guesses = ["HA", "NA", "PB1", "PB2", "PA", "NP", "NS", "MP", "L", "S"];
  for (const part of parts) {
    if (guesses.indexOf(part.toUpperCase()) !== -1) return part;
  }
  return undefined;
};

const constructPathToGet = (source, providedUrl) => {
  /* the path is lowercased _except for_ the github org & repo names */
  let auspiceURL; // the URL to be displayed in Auspice
  let fetchURL; // could be local path or http(s)://
  let secondTreeFetchURL;
  let datasetFields; // this _does not_ take into account the 2nd tree
  let treeName;

  const parts = providedUrl.replace(/^\//, '').replace(/\/$/, '').split("/");
  const makeLower = (arr) => arr.map((x) => x.toLowerCase());

  /* does the URL specify two trees? */
  let treeTwoName;
  for (let i=0; i<parts.length; i++) {
    if (parts[i].indexOf("-") !== -1) {
      [treeName, treeTwoName] = parts[i].split("-");
      parts[i] = treeName; // only use the first tree from now on
      break;
    }
  }
  if (!treeName) {
    treeName = guessTreeName(parts);
  }

  if (source === "local") {
    if (parts[0].toLowerCase() !== "local") {
      parts.unshift("local");
    }
    auspiceURL = "local/";
    datasetFields = manifestHelpers.checkFieldsAgainstManifest(makeLower(parts).slice(1), source);
    fetchURL = global.LOCAL_DATA_PATH;
  } else if (source === "github") {
    if (parts.length < 3) {
      throw new Error("Community URLs must be of format community/githubOrgName/repoName/...");
    }
    fetchURL = `https://rawgit.com/${parts[1]}/${parts[2]}/master/auspice`;
    auspiceURL = `community/${parts[1]}/`;
    datasetFields = parts.slice(2); // case sensitive!
  } else if (source === "staging") {
    fetchURL = global.REMOTE_DATA_STAGING_BASEURL;
    auspiceURL = "staging/";
    datasetFields = manifestHelpers.checkFieldsAgainstManifest(makeLower(parts).slice(1), source);
  } else {
    /* default (for nextstrain.org, this is the data.nextstrain S3 bucket) */
    fetchURL = global.REMOTE_DATA_LIVE_BASEURL;
    auspiceURL = "";
    datasetFields = manifestHelpers.checkFieldsAgainstManifest(makeLower(parts), source);
  }

  if (treeTwoName) {
    const treeIdx = datasetFields.indexOf(treeName);
    const fieldsTT = datasetFields.slice();
    fieldsTT[treeIdx] = treeTwoName;
    secondTreeFetchURL = fetchURL + "/" + fieldsTT.join("_") + ".json";

    const fieldsAus = datasetFields.slice();
    fieldsAus[treeIdx] = `${treeName}-${treeTwoName}`;
    auspiceURL += fieldsAus.join("/");
  } else {
    auspiceURL += datasetFields.join("/");
  }

  fetchURL = fetchURL + "/" + datasetFields.join("_") + ".json";

  return {auspiceURL, fetchURL, secondTreeFetchURL, datasetFields, treeName, treeTwoName};
};

module.exports = {
  getSource,
  constructPathToGet,
  collectDatasets,
  guessTreeName
};
<commit_msg>add second tree options for partial datapaths (if applicable)<commit_after>const manifestHelpers = require("./manifestHelpers");
const path = require("path");

const urlToParts = (url, lower) => {
  if (lower) return url.toLowerCase().replace(/^\/+/, "").replace(/\/+$/, "").split("/");
  return url.replace(/^\/+/, "").replace(/\/+$/, "").split("/");
};

const getSource = (url) => {
  let parts = urlToParts(url, true);
  if (parts[0] === "status") {
    parts = parts.slice(1);
  }
  if (!parts.length || (parts.length === 1 && parts[0] === '')) {
    return "live";
  }
  if (parts[0] === "local") return "local";
  else if (parts[0] === "staging") return "staging";
  else if (parts[0] === "community") return "github";
  return "live";
};

const collectDatasets = (source) => {
  if (source === "local" && global.LOCAL_MANIFEST) {
    return {available: global.LOCAL_MANIFEST, source};
  } else if (source === "live" && global.LIVE_MANIFEST) {
    return {available: global.LIVE_MANIFEST, source};
  } else if (source === "staging" && global.STAGING_MANIFEST) {
    return {available: global.STAGING_MANIFEST, source};
  }
  return {available: undefined, source};
};

const guessTreeName = (parts) => {
  const guesses = ["HA", "NA", "PB1", "PB2", "PA", "NP", "NS", "MP", "L", "S"];
  for (const part of parts) {
    if (guesses.indexOf(part.toUpperCase()) !== -1) return part;
  }
  return undefined;
};

const constructPathToGet = (source, providedUrl) => {
  /* the path is lowercased _except for_ the github org & repo names */
  let auspiceURL; // the URL to be displayed in Auspice
  let fetchURL; // could be local path or http(s)://
  let secondTreeFetchURL;
  let datasetFields; // this _does not_ take into account the 2nd tree
  let treeName;

  const parts = providedUrl.replace(/^\//, '').replace(/\/$/, '').split("/");
  const makeLower = (arr) => arr.map((x) => x.toLowerCase());

  /* does the URL specify two trees? */
  let treeTwoName;
  for (let i=0; i<parts.length; i++) {
    if (parts[i].indexOf("-") !== -1) {
      [treeName, treeTwoName] = parts[i].split("-");
      parts[i] = treeName; // only use the first tree from now on
      break;
    }
  }

  if (source === "local") {
    if (parts[0].toLowerCase() !== "local") {
      parts.unshift("local");
    }
    auspiceURL = "local/";
    datasetFields = manifestHelpers.checkFieldsAgainstManifest(makeLower(parts).slice(1), source);
    fetchURL = global.LOCAL_DATA_PATH;
  } else if (source === "github") {
    if (parts.length < 3) {
      throw new Error("Community URLs must be of format community/githubOrgName/repoName/...");
    }
    fetchURL = `https://rawgit.com/${parts[1]}/${parts[2]}/master/auspice`;
    auspiceURL = `community/${parts[1]}/`;
    datasetFields = parts.slice(2); // case sensitive!
  } else if (source === "staging") {
    fetchURL = global.REMOTE_DATA_STAGING_BASEURL;
    auspiceURL = "staging/";
    datasetFields = manifestHelpers.checkFieldsAgainstManifest(makeLower(parts).slice(1), source);
  } else {
    /* default (for nextstrain.org, this is the data.nextstrain S3 bucket) */
    fetchURL = global.REMOTE_DATA_LIVE_BASEURL;
    auspiceURL = "";
    datasetFields = manifestHelpers.checkFieldsAgainstManifest(makeLower(parts), source);
  }

  if (!treeName) {
    treeName = guessTreeName(datasetFields);
  }
  if (treeTwoName) {
    const treeIdx = datasetFields.indexOf(treeName);
    const fieldsTT = datasetFields.slice();
    fieldsTT[treeIdx] = treeTwoName;
    secondTreeFetchURL = fetchURL + "/" + fieldsTT.join("_") + ".json";

    const fieldsAus = datasetFields.slice();
    fieldsAus[treeIdx] = `${treeName}-${treeTwoName}`;
    auspiceURL += fieldsAus.join("/");
  } else {
    auspiceURL += datasetFields.join("/");
  }

  fetchURL = fetchURL + "/" + datasetFields.join("_") + ".json";

  return {auspiceURL, fetchURL, secondTreeFetchURL, datasetFields, treeName, treeTwoName};
};

module.exports = {
  getSource,
  constructPathToGet,
  collectDatasets,
  guessTreeName
};
<|endoftext|>


<commit_before>/* jshint node: true */
var logger = require("../logger/logger");

var wrap = require('co-express');

var util = require('util');

var mongoose = require('../config/db.js').mongoose;
var Book = mongoose.model('Book', require('../models/book'));

function getBook(bookId) {
    return new Promise(function(resolve, reject) {
        Book.findOne({ _id: bookId }, function(err, book) {
            /* istanbul ignore next */
            if (err) {
                logger.error(err);
                return reject(err);
            }

            resolve(book);
        });
    });
}

var getRelatedBooks = wrap(function*(bookId) {
    //using interests and then price
    var book = yield getBook(bookId);
    var interests = book.interests
    if(!util.isArray(interests)){
        interests = [interests];
    }

    return new Promise(function(resolve, reject) {
        Book.find({ $and: [{ _id: { $ne: book._id } }, { interests: { $elemMatch: { $in: interests } } }] }).exec(function(err, relatedBooks) {
            /* istanbul ignore next */
            if (err) {
                logger.error(err);
                return reject(err);
            }

            resolve(relatedBooks);
        });
    });
});

function checkIfBookIsForLoan(book) {

}

function checkIfBookIsForSale(book) {

}

function checkIfUserCanReturnBook(book) {

}

function convertToISBN13(ISBN10) {

}

function convertToISBN10(ISBN13) {
    //must begin with 978

}

/*
Wikipedia gives it as

Remove the ISBN 10 check digit from the end
Add 978 to the front
Add the ISBN-13 check digit to the end
The check digit is calculated as per Wikipedia

Take each digit as an element in in an array x_ i.e. the ith digit i the ISBM is x_i e.g. x4 is the 4th digit.
Multiply the even digits by 3 and sum these.
add these to the sum of the odd digits
find the remainder mod 10
subtract from 10 (you have a number 1-10)
This is the result as a number between 1 and 10, unless the figure is 10 then the result is 0

x_13 = (10 - (x_1 + 3x_2 + x_3 + 3x_4 + ... + x_11 + 3x_12) mod10 ) mod 10
*/

function getAmazonBookCover(ISBN) {
    var url = "http://images.amazon.com/images/P/" + ISBN;
    //eg ISBN 0738202967 isbn-10
    //http://images.amazon.com/images/P/0738202967
    url = "http://images.amazon.com/images/P/" + 0738202967;
    ISBN = 0738202967;

    //remove dashes

}

module.exports = {
    getAmazonBookCover: getAmazonBookCover,
    getBook: getBook,
    getRelatedBooks: getRelatedBooks
}
<commit_msg>cleanup<commit_after>/* jshint node: true */
var logger = require("../logger/logger");

var wrap = require('co-express');

var util = require('util');

var mongoose = require('../config/db.js').mongoose;
var Book = mongoose.model('Book', require('../models/book'));

function getBook(bookId) {
    return new Promise(function(resolve, reject) {
        Book.findOne({ _id: bookId }, function(err, book) {
            /* istanbul ignore next */
            if (err) {
                logger.error(err);
                return reject(err);
            }

            resolve(book);
        });
    });
}

var getRelatedBooks = wrap(function*(bookId) {
    //using interests and then price
    var book = yield getBook(bookId);
    var interests = book.interests
    if(!util.isArray(interests)){
        interests = [interests];
    }

    return new Promise(function(resolve, reject) {
        Book.find({ $and: [{ _id: { $ne: book._id } }, { interests: { $elemMatch: { $in: interests } } }] }).exec(function(err, relatedBooks) {
            /* istanbul ignore next */
            if (err) {
                logger.error(err);
                return reject(err);
            }

            resolve(relatedBooks);
        });
    });
});

function convertToISBN13(ISBN10) {

}

function convertToISBN10(ISBN13) {
    //must begin with 978

}

/*
Wikipedia gives it as

Remove the ISBN 10 check digit from the end
Add 978 to the front
Add the ISBN-13 check digit to the end
The check digit is calculated as per Wikipedia

Take each digit as an element in in an array x_ i.e. the ith digit i the ISBM is x_i e.g. x4 is the 4th digit.
Multiply the even digits by 3 and sum these.
add these to the sum of the odd digits
find the remainder mod 10
subtract from 10 (you have a number 1-10)
This is the result as a number between 1 and 10, unless the figure is 10 then the result is 0

x_13 = (10 - (x_1 + 3x_2 + x_3 + 3x_4 + ... + x_11 + 3x_12) mod10 ) mod 10
*/

function getAmazonBookCover(ISBN) {
    var url = "http://images.amazon.com/images/P/" + ISBN;
    //eg ISBN 0738202967 isbn-10
    //http://images.amazon.com/images/P/0738202967
    url = "http://images.amazon.com/images/P/" + 0738202967;
    ISBN = 0738202967;

    //remove dashes

}

module.exports = {
    getAmazonBookCover: getAmazonBookCover,
    getBook: getBook,
    getRelatedBooks: getRelatedBooks
}
<|endoftext|>


<commit_before>angular.module('schemaForm').factory('formFormatters', [function () {
  var formatters = {
    'carNumber': function (input) {
      if (/^[a-zA-Z]{2}\s?\d{5}$/.test(input)) {
        input = input.replace(/^([a-zA-Z]{2})\s?(\d{5})$/, function (text, letters, numbers) {
          return letters.toUpperCase() + numbers;
        });
      }

      return input;
    },
    'number': function (input) {
      var parsed = parseInt(input, 10);
      if (isNaN(parsed)) {
        return undefined;
      } else {
        return parsed;
      }
    },
    'phoneNumber': function(input){
      input = input.replace(/\s+/g, '');
      input = input.replace(/\-+/g, '');

      if (/^\\d{8}$/.test(input)) {
        return input;
      }
    }
  };

  return {
    getFormatter: function (type) {
      return formatters[type];
    }
  };

}]);
<commit_msg>Added form number formatter<commit_after>angular.module('schemaForm').factory('formFormatters', [function () {
  var formatters = {
    'carNumber': function (input) {
      if (/^[a-zA-Z]{2}\s?\d{5}$/.test(input)) {
        input = input.replace(/^([a-zA-Z]{2})\s?(\d{5})$/, function (text, letters, numbers) {
          return letters.toUpperCase() + numbers;
        });
      }

      return input;
    },
    'number': function (input) {
      var parsed = parseInt(input, 10);
      if (isNaN(parsed)) {
        return undefined;
      } else {
        return parsed;
      }
    },

    'phoneNumber': function(input){
      input = input.replace(/\s+/g, '');
      input = input.replace(/\-+/g, '');

      if (/^\\d{8}$/.test(input)) {
        return input;
      }
    }
  };

  return {
    getFormatter: function (type) {
      return formatters[type];
    }
  };

}]);
<|endoftext|>


<commit_before>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import type {ReactPriorityLevel} from './SchedulerWithReactIntegration';

import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';

import {
  ImmediatePriority,
  UserBlockingPriority,
  NormalPriority,
  IdlePriority,
} from './SchedulerWithReactIntegration';

export type ExpirationTime = number;

export const NoWork = 0;
// TODO: Think of a better name for Never. The key difference with Idle is that
// Never work can be committed in an inconsistent state without tearing the UI.
// The main example is offscreen content, like a hidden subtree. So one possible
// name is Offscreen. However, it also includes dehydrated Suspense boundaries,
// which are inconsistent in the sense that they haven't finished yet, but
// aren't visibly inconsistent because the server rendered HTML matches what the
// hydrated tree would look like.
export const Never = 1;
// Idle is slightly higher priority than Never. It must completely finish in
// order to be consistent.
export const Idle = 2;
// Continuous Hydration is slightly higher than Idle and is used to increase
// priority of hover targets.
export const ContinuousHydration = 3;
export const Sync = MAX_SIGNED_31_BIT_INT;
export const Batched = Sync - 1;

const UNIT_SIZE = 10;
const MAGIC_NUMBER_OFFSET = Batched - 1;

// 1 unit of expiration time represents 10ms.
export function msToExpirationTime(ms: number): ExpirationTime {
  // Always add an offset so that we don't clash with the magic number for NoWork.
  return MAGIC_NUMBER_OFFSET - ((ms / UNIT_SIZE) | 0);
}

export function expirationTimeToMs(expirationTime: ExpirationTime): number {
  return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
}

function ceiling(num: number, precision: number): number {
  return (((num / precision) | 0) + 1) * precision;
}

function computeExpirationBucket(
  currentTime,
  expirationInMs,
  bucketSizeMs,
): ExpirationTime {
  return (
    MAGIC_NUMBER_OFFSET -
    ceiling(
      MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE,
      bucketSizeMs / UNIT_SIZE,
    )
  );
}

// TODO: This corresponds to Scheduler's NormalPriority, not LowPriority. Update
// the names to reflect.
export const LOW_PRIORITY_EXPIRATION = 5000;
export const LOW_PRIORITY_BATCH_SIZE = 250;

export function computeAsyncExpiration(
  currentTime: ExpirationTime,
): ExpirationTime {
  return computeExpirationBucket(
    currentTime,
    LOW_PRIORITY_EXPIRATION,
    LOW_PRIORITY_BATCH_SIZE,
  );
}

export function computeSuspenseExpiration(
  currentTime: ExpirationTime,
  timeoutMs: number,
): ExpirationTime {
  // TODO: Should we warn if timeoutMs is lower than the normal pri expiration time?
  return computeExpirationBucket(
    currentTime,
    timeoutMs,
    LOW_PRIORITY_BATCH_SIZE,
  );
}

// We intentionally set a higher expiration time for interactive updates in
// dev than in production.
//
// If the main thread is being blocked so long that you hit the expiration,
// it's a problem that could be solved with better scheduling.
//
// People will be more likely to notice this and fix it with the long
// expiration time in development.
//
// In production we opt for better UX at the risk of masking scheduling
// problems, by expiring fast.
export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150;
export const HIGH_PRIORITY_BATCH_SIZE = 100;

export function computeInteractiveExpiration(currentTime: ExpirationTime) {
  return computeExpirationBucket(
    currentTime,
    HIGH_PRIORITY_EXPIRATION,
    HIGH_PRIORITY_BATCH_SIZE,
  );
}

export function inferPriorityFromExpirationTime(
  currentTime: ExpirationTime,
  expirationTime: ExpirationTime,
): ReactPriorityLevel {
  if (expirationTime === Sync) {
    return ImmediatePriority;
  }
  if (expirationTime === Never || expirationTime === Idle) {
    return IdlePriority;
  }
  const msUntil =
    expirationTimeToMs(expirationTime) - expirationTimeToMs(currentTime);
  if (msUntil <= 0) {
    return ImmediatePriority;
  }
  if (msUntil <= HIGH_PRIORITY_EXPIRATION + HIGH_PRIORITY_BATCH_SIZE) {
    return UserBlockingPriority;
  }
  if (msUntil <= LOW_PRIORITY_EXPIRATION + LOW_PRIORITY_BATCH_SIZE) {
    return NormalPriority;
  }

  // TODO: Handle LowPriority

  // Assume anything lower has idle priority
  return IdlePriority;
}
<commit_msg>Update ReactFiberExpirationTime.js (#17825)<commit_after>/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import type {ReactPriorityLevel} from './SchedulerWithReactIntegration';

import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';

import {
  ImmediatePriority,
  UserBlockingPriority,
  NormalPriority,
  IdlePriority,
} from './SchedulerWithReactIntegration';

export type ExpirationTime = number;

export const NoWork = 0;
// TODO: Think of a better name for Never. The key difference with Idle is that
// Never work can be committed in an inconsistent state without tearing the UI.
// The main example is offscreen content, like a hidden subtree. So one possible
// name is Offscreen. However, it also includes dehydrated Suspense boundaries,
// which are inconsistent in the sense that they haven't finished yet, but
// aren't visibly inconsistent because the server rendered HTML matches what the
// hydrated tree would look like.
export const Never = 1;
// Idle is slightly higher priority than Never. It must completely finish in
// order to be consistent.
export const Idle = 2;
// Continuous Hydration is slightly higher than Idle and is used to increase
// priority of hover targets.
export const ContinuousHydration = 3;
export const Sync = MAX_SIGNED_31_BIT_INT;
export const Batched = Sync - 1;

const UNIT_SIZE = 10;
const MAGIC_NUMBER_OFFSET = Batched - 1;

// 1 unit of expiration time represents 10ms.
export function msToExpirationTime(ms: number): ExpirationTime {
  // Always subtract an offset so that we don't clash with the magic number for NoWork.
  return MAGIC_NUMBER_OFFSET - ((ms / UNIT_SIZE) | 0);
}

export function expirationTimeToMs(expirationTime: ExpirationTime): number {
  return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
}

function ceiling(num: number, precision: number): number {
  return (((num / precision) | 0) + 1) * precision;
}

function computeExpirationBucket(
  currentTime,
  expirationInMs,
  bucketSizeMs,
): ExpirationTime {
  return (
    MAGIC_NUMBER_OFFSET -
    ceiling(
      MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE,
      bucketSizeMs / UNIT_SIZE,
    )
  );
}

// TODO: This corresponds to Scheduler's NormalPriority, not LowPriority. Update
// the names to reflect.
export const LOW_PRIORITY_EXPIRATION = 5000;
export const LOW_PRIORITY_BATCH_SIZE = 250;

export function computeAsyncExpiration(
  currentTime: ExpirationTime,
): ExpirationTime {
  return computeExpirationBucket(
    currentTime,
    LOW_PRIORITY_EXPIRATION,
    LOW_PRIORITY_BATCH_SIZE,
  );
}

export function computeSuspenseExpiration(
  currentTime: ExpirationTime,
  timeoutMs: number,
): ExpirationTime {
  // TODO: Should we warn if timeoutMs is lower than the normal pri expiration time?
  return computeExpirationBucket(
    currentTime,
    timeoutMs,
    LOW_PRIORITY_BATCH_SIZE,
  );
}

// We intentionally set a higher expiration time for interactive updates in
// dev than in production.
//
// If the main thread is being blocked so long that you hit the expiration,
// it's a problem that could be solved with better scheduling.
//
// People will be more likely to notice this and fix it with the long
// expiration time in development.
//
// In production we opt for better UX at the risk of masking scheduling
// problems, by expiring fast.
export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150;
export const HIGH_PRIORITY_BATCH_SIZE = 100;

export function computeInteractiveExpiration(currentTime: ExpirationTime) {
  return computeExpirationBucket(
    currentTime,
    HIGH_PRIORITY_EXPIRATION,
    HIGH_PRIORITY_BATCH_SIZE,
  );
}

export function inferPriorityFromExpirationTime(
  currentTime: ExpirationTime,
  expirationTime: ExpirationTime,
): ReactPriorityLevel {
  if (expirationTime === Sync) {
    return ImmediatePriority;
  }
  if (expirationTime === Never || expirationTime === Idle) {
    return IdlePriority;
  }
  const msUntil =
    expirationTimeToMs(expirationTime) - expirationTimeToMs(currentTime);
  if (msUntil <= 0) {
    return ImmediatePriority;
  }
  if (msUntil <= HIGH_PRIORITY_EXPIRATION + HIGH_PRIORITY_BATCH_SIZE) {
    return UserBlockingPriority;
  }
  if (msUntil <= LOW_PRIORITY_EXPIRATION + LOW_PRIORITY_BATCH_SIZE) {
    return NormalPriority;
  }

  // TODO: Handle LowPriority

  // Assume anything lower has idle priority
  return IdlePriority;
}
<|endoftext|>