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

if (!process.env.CI) require('dotenv-safe').load()
const args = require('minimist')(process.argv.slice(2), {
  boolean: ['automaticRelease', 'notesOnly', 'stable']
})
const ciReleaseBuild = require('./ci-release-build')
const GitHub = require('github')
const { execSync } = require('child_process')
const { GitProcess } = require('dugite')

const path = require('path')
const readline = require('readline')
const releaseNotesGenerator = require('./release-notes/index.js')
const { getCurrentBranch } = require('./lib/utils.js')
const bumpType = args._[0]
const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron'

require('colors')
const pass = '\u2713'.green
const fail = '\u2717'.red

// TODO (future) automatically determine version based on conventional commits
// via conventional-recommended-bump

if (!bumpType && !args.notesOnly) {
  console.log(`Usage: prepare-release [stable | beta | nightly]` +
     ` (--stable) (--notesOnly) (--automaticRelease) (--branch)`)
  process.exit(1)
}

const github = new GitHub()
const gitDir = path.resolve(__dirname, '..')
github.authenticate({ type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN })

async function getNewVersion (dryRun) {
  if (!dryRun) {
    console.log(`Bumping for new "${bumpType}" version.`)
  }
  const bumpScript = path.join(__dirname, 'bump-version.js')
  const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`]
  if (dryRun) scriptArgs.push('--dry-run')
  try {
    let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' })
    bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim()
    const newVersion = `v${bumpVersion}`
    if (!dryRun) {
      console.log(`${pass} Successfully bumped version to ${newVersion}`)
    }
    return newVersion
  } catch (err) {
    console.log(`${fail} Could not bump version, error was:`, err)
    throw err
  }
}

async function getReleaseNotes (currentBranch) {
  if (bumpType === 'nightly') {
    return 'Nightlies do not get release notes, please compare tags for info'
  }
  console.log(`Generating release notes for ${currentBranch}.`)
  const releaseNotes = await releaseNotesGenerator(currentBranch)
  if (releaseNotes.warning) {
    console.warn(releaseNotes.warning)
  }
  return releaseNotes
}

async function createRelease (branchToTarget, isBeta) {
  const releaseNotes = await getReleaseNotes(branchToTarget)
  const newVersion = await getNewVersion()
  await tagRelease(newVersion)
  const githubOpts = {
    owner: 'electron',
    repo: targetRepo
  }
  console.log(`Checking for existing draft release.`)
  const releases = await github.repos.getReleases(githubOpts)
    .catch(err => {
      console.log(`${fail} Could not get releases. Error was: `, err)
    })
  const drafts = releases.data.filter(release => release.draft &&
    release.tag_name === newVersion)
  if (drafts.length > 0) {
    console.log(`${fail} Aborting because draft release for
      ${drafts[0].tag_name} already exists.`)
    process.exit(1)
  }
  console.log(`${pass} A draft release does not exist; creating one.`)
  githubOpts.draft = true
  githubOpts.name = `electron ${newVersion}`
  if (isBeta) {
    if (newVersion.indexOf('nightly') > 0) {
      githubOpts.body = `Note: This is a nightly release.  Please file new issues ` +
        `for any bugs you find in it.\n \n This release is published to npm ` +
        `under the nightly tag and can be installed via npm install electron@nightly, ` +
        `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`
    } else {
      githubOpts.body = `Note: This is a beta release.  Please file new issues ` +
        `for any bugs you find in it.\n \n This release is published to npm ` +
        `under the beta tag and can be installed via npm install electron@beta, ` +
        `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`
    }
    githubOpts.name = `${githubOpts.name}`
    githubOpts.prerelease = true
  } else {
    githubOpts.body = releaseNotes
  }
  githubOpts.tag_name = newVersion
  githubOpts.target_commitish = newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget
  const release = await github.repos.createRelease(githubOpts)
    .catch(err => {
      console.log(`${fail} Error creating new release: `, err)
      process.exit(1)
    })
  console.log(`Release has been created with id: ${release.id}.`)
  console.log(`${pass} Draft release for ${newVersion} successful.`)
}

async function pushRelease (branch) {
  const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], gitDir)
  if (pushDetails.exitCode === 0) {
    console.log(`${pass} Successfully pushed the release.  Wait for ` +
      `release builds to finish before running "npm run release".`)
  } else {
    console.log(`${fail} Error pushing the release: ` +
        `${pushDetails.stderr}`)
    process.exit(1)
  }
}

async function runReleaseBuilds (branch) {
  await ciReleaseBuild(branch, {
    ghRelease: true,
    automaticRelease: args.automaticRelease
  })
}

async function tagRelease (version) {
  console.log(`Tagging release ${version}.`)
  const checkoutDetails = await GitProcess.exec([ 'tag', '-a', '-m', version, version ], gitDir)
  if (checkoutDetails.exitCode === 0) {
    console.log(`${pass} Successfully tagged ${version}.`)
  } else {
    console.log(`${fail} Error tagging ${version}: ` +
      `${checkoutDetails.stderr}`)
    process.exit(1)
  }
}

async function verifyNewVersion () {
  const newVersion = await getNewVersion(true)
  let response
  if (args.automaticRelease) {
    response = 'y'
  } else {
    response = await promptForVersion(newVersion)
  }
  if (response.match(/^y/i)) {
    console.log(`${pass} Starting release of ${newVersion}`)
  } else {
    console.log(`${fail} Aborting release of ${newVersion}`)
    process.exit()
  }
}

async function promptForVersion (version) {
  return new Promise((resolve, reject) => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    })
    rl.question(`Do you want to create the release ${version.green} (y/N)? `, (answer) => {
      rl.close()
      resolve(answer)
    })
  })
}

// function to determine if there have been commits to master since the last release
async function changesToRelease () {
  const lastCommitWasRelease = new RegExp(`^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$`, 'g')
  const lastCommit = await GitProcess.exec(['log', '-n', '1', `--pretty=format:'%s'`], gitDir)
  return !lastCommitWasRelease.test(lastCommit.stdout)
}

async function prepareRelease (isBeta, notesOnly) {
  if (args.dryRun) {
    const newVersion = await getNewVersion(true)
    console.log(newVersion)
  } else {
    const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(gitDir)
    if (notesOnly) {
      const releaseNotes = await getReleaseNotes(currentBranch)
      console.log(`Draft release notes are: \n${releaseNotes.text}`)
    } else {
      const changes = await changesToRelease(currentBranch)
      if (changes) {
        await verifyNewVersion()
        await createRelease(currentBranch, isBeta)
        await pushRelease(currentBranch)
        await runReleaseBuilds(currentBranch)
      } else {
        console.log(`There are no new changes to this branch since the last release, aborting release.`)
        process.exit(1)
      }
    }
  }
}

prepareRelease(!args.stable, args.notesOnly)
<commit_msg>chore: release.id => release.data.id (#16132)<commit_after>#!/usr/bin/env node

if (!process.env.CI) require('dotenv-safe').load()
const args = require('minimist')(process.argv.slice(2), {
  boolean: ['automaticRelease', 'notesOnly', 'stable']
})
const ciReleaseBuild = require('./ci-release-build')
const GitHub = require('github')
const { execSync } = require('child_process')
const { GitProcess } = require('dugite')

const path = require('path')
const readline = require('readline')
const releaseNotesGenerator = require('./release-notes/index.js')
const { getCurrentBranch } = require('./lib/utils.js')
const bumpType = args._[0]
const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron'

require('colors')
const pass = '\u2713'.green
const fail = '\u2717'.red

// TODO (future) automatically determine version based on conventional commits
// via conventional-recommended-bump

if (!bumpType && !args.notesOnly) {
  console.log(`Usage: prepare-release [stable | beta | nightly]` +
     ` (--stable) (--notesOnly) (--automaticRelease) (--branch)`)
  process.exit(1)
}

const github = new GitHub()
const gitDir = path.resolve(__dirname, '..')
github.authenticate({ type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN })

async function getNewVersion (dryRun) {
  if (!dryRun) {
    console.log(`Bumping for new "${bumpType}" version.`)
  }
  const bumpScript = path.join(__dirname, 'bump-version.js')
  const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`]
  if (dryRun) scriptArgs.push('--dry-run')
  try {
    let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' })
    bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim()
    const newVersion = `v${bumpVersion}`
    if (!dryRun) {
      console.log(`${pass} Successfully bumped version to ${newVersion}`)
    }
    return newVersion
  } catch (err) {
    console.log(`${fail} Could not bump version, error was:`, err)
    throw err
  }
}

async function getReleaseNotes (currentBranch) {
  if (bumpType === 'nightly') {
    return 'Nightlies do not get release notes, please compare tags for info'
  }
  console.log(`Generating release notes for ${currentBranch}.`)
  const releaseNotes = await releaseNotesGenerator(currentBranch)
  if (releaseNotes.warning) {
    console.warn(releaseNotes.warning)
  }
  return releaseNotes
}

async function createRelease (branchToTarget, isBeta) {
  const releaseNotes = await getReleaseNotes(branchToTarget)
  const newVersion = await getNewVersion()
  await tagRelease(newVersion)
  const githubOpts = {
    owner: 'electron',
    repo: targetRepo
  }
  console.log(`Checking for existing draft release.`)
  const releases = await github.repos.getReleases(githubOpts)
    .catch(err => {
      console.log(`${fail} Could not get releases. Error was: `, err)
    })
  const drafts = releases.data.filter(release => release.draft &&
    release.tag_name === newVersion)
  if (drafts.length > 0) {
    console.log(`${fail} Aborting because draft release for
      ${drafts[0].tag_name} already exists.`)
    process.exit(1)
  }
  console.log(`${pass} A draft release does not exist; creating one.`)
  githubOpts.draft = true
  githubOpts.name = `electron ${newVersion}`
  if (isBeta) {
    if (newVersion.indexOf('nightly') > 0) {
      githubOpts.body = `Note: This is a nightly release.  Please file new issues ` +
        `for any bugs you find in it.\n \n This release is published to npm ` +
        `under the nightly tag and can be installed via npm install electron@nightly, ` +
        `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`
    } else {
      githubOpts.body = `Note: This is a beta release.  Please file new issues ` +
        `for any bugs you find in it.\n \n This release is published to npm ` +
        `under the beta tag and can be installed via npm install electron@beta, ` +
        `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`
    }
    githubOpts.name = `${githubOpts.name}`
    githubOpts.prerelease = true
  } else {
    githubOpts.body = releaseNotes
  }
  githubOpts.tag_name = newVersion
  githubOpts.target_commitish = newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget
  const release = await github.repos.createRelease(githubOpts)
    .catch(err => {
      console.log(`${fail} Error creating new release: `, err)
      process.exit(1)
    })
  console.log(`Release has been created with id: ${release.data.id}.`)
  console.log(`${pass} Draft release for ${newVersion} successful.`)
}

async function pushRelease (branch) {
  const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], gitDir)
  if (pushDetails.exitCode === 0) {
    console.log(`${pass} Successfully pushed the release.  Wait for ` +
      `release builds to finish before running "npm run release".`)
  } else {
    console.log(`${fail} Error pushing the release: ` +
        `${pushDetails.stderr}`)
    process.exit(1)
  }
}

async function runReleaseBuilds (branch) {
  await ciReleaseBuild(branch, {
    ghRelease: true,
    automaticRelease: args.automaticRelease
  })
}

async function tagRelease (version) {
  console.log(`Tagging release ${version}.`)
  const checkoutDetails = await GitProcess.exec([ 'tag', '-a', '-m', version, version ], gitDir)
  if (checkoutDetails.exitCode === 0) {
    console.log(`${pass} Successfully tagged ${version}.`)
  } else {
    console.log(`${fail} Error tagging ${version}: ` +
      `${checkoutDetails.stderr}`)
    process.exit(1)
  }
}

async function verifyNewVersion () {
  const newVersion = await getNewVersion(true)
  let response
  if (args.automaticRelease) {
    response = 'y'
  } else {
    response = await promptForVersion(newVersion)
  }
  if (response.match(/^y/i)) {
    console.log(`${pass} Starting release of ${newVersion}`)
  } else {
    console.log(`${fail} Aborting release of ${newVersion}`)
    process.exit()
  }
}

async function promptForVersion (version) {
  return new Promise((resolve, reject) => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    })
    rl.question(`Do you want to create the release ${version.green} (y/N)? `, (answer) => {
      rl.close()
      resolve(answer)
    })
  })
}

// function to determine if there have been commits to master since the last release
async function changesToRelease () {
  const lastCommitWasRelease = new RegExp(`^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$`, 'g')
  const lastCommit = await GitProcess.exec(['log', '-n', '1', `--pretty=format:'%s'`], gitDir)
  return !lastCommitWasRelease.test(lastCommit.stdout)
}

async function prepareRelease (isBeta, notesOnly) {
  if (args.dryRun) {
    const newVersion = await getNewVersion(true)
    console.log(newVersion)
  } else {
    const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(gitDir)
    if (notesOnly) {
      const releaseNotes = await getReleaseNotes(currentBranch)
      console.log(`Draft release notes are: \n${releaseNotes.text}`)
    } else {
      const changes = await changesToRelease(currentBranch)
      if (changes) {
        await verifyNewVersion()
        await createRelease(currentBranch, isBeta)
        await pushRelease(currentBranch)
        await runReleaseBuilds(currentBranch)
      } else {
        console.log(`There are no new changes to this branch since the last release, aborting release.`)
        process.exit(1)
      }
    }
  }
}

prepareRelease(!args.stable, args.notesOnly)
<|endoftext|>


<commit_before>// No Persistent Fighter Chain Popup (Rydian)

var pattern = scan('83 79 44 01 75 09 6A 00 6A 00 E8');

patch(pattern.add(4), 0xEB);
<commit_msg>Updated description<commit_after>// Description:
// While in hide interface mode, using chain combo fighter skills will turn the interface back on. This disables this trigger, persisting the interface to stay hidden. (created by Rydian)

var secondchain = scan('83 79 44 01 75 09 6A 00 6A 00 E8');
patch(secondchain.add(4), 0xEB);
var thirdchain = scan('83 79 44 01 75 09 6A 00 6A 00 E8');
patch(thirdchain.add(4), 0xEB);
<|endoftext|>


<commit_before>/* eslint-disable no-underscore-dangle, no-restricted-syntax */
import isObject from './isObject';
import has from './has';
import length from './length';
import nth from './nth';

export const _keys = obj => {
  const hasDontEnumBug = !Object.prototype.propertyIsEnumerable.call(
    { toString: null },
    'toString',
  );

  const dontEnums = [
    'toString',
    'toLocaleString',
    'valueOf',
    'hasOwnProperty',
    'isPrototypeOf',
    'propertyIsEnumerable',
    'constructor',
  ];

  const dontEnumsLength = length(dontEnums);

  if (!isObject(obj)) throw new TypeError('Keys called on non-object');

  const result = [];

  for (const prop in obj) {
    if (has(prop, obj)) result.push(prop);
  }

  if (hasDontEnumBug) {
    for (let i = 0; i < dontEnumsLength; i++) {
      const prop = nth(i, dontEnums);
      if (has(prop, obj)) {
        result.push(prop);
      }
    }
  }

  return result;
};

export default Object.keys || _keys;
<commit_msg>fix(keys): Initialize internal keys as iife<commit_after>/* eslint-disable no-underscore-dangle, no-restricted-syntax */
import isObject from './isObject';
import has from './has';
import length from './length';
import nth from './nth';

export const _keys = (function _keys() {
  const hasDontEnumBug = !Object.prototype.propertyIsEnumerable.call(
    { toString: null },
    'toString',
  );

  const dontEnums = [
    'toString',
    'toLocaleString',
    'valueOf',
    'hasOwnProperty',
    'isPrototypeOf',
    'propertyIsEnumerable',
    'constructor',
  ];

  const dontEnumsLength = length(dontEnums);

  return obj => {
    if (!isObject(obj)) throw new TypeError('Keys called on non-object');

    const result = [];

    for (const prop in obj) {
      if (has(prop, obj)) result.push(prop);
    }

    if (hasDontEnumBug) {
      for (let i = 0; i < dontEnumsLength; i++) {
        const prop = nth(i, dontEnums);
        if (has(prop, obj)) result.push(prop);
      }
    }

    return result;
  };
})();

export default Object.keys || _keys;
<|endoftext|>


<commit_before>/* global describe, it */

const path = require('path')

const BUILD_DIR = path.join(process.cwd(), '__tests__/test-utils/tmp')
const MOCK_DIR = path.join(process.cwd(), '__tests__/test-utils/mocks')

describe('lib > push to s3', () => {
  const configPush = require('../../lib/push-to-s3')
  const loadConfig = require('../../lib/load-config')
  const createLogger = require('../../lib/logger')

  it('should compile JavaScript and CSS and send to s3 via aws-sdk', () => {
    const config = loadConfig(process.cwd(), 'configurations/default', 'development')
    const push = configPush({
      env: 'development',
      config,
      log: createLogger(),
      minify: false,
      s3bucket: 'test-bucket'
    })
    return Promise.all([
      push([`${MOCK_DIR}/index.js`, `${BUILD_DIR}/index.js`]),
      push([`${MOCK_DIR}/index.css`, `${BUILD_DIR}/index.css`])
    ])
  })
})
<commit_msg>test(logger): Remove create logger<commit_after>/* global describe, it */

const path = require('path')

const BUILD_DIR = path.join(process.cwd(), '__tests__/test-utils/tmp')
const MOCK_DIR = path.join(process.cwd(), '__tests__/test-utils/mocks')

describe('lib > push to s3', () => {
  const configPush = require('../../lib/push-to-s3')
  const loadConfig = require('../../lib/load-config')

  it('should compile JavaScript and CSS and send to s3 via aws-sdk', () => {
    const config = loadConfig(process.cwd(), 'configurations/default', 'development')
    const push = configPush({
      env: 'development',
      config,
      minify: false,
      s3bucket: 'test-bucket'
    })
    return Promise.all([
      push([`${MOCK_DIR}/index.js`, `${BUILD_DIR}/index.js`]),
      push([`${MOCK_DIR}/index.css`, `${BUILD_DIR}/index.css`])
    ])
  })
})
<|endoftext|>


<commit_before>jest.unmock('../lib/twitter-client')

var Bot = require('../lib/bot')
var TwitterClient = require('../lib/twitter-client')

var bot = new Bot()

test('should default to consumer key from enviromental variable', () => {

  var client = new TwitterClient(bot)
  client.send(null, 'text')

  expect(client.twit.post).toBeCalledWith('statuses/update', { status: 'text' });
});

test('should use enviromental variables for key, secrets and tokens', () => {

  process.env.TWITTER_CONSUMER_KEY = 'key'
  process.env.TWITTER_CONSUMER_SECRET = 'secret'
  process.env.TWITTER_ACCESS_TOKEN = 'token'
  process.env.TWITTER_ACCESS_TOKEN_SECRET = 'secret'

  var client = new TwitterClient(bot)

  expect(client.config.consumer_key).toEqual('key')
  expect(client.config.consumer_secret).toEqual('secret')
  expect(client.config.access_token).toEqual('token')
  expect(client.config.access_token_secret).toEqual('secret')

  process.env.TWITTER_CONSUMER_KEY = undefined
  process.env.TWITTER_CONSUMER_SECRET = undefined
  process.env.TWITTER_ACCESS_TOKEN = undefined
  process.env.TWITTER_ACCESS_TOKEN_SECRET = undefined
});

test('should use configuration object for key, secrets and tokens', () => {

  var config = {
    consumer_key: 'key',
    consumer_secret: 'secret',
    access_token: 'token',
    access_token_secret: 'secret'
  }

  var client = new TwitterClient(bot, config)

  expect(client.config.consumer_key).toEqual('key')
  expect(client.config.consumer_secret).toEqual('secret')
  expect(client.config.access_token).toEqual('token')
  expect(client.config.access_token_secret).toEqual('secret')
});

test('should listen for tweets mentioning the bot', () => {

  var client = new TwitterClient(bot)
  client.send(null, 'text')

  expect(client.twit.stream).toBeCalledWith('statuses/filter', { track: '@' + bot.name });
});

test('should handle for tweets mentioning the bot', () => {

  var handler = jest.fn()

  var originalImp = TwitterClient.prototype.createTweetHandler
  TwitterClient.prototype.createTweetHandler = function() {
    return handler
  }

  var client = new TwitterClient(bot)

  expect(client.stream.on).toBeCalledWith('tweet', handler)

  TwitterClient.prototype.createTweetHandler = originalImp
});

//       var session = new Session(tweet.user.id, {}, this)
//

test('should trigger received_message event on bot for message', () => {

  var handler = jest.fn()

  var client = new TwitterClient(bot)
  var session = client.createTweetHandler()({
    user: {
      id: "1"
    },
    text: 'text'
  })

  expect(bot.trigger).toBeCalledWith('message_received', {
    text: 'text'
  }, session)
});

test('posts status when sending message', () => {

  var client = new TwitterClient(bot)
  client.send(null, 'text')

  expect(client.twit.post).toBeCalledWith('statuses/update', { status: 'text' });
});
<commit_msg>Session test.<commit_after>jest.unmock('../lib/twitter-client')

var Bot = require('../lib/bot')
var TwitterClient = require('../lib/twitter-client')

var bot = new Bot()

test('should default to consumer key from enviromental variable', () => {

  var client = new TwitterClient(bot)
  client.send(null, 'text')

  expect(client.twit.post).toBeCalledWith('statuses/update', { status: 'text' });
});

test('should use enviromental variables for key, secrets and tokens', () => {

  process.env.TWITTER_CONSUMER_KEY = 'key'
  process.env.TWITTER_CONSUMER_SECRET = 'secret'
  process.env.TWITTER_ACCESS_TOKEN = 'token'
  process.env.TWITTER_ACCESS_TOKEN_SECRET = 'secret'

  var client = new TwitterClient(bot)

  expect(client.config.consumer_key).toEqual('key')
  expect(client.config.consumer_secret).toEqual('secret')
  expect(client.config.access_token).toEqual('token')
  expect(client.config.access_token_secret).toEqual('secret')

  process.env.TWITTER_CONSUMER_KEY = undefined
  process.env.TWITTER_CONSUMER_SECRET = undefined
  process.env.TWITTER_ACCESS_TOKEN = undefined
  process.env.TWITTER_ACCESS_TOKEN_SECRET = undefined
});

test('should use configuration object for key, secrets and tokens', () => {

  var config = {
    consumer_key: 'key',
    consumer_secret: 'secret',
    access_token: 'token',
    access_token_secret: 'secret'
  }

  var client = new TwitterClient(bot, config)

  expect(client.config.consumer_key).toEqual('key')
  expect(client.config.consumer_secret).toEqual('secret')
  expect(client.config.access_token).toEqual('token')
  expect(client.config.access_token_secret).toEqual('secret')
});

test('should listen for tweets mentioning the bot', () => {

  var client = new TwitterClient(bot)
  client.send(null, 'text')

  expect(client.twit.stream).toBeCalledWith('statuses/filter', { track: '@' + bot.name });
});

test('should handle for tweets mentioning the bot', () => {

  var handler = jest.fn()

  var originalImp = TwitterClient.prototype.createTweetHandler
  TwitterClient.prototype.createTweetHandler = function() {
    return handler
  }

  var client = new TwitterClient(bot)

  expect(client.stream.on).toBeCalledWith('tweet', handler)

  TwitterClient.prototype.createTweetHandler = originalImp
});

//       var session = new Session(tweet.user.id, {}, this)
//

test('should create valid session for message', () => {

  var handler = jest.fn()

  var client = new TwitterClient(bot)
  var session = client.createTweetHandler()({
    user: {
      id: "1"
    },
    text: 'text'
  })

  expect(session.user).toEqual("1")
  expect(session.context).toEqual({})
  expect(session.client).toBe(client)
});

test('should trigger received_message event on bot for message', () => {

  var handler = jest.fn()

  var client = new TwitterClient(bot)
  var session = client.createTweetHandler()({
    user: {
      id: "1"
    },
    text: 'text'
  })

  expect(bot.trigger).toBeCalledWith('message_received', {
    text: 'text'
  }, session)
});

test('posts status when sending message', () => {

  var client = new TwitterClient(bot)
  client.send(null, 'text')

  expect(client.twit.post).toBeCalledWith('statuses/update', { status: 'text' });
});
<|endoftext|>


<commit_before>var BezierEasing = require("bezier-easing");

var cumulativeOffset = function(element) {
  var top = 0, left = 0;
  do {
    top += element.offsetTop  || 0;
    left += element.offsetLeft || 0;
    element = element.offsetParent;
  } while(element);

  return {
    top: top,
    left: left
  };
};

function BezierEasingEditor (canvas) {
  var ctx = canvas.getContext("2d");
  var self = this;

  var bezier;
  var fun;

  var currentStep = [0, 0];

  function setBezier (b) {
    bezier = b;
    for (var i = 0; i < b.length; ++i)
      b[i] = Math.floor(b[i]*100)/100;
    fun = BezierEasing.apply(this, b);
    if (self.onChange) self.onChange(fun);
  }
  function getBezierFunction () {
    return fun;
  }
  this.getEasing = getBezierFunction;

  var HANDLE_RADIUS = 0.03;

  // state variables
  // handles positions
  var handle = [ null, [0.25, 0.25], [0.75, 0.75] ];
  var draggingHandle = 0;
  var hoveringHandle = 0;

  var hasChanged;
  var hovering = false;
  var stime = +new Date();
  var oneHandleClicked = false;

  function positionWithE (e) {
    var o = cumulativeOffset(canvas);
    return { x: relativeX(e.clientX-o.left), y: relativeY(e.clientY-o.top) };
  }

  function setup() {
    canvas.addEventListener("mousedown", function (e) {
      e.preventDefault();
      var p = positionWithE(e);
      var hnum = findHandle(p.x, p.y);
      if (hnum) {
        draggingHandle = hnum;
        oneHandleClicked = true;
      }
    });
    canvas.addEventListener("mouseup", function (e) {
      var p = positionWithE(e);
      if (draggingHandle) {
        setHandle(draggingHandle, p.x, p.y);
        draggingHandle = 0;
      }
    });
    canvas.addEventListener("mousemove", function (e) {
      e.preventDefault();
      var p = positionWithE(e);
      if (draggingHandle) {
        setHandle(draggingHandle, p.x, p.y);
      }
      hoveringHandle = draggingHandle || findHandle(p.x, p.y);
    });
    canvas.addEventListener("mouseover", function () {
      hovering = true;
      hasChanged = true;
    });
    canvas.addEventListener("mouseout", function () {
      hovering = false;
      hasChanged = true;
      draggingHandle = 0;
      hoveringHandle = 0;
    });

    syncBezier();
  }

  function render () {
    var now = +new Date();

    ctx.save();
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    // Draw grid

    ctx.translate(0, canvas.height);
    ctx.scale(canvas.width, -canvas.height);

    // Draw projections
    ctx.lineWidth = 0.01;
    ctx.strokeStyle = "rgba(0,0,0,0.5)";
    ctx.beginPath();
    ctx.moveTo(currentStep[0], 0);
    ctx.lineTo(currentStep[0], currentStep[1]);
    ctx.lineTo(0, currentStep[1]);
    ctx.stroke();

    // Draw bezier
    ctx.lineWidth = 0.02;
    ctx.strokeStyle = "black";
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.bezierCurveTo(handle[1][0], handle[1][1], handle[2][0], handle[2][1], 1, 1);
    ctx.stroke();

    // Draw handle
    ctx.strokeStyle = "red";
    ctx.fillStyle = "white";
    ctx.lineWidth = 0.01;

    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(handle[1][0], handle[1][1]);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(1, 1);
    ctx.lineTo(handle[2][0], handle[2][1]);
    ctx.stroke();

    var r = HANDLE_RADIUS;
    if (!oneHandleClicked) {
      r += 0.2*HANDLE_RADIUS*Math.cos((stime-now)/150);
    }
    for (var i=1; i<handle.length; ++i) {
      var h = handle[i];
      ctx.beginPath();
      ctx.arc(h[0], h[1], r, 0, Math.PI*2);
      ctx.fillStyle = (hoveringHandle === i) ? "red" : "white";
      ctx.fill();
      ctx.stroke();
    }
    ctx.restore();
  }

  function syncBezier () {
    setBezier([ handle[1][0], handle[1][1], handle[2][0], handle[2][1] ]);
  }

  function findHandle (x, y) {
    for (var i=1; i<handle.length; ++i) {
      var h = handle[i];
      var radius = HANDLE_RADIUS;
      var dx = x - h[0];
      var dy = y - h[1];
      if (dx*dx+dy*dy < radius*radius)
        return i;
    }
    return 0;
  }

  function setHandle (num, x, y) {
    handle [num] = [x, y];
    syncBezier();
    render();
  }

  function relativeX (x) {
    return x/canvas.width;
  }
  function relativeY (y) {
    return 1-y/canvas.height;
  }

  setup();
  (function loop () {
    window.requestAnimationFrame(function() {
      loop();
      render();
    }, canvas);
  }());
}

module.exports = BezierEasingEditor;
<commit_msg>Bugfix the easing editor when scroll>0<commit_after>var BezierEasing = require("bezier-easing");

var cumulativeOffset = function(element) {
  var top = 0, left = 0;
  do {
    top += element.offsetTop  || 0;
    left += element.offsetLeft || 0;
    element = element.offsetParent;
  } while(element);

  return {
    top: top,
    left: left
  };
};

function BezierEasingEditor (canvas) {
  var ctx = canvas.getContext("2d");
  var self = this;

  var bezier;
  var fun;

  var currentStep = [0, 0];

  function setBezier (b) {
    bezier = b;
    for (var i = 0; i < b.length; ++i)
      b[i] = Math.floor(b[i]*100)/100;
    fun = BezierEasing.apply(this, b);
    if (self.onChange) self.onChange(fun);
  }
  function getBezierFunction () {
    return fun;
  }
  this.getEasing = getBezierFunction;

  var HANDLE_RADIUS = 0.03;

  // state variables
  // handles positions
  var handle = [ null, [0.25, 0.25], [0.75, 0.75] ];
  var draggingHandle = 0;
  var hoveringHandle = 0;

  var hasChanged;
  var hovering = false;
  var stime = +new Date();
  var oneHandleClicked = false;

  function positionWithE (e) {
    var o = cumulativeOffset(canvas);
    return { x: relativeX(e.clientX+window.scrollX-o.left), y: relativeY(e.clientY+window.scrollY-o.top) };
  }

  function setup() {
    canvas.addEventListener("mousedown", function (e) {
      e.preventDefault();
      var p = positionWithE(e);
      var hnum = findHandle(p.x, p.y);
      if (hnum) {
        draggingHandle = hnum;
        oneHandleClicked = true;
      }
    });
    canvas.addEventListener("mouseup", function (e) {
      var p = positionWithE(e);
      if (draggingHandle) {
        setHandle(draggingHandle, p.x, p.y);
        draggingHandle = 0;
      }
    });
    canvas.addEventListener("mousemove", function (e) {
      e.preventDefault();
      var p = positionWithE(e);
      if (draggingHandle) {
        setHandle(draggingHandle, p.x, p.y);
      }
      hoveringHandle = draggingHandle || findHandle(p.x, p.y);
    });
    canvas.addEventListener("mouseover", function () {
      hovering = true;
      hasChanged = true;
    });
    canvas.addEventListener("mouseout", function () {
      hovering = false;
      hasChanged = true;
      draggingHandle = 0;
      hoveringHandle = 0;
    });

    syncBezier();
  }

  function render () {
    var now = +new Date();

    ctx.save();
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    // Draw grid

    ctx.translate(0, canvas.height);
    ctx.scale(canvas.width, -canvas.height);

    // Draw projections
    ctx.lineWidth = 0.01;
    ctx.strokeStyle = "rgba(0,0,0,0.5)";
    ctx.beginPath();
    ctx.moveTo(currentStep[0], 0);
    ctx.lineTo(currentStep[0], currentStep[1]);
    ctx.lineTo(0, currentStep[1]);
    ctx.stroke();

    // Draw bezier
    ctx.lineWidth = 0.02;
    ctx.strokeStyle = "black";
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.bezierCurveTo(handle[1][0], handle[1][1], handle[2][0], handle[2][1], 1, 1);
    ctx.stroke();

    // Draw handle
    ctx.strokeStyle = "red";
    ctx.fillStyle = "white";
    ctx.lineWidth = 0.01;

    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(handle[1][0], handle[1][1]);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(1, 1);
    ctx.lineTo(handle[2][0], handle[2][1]);
    ctx.stroke();

    var r = HANDLE_RADIUS;
    if (!oneHandleClicked) {
      r += 0.2*HANDLE_RADIUS*Math.cos((stime-now)/150);
    }
    for (var i=1; i<handle.length; ++i) {
      var h = handle[i];
      ctx.beginPath();
      ctx.arc(h[0], h[1], r, 0, Math.PI*2);
      ctx.fillStyle = (hoveringHandle === i) ? "red" : "white";
      ctx.fill();
      ctx.stroke();
    }
    ctx.restore();
  }

  function syncBezier () {
    setBezier([ handle[1][0], handle[1][1], handle[2][0], handle[2][1] ]);
  }

  function findHandle (x, y) {
    for (var i=1; i<handle.length; ++i) {
      var h = handle[i];
      var radius = HANDLE_RADIUS;
      var dx = x - h[0];
      var dy = y - h[1];
      if (dx*dx+dy*dy < radius*radius)
        return i;
    }
    return 0;
  }

  function setHandle (num, x, y) {
    handle [num] = [x, y];
    syncBezier();
    render();
  }

  function relativeX (x) {
    return x/canvas.width;
  }
  function relativeY (y) {
    return 1-y/canvas.height;
  }

  setup();
  (function loop () {
    window.requestAnimationFrame(function() {
      loop();
      render();
    }, canvas);
  }());
}

module.exports = BezierEasingEditor;
<|endoftext|>


<commit_before>/**
 * @constructor
 */
FormBuilder = function (id, name) {

    /**
     * @var {string}
     * @protected
     */
    this.id_ = id;

    /**
     * @var {string}
     * @protected
     */
    this.name_ = name;

    /**
     * Holds list of form types.
     *
     * @var {Object}
     * @protected
     */
    this.types_ = [];

    /**
     * @var {Boolean}
     * @protected
     */
    this.allowManualFieldName_ = false;

}

/**
 * Returns a form id.
 *
 * @return {string} The form id
 */
FormBuilder.prototype.getId = function () {
    return this.id_;
};

/**
 * Returns a form name.
 *
 * @return {string} The form name
 */
FormBuilder.prototype.getName = function () {
    return this.name_;
};

/**
 * Sets whether or not to allow override the field name.
 *
 * @param {Boolean} allowOverride Allow override
 */
FormBuilder.prototype.setAllowManualFieldName = function (allowOverride) {
    this.allowManualFieldName_ = allowOverride;
};

/**
 * Gets whether or not to allow override the field name.
 *
 * @return {Boolean} Allow override
 */
FormBuilder.prototype.isAllowManualFieldName = function () {
    return this.allowManualFieldName_;
};

/**
 * Initialize form types and properties.
 *
 * @param {Object}  prop                 An array of properties (type => key => value)
 * @param {Boolean} isPreviewMode        Is preview mode
 */
FormBuilder.prototype.init = function (prop, isPreviewMode) {
    var $this = this, openingCollapse = false;

    // create (default) form type
    var elem = new FormBuilder.FormType();

    // set the builder, need when reRender()
    elem.setBuilder($this);

    // load form properties
    if (prop.form) elem.load(prop.form);

    // add new type
    $this.addType(elem);

    // add new field to list (place first)
    var formList = $('#' + $this.id_ + ' .form-list');

    formList.prepend(elem.render());

    // add fields
    if (prop.fields) {
        $.each(prop.fields, function (key, value) {
            elem = eval('new FormBuilder.' + value.type.charAt(0).toUpperCase() + value.type.slice(1) + 'Type()');
            elem.load(value);

            if ($this.getName()) key = $this.getName() + '[' + key + ']';
            elem.setFieldName(key);

            // set the builder, need when reRender()
            elem.setBuilder($this);

            // add new type
            $this.addType(elem);

            if (isPreviewMode) {
                if (elem.getType() == 'formcollapse') {
                    if (openingCollapse) {
                        formList = formList.parent();
                    }

                    var statusClass = '', style = '';
                    if (elem.getProperty('status').value === 'Closed') {
                        statusClass = '-closed';
                        if (elem.getProperty('visibility').value === 'Hidden') {
                            style = 'style="display:none"';
                        }
                    }

                    formList.append('

');
                    formList = formList.find('ul#section_' + elem.getFieldName());

                    openingCollapse = true;
                }
            }

            // add new field to list (place last)
            formList.append(elem.render());
        });
    }
};

/**
 * Simple JavaScript Inheritance.
 */

// the base Class implementation (does nothing)
FormBuilder.Class = function () {}

// create a new Class that inherits from this class
FormBuilder.Class.extend = function (prop) {
    var fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;

    var _super = this.prototype;

    // instantiate a base class (but only create the instance,
    // don't run the init constructor)
    var initializing = true;
    var prototype = new this();
    initializing = false;

    // copy the properties over onto the new prototype
    for (var name in prop) {
        // check if we're overwriting an existing function
        prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function (name, fn) {
            return function () {
                var tmp = this._super;

                // add a new ._super() method that is the same method
                // but on the super-class
                this._super = _super[name];

                // the method only need to be bound temporarily, so we
                // remove it when we're done executing
                var ret = fn.apply(this, arguments);
                this._super = tmp;

                return ret;
            };
        })(name, prop[name]) : prop[name];
    }

    // the dummy class constructor
    function Class() {
        // all construction is actually done in the init method
        if (!initializing && this.init) this.init.apply(this, arguments);
    }

    // populate our constructed prototype object
    Class.prototype = prototype;

    // enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // and make this class extendable
    Class.extend = arguments.callee;

    return Class;
};

/**
 * Returns a list of form types with (property) key => value.
 *
 * @param {Boolean} onlyValue Whether or not to get entered (or selected) value
 *
 * @return {array} List of types with values
 */
FormBuilder.prototype.getFormData = function (onlyValue) {
    var tmp = {};
    $.each (this.types_, function (key, value) {
        // get entered or selected value
        if (onlyValue) {
            v = value.val();
            if (v !== false) {
                tmp[value.getFieldName()] = value.val();
            }
        }
        // get all properties (definitions)
        else {
            tmp[key] = value.getFormData();
        }
    });
    return tmp;
};

/**
 * Returns all form types.
 *
 * @return {array} List of form types
 */
FormBuilder.prototype.getTypes = function () {
    return this.types_;
};

/**
 * Adds a form type.
 *
 * @param {FormBuilder.Type} type A form type instance
 */
FormBuilder.prototype.addType = function (type) {
    this.types_.push(type);
};

/**
 * removes a form type.
 *
 * @param {string} name The field name
 *
 * @return {Boolean} True is successfully removed, otherwise false
 */
FormBuilder.prototype.removeType = function (name) {
    for (key in this.types_) {
        if (this.types_[key].getFieldName() == name) {
            this.types_.splice(key, 1);
            return true;
        }
    }
    return false;
};

/**
 * Returns a form type.
 *
 * @param {string} name The field name
 *
 * @return {FormBuilder.Type} The form type instance
 */
FormBuilder.prototype.getType = function (name) {
    for (key in this.types_) {
        if (this.types_[key].getFieldName() == name) {
            return this.types_[key];
        }
    }
    return null;
};

/**
 * Sorts list of types.
 *
 * @param {Object} types A list of types (ids)
 */
FormBuilder.prototype.sort = function (types) {
    var tmp = [], type;
    for (key in types) {
        type = this.getType(types[key]);
        if (type) tmp.push(type);
    }
    this.types_ = tmp;
};

/**
 * Returns an html table of fields based on a form type.
 *
 * @param {FormBuilder.Type} type A form type instance
 *
 * @return {string} The html table
 */
FormBuilder.prototype.makeProperties = function (type) {
    var tmp, rows = '', fieldDetails = '',
        properties = type.getProperties();

    for (key in properties) {
        tmp = $.extend(true, {}, properties[key]);

        // skip if hidden property
        if (tmp.hidden) continue;

        // override dropdown options
        if (typeof tmp.dropdown == 'string') {
            tmp.dropdown = $.extend(true, {}, type.getProperty(tmp.dropdown));

            // if the property has a splitter value then it means this is a list value
            // it should be splitted
            if (tmp.dropdown.textarea && tmp.dropdown.splitter) {
                tmp.dropdown = tmp.dropdown.value.split(tmp.dropdown.splitter);
                for (k in tmp.dropdown) {
                    tmp.dropdown[k] = [tmp.dropdown[k], tmp.dropdown[k]];
                }
            } else {
                tmp.dropdown = tmp.dropdown.dropdown;
            }
        }

        rows = rows + Utils.tmpl('
<@=label@> <@ if (tip) { @><@=tip@><@ } @>	<@=field@>
', {
            label : properties[key].text,
            tip   : Consts.tips[key] ? Consts.tips[key].tip : null,
            field : this.drawField(key, tmp)
        });
    };

    if (this.allowManualFieldName_ && properties.hasOwnProperty('fieldName')) {
        tmp = $.extend(true, {}, properties['fieldName']);

        fieldDetails = Utils.tmpl('
<legend>Field Details</legend>

<@=label@> <@ if (tip) { @><@=tip@><@ } @>	<@=field@>

', {
            label : properties['fieldName'].text,
            tip   : Consts.tips['fieldName'] ? Consts.tips['fieldName'].tip : null,
            field : this.drawField('fieldName', tmp)
        });
    }

    return Utils.tmpl('<input type="hidden" name="elem_id" value="<@=id@>" />

<@=rows@>

<@=fieldDetails@>', {
        id           : type.getFieldName(),
        rows         : rows,
        fieldDetails : fieldDetails
    });
};

/**
 * Returns an html field based on a property options.
 *
 * @param {string} id      Property id
 * @param {Object} options Property options
 *
 * @return {string} The html field
 */
FormBuilder.prototype.drawField = function (id, options) {
    if (options.textarea) {
        // if the property has a splitter value then it means this is a list value
        // it should be splitted and displayed in the textarea
        if (options.splitter) {
            options.value = options.value.split(options.splitter).join('\n');
        }

        return Utils.tmpl('<textarea name="<@=id@>" class="edit-textarea"><@=value@></textarea>', {
            id    : id,
            value : options.value
        });
    }
    else if (options.dropdown) {
        options.dropdown = options.dropdown || [];

        $.each(options.dropdown, function (k, v) {
            options.dropdown[k] = '<option value="' + v[0] + '" ' + (options.value == v[0] ? 'selected="selected"' : null) + '>' + v[1] + '</option>';
        });

        return Utils.tmpl('<select name="<@=id@>" class="edit-dropdown"><@=options@></select>', {
            id      : id,
            options : options.dropdown.join('')
        });
    }

    return Utils.tmpl('<input type="<@=type@>" name="<@=id@>" value="<@=value@>" class="edit-text" />', {
        type  : options.type || 'text',
        id    : id,
        value : options.value
    });
};
<commit_msg>Init form type field name on property popup<commit_after>/**
 * @constructor
 */
FormBuilder = function (id, name) {

    /**
     * @var {string}
     * @protected
     */
    this.id_ = id;

    /**
     * @var {string}
     * @protected
     */
    this.name_ = name;

    /**
     * Holds list of form types.
     *
     * @var {Object}
     * @protected
     */
    this.types_ = [];

    /**
     * @var {Boolean}
     * @protected
     */
    this.allowManualFieldName_ = false;

}

/**
 * Returns a form id.
 *
 * @return {string} The form id
 */
FormBuilder.prototype.getId = function () {
    return this.id_;
};

/**
 * Returns a form name.
 *
 * @return {string} The form name
 */
FormBuilder.prototype.getName = function () {
    return this.name_;
};

/**
 * Sets whether or not to allow override the field name.
 *
 * @param {Boolean} allowOverride Allow override
 */
FormBuilder.prototype.setAllowManualFieldName = function (allowOverride) {
    this.allowManualFieldName_ = allowOverride;
};

/**
 * Gets whether or not to allow override the field name.
 *
 * @return {Boolean} Allow override
 */
FormBuilder.prototype.isAllowManualFieldName = function () {
    return this.allowManualFieldName_;
};

/**
 * Initialize form types and properties.
 *
 * @param {Object}  prop                 An array of properties (type => key => value)
 * @param {Boolean} isPreviewMode        Is preview mode
 */
FormBuilder.prototype.init = function (prop, isPreviewMode) {
    var $this = this, openingCollapse = false;

    // create (default) form type
    var elem = new FormBuilder.FormType();

    // set the builder, need when reRender()
    elem.setBuilder($this);

    // load form properties
    if (prop.form) elem.load(prop.form);

    // add new type
    $this.addType(elem);

    // add new field to list (place first)
    var formList = $('#' + $this.id_ + ' .form-list');

    formList.prepend(elem.render());

    // add fields
    if (prop.fields) {
        $.each(prop.fields, function (key, value) {
            elem = eval('new FormBuilder.' + value.type.charAt(0).toUpperCase() + value.type.slice(1) + 'Type()');
            elem.load(value);

            if ($this.getName()) key = $this.getName() + '[' + key + ']';
            elem.setFieldName(key);

            // set the builder, need when reRender()
            elem.setBuilder($this);

            // add new type
            $this.addType(elem);

            if (isPreviewMode) {
                if (elem.getType() == 'formcollapse') {
                    if (openingCollapse) {
                        formList = formList.parent();
                    }

                    var statusClass = '', style = '';
                    if (elem.getProperty('status').value === 'Closed') {
                        statusClass = '-closed';
                        if (elem.getProperty('visibility').value === 'Hidden') {
                            style = 'style="display:none"';
                        }
                    }

                    formList.append('

');
                    formList = formList.find('ul#section_' + elem.getFieldName());

                    openingCollapse = true;
                }
            }

            // add new field to list (place last)
            formList.append(elem.render());
        });
    }
};

/**
 * Simple JavaScript Inheritance.
 */

// the base Class implementation (does nothing)
FormBuilder.Class = function () {}

// create a new Class that inherits from this class
FormBuilder.Class.extend = function (prop) {
    var fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;

    var _super = this.prototype;

    // instantiate a base class (but only create the instance,
    // don't run the init constructor)
    var initializing = true;
    var prototype = new this();
    initializing = false;

    // copy the properties over onto the new prototype
    for (var name in prop) {
        // check if we're overwriting an existing function
        prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function (name, fn) {
            return function () {
                var tmp = this._super;

                // add a new ._super() method that is the same method
                // but on the super-class
                this._super = _super[name];

                // the method only need to be bound temporarily, so we
                // remove it when we're done executing
                var ret = fn.apply(this, arguments);
                this._super = tmp;

                return ret;
            };
        })(name, prop[name]) : prop[name];
    }

    // the dummy class constructor
    function Class() {
        // all construction is actually done in the init method
        if (!initializing && this.init) this.init.apply(this, arguments);
    }

    // populate our constructed prototype object
    Class.prototype = prototype;

    // enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // and make this class extendable
    Class.extend = arguments.callee;

    return Class;
};

/**
 * Returns a list of form types with (property) key => value.
 *
 * @param {Boolean} onlyValue Whether or not to get entered (or selected) value
 *
 * @return {array} List of types with values
 */
FormBuilder.prototype.getFormData = function (onlyValue) {
    var tmp = {};
    $.each (this.types_, function (key, value) {
        // get entered or selected value
        if (onlyValue) {
            v = value.val();
            if (v !== false) {
                tmp[value.getFieldName()] = value.val();
            }
        }
        // get all properties (definitions)
        else {
            tmp[key] = value.getFormData();
        }
    });
    return tmp;
};

/**
 * Returns all form types.
 *
 * @return {array} List of form types
 */
FormBuilder.prototype.getTypes = function () {
    return this.types_;
};

/**
 * Adds a form type.
 *
 * @param {FormBuilder.Type} type A form type instance
 */
FormBuilder.prototype.addType = function (type) {
    this.types_.push(type);
};

/**
 * removes a form type.
 *
 * @param {string} name The field name
 *
 * @return {Boolean} True is successfully removed, otherwise false
 */
FormBuilder.prototype.removeType = function (name) {
    for (key in this.types_) {
        if (this.types_[key].getFieldName() == name) {
            this.types_.splice(key, 1);
            return true;
        }
    }
    return false;
};

/**
 * Returns a form type.
 *
 * @param {string} name The field name
 *
 * @return {FormBuilder.Type} The form type instance
 */
FormBuilder.prototype.getType = function (name) {
    for (key in this.types_) {
        if (this.types_[key].getFieldName() == name) {
            return this.types_[key];
        }
    }
    return null;
};

/**
 * Sorts list of types.
 *
 * @param {Object} types A list of types (ids)
 */
FormBuilder.prototype.sort = function (types) {
    var tmp = [], type;
    for (key in types) {
        type = this.getType(types[key]);
        if (type) tmp.push(type);
    }
    this.types_ = tmp;
};

/**
 * Returns an html table of fields based on a form type.
 *
 * @param {FormBuilder.Type} type A form type instance
 *
 * @return {string} The html table
 */
FormBuilder.prototype.makeProperties = function (type) {
    var tmp, rows = '', fieldDetails = '',
        properties = type.getProperties();

    for (key in properties) {
        tmp = $.extend(true, {}, properties[key]);

        // skip if hidden property
        if (tmp.hidden) continue;

        // override dropdown options
        if (typeof tmp.dropdown == 'string') {
            tmp.dropdown = $.extend(true, {}, type.getProperty(tmp.dropdown));

            // if the property has a splitter value then it means this is a list value
            // it should be splitted
            if (tmp.dropdown.textarea && tmp.dropdown.splitter) {
                tmp.dropdown = tmp.dropdown.value.split(tmp.dropdown.splitter);
                for (k in tmp.dropdown) {
                    tmp.dropdown[k] = [tmp.dropdown[k], tmp.dropdown[k]];
                }
            } else {
                tmp.dropdown = tmp.dropdown.dropdown;
            }
        }

        rows = rows + Utils.tmpl('
<@=label@> <@ if (tip) { @><@=tip@><@ } @>	<@=field@>
', {
            label : properties[key].text,
            tip   : Consts.tips[key] ? Consts.tips[key].tip : null,
            field : this.drawField(key, tmp)
        });
    };

    if (this.allowManualFieldName_ && properties.hasOwnProperty('fieldName')) {
        tmp = $.extend(true, {}, properties['fieldName']);
        tmp.value = type.getFieldName();

        fieldDetails = Utils.tmpl('
<legend>Field Details</legend>

<@=label@> <@ if (tip) { @><@=tip@><@ } @>	<@=field@>

', {
            label : properties['fieldName'].text,
            tip   : Consts.tips['fieldName'] ? Consts.tips['fieldName'].tip : null,
            field : this.drawField('fieldName', tmp)
        });
    }

    return Utils.tmpl('<input type="hidden" name="elem_id" value="<@=id@>" />

<@=rows@>

<@=fieldDetails@>', {
        id           : type.getFieldName(),
        rows         : rows,
        fieldDetails : fieldDetails
    });
};

/**
 * Returns an html field based on a property options.
 *
 * @param {string} id      Property id
 * @param {Object} options Property options
 *
 * @return {string} The html field
 */
FormBuilder.prototype.drawField = function (id, options) {
    if (options.textarea) {
        // if the property has a splitter value then it means this is a list value
        // it should be splitted and displayed in the textarea
        if (options.splitter) {
            options.value = options.value.split(options.splitter).join('\n');
        }

        return Utils.tmpl('<textarea name="<@=id@>" class="edit-textarea"><@=value@></textarea>', {
            id    : id,
            value : options.value
        });
    }
    else if (options.dropdown) {
        options.dropdown = options.dropdown || [];

        $.each(options.dropdown, function (k, v) {
            options.dropdown[k] = '<option value="' + v[0] + '" ' + (options.value == v[0] ? 'selected="selected"' : null) + '>' + v[1] + '</option>';
        });

        return Utils.tmpl('<select name="<@=id@>" class="edit-dropdown"><@=options@></select>', {
            id      : id,
            options : options.dropdown.join('')
        });
    }

    return Utils.tmpl('<input type="<@=type@>" name="<@=id@>" value="<@=value@>" class="edit-text" />', {
        type  : options.type || 'text',
        id    : id,
        value : options.value
    });
};
<|endoftext|>


<commit_before>var dat = require('/dat');
var login = require('/ui/common/login');

exports.createPostWindow = function() {
	var Cloud = require('ti.cloud');
	Cloud.debug = true;
	var token = dat.getMyStoredAccessToken();
	if (token) {

		Cloud.accessToken = token;
		// Ti.App.fireEvent('loggedIn',{});

	} else {

		Cloud.Users.secureLogin({
			title : 'Login to your Account'
		}, function(e) {
			if (e.success) {
				dat.setMyStoredAccessToken(Cloud.accessToken, Cloud.expiresIn);
				// Ti.App.fireEvent('loggedIn',{});

			} else {
				// alert('Error:\\n' +
				// ((e.error && e.message) || JSON.stringify(e)));
			}
		});

	}
	var osname = Ti.Platform.osname;

	var win = Ti.UI.createWindow({
		backgroundColor : "white",
		layout : 'vertical',
		navBarHidden : true
	});
var createWhenPossible = function(e){
	if( Titanium.Network.networkType != Titanium.Network.NETWORK_NONE){
		//send update the database
		createEntry(e);
	}else{

	setTimeout(function(){
		createWhenPossible(e);
	}, 1000);
	}
}

var createEntry = function(e) {
		var Cloud = require('ti.cloud');
		Cloud.debug = true;
		Cloud.Objects.create({
			classname : 'jobs',
			fields : e
		}, function(e) {
			if (e.success) {
				var job = e.jobs[0];
				alert('Success:\n' + 'coordinates: ' + job.coordinates + '\n' + 'description: ' + job.description + '\n' + 'expiration: ' + job.expiration + '\n' + 'wage: ' + job.wage + '\n' + 'time_estimate: ' + job.time_estimate + '\n' + 'claimed: ' + job.claimed + '\n' + 'created_at: ' + job.created_at);
				// fireEvent('Success',{});
				Ti.API.info(Titanium.UI.currentWindow);
				win.close();
			} else {
				alert('Error:\n' + ((e.error && e.message) || JSON.stringify(e)));
				login.createLoginWindow();
			}
		});
	};

	var self = Ti.UI.createView({
		layout : 'vertical'
	});
	var Cloud = require('ti.cloud');
	Cloud.debug = true;

	// Get user location
	var latitude;
	var longitude;

	Ti.Geolocation.purpose = Ti.Geolocation.ACTIVITYTYPE_OTHER;

	var loc = Ti.Geolocation.getCurrentPosition(function(e) {
		if (e.error) {
			alert('Cannot get your current location, please manually set your location.');
			latitude = 0;
			longitude = 0;
			return;
		} else {
			alert('Success getting location ');
			latitude = e.coords.latitude;
			longitude = e.coords.longitude;
			return;
		}
	});
		Ti.API.info('latitude '+latitude);
	Ti.API.info('longitude '+longitude);
	// Manual location button
	var loc_button = Ti.UI.createButton({
		color : '#000000',
		title : 'Manually set location..',
		width : Ti.UI.FILL,
		padding : 10
	});

	loc_button.addEventListener('click', function(e) {
		var mapWindow = Ti.UI.createWindow({
			backgroundColor : "white",
			layout : 'vertical',
			navBarHidden : true
		});

		if (osname == 'android') {
			var MapModule = require('ti.map');

			// android specific code
			var locPin = MapModule.createAnnotation({
				latitude : latitude,
				longitude : longitude,
				title : "Job Location",
				pincolor : MapModule.ANNOTATION_RED,
				animate : true,
				draggable : true,
				leftView: Ti.UI.createButton({title: 'Detail'}),
				myid : 1 // Custom property to uniquely identify this annotation.
			});

			var map = MapModule.createView({
				height : '95%',
				userLocation : true,
				mapType : MapModule.NORMAL_TYPE,
				animate : true,
				region : {
					latitude : latitude,
					longitude : longitude,
					latitudeDelta : 0.1,
					longitudeDelta : 0.1
				},
				top : 0,
				left : 0,
				annotations : [locPin]
			});
		} else {
			// code
			var locPin = Titanium.Map.createAnnotation({
				latitude : latitude,
				longitude : longitude,
				title : "Job Location",
				pincolor : Titanium.Map.ANNOTATION_RED,
				animate : true,
				draggable : true,
				leftButton : Titanium.UI.iPhone.SystemButton.INFO_LIGHT,
				myid : 1 // Custom property to uniquely identify this annotation.
			});

			var map = Titanium.Map.createView({
				height : '95%',
				userLocation : true,
				mapType : Titanium.Map.STANDARD_TYPE,
				animate : true,
				region : {
					latitude : latitude,
					longitude : longitude,
					latitudeDelta : 0.1,
					longitudeDelta : 0.1
				},
				top : 0,
				left : 0,
				annotations : [locPin]
			});
		}

		var doneButton = Ti.UI.createButton({
			color : '#000000',
			title : 'Okay',
			width : Ti.UI.FILL,
			padding : 10
		});

		doneButton.addEventListener('click',function(e) {
			// Set new latitude and longitude
			latitude = locPin.getLatitude();
			longitude = locPin.getLongitude();
			// Close map window
			mapWindow.close();
		});

		mapWindow.add(map);
		mapWindow.add(doneButton);
		mapWindow.open();
	});

	self.add(loc_button);
	/*
	var long_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Longitude',
		// height : '50%',
		width : Ti.UI.FILL
	});

	var long_field = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',
		value : longitude,
		width : Ti.UI.FILL

	});

	var lat_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Latitude',
		// height : '50%',
		width : Ti.UI.FILL
	});

	var lat_field = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',
		value : latitude,
		width : Ti.UI.FILL

	});

	self.add(long_label);
	self.add(long_field);
	self.add(lat_label);
	self.add(lat_field);
*/
	// Form fields:
	// Description
	var description_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Job Description',
		// height : '50%',
		width : Ti.UI.FILL
	});

	var description = Ti.UI.createTextArea({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',
		top : 10,

		width : Ti.UI.FILL

	});

	// Wage
	var wage_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Offering Wage',
		// height : 'auto',
		width : Ti.UI.FILL
	});

	var wage = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',

		width : Ti.UI.FILL

	});

	// Time estimate
	var estimate_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Time Estimate (hours)',
		// height : 'auto',
		width : Ti.UI.FILL
	});

	var estimate = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',

		width : Ti.UI.FILL
	});

	// Expiration date
	var date_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Expiration date',
		// height : 'auto',
		width : Ti.UI.FILL
	});

	var picker = Ti.UI.createPicker({
		type : Ti.UI.PICKER_TYPE_DATE,
		minDate : new Date(),
		value : new Date()
	});

	picker.addEventListener('change', function(e) {
		Ti.API.info("User selected date: " + e.value.toLocaleString());
	});

	self.add(description_label);
	self.add(description);
	self.add(wage_label);
	self.add(wage);
	self.add(estimate_label);
	self.add(estimate);
	self.add(date_label);
	self.add(picker);

	var label = Ti.UI.createButton({
		color : '#000000',
		title : 'Post',

		width : Ti.UI.FILL,
		padding : 10
	});

	self.add(label);
	var cancel = Ti.UI.createButton({
		color : '#000000',
		title : 'Cancel',

		width : Ti.UI.FILL,
		padding : 10
	});

	self.add(cancel);

	//Add behavior for UI
	label.addEventListener('click', function(){
		var fields = {
			"coordinates" : [longitude, latitude], // note: these are [long, lat]
			"description" : description.value,
			"expiration" : picker.value,
			"wage" : wage.value,
			"time_estimate" : estimate.value,
			"claimed" : false
		};

		createWhenPossible(fields);
	});
	cancel.addEventListener('click', function(e) {
		win.close();
	});

	win.add(self);
	win.open();
}

// module.exports = Post;
<commit_msg>Post buttons on bottom<commit_after>var dat = require('/dat');
var login = require('/ui/common/login');

exports.createPostWindow = function() {
	var Cloud = require('ti.cloud');
	Cloud.debug = true;
	var token = dat.getMyStoredAccessToken();
	if (token) {

		Cloud.accessToken = token;
		// Ti.App.fireEvent('loggedIn',{});

	} else {

		Cloud.Users.secureLogin({
			title : 'Login to your Account'
		}, function(e) {
			if (e.success) {
				dat.setMyStoredAccessToken(Cloud.accessToken, Cloud.expiresIn);
				// Ti.App.fireEvent('loggedIn',{});

			} else {
				// alert('Error:\\n' +
				// ((e.error && e.message) || JSON.stringify(e)));
			}
		});

	}
	var osname = Ti.Platform.osname;

	var win = Ti.UI.createWindow({
		backgroundColor : "white",
		layout : 'vertical',
		navBarHidden : true
	});
var createWhenPossible = function(e){
	if( Titanium.Network.networkType != Titanium.Network.NETWORK_NONE){
		//send update the database
		createEntry(e);
	}else{

	setTimeout(function(){
		createWhenPossible(e);
	}, 1000);
	}
}

var createEntry = function(e) {
		var Cloud = require('ti.cloud');
		Cloud.debug = true;
		Cloud.Objects.create({
			classname : 'jobs',
			fields : e
		}, function(e) {
			if (e.success) {
				var job = e.jobs[0];
				alert('Success:\n' + 'coordinates: ' + job.coordinates + '\n' + 'description: ' + job.description + '\n' + 'expiration: ' + job.expiration + '\n' + 'wage: ' + job.wage + '\n' + 'time_estimate: ' + job.time_estimate + '\n' + 'claimed: ' + job.claimed + '\n' + 'created_at: ' + job.created_at);
				// fireEvent('Success',{});
				Ti.API.info(Titanium.UI.currentWindow);
				win.close();
			} else {
				alert('Error:\n' + ((e.error && e.message) || JSON.stringify(e)));
				login.createLoginWindow();
			}
		});
	};

	var self = Ti.UI.createScrollView({
		layout : 'vertical',
		height : '90%'
	});
	var Cloud = require('ti.cloud');
	Cloud.debug = true;

	// Get user location
	var latitude;
	var longitude;

	Ti.Geolocation.purpose = Ti.Geolocation.ACTIVITYTYPE_OTHER;

	var loc = Ti.Geolocation.getCurrentPosition(function(e) {
		if (e.error) {
			alert('Cannot get your current location, please manually set your location.');
			latitude = 0;
			longitude = 0;
			return;
		} else {
			alert('Success getting location ');
			latitude = e.coords.latitude;
			longitude = e.coords.longitude;
			return;
		}
	});
		Ti.API.info('latitude '+latitude);
	Ti.API.info('longitude '+longitude);
	// Manual location button
	var loc_button = Ti.UI.createButton({
		color : '#000000',
		title : 'Manually set location..',
		width : Ti.UI.FILL,
		padding : 10
	});

	loc_button.addEventListener('click', function(e) {
		var mapWindow = Ti.UI.createWindow({
			backgroundColor : "white",
			layout : 'vertical',
			navBarHidden : true
		});

		if (osname == 'android') {
			var MapModule = require('ti.map');

			// android specific code
			var locPin = MapModule.createAnnotation({
				latitude : latitude,
				longitude : longitude,
				title : "Job Location",
				pincolor : MapModule.ANNOTATION_RED,
				animate : true,
				draggable : true,
				leftView: Ti.UI.createButton({title: 'Detail'}),
				myid : 1 // Custom property to uniquely identify this annotation.
			});

			var map = MapModule.createView({
				height : '95%',
				userLocation : true,
				mapType : MapModule.NORMAL_TYPE,
				animate : true,
				region : {
					latitude : latitude,
					longitude : longitude,
					latitudeDelta : 0.1,
					longitudeDelta : 0.1
				},
				top : 0,
				left : 0,
				annotations : [locPin]
			});
		} else {
			// code
			var locPin = Titanium.Map.createAnnotation({
				latitude : latitude,
				longitude : longitude,
				title : "Job Location",
				pincolor : Titanium.Map.ANNOTATION_RED,
				animate : true,
				draggable : true,
				leftButton : Titanium.UI.iPhone.SystemButton.INFO_LIGHT,
				myid : 1 // Custom property to uniquely identify this annotation.
			});

			var map = Titanium.Map.createView({
				height : '95%',
				userLocation : true,
				mapType : Titanium.Map.STANDARD_TYPE,
				animate : true,
				region : {
					latitude : latitude,
					longitude : longitude,
					latitudeDelta : 0.1,
					longitudeDelta : 0.1
				},
				top : 0,
				left : 0,
				annotations : [locPin]
			});
		}

		var doneButton = Ti.UI.createButton({
			color : '#000000',
			title : 'Okay',
			width : Ti.UI.FILL,
			padding : 10
		});

		doneButton.addEventListener('click',function(e) {
			// Set new latitude and longitude
			latitude = locPin.getLatitude();
			longitude = locPin.getLongitude();
			// Close map window
			mapWindow.close();
		});

		mapWindow.add(map);
		mapWindow.add(doneButton);
		mapWindow.open();
	});

	self.add(loc_button);
	/*
	var long_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Longitude',
		// height : '50%',
		width : Ti.UI.FILL
	});

	var long_field = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',
		value : longitude,
		width : Ti.UI.FILL

	});

	var lat_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Latitude',
		// height : '50%',
		width : Ti.UI.FILL
	});

	var lat_field = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',
		value : latitude,
		width : Ti.UI.FILL

	});

	self.add(long_label);
	self.add(long_field);
	self.add(lat_label);
	self.add(lat_field);
*/
	// Form fields:
	// Description
	var description_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Job Description',
		// height : '50%',
		width : Ti.UI.FILL
	});

	var description = Ti.UI.createTextArea({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',
		top : 10,

		width : Ti.UI.FILL

	});

	// Wage
	var wage_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Offering Wage',
		// height : 'auto',
		width : Ti.UI.FILL
	});

	var wage = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',

		width : Ti.UI.FILL

	});

	// Time estimate
	var estimate_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Time Estimate (hours)',
		// height : 'auto',
		width : Ti.UI.FILL
	});

	var estimate = Ti.UI.createTextField({
		borderStyle : Ti.UI.INPUT_BORDERSTYLE_ROUNDED,
		color : '#336699',

		width : Ti.UI.FILL
	});

	// Expiration date
	var date_label = Ti.UI.createLabel({
		color : '#000000',
		text : 'Expiration date',
		// height : 'auto',
		width : Ti.UI.FILL
	});

	var picker = Ti.UI.createPicker({
		type : Ti.UI.PICKER_TYPE_DATE,
		minDate : new Date(),
		value : new Date()
	});

	picker.addEventListener('change', function(e) {
		Ti.API.info("User selected date: " + e.value.toLocaleString());
	});

	self.add(description_label);
	self.add(description);
	self.add(wage_label);
	self.add(wage);
	self.add(estimate_label);
	self.add(estimate);
	self.add(date_label);
	self.add(picker);
	win.add(self);
	var label = Ti.UI.createButton({
		color : '#000000',
		title : 'Post',
		height : '5%',
		width : Ti.UI.FILL,
		padding : 10
	});

	win.add(label);
	var cancel = Ti.UI.createButton({
		color : '#000000',
		title : 'Cancel',
		height : '5%',
		width : Ti.UI.FILL,
		padding : 10
	});

	win.add(cancel);

	//Add behavior for UI
	label.addEventListener('click', function(){
		var fields = {
			"coordinates" : [longitude, latitude], // note: these are [long, lat]
			"description" : description.value,
			"expiration" : picker.value,
			"wage" : wage.value,
			"time_estimate" : estimate.value,
			"claimed" : false
		};

		createWhenPossible(fields);
	});
	cancel.addEventListener('click', function(e) {
		win.close();
	});

	win.open();
}

// module.exports = Post;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import {
  View,
  Text,
  Animated,
  Platform,
  TextInput,
  StyleSheet,
  Dimensions,
  ToastAndroid,
  TouchableNativeFeedback,
  TouchableWithoutFeedback
} from 'react-native'

import MapView from 'react-native-maps'
import DismissKeyboard from 'dismissKeyboard'
import Icon from 'react-native-vector-icons/Ionicons'
import IconMDI from 'react-native-vector-icons/MaterialIcons'

import {
  CoordinatorLayout,
  BottomSheetBehavior,
  FloatingActionButton,
} from 'react-native-bottom-sheet-behavior'

const AnimatedIcon = Animated.createAnimatedComponent(Icon)

const { width, height } = Dimensions.get('window')

const RippleColor = Platform.Version >= 21
  ? TouchableNativeFeedback.Ripple('#d1d1d1')
  : null

const WHITE = '#FFFFFF'
const PRIMARY_COLOR = '#4589f2'

class GoogleMapsView extends Component {
  static contextTypes = {
    openDrawer: PropTypes.func,
  };

  state = {
    fabColor: PRIMARY_COLOR,
    bottomSheetState: BottomSheetBehavior.STATE_COLLAPSED,
    bottomSheetColor: new Animated.Value(0)
  };

  componentDidMount() {
    // Set FAB anchor to follow bottomSheet
    this.refs.floating.setAnchorId(this.refs.bottomSheet)
  }

  handleOpenDrawer() {
    DismissKeyboard()
    this.context.openDrawer()
  }

  handleFabPress() {
    ToastAndroid.show('Pressed', ToastAndroid.SHORT)
  }

  handleBottomSheetOnPress() {
    const { bottomSheetState, bottomSheetColor } = this.state

    if (bottomSheetState === BottomSheetBehavior.STATE_COLLAPSED) {
      Animated.timing(bottomSheetColor, { duration: 0, toValue: 1 }).start()
      this.setState({ bottomSheetState: BottomSheetBehavior.STATE_EXPANDED, fabColor: WHITE })
    } else if (bottomSheetState === BottomSheetBehavior.STATE_EXPANDED) {
      Animated.timing(bottomSheetColor, { duration: 30, toValue: 0 }).start()
      this.setState({ bottomSheetState: BottomSheetBehavior.STATE_COLLAPSED, fabColor: PRIMARY_COLOR })
    }
  }

  handleBottomSheetChange(e) {
    const newState = e.nativeEvent.state

    // Turn active color when starting dragging and remains expanded
    if (newState === BottomSheetBehavior.STATE_DRAGGING ||
        newState === BottomSheetBehavior.STATE_EXPANDED) {
      this.setState({ fabColor: WHITE })
      Animated.timing(this.state.bottomSheetColor, {
        duration: 50,
        toValue: 1,
      }).start()
    }

    if (newState === BottomSheetBehavior.STATE_COLLAPSED) {
      this.setState({ fabColor: PRIMARY_COLOR })
      Animated.timing(this.state.bottomSheetColor, {
        duration: 50,
        toValue: 0,
      }).start()
    }

    this.bottomSheetState = newState
    this.setState({ bottomSheetState: newState })
  }

  handleSlide(e) {
    this.offset = e.nativeEvent.offset

    const { bottomSheetColor } = this.state

    if (this.bottomSheetState === BottomSheetBehavior.STATE_SETTLING && this.offset < 0.2) {
      this.setState({ fabColor: PRIMARY_COLOR })
      Animated.timing(bottomSheetColor, {
        duration: 50,
        toValue: 0,
      }).start()
    } else {
      this.setState({ fabColor: WHITE })
      Animated.timing(bottomSheetColor, {
        duration: 50,
        toValue: 1,
      }).start()
    }
  }

  renderDetailItem(icon, text) {
    return (
      <TouchableNativeFeedback delayPressIn={0} delayPressOut={0} background={RippleColor}>
        <View>
          <View pointerEvents="none" style={styles.detailItem}>
            <Icon name={icon} size={18} color={PRIMARY_COLOR} />
            <Text pointerEvents="none" style={styles.detailText}>{text}</Text>
          </View>
        </View>
      </TouchableNativeFeedback>
    )
  }

  renderFloatingActionButton() {
    const { fabColor } = this.state
    return (
      <FloatingActionButton
        ref="floating"
        rippleEffect={true}
        icon={"directions"}
        iconProvider={IconMDI}
        iconColor={fabColor === WHITE ? PRIMARY_COLOR : WHITE}
        onPress={this.handleFabPress}
        backgroundColor={fabColor}
      />
    )
  }

  renderBottomSheet() {
    const { bottomSheetState, bottomSheetColor } = this.state
    const headerAnimated = {
      backgroundColor: bottomSheetColor.interpolate({
        inputRange:  [0, 1],
        outputRange: [WHITE, PRIMARY_COLOR]
      })
    }
    const textAnimated = {
      color: bottomSheetColor.interpolate({
        inputRange:  [0, 1],
        outputRange: ['#333', WHITE]
      })
    }
    const starsAnimated = bottomSheetColor.interpolate({
      inputRange:  [0, 1],
      outputRange: ['#FF5722', WHITE]
    })
    const routeTextAnimated = bottomSheetColor.interpolate({
      inputRange:  [0, 1],
      outputRange: [PRIMARY_COLOR, WHITE]
    })

    return (
      <BottomSheetBehavior
        ref="bottomSheet"
        peekHeight={90}
        state={bottomSheetState}
        onSlide={::this.handleSlide}
        onStateChange={::this.handleBottomSheetChange}>
        <View style={styles.bottomSheet}>
          <TouchableWithoutFeedback onPress={::this.handleBottomSheetOnPress}>
            <Animated.View style={[styles.bottomSheetHeader, headerAnimated]}>
              <View style={styles.bottomSheetLeft}>
                <Animated.Text style={[styles.bottomSheetTitle, textAnimated]}>
                  React Native Bar!
                </Animated.Text>
                <View style={styles.starsContainer}>
                  <Animated.Text style={{ color: starsAnimated, marginRight: 8 }}>5.0</Animated.Text>
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={{ color: starsAnimated }} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={{ color: starsAnimated }} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={{ color: starsAnimated }} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={{ color: starsAnimated }} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={{ color: starsAnimated }} />
                </View>
              </View>
              <View style={styles.bottomSheetRight}>
                <Animated.Text style={[styles.routeLabel, { color: routeTextAnimated }]}>Route</Animated.Text>
              </View>
            </Animated.View>
          </TouchableWithoutFeedback>
          <View style={styles.bottomSheetContent}>
            <View style={styles.sectionIcons}>
              <View style={styles.iconBox}>
                <Icon name="md-call" size={22} color={PRIMARY_COLOR} />
                <Text style={styles.iconLabel}>CALL</Text>
              </View>
              <View style={styles.iconBox}>
                <Icon name="md-star" size={22} color={PRIMARY_COLOR} />
                <Text style={styles.iconLabel}>SAVE</Text>
              </View>
              <View style={styles.iconBox}>
                <Icon name="md-globe" size={22} color={PRIMARY_COLOR} />
                <Text style={styles.iconLabel}>WEBSITE</Text>
              </View>
            </View>
            <View style={styles.detailListSection}>
              {this.renderDetailItem('md-locate', 'Av. Lorem Ipsum dolor sit amet - consectetur adipising elit.')}
              {this.renderDetailItem('md-timer', 'Open now: 06:22:00')}
              {this.renderDetailItem('md-call', '(11) 9999-9999')}
              {this.renderDetailItem('md-globe', 'https://github.com/cesardeazevedo/react-native-bottom-sheet-behavior')}
              {this.renderDetailItem('md-create', 'Suggest an edit')}
            </View>
          </View>
        </View>
      </BottomSheetBehavior>
    )
  }

  renderMaps() {
    return (
      <View style={styles.containerMap}>
        <MapView
          style={styles.map}
          initialRegion={{
            latitude: -22.920,
            longitude: -43.190,
            latitudeDelta: 0.1022,
            longitudeDelta: 0.0421,
          }}
        />
      </View>
    )
  }

  renderToolbar() {
    return (
      <View style={styles.toolbar}>
        <TouchableNativeFeedback
          delayPressIn={0}
          delayPressOut={0}
          background={RippleColor}
          onPress={::this.handleOpenDrawer}>
          <View style={styles.buttonIcon}>
            <Icon name="md-menu" size={20} />
          </View>
        </TouchableNativeFeedback>
        <TextInput
          style={styles.textInput}
          placeholder="Try restaurants, coffee"
          placeholderTextColor="#c2c2c2"
          underlineColorAndroid="transparent" />
        <TouchableNativeFeedback
          delayPressIn={0}
          delayPressOut={0}
          background={RippleColor}>
          <View style={styles.buttonIcon}>
            <Icon name="md-mic" size={20} />
          </View>
        </TouchableNativeFeedback>
      </View>
    )
  }

  render() {
    return (
      <CoordinatorLayout style={styles.container}>
        <View style={styles.content}>
          {this.renderMaps()}
          {this.renderToolbar()}
        </View>
        {this.renderBottomSheet()}
        {this.renderFloatingActionButton()}
      </CoordinatorLayout>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
  content: {
    backgroundColor: 'transparent',
  },
  containerMap: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    height,
    width,
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
  map: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
  toolbar: {
    marginTop: 32,
    marginHorizontal: 12,
    flexDirection: 'row',
    justifyContent: 'space-between',
    borderWidth: 0.5,
    borderColor: 'grey',
    borderRadius: 4,
    backgroundColor: WHITE,
  },
  buttonIcon: {
    padding: 16,
    borderRadius: 50,
  },
  textInput: {
    flex: 1,
    height: 50,
    marginHorizontal: 12,
  },
  bottomSheet: {
    zIndex: 5,
    backgroundColor: 'transparent'
  },
  bottomSheetHeader: {
    padding: 24,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  bottomSheetLeft: {
    flexDirection: 'column'
  },
  bottomSheetRight: {
    flexDirection: 'column'
  },
  bottomSheetTitle: {
    fontFamily: 'sans-serif-medium',
    fontSize: 18,
  },
  bottomSheetContent: {
    alignItems: 'center',
    backgroundColor: WHITE,
  },
  starsContainer: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  star: {
    marginHorizontal: 2,
  },
  routeLabel: {
    marginTop: 32,
    fontSize: 12,
    color: PRIMARY_COLOR,
  },
  sectionIcons: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 18,
    borderBottomWidth: 1,
    borderColor: '#eee'
  },
  iconBox: {
    flex: 1,
    borderRadius: 50,
    alignItems: 'center',
    flexDirection: 'column'
  },
  iconLabel: {
    fontSize: 14,
    marginTop: 4,
    color: PRIMARY_COLOR
  },
  detailListSection: {
    paddingTop: 4,
  },
  detailItem: {
    height: 38,
    alignItems: 'center',
    flexDirection: 'row',
    paddingHorizontal: 22,
  },
  detailText: {
    color: '#333',
    fontSize: 12,
    marginLeft: 24,
  },
})

export default GoogleMapsView
<commit_msg>Refactor on GoogleMaps example<commit_after>import React, { Component, PropTypes } from 'react'
import {
  View,
  Text,
  Animated,
  Platform,
  TextInput,
  StyleSheet,
  Dimensions,
  ToastAndroid,
  TouchableNativeFeedback,
  TouchableWithoutFeedback
} from 'react-native'

import MapView from 'react-native-maps'
import DismissKeyboard from 'dismissKeyboard'
import Icon from 'react-native-vector-icons/Ionicons'
import IconMDI from 'react-native-vector-icons/MaterialIcons'

import {
  CoordinatorLayout,
  BottomSheetBehavior,
  FloatingActionButton,
} from 'react-native-bottom-sheet-behavior'

const AnimatedIcon = Animated.createAnimatedComponent(Icon)

const { width, height } = Dimensions.get('window')

const duration = 120
const RippleColor = (...args) =>
  Platform.Version >= 21
    ? TouchableNativeFeedback.Ripple(...args)
    : null

const WHITE = '#FFFFFF'
const PRIMARY_COLOR = '#4589f2'
const TEXT_BASE_COLOR = '#333'
const STAR_COLOR = '#FF5722'

class GoogleMapsView extends Component {
  static contextTypes = {
    openDrawer: PropTypes.func,
  };

  state = {
    bottomSheetColor: 0,
    bottomSheetColorAnimated: new Animated.Value(0),
  };

  componentDidMount() {
    this.lastState = BottomSheetBehavior.STATE_COLLAPSED
    this.refs.floating.setAnchorId(this.refs.bottomSheet)
  }

  handleOpenDrawer() {
    DismissKeyboard()
    this.context.openDrawer()
  }

  handleFabPress() {
    ToastAndroid.show('Pressed', ToastAndroid.SHORT)
  }

  handleBottomSheetOnPress(e) {
    const { bottomSheet } = this.refs

    if (this.lastState === BottomSheetBehavior.STATE_COLLAPSED) {
      this.setState({ bottomSheetColor: 1 })
      bottomSheet.setBottomSheetState(BottomSheetBehavior.STATE_EXPANDED)
    } else if (this.lastState === BottomSheetBehavior.STATE_EXPANDED) {
      this.setState({ bottomSheetColor: 0 })
      bottomSheet.setBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED)
    }
  }

  handleBottomSheetChange(e) {
    const newState = e.nativeEvent.state

    if (this.offset > 0.1 &&
        newState === BottomSheetBehavior.STATE_DRAGGING ||
        newState === BottomSheetBehavior.STATE_EXPANDED) {
        this.setState({ bottomSheetColor: 1 })
    }
    if (newState == BottomSheetBehavior.STATE_SETTLING && !this.settlingExpanded) {
      this.setState({ bottomSheetColor: 0 })
    }

    this.lastState = newState
  }

  handleSlide(e) {
    const { bottomSheetColor } = this.state
    const offset = parseFloat(e.nativeEvent.offset.toFixed(2))

    this.settlingExpanded = offset >= this.offset
    this.offset = offset

    if (offset === 0) {
      this.setState({ bottomSheetColor: 0 })
    } else if (bottomSheetColor !== 1 && this.lastState === BottomSheetBehavior.STATE_DRAGGING) {
      this.setState({ bottomSheetColor: 1 })
    }
  }

  renderDetailItem(icon, text) {
    return (
      <TouchableNativeFeedback delayPressIn={0} delayPressOut={0} background={RippleColor('#d1d1d1')}>
        <View>
          <View pointerEvents="none" style={styles.detailItem}>
            <Icon name={icon} size={18} color={PRIMARY_COLOR} />
            <Text pointerEvents="none" style={styles.detailText}>{text}</Text>
          </View>
        </View>
      </TouchableNativeFeedback>
    )
  }

  renderFloatingActionButton() {
    const { bottomSheetColor } = this.state
    const isExpanded = bottomSheetColor === 1
    return (
      <FloatingActionButton
        ref="floating"
        rippleEffect={true}
        icon={"directions"}
        iconProvider={IconMDI}
        iconColor={!isExpanded ? WHITE : PRIMARY_COLOR}
        onPress={::this.handleFabPress}
        backgroundColor={isExpanded ? WHITE : PRIMARY_COLOR}
      />
    )
  }

  renderBottomSheet() {
    const {
      bottomSheetColor,
      bottomSheetColorAnimated
    } = this.state

    Animated.timing(bottomSheetColorAnimated, {
      duration,
      toValue: bottomSheetColor,
    }).start()

    const headerAnimated = {
      backgroundColor: bottomSheetColorAnimated.interpolate({
        inputRange:  [0, 1],
        outputRange: [WHITE, PRIMARY_COLOR],
      })
    }
    const textAnimated = {
      color: bottomSheetColorAnimated.interpolate({
        inputRange:  [0, 1],
        outputRange: [TEXT_BASE_COLOR, WHITE],
      })
    }
    const starsAnimated = {
      color: bottomSheetColorAnimated.interpolate({
        inputRange:  [0, 1],
        outputRange: [STAR_COLOR, WHITE],
      })
    }
    const routeTextAnimated = {
      color: bottomSheetColorAnimated.interpolate({
        inputRange:  [0, 1],
        outputRange: [PRIMARY_COLOR, WHITE],
      })
    }

    return (
      <BottomSheetBehavior
        ref="bottomSheet"
        peekHeight={90}
        onSlide={::this.handleSlide}
        onStateChange={::this.handleBottomSheetChange}>
        <View style={styles.bottomSheet}>
          <TouchableWithoutFeedback onPress={::this.handleBottomSheetOnPress}>
            <Animated.View style={[styles.bottomSheetHeader, headerAnimated]}>
              <View style={styles.bottomSheetLeft}>
                <Animated.Text style={[styles.bottomSheetTitle, textAnimated]}>
                  React Native Bar!
                </Animated.Text>
                <View style={styles.starsContainer}>
                  <Animated.Text style={[starsAnimated, { marginRight: 8 }]}>5.0</Animated.Text>
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={starsAnimated} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={starsAnimated} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={starsAnimated} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={starsAnimated} />
                  <AnimatedIcon name="md-star" size={16} style={styles.star} style={starsAnimated} />
                </View>
              </View>
              <View style={styles.bottomSheetRight}>
                <Animated.Text style={[styles.routeLabel, routeTextAnimated]}>Route</Animated.Text>
              </View>
            </Animated.View>
          </TouchableWithoutFeedback>
          <View style={styles.bottomSheetContent}>
            <View style={styles.sectionIcons}>
              <View style={styles.iconBox}>
                <Icon name="md-call" size={22} color={PRIMARY_COLOR} />
                <Text style={styles.iconLabel}>CALL</Text>
              </View>
              <View style={styles.iconBox}>
                <Icon name="md-star" size={22} color={PRIMARY_COLOR} />
                <Text style={styles.iconLabel}>SAVE</Text>
              </View>
              <View style={styles.iconBox}>
                <Icon name="md-globe" size={22} color={PRIMARY_COLOR} />
                <Text style={styles.iconLabel}>WEBSITE</Text>
              </View>
            </View>
            <View style={styles.detailListSection}>
              {this.renderDetailItem('md-locate', 'Av. Lorem Ipsum dolor sit amet - consectetur adipising elit.')}
              {this.renderDetailItem('md-timer', 'Open now: 06:22:00')}
              {this.renderDetailItem('md-call', '(11) 9999-9999')}
              {this.renderDetailItem('md-globe', 'https://github.com/cesardeazevedo/react-native-bottom-sheet-behavior')}
              {this.renderDetailItem('md-create', 'Suggest an edit')}
            </View>
          </View>
        </View>
      </BottomSheetBehavior>
    )
  }

  renderMaps() {
    return (
      <View style={styles.containerMap}>
        <MapView
          style={styles.map}
          initialRegion={{
            latitude: -22.920,
            longitude: -43.190,
            latitudeDelta: 0.1022,
            longitudeDelta: 0.0421,
          }}
        />
      </View>
    )
  }

  renderToolbar() {
    return (
      <View style={styles.toolbar}>
        <TouchableNativeFeedback
          delayPressIn={0}
          delayPressOut={0}
          background={RippleColor('#d1d1d1', true)}
          onPress={::this.handleOpenDrawer}>
          <View style={styles.buttonIcon}>
            <Icon name="md-menu" size={20} />
          </View>
        </TouchableNativeFeedback>
        <TextInput
          style={styles.textInput}
          placeholder="Try restaurants, coffee"
          placeholderTextColor="#c2c2c2"
          underlineColorAndroid="transparent" />
        <TouchableNativeFeedback
          delayPressIn={0}
          delayPressOut={0}
          background={RippleColor('#d1d1d1', true)}>
          <View style={styles.buttonIcon}>
            <Icon name="md-mic" size={20} />
          </View>
        </TouchableNativeFeedback>
      </View>
    )
  }

  render() {
    return (
      <CoordinatorLayout style={styles.container}>
        <View style={styles.content}>
          {this.renderMaps()}
          {this.renderToolbar()}
        </View>
        {this.renderBottomSheet()}
        {this.renderFloatingActionButton()}
      </CoordinatorLayout>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
  content: {
    backgroundColor: 'transparent',
  },
  containerMap: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    height,
    width,
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
  map: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
  toolbar: {
    flex: 1,
    flexDirection: 'row',
    marginTop: 32,
    marginHorizontal: 12,
    alignItems: 'center',
    justifyContent: 'space-between',
    borderWidth: 0.5,
    borderColor: 'grey',
    borderRadius: 4,
    backgroundColor: WHITE,
  },
  buttonIcon: {
    padding: 16,
    borderRadius: 50,
  },
  textInput: {
    flex: 1,
    fontSize: 16,
    marginHorizontal: 8,
  },
  bottomSheet: {
    zIndex: 5,
    backgroundColor: 'transparent'
  },
  bottomSheetHeader: {
    padding: 24,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  bottomSheetLeft: {
    flexDirection: 'column'
  },
  bottomSheetRight: {
    flexDirection: 'column'
  },
  bottomSheetTitle: {
    fontFamily: 'sans-serif-medium',
    fontSize: 18,
  },
  bottomSheetContent: {
    alignItems: 'center',
    backgroundColor: WHITE,
  },
  starsContainer: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  star: {
    marginHorizontal: 2,
  },
  routeLabel: {
    marginTop: 32,
    fontSize: 12,
    color: PRIMARY_COLOR,
  },
  sectionIcons: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 18,
    borderBottomWidth: 1,
    borderColor: '#eee'
  },
  iconBox: {
    flex: 1,
    borderRadius: 50,
    alignItems: 'center',
    flexDirection: 'column'
  },
  iconLabel: {
    fontSize: 14,
    marginTop: 4,
    color: PRIMARY_COLOR
  },
  detailListSection: {
    paddingTop: 4,
  },
  detailItem: {
    height: 38,
    alignItems: 'center',
    flexDirection: 'row',
    paddingHorizontal: 22,
  },
  detailText: {
    color: '#333',
    fontSize: 12,
    marginLeft: 24,
  },
})

export default GoogleMapsView
<|endoftext|>