<commit_before>
define([
  'jquery',
  'underscore',
  'backbone'
], function ($, _, Backbone) {

  var OlayList = Backbone.Collection.extend ({

    url: 'https://eksisozluk.com/olay',

    parse: function (resp) {
      var entries = [];
      var htmlDoc = $.parseHTML (resp);
      $(htmlDoc).find ('#content ul.topic-list li a').each (function () {

        if ($(this).attr ('class') == 'empty-index-item')
          return;

        var entry_url = $(this).attr ('href').replace (/\?.*/, '');
        var entry_snapshot = $(this).attr ('href').replace (/.*snapshot=/, '');
        // TODO: remove entry count from title
        var entry_title = $(this).text ();
        var entry_count = parseInt ($(this).find ('small').text ());

        entries.push ({ url: entry_url,
                        title: entry_title,
                        count: entry_count,
                        snapshot: entry_snapshot });
      });

      return entries;
    },

    fetch: function (options) {
      options = options || {};
      options.dataType = 'html';
      return Backbone.Collection.prototype.fetch.call (this, options);
    }

  });

  return OlayList;

});
<commit_msg>fix olay link<commit_after>
define([
  'jquery',
  'underscore',
  'backbone'
], function ($, _, Backbone) {

  var OlayList = Backbone.Collection.extend ({

    url: 'https://eksisozluk.com/basliklar/olay',

    parse: function (resp) {
      var entries = [];
      var htmlDoc = $.parseHTML (resp);
      $(htmlDoc).find ('#content ul.topic-list li a').each (function () {

        if ($(this).attr ('class') == 'empty-index-item')
          return;

        var entry_url = $(this).attr ('href').replace (/\?.*/, '');
        var entry_snapshot = $(this).attr ('href').replace (/.*snapshot=/, '');
        // TODO: remove entry count from title
        var entry_title = $(this).text ();
        var entry_count = parseInt ($(this).find ('small').text ());

        entries.push ({ url: entry_url,
                        title: entry_title,
                        count: entry_count,
                        snapshot: entry_snapshot });
      });

      return entries;
    },

    fetch: function (options) {
      options = options || {};
      options.dataType = 'html';
      return Backbone.Collection.prototype.fetch.call (this, options);
    }

  });

  return OlayList;

});
<|endoftext|>


<commit_before>"use strict";
/**
 * Configure angular services to the requirements
 * of sn.fm.player angular module e.g. Creates player
 * playlist routes.
 * @module sn.fm.player
 * @author SOON_
 */
angular.module("sn.fm.player").config([
    "$routeProvider",
    "$locationProvider",
    /**
     * @constructor
     * @param {Service} $routeProvider
     * @param {Service} $locationProvider
     */
    function ($routeProvider, $locationProvider) {

        $locationProvider.html5Mode(true).hashPrefix = "!";

        $routeProvider
            .when("/", {
                templateUrl: "partials/player.html",
                controller: "PlayerCtrl",
                resolve: {
                    playlistData: ["PlayerQueueResource", "$route", function (PlayerQueueResource, $route){
                        return PlayerQueueResource.query($route.current.params);
                    }],
                    currentTrack: ["PlayerTransportResource", function (PlayerTransportResource){
                        return PlayerTransportResource.get({});
                    }],
                    PlayerMuteResource: ["PlayerMuteResource", function (PlayerMuteResource){
                        return PlayerMuteResource.get();
                    }]
                }
            })

            .when("/500", {
                templateUrl: "partials/500.html"
            })

            .otherwise({
                redirectTo: "/"
            });

    }
]);
<commit_msg>remove PlayerTransportResource.get redundant params object<commit_after>"use strict";
/**
 * Configure angular services to the requirements
 * of sn.fm.player angular module e.g. Creates player
 * playlist routes.
 * @module sn.fm.player
 * @author SOON_
 */
angular.module("sn.fm.player").config([
    "$routeProvider",
    "$locationProvider",
    /**
     * @constructor
     * @param {Service} $routeProvider
     * @param {Service} $locationProvider
     */
    function ($routeProvider, $locationProvider) {

        $locationProvider.html5Mode(true).hashPrefix = "!";

        $routeProvider
            .when("/", {
                templateUrl: "partials/player.html",
                controller: "PlayerCtrl",
                resolve: {
                    playlistData: ["PlayerQueueResource", "$route", function (PlayerQueueResource, $route){
                        return PlayerQueueResource.query($route.current.params);
                    }],
                    currentTrack: ["PlayerTransportResource", function (PlayerTransportResource){
                        return PlayerTransportResource.get();
                    }],
                    PlayerMuteResource: ["PlayerMuteResource", function (PlayerMuteResource){
                        return PlayerMuteResource.get();
                    }]
                }
            })

            .when("/500", {
                templateUrl: "partials/500.html"
            })

            .otherwise({
                redirectTo: "/"
            });

    }
]);
<|endoftext|>


<commit_before>import React from 'react'
import { browserHistory, withRouter } from 'react-router'
import PropTypes from 'prop-types'
import PanelShell, { renderItems } from '@components/PanelShell'
import { Email, Verify, Password, Username, Hooray } from './views'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { AccountActions } from '../account/store/account'
import { IdentityActions } from '../profiles/store/identity'
import { SettingsActions } from '../account/store/settings'
import { connectToGaiaHub } from '../account/utils/blockstack-inc'
import { RegistrationActions } from '../profiles/store/registration'
import { BLOCKSTACK_INC } from '../account/utils/index'
import { setCoreStorageConfig } from '@utils/api-utils'
import { hasNameBeenPreordered } from '@utils/name-utils'
import { verifyAuthRequestAndLoadManifest } from 'blockstack'
import queryString from 'query-string'
import log4js from 'log4js'

const logger = log4js.getLogger('onboarding/index.js')

const VIEWS = {
  EMAIL: 0,
  EMAIL_VERIFY: 1,
  PASSWORD: 2,
  USERNAME: 3,
  HOORAY: 4
}

const HEROKU_URL = 'https://obscure-retreat-87934.herokuapp.com'

function mapStateToProps(state) {
  return {
    api: state.settings.api,
    updateApi: PropTypes.func.isRequired,
    promptedForEmail: state.account.promptedForEmail,
    encryptedBackupPhrase: state.account.encryptedBackupPhrase,
    localIdentities: state.profiles.identity.localIdentities,
    identityAddresses: state.account.identityAccount.addresses,
    identityKeypairs: state.account.identityAccount.keypairs,
    connectedStorageAtLeastOnce: state.account.connectedStorageAtLeastOnce,
    storageConnected: state.settings.api.storageConnected,
    email: state.account.email,
    registration: state.profiles.registration
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(Object.assign({},
    AccountActions, SettingsActions, IdentityActions, RegistrationActions),
    dispatch)
}

function sendRecovery(blockstackId, email, encryptedSeed) {
  const { protocol, hostname, port } = location
  const thisUrl = `${protocol}//${hostname}${port && `:${port}`}`
  const seedRecovery = `${thisUrl}/seed?encrypted=${encryptedSeed}`

  const options = {
    method: 'POST',
    body: JSON.stringify({
      email,
      seedRecovery,
      blockstackId
    }),
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json'
    }
  }

  return fetch(`${HEROKU_URL}/recovery`, options)
    .then(
      () => {
        console.log(`emailNotifications: sent ${email} recovery email`)
      },
      error => {
        console.log('emailNotifications: error', error)
      }
    )
    .catch(error => {
      console.log('emailNotifications: error', error)
    })
}

function sendRestore(blockstackId, email, encryptedSeed) {
  const { protocol, hostname, port } = location
  const thisUrl = `${protocol}//${hostname}${port && `:${port}`}`
  const restoreLink = `${thisUrl}/sign-in?seed=${encryptedSeed}`

  const options = {
    method: 'POST',
    body: JSON.stringify({
      email,
      restoreLink,
      blockstackId
    }),
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json'
    }
  }

  return fetch(`${HEROKU_URL}/restore`, options)
    .then(
      () => {
        console.log(`emailNotifications: sent ${email} restore email`)
      },
      error => {
        console.log('emailNotifications: error', error)
      }
    )
    .catch(error => {
      console.log('emailNotifications: error', error)
    })
}

class Onboarding extends React.Component {
  state = {
    authRequest: '',
    email: '',
    password: '',
    username: '',
    seed: '',
    appManifest: null,
    emailSubmitted: false,
    view: VIEWS.EMAIL,
    usernameRegistrationInProgress: false
  }

  componentWillMount() {
    const { location } = this.props
    if (location.query.verified) {
      this.setState({ email: location.query.verified })
      this.updateView(VIEWS.PASSWORD)
    }
  }

  componentDidMount() {
    this.decodeAndSaveAuthRequest()
    if (!this.props.api.subdomains['test-personal.id']) {
      this.props.resetApi(this.props.api)
    }
  }

  componentWillReceiveProps(nextProps) {
    const { registration } = nextProps

    if (this.state.usernameRegistrationInProgress && registration.registrationSubmitted) {
      this.updateView(VIEWS.HOORAY)
    } else if (registration.error) {
      logger.error(`username registration error: ${registration.error}`)
      this.setState({
        usernameRegistrationInProgress: false
      })
    }
  }

  updateURL = view => {
    const historyChange = slug => {
      if (this.props.location.pathname !== `/sign-up/${slug}`) {
        return this.props.router.push(`/sign-up/${slug}`, this.state)
      } else {
        return null
      }
    }

    switch (view) {
      case VIEWS.EMAIL_VERIFY:
        return historyChange('verify')
      case VIEWS.PASSWORD:
        return historyChange('password')
      case VIEWS.USERNAME:
        return historyChange('username')
      case VIEWS.HOORAY:
        return historyChange('success')
      default:
        return null
    }
  }

  componentDidUpdate() {
    this.updateURL(this.state.view)
  }

  decodeAndSaveAuthRequest() {
    const queryDict = queryString.parse(this.props.location.search)
    if (queryDict.redirect !== null && queryDict.redirect !== undefined) {
      const searchString = queryDict.redirect.replace('/auth', '')
      var redirectQueryDict = queryString.parse(searchString)
      if (redirectQueryDict.authRequest !== null && redirectQueryDict.authRequest !== undefined) {
        const authRequest = redirectQueryDict.authRequest
        verifyAuthRequestAndLoadManifest(authRequest)
        .then(appManifest => {
          this.setState({
            authRequest,
            appManifest
          })
        }, () => {
          logger.error('verifyAuthRequestAndLoadManifest: invalid authentication request')
        }).catch((e) => {
          logger.error('verifyAuthRequestAndLoadManifest: error', e)
        })
      }
    }
  }

  updateValue = (key, value) => {
    this.setState({ [key]: value })
  }

  updateView = view => this.setState({ view })

  verifyEmail(email) {
    this.setState({ emailSubmitted: true })

    const { protocol, hostname, port } = location
    const thisUrl = `${protocol}//${hostname}${port && `:${port}`}`
    const emailVerificationLink = `${thisUrl}/sign-up?verified=${email}`

    const options = {
      method: 'POST',
      body: JSON.stringify({
        email,
        emailVerificationLink
      }),
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      }
    }

    return fetch(`${HEROKU_URL}/verify`, options)
      .then(
        () => {
          console.log(`emailNotifications: sent ${email} an email verification`)
        },
        error => {
          this.setState({ emailSubmitted: false })
          console.log('emailNotifications: error', error)
        }
      )
      .catch(error => {
        console.log('emailNotifications: error', error)
        this.setState({ emailSubmitted: false })
      })
  }

  submitPassword = () => {
    const { username, email } = this.state
    if (username.length < 1) {
      this.setState({
        email
      })
    }

    logger.debug('creating account')
    this.createAccount(this.state.password)
      .then(() => this.connectStorage())
      .then(() => {
        this.updateView(VIEWS.USERNAME)
      })
  }

  createAccount(password) {
    const firstIdentityIndex = 0
    return this.props.initializeWallet(password, null)
    .then(() => {
      logger.debug('creating new identity')
      const ownerAddress = this.props.identityAddresses[firstIdentityIndex]
      return this.props.createNewIdentityWithOwnerAddress(firstIdentityIndex, ownerAddress)
    }).then(() => {
      return this.props.setDefaultIdentity(firstIdentityIndex)
    })
  }

  connectStorage() {
    const storageProvider = this.props.api.gaiaHubUrl
    const signer = this.props.identityKeypairs[0].key
    return connectToGaiaHub(storageProvider, signer).then(gaiaHubConfig => {
      const newApi = Object.assign({}, this.props.api, {
        gaiaHubConfig,
        hostedDataLocation: BLOCKSTACK_INC
      })
      this.props.updateApi(newApi)
      const identityIndex = 0
      const identity = this.props.localIdentities[identityIndex]
      const identityAddress = identity.ownerAddress
      const profileSigningKeypair = this.props.identityKeypairs[identityIndex]
      const profile = identity.profile
      setCoreStorageConfig(
        newApi,
        identityIndex,
        identityAddress,
        profile,
        profileSigningKeypair,
        identity
      ).then(indexUrl => {
        logger.debug('connectStorage: storage initialized')
        const newApi2 = Object.assign({}, newApi, { storageConnected: true })
        this.props.updateApi(newApi2)
        this.props.storageIsConnected()
        logger.debug('connectStorage: storage configured')
      })
    })
  }

  submitUsername = (username) => {
    console.log('about to submit username')
    const suffix = '.test-personal.id'
    username += suffix
    logger.trace('registerUsername')
    const nameHasBeenPreordered = hasNameBeenPreordered(username, this.props.localIdentities)
    if (nameHasBeenPreordered) {
      logger.error(`registerUsername: username '${username}' has already been preordered`)
    } else {
      this.setState({
        usernameRegistrationInProgress: true
      })
      logger.debug(`registerUsername: will try and register username: ${username}`)
      const address = this.props.identityAddresses[0]
      const identity = this.props.localIdentities[0]
      const keypair = this.props.identityKeypairs[0]
      this.props.registerName(this.props.api, username, identity,
        0, address, keypair)
    }
  }

  redirectToAuth = () => {
    this.props.router.push(`/auth/?authRequest=${this.state.authRequest}`)
  }

  goToBackup = () => {
    browserHistory.push({
      pathname: '/seed',
      state: { seed: this.state.seed }
    })
  }

  submitEmailForVerification = () => {
    // Short-circuit email verification
    this.verifyEmail(this.state.email)
    .then(() => {
      this.updateView(VIEWS.PASSWORD)
    })

    // verifyEmail(this.state.email)
    // this.updateView(VIEWS.EMAIL_VERIFY)
  }

  render() {
    const { email, password, username, emailSubmitted, view } = this.state
    const icons = this.state.appManifest ? this.state.appManifest.icons : []
    const appIconURL = icons.length > 0 ? icons[0].src : '/images/app-icon-hello-blockstack.png'
    console.log(this.state.appManifest)
    const appName = this.state.appManifest ? this.state.appManifest.name : ''

    const views = [
      {
        show: VIEWS.EMAIL,
        Component: Email,
        props: {
          email,
          next: this.submitEmailForVerification,
          submitted: emailSubmitted,
          updateValue: this.updateValue,
          appIconURL: appIconURL
        }
      },
      {
        show: VIEWS.EMAIL_VERIFY,
        Component: Verify,
        props: {
          email,
          resend: this.submitEmailForVerification,
          next: () => this.updateView(VIEWS.PASSWORD)
        }
      },
      {
        show: VIEWS.PASSWORD,
        Component: Password,
        props: {
          password,
          next: this.submitPassword,
          updateValue: this.updateValue
        }
      },
      {
        show: VIEWS.USERNAME,
        Component: Username,
        props: {
          username,
          next: this.submitUsername,
          previous: () => this.updateView(VIEWS.PASSWORD),
          updateValue: this.updateValue,
          isProcessing: this.state.usernameRegistrationInProgress
        }
      },
      {
        show: VIEWS.HOORAY,
        Component: Hooray,
        props: {
          email,
          password,
          username,
          appIconURL: appIconURL,
          appName: appName,
          goToRecovery: this.goToBackup,
          goToApp: () => this.redirectToAuth()
        }
      }
    ]

    return <PanelShell>{renderItems(views, view)}</PanelShell>
  }
}

Onboarding.propTypes = {
  api: PropTypes.object.isRequired,
  location: PropTypes.object,
  router: PropTypes.object,
  identityAddresses: PropTypes.array,
  createNewIdentityWithOwnerAddress: PropTypes.func.isRequired,
  setDefaultIdentity: PropTypes.func.isRequired,
  initializeWallet: PropTypes.func.isRequired,
  updateApi: PropTypes.func.isRequired,
  localIdentities: PropTypes.array.isRequired,
  identityKeypairs: PropTypes.array.isRequired,
  storageIsConnected: PropTypes.func.isRequired,
  registerName: PropTypes.func.isRequired,
  resetApi: PropTypes.func.isRequired
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Onboarding))
<commit_msg>Send recovery and restore emails.<commit_after>import React from 'react'
import { browserHistory, withRouter } from 'react-router'
import PropTypes from 'prop-types'
import PanelShell, { renderItems } from '@components/PanelShell'
import { Email, Verify, Password, Username, Hooray } from './views'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { AccountActions } from '../account/store/account'
import { IdentityActions } from '../profiles/store/identity'
import { SettingsActions } from '../account/store/settings'
import { connectToGaiaHub } from '../account/utils/blockstack-inc'
import { RegistrationActions } from '../profiles/store/registration'
import { BLOCKSTACK_INC } from '../account/utils/index'
import { setCoreStorageConfig } from '@utils/api-utils'
import { hasNameBeenPreordered } from '@utils/name-utils'
import { verifyAuthRequestAndLoadManifest } from 'blockstack'
import queryString from 'query-string'
import log4js from 'log4js'

const logger = log4js.getLogger('onboarding/index.js')

const VIEWS = {
  EMAIL: 0,
  EMAIL_VERIFY: 1,
  PASSWORD: 2,
  USERNAME: 3,
  HOORAY: 4
}

const HEROKU_URL = 'https://obscure-retreat-87934.herokuapp.com'

function mapStateToProps(state) {
  return {
    api: state.settings.api,
    updateApi: PropTypes.func.isRequired,
    promptedForEmail: state.account.promptedForEmail,
    encryptedBackupPhrase: state.account.encryptedBackupPhrase,
    localIdentities: state.profiles.identity.localIdentities,
    identityAddresses: state.account.identityAccount.addresses,
    identityKeypairs: state.account.identityAccount.keypairs,
    connectedStorageAtLeastOnce: state.account.connectedStorageAtLeastOnce,
    storageConnected: state.settings.api.storageConnected,
    email: state.account.email,
    registration: state.profiles.registration
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(Object.assign({},
    AccountActions, SettingsActions, IdentityActions, RegistrationActions),
    dispatch)
}

class Onboarding extends React.Component {
  state = {
    authRequest: '',
    email: '',
    password: '',
    username: '',
    seed: '',
    appManifest: null,
    emailSubmitted: false,
    emailsSent: false,
    view: VIEWS.EMAIL,
    usernameRegistrationInProgress: false
  }

  componentWillMount() {
    const { location } = this.props
    if (location.query.verified) {
      this.setState({ email: location.query.verified })
      this.updateView(VIEWS.PASSWORD)
    }
  }

  componentDidMount() {
    this.decodeAndSaveAuthRequest()
    if (!this.props.api.subdomains['test-personal.id']) {
      this.props.resetApi(this.props.api)
    }
  }

  componentWillReceiveProps(nextProps) {
    const { registration } = nextProps
    if (this.state.usernameRegistrationInProgress && registration.registrationSubmitted) {
      if (!this.state.emailsSent) {
        this.sendEmails().then(() => this.updateView(VIEWS.HOORAY))
      } else {
        this.updateView(VIEWS.HOORAY)
      }

    } else if (registration.error) {
      logger.error(`username registration error: ${registration.error}`)
      this.setState({
        usernameRegistrationInProgress: false
      })
    }
  }

  updateURL = view => {
    const historyChange = slug => {
      if (this.props.location.pathname !== `/sign-up/${slug}`) {
        return this.props.router.push(`/sign-up/${slug}`, this.state)
      } else {
        return null
      }
    }

    switch (view) {
      case VIEWS.EMAIL_VERIFY:
        return historyChange('verify')
      case VIEWS.PASSWORD:
        return historyChange('password')
      case VIEWS.USERNAME:
        return historyChange('username')
      case VIEWS.HOORAY:
        return historyChange('success')
      default:
        return null
    }
  }

  componentDidUpdate() {
    this.updateURL(this.state.view)
  }

  decodeAndSaveAuthRequest() {
    const queryDict = queryString.parse(this.props.location.search)
    if (queryDict.redirect !== null && queryDict.redirect !== undefined) {
      const searchString = queryDict.redirect.replace('/auth', '')
      var redirectQueryDict = queryString.parse(searchString)
      if (redirectQueryDict.authRequest !== null && redirectQueryDict.authRequest !== undefined) {
        const authRequest = redirectQueryDict.authRequest
        verifyAuthRequestAndLoadManifest(authRequest)
        .then(appManifest => {
          this.setState({
            authRequest,
            appManifest
          })
        }, () => {
          logger.error('verifyAuthRequestAndLoadManifest: invalid authentication request')
        }).catch((e) => {
          logger.error('verifyAuthRequestAndLoadManifest: error', e)
        })
      }
    }
  }

  updateValue = (key, value) => {
    this.setState({ [key]: value })
  }

  updateView = view => this.setState({ view })

  sendRecovery(blockstackId, email, encryptedSeed) {
    const { protocol, hostname, port } = location
    const thisUrl = `${protocol}//${hostname}${port && `:${port}`}`
    const seedRecovery = `${thisUrl}/seed?encrypted=${encryptedSeed}`

    const options = {
      method: 'POST',
      body: JSON.stringify({
        email,
        seedRecovery,
        blockstackId
      }),
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      }
    }

    return fetch(`${HEROKU_URL}/recovery`, options)
      .then(
        () => {
          console.log(`emailNotifications: sent ${email} recovery email`)
        },
        error => {
          console.log('emailNotifications: error', error)
        }
      )
      .catch(error => {
        console.log('emailNotifications: error', error)
      })
  }

  sendRestore(blockstackId, email, encryptedSeed) {
    const { protocol, hostname, port } = location
    const thisUrl = `${protocol}//${hostname}${port && `:${port}`}`
    const restoreLink = `${thisUrl}/sign-in?seed=${encryptedSeed}`

    const options = {
      method: 'POST',
      body: JSON.stringify({
        email,
        restoreLink,
        blockstackId
      }),
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      }
    }

    return fetch(`${HEROKU_URL}/restore`, options)
      .then(
        () => {
          console.log(`emailNotifications: sent ${email} restore email`)
        },
        error => {
          console.log('emailNotifications: error', error)
        }
      )
      .catch(error => {
        console.log('emailNotifications: error', error)
      })
  }

  verifyEmail(email) {
    this.setState({ emailSubmitted: true })

    const { protocol, hostname, port } = location
    const thisUrl = `${protocol}//${hostname}${port && `:${port}`}`
    const emailVerificationLink = `${thisUrl}/sign-up?verified=${email}`

    const options = {
      method: 'POST',
      body: JSON.stringify({
        email,
        emailVerificationLink
      }),
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      }
    }

    return fetch(`${HEROKU_URL}/verify`, options)
      .then(
        () => {
          console.log(`emailNotifications: sent ${email} an email verification`)
        },
        error => {
          this.setState({ emailSubmitted: false })
          console.log('emailNotifications: error', error)
        }
      )
      .catch(error => {
        console.log('emailNotifications: error', error)
        this.setState({ emailSubmitted: false })
      })
  }

  submitPassword = () => {
    const { username, email } = this.state
    if (username.length < 1) {
      this.setState({
        email
      })
    }

    logger.debug('creating account')
    this.createAccount(this.state.password)
      .then(() => this.connectStorage())
      .then(() => {
        this.updateView(VIEWS.USERNAME)
      })
  }

  createAccount(password) {
    const firstIdentityIndex = 0
    return this.props.initializeWallet(password, null)
    .then(() => {
      logger.debug('creating new identity')
      const ownerAddress = this.props.identityAddresses[firstIdentityIndex]
      return this.props.createNewIdentityWithOwnerAddress(firstIdentityIndex, ownerAddress)
    }).then(() => {
      return this.props.setDefaultIdentity(firstIdentityIndex)
    })
  }

  connectStorage() {
    const storageProvider = this.props.api.gaiaHubUrl
    const signer = this.props.identityKeypairs[0].key
    return connectToGaiaHub(storageProvider, signer).then(gaiaHubConfig => {
      const newApi = Object.assign({}, this.props.api, {
        gaiaHubConfig,
        hostedDataLocation: BLOCKSTACK_INC
      })
      this.props.updateApi(newApi)
      const identityIndex = 0
      const identity = this.props.localIdentities[identityIndex]
      const identityAddress = identity.ownerAddress
      const profileSigningKeypair = this.props.identityKeypairs[identityIndex]
      const profile = identity.profile
      setCoreStorageConfig(
        newApi,
        identityIndex,
        identityAddress,
        profile,
        profileSigningKeypair,
        identity
      ).then(indexUrl => {
        logger.debug('connectStorage: storage initialized')
        const newApi2 = Object.assign({}, newApi, { storageConnected: true })
        this.props.updateApi(newApi2)
        this.props.storageIsConnected()
        logger.debug('connectStorage: storage configured')
      })
    })
  }

  submitUsername = (username) => {
    console.log('about to submit username')
    const suffix = '.test-personal.id'
    username += suffix
    logger.trace('registerUsername')
    const nameHasBeenPreordered = hasNameBeenPreordered(username, this.props.localIdentities)
    if (nameHasBeenPreordered) {
      logger.error(`registerUsername: username '${username}' has already been preordered`)
    } else {
      this.setState({
        usernameRegistrationInProgress: true
      })
      logger.debug(`registerUsername: will try and register username: ${username}`)
      const address = this.props.identityAddresses[0]
      const identity = this.props.localIdentities[0]
      const keypair = this.props.identityKeypairs[0]
      this.props.registerName(this.props.api, username, identity,
        0, address, keypair)
    }
  }

  redirectToAuth = () => {
    this.props.router.push(`/auth/?authRequest=${this.state.authRequest}`)
  }

  goToBackup = () => {
    browserHistory.push({
      pathname: '/seed',
      state: { seed: this.state.seed }
    })
  }

  sendEmails = () => {
    this.setState({ emailsSent: true })
    const username = this.state.username
    const email = this.state.email
    const encryptedBackupPhrase = this.props.encryptedBackupPhrase
    return Promise.all([
      this.sendRestore(username, email, encryptedBackupPhrase),
      this.sendRecovery(username, email, encryptedBackupPhrase)
    ])
  }

  submitEmailForVerification = () => {
    // Skip email verification
    this.updateView(VIEWS.PASSWORD)

    // verifyEmail(this.state.email)
    // this.updateView(VIEWS.EMAIL_VERIFY)
  }

  render() {
    const { email, password, username, emailSubmitted, view } = this.state
    const icons = this.state.appManifest ? this.state.appManifest.icons : []
    const appIconURL = icons.length > 0 ? icons[0].src : '/images/app-icon-hello-blockstack.png'
    const appName = this.state.appManifest ? this.state.appManifest.name : ''

    const views = [
      {
        show: VIEWS.EMAIL,
        Component: Email,
        props: {
          email,
          next: this.submitEmailForVerification,
          submitted: emailSubmitted,
          updateValue: this.updateValue,
          appIconURL: appIconURL
        }
      },
      {
        show: VIEWS.EMAIL_VERIFY,
        Component: Verify,
        props: {
          email,
          resend: this.submitEmailForVerification,
          next: () => this.updateView(VIEWS.PASSWORD)
        }
      },
      {
        show: VIEWS.PASSWORD,
        Component: Password,
        props: {
          password,
          next: this.submitPassword,
          updateValue: this.updateValue
        }
      },
      {
        show: VIEWS.USERNAME,
        Component: Username,
        props: {
          username,
          next: this.submitUsername,
          previous: () => this.updateView(VIEWS.PASSWORD),
          updateValue: this.updateValue,
          isProcessing: this.state.usernameRegistrationInProgress
        }
      },
      {
        show: VIEWS.HOORAY,
        Component: Hooray,
        props: {
          email,
          password,
          username,
          appIconURL: appIconURL,
          appName: appName,
          goToRecovery: this.goToBackup,
          goToApp: () => this.redirectToAuth()
        }
      }
    ]

    return <PanelShell>{renderItems(views, view)}</PanelShell>
  }
}

Onboarding.propTypes = {
  api: PropTypes.object.isRequired,
  location: PropTypes.object,
  router: PropTypes.object,
  identityAddresses: PropTypes.array,
  createNewIdentityWithOwnerAddress: PropTypes.func.isRequired,
  setDefaultIdentity: PropTypes.func.isRequired,
  initializeWallet: PropTypes.func.isRequired,
  updateApi: PropTypes.func.isRequired,
  localIdentities: PropTypes.array.isRequired,
  identityKeypairs: PropTypes.array.isRequired,
  storageIsConnected: PropTypes.func.isRequired,
  registerName: PropTypes.func.isRequired,
  resetApi: PropTypes.func.isRequired
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Onboarding))
<|endoftext|>


<commit_before>/* globals d3:false */

var pty = {
    version: '0.1.0'  // semver
};

// SVG Transformations
// -------------------
pty.svg = {};

// SVG Translation
pty.svg.translate = function(dx, dy) {
    if ((arguments.length === 1) && (dx.length === 2)) {
        dy = dx[1]; dx = dx[0];
    }

    return 'translate(' + [dx, dy] + ')';
};

// SVG Scale
pty.svg.scale = function(sx, sy) {
    if (arguments.length < 2) { sy = sx; }
    return 'scale(' + [sx, sy] + ')';
};

// Charting Module
pty.chart = {};

// Network Chart
// -------------

pty.chart.network = function() {

    // Chart Attributes
    var me = {
        width: 400,
        height: 400,
        nodeRadius: 20,
        charge: function(d, i) { return -4e3; },
        friction: 0.5,
        linkStrength: 0.2,
        linkDistance: 120,
        nodeClass: function(d, i) { return ''; },
        nodeBaseURL: function(d) { return ''; },
        nodeURL: function(d) { return ''; },
        nodeLabel: function(d, i) { return ''; },
        fixCenter: true,
        duration: 2000,
        delay: 200,
        zoomExtent: [1, 8],
        controlsPosition: [10, 10]
    };

    // Flag to know if the network chart has been drawn
    var initialData = false;

    // Charting Function
    function chart(selection) {
        selection.each(function(data) {

            var div = d3.select(this),
                svg = div.selectAll('svg').data([data]),
                svgEnter = svg.enter().append('svg').call(chart.init);

            var gContainer = svg.select('g.network-chart'),
                gBackground = gContainer.select('g.background'),
                gControls = gContainer.select('g.controls'),
                gZoomCont = gContainer.select('g.zoom-container'),
                gChart = gZoomCont.select('g.network'),
                gLinks = gChart.select('g.links'),
                gNodes = gChart.select('g.nodes'),
                gLabels = gChart.select('g.labels'),
                gNodeUrl = gControls.select('g.url-container'),
                nodeUrlLabel = gNodeUrl.select('text.url-container'),
                nodeUrlLink = gNodeUrl.select('a.url-container');

            // Controls
            var gControlRefresh = gControls.select('g.control-item.refresh'),
                gControlZoomIn = gControls.select('g.control-item.zoom-in'),
                gControlZoomOut = gControls.select('g.control-item.zoom-out');

            // Process the data to remove duplicate links
            var networkData = chart.parseNetworkData(data);

            // Mouse zoom callback
            function onZoom() {
                var d = d3.event.translate,
                    s = d3.event.scale;
                gChart.attr('transform', pty.svg.translate(d) + pty.svg.scale(s));
            }

            // Configure the zoom behavior
            var zoomBehavior = d3.behavior.zoom()
                .scaleExtent(me.zoomExtent)
                .on('zoom', onZoom);

            // Bind the zoom behavior to the background rectangle
            gBackground.select('rect.background').call(zoomBehavior);

            // The click callback requests the network of the clicked node
            function onClick(d, i) {
                d3.json(me.nodeBaseURL(d), function(error, nodeData) {
                    var olddata;

                    if (!error) {
                        olddata = div.data()[0];
                        olddata.nodes = olddata.nodes.concat(nodeData.nodes);
                        olddata.links = olddata.links.concat(nodeData.links);

                        div.data([olddata]).call(chart);
                    }
                });

                d.isclick = false;
            }

            // Force layout
            // ------------

            // Configure and start the force layout
            var force = d3.layout.force()
                .charge(me.charge)
                .friction(me.friction)
                .linkStrength(me.linkStrength)
                .size([me.width, me.height])
                .nodes(networkData.nodes)
                .links(networkData.links)
                .start();

            // Set the label to the root node
            if (!svgEnter.empty()) {
                nodeUrlLink.attr('xlink:href', me.nodeURL(networkData.rootNode));
                nodeUrlLabel.text(me.nodeLabel(networkData.rootNode));
            }

            // Graphic Elements
            // ----------------

            // Links
            // -----

            var links = gLinks.selectAll('line.link')
                .data(force.links(), function(d) { return d.linkID; });

            links.enter().append('line')
                .attr('class', 'link')
                .classed('center-link', function(d) {
                    return (d.from === networkData.root) || (d.to === networkData.root);
                });

            // Remove unused links
            links.exit().remove();

            // Nodes
            // -----

            var circles = gNodes.selectAll('circle.node')
                .data(force.nodes(), function(d) { return d.id; });

            // Update
            circles.classed('node-clickable', function(d) { return d.isclick; });

            circles.enter().append('circle')
                .attr('class', function(d, i) { return me.nodeClass(d, i) + ' node'; })
                .attr('r', me.nodeRadius)
                .classed('node', true)
                .classed('node-center', function(d) { return d.id === data.root; })
                .classed('node-clickable', function(d) { return (d.id !== data.root) && (d.isclick); })
                .on('mouseover', function(d) { d3.select(this).classed('node-highlight', true); })
                .on('mouseout', function(d) { d3.select(this).classed('node-highlight', false); })
                .on('click', function(d, i) {
                    if (d3.select(this).classed('node-clickable')) { onClick(d, i); }
                    // Update the title and link of the lower left label
                    nodeUrlLink.attr('xlink:href', me.nodeURL(d));
                    nodeUrlLabel.text('' + me.nodeLabel(d));
                });

            circles.call(force.drag);

            // Drop the circles on exit
            circles.exit().transition()
                .delay(me.delay)
                .duration(me.duration)
                .attr('cy', me.width)
                .remove();

            // Labels
            // ------

            // Select the labels
            var nodeLabels = gLabels.selectAll('text.node-label')
                .data(force.nodes(), function(d) { return d.id; });

            // Append the text elements on enter
            nodeLabels.enter().append('text')
                .text(me.nodeLabel)
                .attr('x', function(d, i) { return d.x + me.nodeRadius; })
                .attr('y', function(d, i) { return d.y - me.nodeRadius; })
                .attr('class', 'node-label');

            // Remove the labels on exit
            nodeLabels.exit().remove();

            // Update the position of nodes, labels and links on each tick event
            // of the force layout.
            force.on('tick', function() {
                links
                    .attr('x1', function(d) { return d.source.x; })
                    .attr('y1', function(d) { return d.source.y; })
                    .attr('x2', function(d) { return d.target.x; })
                    .attr('y2', function(d) { return d.target.y; });

                circles
                    .attr('cx', function(d) { return d.x; })
                    .attr('cy', function(d) { return d.y; });

                nodeLabels
                    .attr('x', function(d, i) { return d.x + me.nodeRadius; })
                    .attr('y', function(d, i) { return d.y - me.nodeRadius; });
            });

            // Control Callbacks
            //------------------

            // Refresh
            gControlRefresh.on('click', function() {
                var firstData = {
                    root: data.root,
                    nodes: data.nodes.filter(function(d) { return d.__first; }),
                    links: data.links.filter(function(d) { return d.__first; })
                };
                div.data([firstData]).call(chart);
            });

            // Zoom In
            gControlZoomIn.on('click', function() {
                // Compute the new zoom level and update the zoom behavior
                var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]);
                zoomBehavior.scale(newScale);

                // Scale the chart group
                gChart.transition().duration(me.duration)
                    .attr('transform', pty.svg.scale(newScale));
            });

            // Zoom Out
            gControlZoomOut.on('click', function() {
                // Compute the new zoom level and update the zoom behavior
                var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]);
                zoomBehavior.scale(newScale);

                // Scale the chart group
                gChart.transition().duration(me.duration)
                    .attr('transform', pty.svg.scale(newScale));
            });

        });
    }

    chart.init = function(selection) {
        selection.each(function(data) {

            var svgEnter = d3.select(this),
                gContainer = svgEnter.append('g').attr('class', 'network-chart'),
                gBackground = gContainer.append('g').attr('class', 'background'),
                gControls = gContainer.append('g').attr('class', 'controls'),
                gBrand = gContainer.append('g').attr('class', 'brand'),
                gZoomCont = gContainer.append('g').attr('class', 'zoom-container'),
                gChart = gZoomCont.append('g').attr('class', 'network');

            // Set the SVG element width and height
            svgEnter.attr('width', me.width).attr('height', me.height);

            // Background
            gBackground.append('rect')
                .attr('width', me.width)
                .attr('height', me.height)
                .attr('class', 'background');

            // Nodes, Links and Labels
            gChart.append('g').attr('class', 'links');
            gChart.append('g').attr('class', 'nodes');
            gChart.append('g').attr('class', 'labels');

            // Controls
            // --------

            gControls
                .attr('transform', pty.svg.translate(me.controlsPosition));

            // Refresh Button
            var controls = [
                {name: 'refresh',  icon: '\uf0e2'},
                {name: 'zoom-in',  icon: '\uf067'},
                {name: 'zoom-out', icon: '\uf068'}
            ];

            // Controls Scale
            var yScale = d3.scale.ordinal()
                .domain(d3.range(controls.length))
                .rangeBands([0, 30 * controls.length], 0.1);

            var bgSize = yScale.rangeBand();

            var gControlItem = gControls.selectAll('g.control-item')
                .data(controls);

            gControlItem.enter().append('g')
                .attr('class', function(d) { return 'control-item ' + d.name; })
                .attr('transform', function(d, i) { return pty.svg.translate(0, yScale(i)); });

            gControlItem.append('rect')
                .attr('width', bgSize)
                .attr('height', bgSize)
                .attr('rx', 0.25 * bgSize)
                .attr('ry', 0.25 * bgSize);

            var iconLabel = gControlItem.append('text')
                .attr('class', function(d) { return d.name; })
                .attr('font-family', 'FontAwesome')
                .attr('fill', 'white')
                .text(function(d) { return d.icon; });

            iconLabel
                .attr('x', function() { return 0.5 * (bgSize - this.getBBox().width); })
                .attr('y', function() { return 0.5 * (bgSize + this.getBBox().height) - 2; });

            // Brand
            // -----
            gBrand.attr('transform', pty.svg.translate(me.width - 4, me.height - 4));

            var brandLabel = gBrand.append('a')
                .attr('xlink:href', 'http://www.masega.co')
                .append('text')
                .attr('class', 'masega-brand')
                .attr('text-anchor', 'end')
                .text('masega.co');

            // Node Label and Link
            // -------------------
            var gNodeUrl = gControls.append('g')
                .attr('class','url-container')
                .attr('transform', pty.svg.translate(10, me.height - 16));

            // Link
            var gNodeLink = gNodeUrl.append('a')
                .attr('class', 'url-container');

            // Icon
            gNodeLink.append('text')
                .attr('class', 'url-container-icon')
                .text('\uf0c1');

            // Label
            gNodeLink.append('text')
                .attr('x', 20)
                .attr('class', 'url-container')
                .text('');
        });
    };

    chart.parseNetworkData = function(jsonData) {

        var data = {
            root: jsonData.root,
            rootNode: null,
            nodes: [],
            links: []
        };

        if (!initialData) {
            jsonData.nodes.forEach(function(d) { d.__first = true; });
            jsonData.links.forEach(function(d) { d.__first = true; });
            initialData = true;
        }

        var idxNodes = {},
            idxLinks = {},
            k = 0;

        // Identify unique nodes and construct a dictionary of indices
        jsonData.nodes.forEach(function(d) {
            if (!idxNodes.hasOwnProperty(d.id)) {
                idxNodes[d.id] = k;
                k += 1;
                data.nodes.push(d);
            }
        });

        data.nodes.forEach( function(d) { d.internalcn = 0; });

        // Identify unique links and set the source and target of each one
        k = 0;
        jsonData.links.forEach(function(d) {

            d.source = Math.min(idxNodes[d.from], idxNodes[d.to]);
            d.target = Math.max(idxNodes[d.from], idxNodes[d.to]);
            d.linkID = d.source + '-' + d.target;

            if (!idxLinks.hasOwnProperty(d.linkID)) {
                idxLinks[d.linkID] = k;
                data.links.push(d);
                data.nodes[idxNodes[d.from]].internalcn += 1;
                data.nodes[idxNodes[d.to]].internalcn += 1;
                k += 1;
            }
        });

        data.nodes.forEach( function(d) { d.isclick = (d.internalcn < d.numcn); });

        // Set the position of the root node and fix it to the center
        data.nodes.forEach(function(d) {
            if (d.id === data.root) {
                d.x = me.width / 2;
                d.y = me.height / 2;
                d.fixed = me.fixCenter;
                data.rootNode = d;
            }
        });

        return data;
    };

    // Accessor Methods
    // ----------------

    // Generate Accessor Methods
    function createAccessor(attr) {
        return function(value) {
            if (!arguments.length) { return me[attr]; }
            me[attr] = value;
            return chart;
        };
    }

    for (var attr in me) {
        if ((!chart[attr]) && (me.hasOwnProperty(attr))) {
            chart[attr] = createAccessor(attr);
        }
    }

    return chart;
};<commit_msg>Fix the zoom translation<commit_after>/* globals d3:false */

var pty = {
    version: '0.1.0'  // semver
};

// SVG Transformations
// -------------------
pty.svg = {};

// SVG Translation
pty.svg.translate = function(dx, dy) {
    if ((arguments.length === 1) && (dx.length === 2)) {
        dy = dx[1]; dx = dx[0];
    }

    return 'translate(' + [dx, dy] + ')';
};

// SVG Scale
pty.svg.scale = function(sx, sy) {
    if (arguments.length < 2) { sy = sx; }
    return 'scale(' + [sx, sy] + ')';
};

// Charting Module
pty.chart = {};

// Network Chart
// -------------

pty.chart.network = function() {

    // Chart Attributes
    var me = {
        width: 400,
        height: 400,
        nodeRadius: 20,
        charge: function(d, i) { return -4e3; },
        friction: 0.5,
        linkStrength: 0.2,
        linkDistance: 120,
        nodeClass: function(d, i) { return ''; },
        nodeBaseURL: function(d) { return ''; },
        nodeURL: function(d) { return ''; },
        nodeLabel: function(d, i) { return ''; },
        fixCenter: true,
        duration: 2000,
        delay: 200,
        zoomExtent: [1, 8],
        controlsPosition: [10, 10]
    };

    // Flag to know if the network chart has been drawn
    var initialData = false;

    // Charting Function
    function chart(selection) {
        selection.each(function(data) {

            var div = d3.select(this),
                svg = div.selectAll('svg').data([data]),
                svgEnter = svg.enter().append('svg').call(chart.init);

            var gContainer = svg.select('g.network-chart'),
                gBackground = gContainer.select('g.background'),
                gControls = gContainer.select('g.controls'),
                gZoomCont = gContainer.select('g.zoom-container'),
                gChart = gZoomCont.select('g.network'),
                gLinks = gChart.select('g.links'),
                gNodes = gChart.select('g.nodes'),
                gLabels = gChart.select('g.labels'),
                gNodeUrl = gControls.select('g.url-container'),
                nodeUrlLabel = gNodeUrl.select('text.url-container'),
                nodeUrlLink = gNodeUrl.select('a.url-container');

            // Controls
            var gControlRefresh = gControls.select('g.control-item.refresh'),
                gControlZoomIn = gControls.select('g.control-item.zoom-in'),
                gControlZoomOut = gControls.select('g.control-item.zoom-out');

            // Process the data to remove duplicate links
            var networkData = chart.parseNetworkData(data);

            // Mouse zoom callback
            function onZoom() {
                var d = d3.event.translate,
                    s = d3.event.scale;
                gChart.attr('transform', pty.svg.translate(d) + pty.svg.scale(s));
            }

            // Configure the zoom behavior
            var zoomBehavior = d3.behavior.zoom()
                .scaleExtent(me.zoomExtent)
                .on('zoom', onZoom);

            // Bind the zoom behavior to the background rectangle
            gBackground.select('rect.background').call(zoomBehavior);

            // The click callback requests the network of the clicked node
            function onClick(d, i) {
                d3.json(me.nodeBaseURL(d), function(error, nodeData) {
                    var olddata;

                    if (!error) {
                        olddata = div.data()[0];
                        olddata.nodes = olddata.nodes.concat(nodeData.nodes);
                        olddata.links = olddata.links.concat(nodeData.links);

                        div.data([olddata]).call(chart);
                    }
                });

                d.isclick = false;
            }

            // Force layout
            // ------------

            // Configure and start the force layout
            var force = d3.layout.force()
                .charge(me.charge)
                .friction(me.friction)
                .linkStrength(me.linkStrength)
                .size([me.width, me.height])
                .nodes(networkData.nodes)
                .links(networkData.links)
                .start();

            // Set the label to the root node
            if (!svgEnter.empty()) {
                nodeUrlLink.attr('xlink:href', me.nodeURL(networkData.rootNode));
                nodeUrlLabel.text(me.nodeLabel(networkData.rootNode));
            }

            // Graphic Elements
            // ----------------

            // Links
            // -----

            var links = gLinks.selectAll('line.link')
                .data(force.links(), function(d) { return d.linkID; });

            links.enter().append('line')
                .attr('class', 'link')
                .classed('center-link', function(d) {
                    return (d.from === networkData.root) || (d.to === networkData.root);
                });

            // Remove unused links
            links.exit().remove();

            // Nodes
            // -----

            var circles = gNodes.selectAll('circle.node')
                .data(force.nodes(), function(d) { return d.id; });

            // Update
            circles.classed('node-clickable', function(d) { return d.isclick; });

            circles.enter().append('circle')
                .attr('class', function(d, i) { return me.nodeClass(d, i) + ' node'; })
                .attr('r', me.nodeRadius)
                .classed('node', true)
                .classed('node-center', function(d) { return d.id === data.root; })
                .classed('node-clickable', function(d) { return (d.id !== data.root) && (d.isclick); })
                .on('mouseover', function(d) { d3.select(this).classed('node-highlight', true); })
                .on('mouseout', function(d) { d3.select(this).classed('node-highlight', false); })
                .on('click', function(d, i) {
                    if (d3.select(this).classed('node-clickable')) { onClick(d, i); }
                    // Update the title and link of the lower left label
                    nodeUrlLink.attr('xlink:href', me.nodeURL(d));
                    nodeUrlLabel.text('' + me.nodeLabel(d));
                });

            circles.call(force.drag);

            // Drop the circles on exit
            circles.exit().transition()
                .delay(me.delay)
                .duration(me.duration)
                .attr('cy', me.width)
                .remove();

            // Labels
            // ------

            // Select the labels
            var nodeLabels = gLabels.selectAll('text.node-label')
                .data(force.nodes(), function(d) { return d.id; });

            // Append the text elements on enter
            nodeLabels.enter().append('text')
                .text(me.nodeLabel)
                .attr('x', function(d, i) { return d.x + me.nodeRadius; })
                .attr('y', function(d, i) { return d.y - me.nodeRadius; })
                .attr('class', 'node-label');

            // Remove the labels on exit
            nodeLabels.exit().remove();

            // Update the position of nodes, labels and links on each tick event
            // of the force layout.
            force.on('tick', function() {
                links
                    .attr('x1', function(d) { return d.source.x; })
                    .attr('y1', function(d) { return d.source.y; })
                    .attr('x2', function(d) { return d.target.x; })
                    .attr('y2', function(d) { return d.target.y; });

                circles
                    .attr('cx', function(d) { return d.x; })
                    .attr('cy', function(d) { return d.y; });

                nodeLabels
                    .attr('x', function(d, i) { return d.x + me.nodeRadius; })
                    .attr('y', function(d, i) { return d.y - me.nodeRadius; });
            });

            // Control Callbacks
            //------------------

            // Refresh
            gControlRefresh.on('click', function() {
                var firstData = {
                    root: data.root,
                    nodes: data.nodes.filter(function(d) { return d.__first; }),
                    links: data.links.filter(function(d) { return d.__first; })
                };
                div.data([firstData]).call(chart);
            });

            // Zoom In
            gControlZoomIn.on('click', function() {
                // Compute the new zoom level and update the zoom behavior
                var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]),
                    translate = pty.svg.translate(zoomBehavior.translate());

                zoomBehavior.scale(newScale);

                // Scale the chart group
                gChart.transition().duration(me.duration)
                    .attr('transform', translate + pty.svg.scale(newScale));
            });

            // Zoom Out
            gControlZoomOut.on('click', function() {
                // Compute the new zoom level and update the zoom behavior
                var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]),
                    translate = pty.svg.translate(zoomBehavior.translate());

                zoomBehavior.scale(newScale);

                // Scale the chart group
                gChart.transition().duration(me.duration)
                    .attr('transform', translate + pty.svg.scale(newScale));
            });

        });
    }

    chart.init = function(selection) {
        selection.each(function(data) {

            var svgEnter = d3.select(this),
                gContainer = svgEnter.append('g').attr('class', 'network-chart'),
                gBackground = gContainer.append('g').attr('class', 'background'),
                gControls = gContainer.append('g').attr('class', 'controls'),
                gBrand = gContainer.append('g').attr('class', 'brand'),
                gZoomCont = gContainer.append('g').attr('class', 'zoom-container'),
                gChart = gZoomCont.append('g').attr('class', 'network');

            // Set the SVG element width and height
            svgEnter.attr('width', me.width).attr('height', me.height);

            // Background
            gBackground.append('rect')
                .attr('width', me.width)
                .attr('height', me.height)
                .attr('class', 'background');

            // Nodes, Links and Labels
            gChart.append('g').attr('class', 'links');
            gChart.append('g').attr('class', 'nodes');
            gChart.append('g').attr('class', 'labels');

            // Controls
            // --------

            gControls
                .attr('transform', pty.svg.translate(me.controlsPosition));

            // Refresh Button
            var controls = [
                {name: 'refresh',  icon: '\uf0e2'},
                {name: 'zoom-in',  icon: '\uf067'},
                {name: 'zoom-out', icon: '\uf068'}
            ];

            // Controls Scale
            var yScale = d3.scale.ordinal()
                .domain(d3.range(controls.length))
                .rangeBands([0, 30 * controls.length], 0.1);

            var bgSize = yScale.rangeBand();

            var gControlItem = gControls.selectAll('g.control-item')
                .data(controls);

            gControlItem.enter().append('g')
                .attr('class', function(d) { return 'control-item ' + d.name; })
                .attr('transform', function(d, i) { return pty.svg.translate(0, yScale(i)); });

            gControlItem.append('rect')
                .attr('width', bgSize)
                .attr('height', bgSize)
                .attr('rx', 0.25 * bgSize)
                .attr('ry', 0.25 * bgSize);

            var iconLabel = gControlItem.append('text')
                .attr('class', function(d) { return d.name; })
                .attr('font-family', 'FontAwesome')
                .attr('fill', 'white')
                .text(function(d) { return d.icon; });

            iconLabel
                .attr('x', function() { return 0.5 * (bgSize - this.getBBox().width); })
                .attr('y', function() { return 0.5 * (bgSize + this.getBBox().height) - 2; });

            // Brand
            // -----
            gBrand.attr('transform', pty.svg.translate(me.width - 4, me.height - 4));

            var brandLabel = gBrand.append('a')
                .attr('xlink:href', 'http://www.masega.co')
                .append('text')
                .attr('class', 'masega-brand')
                .attr('text-anchor', 'end')
                .text('masega.co');

            // Node Label and Link
            // -------------------
            var gNodeUrl = gControls.append('g')
                .attr('class','url-container')
                .attr('transform', pty.svg.translate(10, me.height - 16));

            // Link
            var gNodeLink = gNodeUrl.append('a')
                .attr('class', 'url-container');

            // Icon
            gNodeLink.append('text')
                .attr('class', 'url-container-icon')
                .text('\uf0c1');

            // Label
            gNodeLink.append('text')
                .attr('x', 20)
                .attr('class', 'url-container')
                .text('');
        });
    };

    chart.parseNetworkData = function(jsonData) {

        var data = {
            root: jsonData.root,
            rootNode: null,
            nodes: [],
            links: []
        };

        if (!initialData) {
            jsonData.nodes.forEach(function(d) { d.__first = true; });
            jsonData.links.forEach(function(d) { d.__first = true; });
            initialData = true;
        }

        var idxNodes = {},
            idxLinks = {},
            k = 0;

        // Identify unique nodes and construct a dictionary of indices
        jsonData.nodes.forEach(function(d) {
            if (!idxNodes.hasOwnProperty(d.id)) {
                idxNodes[d.id] = k;
                k += 1;
                data.nodes.push(d);
            }
        });

        data.nodes.forEach( function(d) { d.internalcn = 0; });

        // Identify unique links and set the source and target of each one
        k = 0;
        jsonData.links.forEach(function(d) {

            d.source = Math.min(idxNodes[d.from], idxNodes[d.to]);
            d.target = Math.max(idxNodes[d.from], idxNodes[d.to]);
            d.linkID = d.source + '-' + d.target;

            if (!idxLinks.hasOwnProperty(d.linkID)) {
                idxLinks[d.linkID] = k;
                data.links.push(d);
                data.nodes[idxNodes[d.from]].internalcn += 1;
                data.nodes[idxNodes[d.to]].internalcn += 1;
                k += 1;
            }
        });

        data.nodes.forEach( function(d) { d.isclick = (d.internalcn < d.numcn); });

        // Set the position of the root node and fix it to the center
        data.nodes.forEach(function(d) {
            if (d.id === data.root) {
                d.x = me.width / 2;
                d.y = me.height / 2;
                d.fixed = me.fixCenter;
                data.rootNode = d;
            }
        });

        return data;
    };

    // Accessor Methods
    // ----------------

    // Generate Accessor Methods
    function createAccessor(attr) {
        return function(value) {
            if (!arguments.length) { return me[attr]; }
            me[attr] = value;
            return chart;
        };
    }

    for (var attr in me) {
        if ((!chart[attr]) && (me.hasOwnProperty(attr))) {
            chart[attr] = createAccessor(attr);
        }
    }

    return chart;
};<|endoftext|>


<commit_before><commit_msg>buggy grid editing<commit_after><|endoftext|>


<commit_before>// -- exports --
module.exports.getFile = getFile;
module.exports.getText = getText;
module.exports.clear = clear;

// -- exec --
init();

// -- named values --
var FILE = 'rdc.FILE';
var TEXT = 'rdc.TEXT';
var FILE_MODEL = 'rdcResponseData';
var TEXT_MODEL = 'rdcResponseText';
var DAY_IN_MILLISECONDS = 86400000;
var CACHE = 'rdc.CACHE';
var CACHE_VIA_REMOTE_ERROR = 'rdc.CACHE_VIA_REMOTE_ERROR';
var REMOTE_HOST = 'rdc.REMOTE_HOST';
var REQUEST_COMPLETED_EVENT = 'remoteDataCache:requestCompleted';
var DOWNLOADING_EVENT = 'remoteDataCache:downloading';

// -- private attributes --
var requestStack, downloading;

// -- public attributes (exports) --

// -- private functions --
function init() {
	requestStack = [];
	downloading = false;
	Ti.App.addEventListener(REQUEST_COMPLETED_EVENT, function(request) {
		Ti.API.info(REQUEST_COMPLETED_EVENT + ' ' + request);
		var downloaded = request.responder === REMOTE_HOST || request.responder === CACHE_VIA_REMOTE_ERROR;
		if(downloaded && requestStack.length > 0) download(requestStack.pop());
		else downloading = false;
	});
	// Ti.App.addEventListener(DOWNLOADING_EVENT, function(e) {
		// Ti.API.info(DOWNLOADING_EVENT + ' ' + e);
	// });
};

function constructRequest(type, url, onload, onerror) {
	var request = {
		type: type,
		made: (new Date()).getTime(),
		url: url,
		responder: null,
		responseTime: null,
		onload: onload,
		onerror: onerror,
		response: null
	};
	return request;
};

function error(request) {
	Ti.API.error('remoteDataCache.error ' + JSON.stringify(request));
	Ti.App.fireEvent(REQUEST_COMPLETED_EVENT, request);
	if(request.onerror && typeof request.onerror === 'function') request.onerror();
};

function submit(request) {
	var cachedResponses = getCachedResponses(request);
	if(isCached(request, cachedResponses)) {
		var cachedResponse = cachedResponses.at(0);
		request.response = cachedResponse;
		if(isRecent(cachedResponse)) {
			var now = new Date();
			request.responseTime = now.getTime() - request.made;
			request.responder = CACHE;
			success(request);
		}
		else stack(request);
	}
	else stack(request);
};

function getCachedResponses(request) {
	var modelType = request.type === FILE ? FILE_MODEL : TEXT_MODEL;
	var responses = Alloy.createCollection(modelType);
	var table = responses.config.adapter.collection_name;
	responses.fetch({
		query: 'select * from ' + table + ' where url = "' + request.url + '"'
	});
	return responses;
};

function isCached(request, cachedResponses) {
	if(cachedResponses.length === 0) return false;
	else if (request.type === FILE) {
		var response = cachedResponses.at(0);
		var file = Ti.Filesystem.getFile(response.get('localPath'));
		if(file.exists()) return true;
		else {
			response.destroy();
			return false;
		}
	}
	else return true;
};

function isRecent(response) {
	var now = new Date();
	var delta = now.getTime() - response.get('timeRetrieved');
	return delta < (DAY_IN_MILLISECONDS / 3) ? true : false;
};

function success(request) {
	Ti.API.info('remoteDataCache.success ' + JSON.stringify(request));
	Ti.App.fireEvent(REQUEST_COMPLETED_EVENT, request);
	if(request.onsuccess && typeof request.onsuccess === 'function') request.onsuccess();
};

function stack(request) {
	if(!downloading) {
		downloading = true;
		download(request);
	}
	else requestStack.push(request);
};

function download(request) {
	var cachedResponse = request.response;
	if(!cachedResponse) {
		var modelType = request.type === FILE ? FILE_MODEL : TEXT_MODEL;
		request.response = Alloy.createModel(modelType);
	}
	var client = Ti.Network.createHTTPClient({
		onload: function(e) {
			var now = new Date();
			request.response.save({
				url: request.url,
				timeRetrieved: now.getTime()
			});
			if(request.type === FILE) {
				var file = Ti.Filesystem.createTempFile();
				file.write(this.responseData);
				request.response.save({
					localPath: file.nativePath
				});
			}
			else {
				request.response.save({
					text: this.responseText
				});
			}
			request.responseTime = now.getTime() - request.made;
			request.responder = REMOTE_HOST;
			success(request);
		},
		onerror: function(e) {
			if(cachedResponse) {
				var now = new Date();
				request.responseTime = now.getTime() - request.made;
				request.responder = CACHE_VIA_REMOTE_ERROR;
				success(request);
			}
			else error(request);
		},
		ondatastream: function(e) {
			Ti.App.fireEvent(DOWNLOADING_EVENT, {
				progress: e.progress,
				url: request.url
			});
		}
	});
	client.open('GET', request.url);
	client.send();
};

function clearCachedResponses(requestType) {
	var modelType = requestType === FILE ? FILE_MODEL : TEXT_MODEL;
	var responses = Alloy.createCollection(modelType);
	var table = responses.config.adapter.collection_name;
	responses.fetch({
		query: 'select * from ' + table
	});
	var wrappedResponses = [];
	responses.each(function(response) {
		wrappedResponses.push({
			 response: response
		 });
	});
	for(var i = 0; i < wrappedResponses.length; ++i) wrappedResponses[i].response.destroy();
};

// -- public functions (exports) --
function getFile(args) {
	if(!args) {
		Ti.API.error('remoteDataCache.getFile: arguments expected');
	}
	else if(!args.url) {
		Ti.API.error('remoteDataCache.getFile: url argument expected');
		var request = constructRequest(null, null, null, args.onerror);
		error(request);
	}
	else {
		var request = constructRequest(FILE, args.url, args.onload, args.onerror);
		submit(request);
	}
};

function getText(args) {
	if(!args) {
		Ti.API.error('remoteDataCache.getText: arguments expected');
	}
	else if(!args.url) {
		Ti.API.error('remoteDataCache.getText: url argument expected');
		var request = constructRequest(null, null, null, args.onerror);
		error(request);
	}
	else {
		var request = constructRequest(TEXT, args.url, args.onload, args.onerror);
		submit(request);
	}
};

function isFileCached(url) {
	var request = constructRequest(FILE, url, null, null);
	var cachedResponses = getCachedResponses(request);
	return isCached(request, cachedResponses);
};

function isTextCached(url) {
	var request = constructRequest(TEXT, url, null, null);
	var cachedResponses = getCachedResponses(request);
	return isCached(request, cachedResponses);
};

function clear() {
	clearCachedResponses(FILE);
	clearCachedResponses(TEXT);
};
<commit_msg>Completed remoteDataCache<commit_after>// -- exports --
module.exports.getFile = getFile;
module.exports.getText = getText;
module.exports.clear = clear;

// -- exec --
init();

// -- named values --
var FILE = 'rdc.FILE';
var TEXT = 'rdc.TEXT';
var FILE_MODEL = 'rdcResponseData';
var TEXT_MODEL = 'rdcResponseText';
var DAY_IN_MILLISECONDS = 86400000;
var CACHE = 'rdc.CACHE';
var CACHE_VIA_REMOTE_ERROR = 'rdc.CACHE_VIA_REMOTE_ERROR';
var REMOTE_HOST = 'rdc.REMOTE_HOST';

// -- private attributes --
var requestStack, downloading;

// -- public attributes (exports) --

// -- private functions --
function init() {
	requestStack = [];
	downloading = false;
};

function constructRequest(type, url, onsuccess, onerror, onstream) {
	var request = {
		type: type,
		made: (new Date()).getTime(),
		url: url,
		responder: null,
		responseTime: null,
		onsuccess: onsuccess,
		onerror: onerror,
		onstream: onstream,
		response: null
	};
	return request;
};

function error(request) {
	Ti.API.error('remoteDataCache.error ' + JSON.stringify(request));
	if(request.onerror && typeof request.onerror === 'function') request.onerror(request);
};

function submit(request) {
	var cachedResponses = getCachedResponses(request);
	if(isCached(request, cachedResponses)) {
		var cachedResponse = cachedResponses.at(0);
		request.response = cachedResponse;
		if(isRecent(cachedResponse)) {
			var now = new Date();
			request.responseTime = now.getTime() - request.made;
			request.responder = CACHE;
			success(request);
		}
		else stack(request);
	}
	else stack(request);
};

function getCachedResponses(request) {
	var modelType = request.type === FILE ? FILE_MODEL : TEXT_MODEL;
	var responses = Alloy.createCollection(modelType);
	var table = responses.config.adapter.collection_name;
	responses.fetch({
		query: 'select * from ' + table + ' where url = "' + request.url + '"'
	});
	return responses;
};

function isCached(request, cachedResponses) {
	if(cachedResponses.length === 0) return false;
	else if (request.type === FILE) {
		var response = cachedResponses.at(0);
		var file = Ti.Filesystem.getFile(response.get('localPath'));
		if(file.exists()) return true;
		else {
			response.destroy();
			return false;
		}
	}
	else return true;
};

function isRecent(response) {
	var now = new Date();
	var delta = now.getTime() - response.get('timeRetrieved');
	return delta < (DAY_IN_MILLISECONDS / 3) ? true : false;
};

function success(request) {
	Ti.API.info('remoteDataCache.success ' + JSON.stringify(request));
	var e = request.type === FILE ? request.response.get('localPath') : request.response.get('text');
	if(request.onsuccess && typeof request.onsuccess === 'function') request.onsuccess(e, request);
};

function stack(request) {
	if(!downloading) {
		downloading = true;
		download(request);
	}
	else requestStack.push(request);
};

function download(request) {
	var cachedResponse = request.response;
	if(!cachedResponse) {
		var modelType = request.type === FILE ? FILE_MODEL : TEXT_MODEL;
		request.response = Alloy.createModel(modelType);
	}
	var client = Ti.Network.createHTTPClient({
		onload: function(e) {
			var now = new Date();
			request.response.save({
				url: request.url,
				timeRetrieved: now.getTime()
			});
			if(request.type === FILE) {
				var file = Ti.Filesystem.createTempFile();
				file.write(this.responseData);
				request.response.save({
					localPath: file.nativePath
				});
			}
			else {
				request.response.save({
					text: this.responseText
				});
			}
			request.responseTime = now.getTime() - request.made;
			request.responder = REMOTE_HOST;
			success(request);
			if(requestStack.length > 0) download(requestStack.pop());
			else downloading = false;
		},
		onerror: function(e) {
			if(cachedResponse) {
				var now = new Date();
				request.responseTime = now.getTime() - request.made;
				request.responder = CACHE_VIA_REMOTE_ERROR;
				success(request);
			}
			else error(request);
			if(requestStack.length > 0) download(requestStack.pop());
			else downloading = false;
		},
		ondatastream: function(e) {
			stream(request, e.progress);
		}
	});
	client.open('GET', request.url);
	client.send();
};

function stream(request, progress) {
	Ti.API.debug('remoteDataCache.stream ' + JSON.stringify({
		progress: progress,
		request: request
	}));
	if(request.onstream && typeof request.onstream === 'function') request.onstream(progress, request);
};

function clearCachedResponses(requestType) {
	var modelType = requestType === FILE ? FILE_MODEL : TEXT_MODEL;
	var responses = Alloy.createCollection(modelType);
	var table = responses.config.adapter.collection_name;
	responses.fetch({
		query: 'select * from ' + table
	});
	var wrappedResponses = [];
	responses.each(function(response) {
		wrappedResponses.push({
			 response: response
		 });
	});
	for(var i = 0; i < wrappedResponses.length; ++i) wrappedResponses[i].response.destroy();
};

// -- public functions (exports) --
function getFile(args) {
	if(!args) {
		Ti.API.error('remoteDataCache.getFile: arguments expected');
	}
	else if(!args.url) {
		Ti.API.error('remoteDataCache.getFile: url argument expected');
		var request = constructRequest(null, null, null, args.onerror, null);
		error(request);
	}
	else {
		var request = constructRequest(FILE, args.url, args.onsuccess, args.onerror, args.onstream);
		submit(request);
	}
};

function getText(args) {
	if(!args) {
		Ti.API.error('remoteDataCache.getText: arguments expected');
	}
	else if(!args.url) {
		Ti.API.error('remoteDataCache.getText: url argument expected');
		var request = constructRequest(null, null, null, args.onerror, null);
		error(request);
	}
	else {
		var request = constructRequest(TEXT, args.url, args.onsuccess, args.onerror, args.onstream);
		submit(request);
	}
};

function isFileCached(url) {
	var request = constructRequest(FILE, url, null, null, null);
	var cachedResponses = getCachedResponses(request);
	return isCached(request, cachedResponses);
};

function isTextCached(url) {
	var request = constructRequest(TEXT, url, null, null, null);
	var cachedResponses = getCachedResponses(request);
	return isCached(request, cachedResponses);
};

function clear() {
	clearCachedResponses(FILE);
	clearCachedResponses(TEXT);
};
<|endoftext|>