<commit_before>import { Feature } from 'toolkit/extension/features/feature';
import { isCurrentRouteAccountsPage } from 'toolkit/extension/utils/ynab';
import { controllerLookup } from 'toolkit/extension/utils/ember';
import { getEntityManager } from 'toolkit/extension/utils/ynab';

export class CalculateIRR extends Feature {
  injectCSS() {
    return require('./index.css');
  }

  shouldInvoke() {
    let { selectedAccount } = controllerLookup('accounts');
    return isCurrentRouteAccountsPage() && selectedAccount && !selectedAccount.onBudget;
  }

  invoke() {
    let { selectedAccount, selectedAccountId, filters } = controllerLookup('accounts');
    let { filterFrom, filterTo } = this._getFilterDates(filters);
    let totalIrr = this._calculateIRR(selectedAccountId);
    if (totalIrr === Infinity) {
      return this.destroy();
    }
    if (Number.isNaN(totalIrr)) {
      totalIrr = 'Error';
    } else {
      totalIrr = (100 * totalIrr).toFixed(2) + ' %';
    }
    let filteredIrr = '';
    let irrYear;
    if (filterFrom.getYear() === filterTo.getYear()) {
      filteredIrr = this._calculateIRR(selectedAccountId, {
        fromDate: filterFrom,
        toDate: filterTo,
      });
      if (filteredIrr === Infinity) irrYear = null;
      else irrYear = filterTo.getYear();
      if (Number.isNaN(filteredIrr)) {
        filteredIrr = 'Error';
      } else {
        filteredIrr = (100 * filteredIrr).toFixed(2) + ' %';
      }
    }
    let headerIrr = $(`.tk-accounts-header-irr`);
    let totalIrrContainer = $(`#tk-total-irr`);
    let filteredIrrContainer = $(`#tk-filtered-irr`);

    let textTotal = 'Total Annualized Return';
    let textFiltered = 'Annualized Return';
    if (selectedAccount.isOtherLiabilityAccount || selectedAccount.isMortgage) {
      textTotal = 'Estimated APR';
      textFiltered = 'Estimated APR';
    }

    if (!headerIrr || headerIrr.length === 0) {
      $('.accounts-header-balances').append(
        `

`
      );
    }

    totalIrrContainer.text(totalIrr);
    $(`#tk-accounts-header-total-irr-label`).text(textTotal);
    filteredIrrContainer.text(filteredIrr);
    $(`#tk-accounts-header-filtered-irr-label`).text(irrYear + ' ' + textFiltered);
    $(`#tk-accounts-header-filtered-irr-label`)
      .parent()
      .toggle(irrYear != null);
  }

  destroy() {
    let headerIrr = $(`.tk-accounts-header-irr`);
    if (headerIrr && headerIrr.length > 0) {
      headerIrr[0].parentElement.removeChild(headerIrr[0]);
    }
  }

  observe(changedNodes) {
    if (!this.shouldInvoke()) return;

    if (changedNodes.has('ynab-grid-body')) {
      this.invoke();
    }
  }

  onRouteChanged() {
    if (this.shouldInvoke()) {
      this.invoke();
    } else {
      this.destroy();
    }
  }

  _calculateIRR = (accountId, options) => {
    const account = getEntityManager().getAccountById(accountId);
    const maxApproximationEpsilon = 1e-10;
    const iterMax = 50;
    let transactions = account.getTransactions();

    if (
      transactions.reduce((reduced, transaction) => {
        if (transaction.flag === this.settings.enabled && transaction.cleared !== 'Uncleared') {
          return reduced + transaction.amount;
        }
        return reduced;
      }, 0) === 0
    )
      return Infinity;

    var fromDate = transactions
      .reduce((date, transaction) => {
        if (transaction.date.isBefore(date)) return transaction.date;
        return date;
      }, transactions[0].date)
      .setDate(1)
      .setMonth(0);
    var toDate = transactions.reduce((date, transaction) => {
      if (transaction.date.isAfter(date)) return transaction.date;
      return date;
    }, transactions[0].date);

    if (options && options.fromDate && fromDate.isBefore(options.fromDate)) {
      fromDate = options.fromDate;
    }
    if (options && options.toDate && toDate.isAfter(options.toDate)) {
      toDate = options.toDate;
    }

    let startingValue = transactions.reduce((reduced, transaction) => {
      if (
        transaction.cleared !== 'Uncleared' &&
        !transaction.isTombstone &&
        transaction.date.isBefore(fromDate)
      ) {
        return reduced + transaction.amount;
      }
      return reduced;
    }, 0);

    let currentValue = transactions.reduce((reduced, transaction) => {
      if (
        transaction.cleared !== 'Uncleared' &&
        !transaction.isTombstone &&
        (transaction.date.isBefore(toDate) || transaction.date.equalsDateWithoutTime(toDate))
      ) {
        return reduced + transaction.amount;
      }
      return reduced;
    }, 0);

    let contributions = transactions.filter((trans) => {
      return (
        trans.flag === this.settings.enabled &&
        trans.cleared !== 'Uncleared' &&
        (trans.date.isAfter(fromDate) || trans.date.equalsDateWithoutTime(fromDate)) &&
        (trans.date.isBefore(toDate) || trans.date.equalsDateWithoutTime(toDate))
      );
    });

    var contributionsTotal = contributions.reduce((reduced, transaction) => {
      return reduced + transaction.amount;
    }, 0);
    var resultRate;
    var approximatedRate = contributionsTotal / currentValue;
    var npv;
    var iteration = 0;
    var continueLoop = true;
    var iterationLog = [];

    do {
      resultRate = approximatedRate;
      npv = this._npv({
        startingValue,
        fromDate,
        transaction: contributions,
        rate: resultRate,
        currentValue,
        toDate,
      });
      var npv1 = this._npvFirstDeriv({
        startingValue,
        fromDate,
        transaction: contributions,
        rate: resultRate,
        currentValue,
        toDate,
      });
      approximatedRate = resultRate - npv / npv1;
      continueLoop = Math.abs(approximatedRate - resultRate) > maxApproximationEpsilon;
      iterationLog.push({
        iteration,
        rate: resultRate,
        npv,
        npv1,
        approximatedRate,
        eps: Math.abs(approximatedRate - resultRate),
      });
    } while (continueLoop && ++iteration < iterMax);

    if (continueLoop) {
      return NaN;
    }

    return approximatedRate;
  };

  _npv = (inputs) => {
    let rate = inputs.rate + 1;
    let npv = inputs.currentValue / 1000;
    let currentDate = ynab.utilities.DateWithoutTime.createForToday();
    if (currentDate.isAfter(inputs.toDate)) currentDate = inputs.toDate;

    for (var i = 0; i < inputs.transaction.length; i++) {
      let yearFraction = inputs.transaction[i].date.daysApart(currentDate) / 365;
      npv -= (inputs.transaction[i].amount / 1000) * Math.pow(rate, yearFraction);
    }
    let yearFraction = currentDate.daysApart(inputs.fromDate) / 365;
    npv -= (inputs.startingValue / 1000) * Math.pow(rate, yearFraction);

    return npv;
  };

  _npvFirstDeriv = (inputs) => {
    let rate = inputs.rate + 1;
    let npv1 = 0;
    let currentDate = ynab.utilities.DateWithoutTime.createForToday();
    if (currentDate.isAfter(inputs.toDate)) currentDate = inputs.toDate;

    for (var i = 0; i < inputs.transaction.length; i++) {
      let yearFraction = inputs.transaction[i].date.daysApart(currentDate) / 365;
      npv1 -=
        yearFraction * (inputs.transaction[i].amount / 1000) * Math.pow(rate, yearFraction - 1);
    }
    let yearFraction = currentDate.daysApart(inputs.fromDate) / 365;
    npv1 -= yearFraction * (inputs.startingValue / 1000) * Math.pow(rate, yearFraction - 1);

    return npv1;
  };

  _getFilterDates = (filter) => {
    var filterFrom = ynab.utilities.DateWithoutTime.createFromYearMonthDate(
      filter.fromYear,
      filter.fromMonth,
      1
    );
    var filterTo = ynab.utilities.DateWithoutTime.createFromYearMonthDate(
      filter.toYear,
      filter.toMonth,
      15
    ).endOfMonth();

    return { filterFrom, filterTo };
  };
}
<commit_msg>fix: get selectedAccount from service instead of controller (#2929)<commit_after>import { Feature } from 'toolkit/extension/features/feature';
import { isCurrentRouteAccountsPage } from 'toolkit/extension/utils/ynab';
import { controllerLookup, serviceLookup } from 'toolkit/extension/utils/ember';
import { getEntityManager } from 'toolkit/extension/utils/ynab';

export class CalculateIRR extends Feature {
  injectCSS() {
    return require('./index.css');
  }

  shouldInvoke() {
    let { selectedAccount } = serviceLookup('accounts');
    return isCurrentRouteAccountsPage() && selectedAccount && !selectedAccount.onBudget;
  }

  invoke() {
    let { filters } = controllerLookup('accounts');
    let { selectedAccount, selectedAccountId } = serviceLookup('accounts');
    let { filterFrom, filterTo } = this._getFilterDates(filters);
    let totalIrr = this._calculateIRR(selectedAccountId);
    if (totalIrr === Infinity) {
      return this.destroy();
    }
    if (Number.isNaN(totalIrr)) {
      totalIrr = 'Error';
    } else {
      totalIrr = (100 * totalIrr).toFixed(2) + ' %';
    }
    let filteredIrr = '';
    let irrYear;
    if (filterFrom.getYear() === filterTo.getYear()) {
      filteredIrr = this._calculateIRR(selectedAccountId, {
        fromDate: filterFrom,
        toDate: filterTo,
      });
      if (filteredIrr === Infinity) irrYear = null;
      else irrYear = filterTo.getYear();
      if (Number.isNaN(filteredIrr)) {
        filteredIrr = 'Error';
      } else {
        filteredIrr = (100 * filteredIrr).toFixed(2) + ' %';
      }
    }
    let headerIrr = $(`.tk-accounts-header-irr`);
    let totalIrrContainer = $(`#tk-total-irr`);
    let filteredIrrContainer = $(`#tk-filtered-irr`);

    let textTotal = 'Total Annualized Return';
    let textFiltered = 'Annualized Return';
    if (selectedAccount.isOtherLiabilityAccount || selectedAccount.isMortgage) {
      textTotal = 'Estimated APR';
      textFiltered = 'Estimated APR';
    }

    if (!headerIrr || headerIrr.length === 0) {
      $('.accounts-header-balances').append(
        `

`
      );
    }

    totalIrrContainer.text(totalIrr);
    $(`#tk-accounts-header-total-irr-label`).text(textTotal);
    filteredIrrContainer.text(filteredIrr);
    $(`#tk-accounts-header-filtered-irr-label`).text(irrYear + ' ' + textFiltered);
    $(`#tk-accounts-header-filtered-irr-label`)
      .parent()
      .toggle(irrYear != null);
  }

  destroy() {
    let headerIrr = $(`.tk-accounts-header-irr`);
    if (headerIrr && headerIrr.length > 0) {
      headerIrr[0].parentElement.removeChild(headerIrr[0]);
    }
  }

  observe(changedNodes) {
    if (!this.shouldInvoke()) return;

    if (changedNodes.has('ynab-grid-body')) {
      this.invoke();
    }
  }

  onRouteChanged() {
    if (this.shouldInvoke()) {
      this.invoke();
    } else {
      this.destroy();
    }
  }

  _calculateIRR = (accountId, options) => {
    const account = getEntityManager().getAccountById(accountId);
    const maxApproximationEpsilon = 1e-10;
    const iterMax = 50;
    let transactions = account.getTransactions();

    if (
      transactions.reduce((reduced, transaction) => {
        if (transaction.flag === this.settings.enabled && transaction.cleared !== 'Uncleared') {
          return reduced + transaction.amount;
        }
        return reduced;
      }, 0) === 0
    )
      return Infinity;

    var fromDate = transactions
      .reduce((date, transaction) => {
        if (transaction.date.isBefore(date)) return transaction.date;
        return date;
      }, transactions[0].date)
      .setDate(1)
      .setMonth(0);
    var toDate = transactions.reduce((date, transaction) => {
      if (transaction.date.isAfter(date)) return transaction.date;
      return date;
    }, transactions[0].date);

    if (options && options.fromDate && fromDate.isBefore(options.fromDate)) {
      fromDate = options.fromDate;
    }
    if (options && options.toDate && toDate.isAfter(options.toDate)) {
      toDate = options.toDate;
    }

    let startingValue = transactions.reduce((reduced, transaction) => {
      if (
        transaction.cleared !== 'Uncleared' &&
        !transaction.isTombstone &&
        transaction.date.isBefore(fromDate)
      ) {
        return reduced + transaction.amount;
      }
      return reduced;
    }, 0);

    let currentValue = transactions.reduce((reduced, transaction) => {
      if (
        transaction.cleared !== 'Uncleared' &&
        !transaction.isTombstone &&
        (transaction.date.isBefore(toDate) || transaction.date.equalsDateWithoutTime(toDate))
      ) {
        return reduced + transaction.amount;
      }
      return reduced;
    }, 0);

    let contributions = transactions.filter((trans) => {
      return (
        trans.flag === this.settings.enabled &&
        trans.cleared !== 'Uncleared' &&
        (trans.date.isAfter(fromDate) || trans.date.equalsDateWithoutTime(fromDate)) &&
        (trans.date.isBefore(toDate) || trans.date.equalsDateWithoutTime(toDate))
      );
    });

    var contributionsTotal = contributions.reduce((reduced, transaction) => {
      return reduced + transaction.amount;
    }, 0);
    var resultRate;
    var approximatedRate = contributionsTotal / currentValue;
    var npv;
    var iteration = 0;
    var continueLoop = true;
    var iterationLog = [];

    do {
      resultRate = approximatedRate;
      npv = this._npv({
        startingValue,
        fromDate,
        transaction: contributions,
        rate: resultRate,
        currentValue,
        toDate,
      });
      var npv1 = this._npvFirstDeriv({
        startingValue,
        fromDate,
        transaction: contributions,
        rate: resultRate,
        currentValue,
        toDate,
      });
      approximatedRate = resultRate - npv / npv1;
      continueLoop = Math.abs(approximatedRate - resultRate) > maxApproximationEpsilon;
      iterationLog.push({
        iteration,
        rate: resultRate,
        npv,
        npv1,
        approximatedRate,
        eps: Math.abs(approximatedRate - resultRate),
      });
    } while (continueLoop && ++iteration < iterMax);

    if (continueLoop) {
      return NaN;
    }

    return approximatedRate;
  };

  _npv = (inputs) => {
    let rate = inputs.rate + 1;
    let npv = inputs.currentValue / 1000;
    let currentDate = ynab.utilities.DateWithoutTime.createForToday();
    if (currentDate.isAfter(inputs.toDate)) currentDate = inputs.toDate;

    for (var i = 0; i < inputs.transaction.length; i++) {
      let yearFraction = inputs.transaction[i].date.daysApart(currentDate) / 365;
      npv -= (inputs.transaction[i].amount / 1000) * Math.pow(rate, yearFraction);
    }
    let yearFraction = currentDate.daysApart(inputs.fromDate) / 365;
    npv -= (inputs.startingValue / 1000) * Math.pow(rate, yearFraction);

    return npv;
  };

  _npvFirstDeriv = (inputs) => {
    let rate = inputs.rate + 1;
    let npv1 = 0;
    let currentDate = ynab.utilities.DateWithoutTime.createForToday();
    if (currentDate.isAfter(inputs.toDate)) currentDate = inputs.toDate;

    for (var i = 0; i < inputs.transaction.length; i++) {
      let yearFraction = inputs.transaction[i].date.daysApart(currentDate) / 365;
      npv1 -=
        yearFraction * (inputs.transaction[i].amount / 1000) * Math.pow(rate, yearFraction - 1);
    }
    let yearFraction = currentDate.daysApart(inputs.fromDate) / 365;
    npv1 -= yearFraction * (inputs.startingValue / 1000) * Math.pow(rate, yearFraction - 1);

    return npv1;
  };

  _getFilterDates = (filter) => {
    var filterFrom = ynab.utilities.DateWithoutTime.createFromYearMonthDate(
      filter.fromYear,
      filter.fromMonth,
      1
    );
    var filterTo = ynab.utilities.DateWithoutTime.createFromYearMonthDate(
      filter.toYear,
      filter.toMonth,
      15
    ).endOfMonth();

    return { filterFrom, filterTo };
  };
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import injectSheet from 'react-jss';
import { defineMessages, intlShape } from 'react-intl';
import { H1, H2, Icon } from '@meetfranz/ui';
import color from 'color';

import { mdiRocket } from '@mdi/js';
import PlanItem from './PlanItem';
import { i18nPlanName } from '../../../helpers/plan-helpers';
import { PLANS } from '../../../config';
import { FeatureList } from '../../../components/ui/FeatureList';

const messages = defineMessages({
  welcome: {
    id: 'feature.planSelection.fullscreen.welcome',
    defaultMessage: '!!!Welcome back, {name}',
  },
  subheadline: {
    id: 'feature.planSelection.fullscreen.subheadline',
    defaultMessage: '!!!It\'s time to make a choice. Franz works best on our Personal and Professional plans. Please have a look and choose the best one for you.',
  },
  textFree: {
    id: 'feature.planSelection.free.text',
    defaultMessage: '!!!Basic functionality',
  },
  textPersonal: {
    id: 'feature.planSelection.personal.text',
    defaultMessage: '!!!More services, no waiting - ideal for personal use.',
  },
  textProfessional: {
    id: 'feature.planSelection.pro.text',
    defaultMessage: '!!!Unlimited services and professional features for you - and your team.',
  },
  ctaStayOnFree: {
    id: 'feature.planSelection.cta.stayOnFree',
    defaultMessage: '!!!Stay on Free',
  },
  ctaDowngradeFree: {
    id: 'feature.planSelection.cta.ctaDowngradeFree',
    defaultMessage: '!!!Downgrade to Free',
  },
  actionTrial: {
    id: 'feature.planSelection.cta.trial',
    defaultMessage: '!!!Start my free 14-days Trial',
  },
  shortActionPersonal: {
    id: 'feature.planSelection.cta.upgradePersonal',
    defaultMessage: '!!!Choose Personal',
  },
  shortActionPro: {
    id: 'feature.planSelection.cta.upgradePro',
    defaultMessage: '!!!Choose Professional',
  },
  fullFeatureList: {
    id: 'feature.planSelection.fullFeatureList',
    defaultMessage: '!!!Complete comparison of all plans',
  },
});

const styles = theme => ({
  root: {
    background: theme.colorModalOverlayBackground,
    width: '100%',
    height: '100%',
    position: 'absolute',
    top: 0,
    left: 0,
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 999999,
  },
  container: {
    width: '80%',
    height: 'auto',
    background: theme.styleTypes.primary.accent,
    padding: 40,
    borderRadius: theme.borderRadius,
    maxWidth: 1000,

    '& h1, & h2': {
      textAlign: 'center',
    },
  },
  plans: {
    display: 'flex',
    margin: [40, 0, 0],
    height: 'auto',

    '& > div': {
      margin: [0, 15],
      flex: 1,
      height: 'auto',
      background: theme.styleTypes.primary.contrast,
      boxShadow: [0, 2, 30, color('#000').alpha(0.1).rgb().string()],
    },
  },
  bigIcon: {
    background: theme.styleTypes.danger.accent,
    width: 120,
    height: 120,
    display: 'flex',
    alignItems: 'center',
    borderRadius: '100%',
    justifyContent: 'center',
    margin: [-100, 'auto', 20],

    '& svg': {
      width: '80px !important',
    },
  },
  headline: {
    fontSize: 40,
  },
  subheadline: {
    maxWidth: 660,
    fontSize: 22,
    lineHeight: 1.1,
    margin: [0, 'auto'],
  },
  featureList: {
    '& li': {
      borderBottom: [1, 'solid', '#CECECE'],
    },
  },
  fullFeatureList: {
    marginTop: 40,
    textAlign: 'center',
    display: 'block',
    color: `${theme.styleTypes.primary.contrast} !important`,
  },
});

@injectSheet(styles) @observer
class PlanSelection extends Component {
  static propTypes = {
    classes: PropTypes.object.isRequired,
    firstname: PropTypes.string.isRequired,
    plans: PropTypes.object.isRequired,
    currency: PropTypes.string.isRequired,
    subscriptionExpired: PropTypes.bool.isRequired,
    upgradeAccount: PropTypes.func.isRequired,
    stayOnFree: PropTypes.func.isRequired,
    hadSubscription: PropTypes.bool.isRequired,
  };

  static contextTypes = {
    intl: intlShape,
  };

  render() {
    const {
      classes,
      firstname,
      plans,
      currency,
      subscriptionExpired,
      upgradeAccount,
      stayOnFree,
      hadSubscription,
    } = this.props;

    const { intl } = this.context;

    return (

            <Icon icon={mdiRocket} />

{intl.formatMessage(messages.welcome, { name: firstname })}

{intl.formatMessage(messages.subheadline)}

            <PlanItem
              name={i18nPlanName(PLANS.FREE, intl)}
              text={intl.formatMessage(messages.textFree)}
              price={0}
              currency={currency}
              ctaLabel={intl.formatMessage(subscriptionExpired ? messages.ctaDowngradeFree : messages.ctaStayOnFree)}
              upgrade={() => stayOnFree()}
              simpleCTA
            >
              <FeatureList
                plan={PLANS.FREE}
                className={classes.featureList}
              />
            </PlanItem>
            <PlanItem
              name={i18nPlanName(plans.personal.yearly.id, intl)}
              text={intl.formatMessage(messages.textPersonal)}
              price={plans.personal.yearly.price}
              currency={currency}
              ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPersonal : messages.actionTrial)}
              upgrade={() => upgradeAccount(plans.personal.yearly.id)}
            >
              <FeatureList
                plan={PLANS.PERSONAL}
                className={classes.featureList}
              />
            </PlanItem>
            <PlanItem
              name={i18nPlanName(plans.pro.yearly.id, intl)}
              text={intl.formatMessage(messages.textProfessional)}
              price={plans.pro.yearly.price}
              currency={currency}
              ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPro : messages.actionTrial)}
              upgrade={() => upgradeAccount(plans.personal.yearly.id)}
              perUser
            >
              <FeatureList
                plan={PLANS.PRO}
                className={classes.featureList}
              />
            </PlanItem>

            {intl.formatMessage(messages.fullFeatureList)}

    );
  }
}

export default PlanSelection;
<commit_msg>icon polishing<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import injectSheet from 'react-jss';
import { defineMessages, intlShape } from 'react-intl';
import { H1, H2, Icon } from '@meetfranz/ui';
import color from 'color';

import { mdiRocket } from '@mdi/js';
import PlanItem from './PlanItem';
import { i18nPlanName } from '../../../helpers/plan-helpers';
import { PLANS } from '../../../config';
import { FeatureList } from '../../../components/ui/FeatureList';

const messages = defineMessages({
  welcome: {
    id: 'feature.planSelection.fullscreen.welcome',
    defaultMessage: '!!!Welcome back, {name}',
  },
  subheadline: {
    id: 'feature.planSelection.fullscreen.subheadline',
    defaultMessage: '!!!It\'s time to make a choice. Franz works best on our Personal and Professional plans. Please have a look and choose the best one for you.',
  },
  textFree: {
    id: 'feature.planSelection.free.text',
    defaultMessage: '!!!Basic functionality',
  },
  textPersonal: {
    id: 'feature.planSelection.personal.text',
    defaultMessage: '!!!More services, no waiting - ideal for personal use.',
  },
  textProfessional: {
    id: 'feature.planSelection.pro.text',
    defaultMessage: '!!!Unlimited services and professional features for you - and your team.',
  },
  ctaStayOnFree: {
    id: 'feature.planSelection.cta.stayOnFree',
    defaultMessage: '!!!Stay on Free',
  },
  ctaDowngradeFree: {
    id: 'feature.planSelection.cta.ctaDowngradeFree',
    defaultMessage: '!!!Downgrade to Free',
  },
  actionTrial: {
    id: 'feature.planSelection.cta.trial',
    defaultMessage: '!!!Start my free 14-days Trial',
  },
  shortActionPersonal: {
    id: 'feature.planSelection.cta.upgradePersonal',
    defaultMessage: '!!!Choose Personal',
  },
  shortActionPro: {
    id: 'feature.planSelection.cta.upgradePro',
    defaultMessage: '!!!Choose Professional',
  },
  fullFeatureList: {
    id: 'feature.planSelection.fullFeatureList',
    defaultMessage: '!!!Complete comparison of all plans',
  },
});

const styles = theme => ({
  root: {
    background: theme.colorModalOverlayBackground,
    width: '100%',
    height: '100%',
    position: 'absolute',
    top: 0,
    left: 0,
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 999999,
  },
  container: {
    width: '80%',
    height: 'auto',
    background: theme.styleTypes.primary.accent,
    padding: 40,
    borderRadius: theme.borderRadius,
    maxWidth: 1000,

    '& h1, & h2': {
      textAlign: 'center',
    },
  },
  plans: {
    display: 'flex',
    margin: [40, 0, 0],
    height: 'auto',

    '& > div': {
      margin: [0, 15],
      flex: 1,
      height: 'auto',
      background: theme.styleTypes.primary.contrast,
      boxShadow: [0, 2, 30, color('#000').alpha(0.1).rgb().string()],
    },
  },
  bigIcon: {
    background: theme.styleTypes.danger.accent,
    width: 120,
    height: 120,
    display: 'flex',
    alignItems: 'center',
    borderRadius: '100%',
    justifyContent: 'center',
    margin: [-100, 'auto', 20],

    '& svg': {
      width: '80px !important',
      filter: 'drop-shadow( 0px 2px 3px rgba(0, 0, 0, 0.3))',
    },
  },
  headline: {
    fontSize: 40,
  },
  subheadline: {
    maxWidth: 660,
    fontSize: 22,
    lineHeight: 1.1,
    margin: [0, 'auto'],
  },
  featureList: {
    '& li': {
      borderBottom: [1, 'solid', '#CECECE'],
    },
  },
  fullFeatureList: {
    marginTop: 40,
    textAlign: 'center',
    display: 'block',
    color: `${theme.styleTypes.primary.contrast} !important`,
  },
});

@injectSheet(styles) @observer
class PlanSelection extends Component {
  static propTypes = {
    classes: PropTypes.object.isRequired,
    firstname: PropTypes.string.isRequired,
    plans: PropTypes.object.isRequired,
    currency: PropTypes.string.isRequired,
    subscriptionExpired: PropTypes.bool.isRequired,
    upgradeAccount: PropTypes.func.isRequired,
    stayOnFree: PropTypes.func.isRequired,
    hadSubscription: PropTypes.bool.isRequired,
  };

  static contextTypes = {
    intl: intlShape,
  };

  render() {
    const {
      classes,
      firstname,
      plans,
      currency,
      subscriptionExpired,
      upgradeAccount,
      stayOnFree,
      hadSubscription,
    } = this.props;

    const { intl } = this.context;

    return (

            <Icon icon={mdiRocket} />

{intl.formatMessage(messages.welcome, { name: firstname })}

{intl.formatMessage(messages.subheadline)}

            <PlanItem
              name={i18nPlanName(PLANS.FREE, intl)}
              text={intl.formatMessage(messages.textFree)}
              price={0}
              currency={currency}
              ctaLabel={intl.formatMessage(subscriptionExpired ? messages.ctaDowngradeFree : messages.ctaStayOnFree)}
              upgrade={() => stayOnFree()}
              simpleCTA
            >
              <FeatureList
                plan={PLANS.FREE}
                className={classes.featureList}
              />
            </PlanItem>
            <PlanItem
              name={i18nPlanName(plans.personal.yearly.id, intl)}
              text={intl.formatMessage(messages.textPersonal)}
              price={plans.personal.yearly.price}
              currency={currency}
              ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPersonal : messages.actionTrial)}
              upgrade={() => upgradeAccount(plans.personal.yearly.id)}
            >
              <FeatureList
                plan={PLANS.PERSONAL}
                className={classes.featureList}
              />
            </PlanItem>
            <PlanItem
              name={i18nPlanName(plans.pro.yearly.id, intl)}
              text={intl.formatMessage(messages.textProfessional)}
              price={plans.pro.yearly.price}
              currency={currency}
              ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPro : messages.actionTrial)}
              upgrade={() => upgradeAccount(plans.personal.yearly.id)}
              perUser
            >
              <FeatureList
                plan={PLANS.PRO}
                className={classes.featureList}
              />
            </PlanItem>

            {intl.formatMessage(messages.fullFeatureList)}

    );
  }
}

export default PlanSelection;
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Google Inc., authors, and contributors <see AUTHORS file>
 * Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
 * Created By: brad@reciprocitylabs.com
 * Maintained By: brad@reciprocitylabs.com
 */

(function(can, $) {

can.Control("GGRC.Controllers.PbcWorkflows", {

}, {

  "{CMS.Models.Audit} created" : function(model, ev, instance) {
    var refresh_queue
    , that = this
    if(instance instanceof CMS.Models.Audit) {
      if(instance.auto_generate) {
        refresh_queue = new RefreshQueue();

        instance.program.reify().refresh()
        .then(function(program) {
          can.each(program.objectives.reify(), function(obj) {
            refresh_queue.enqueue(obj);
          });
          return refresh_queue.trigger();
        })
        .then(function(objectives) {
          can.each(objectives, function(objective) {
            that.create_request(instance, objective)
            .then(that.proxy("create_response"));
          });
        });
      } else {
        this.create_objective(instance)
        .then(this.proxy("map_objective_to_program", instance.program.reify()))
        .then(function(object_objective) {
          return object_objective.objective.reify();
        })
        .then(that.proxy("create_request", instance))
        .done(that.proxy("update_objective_title"))
        .done(this.proxy("create_response"));
      }
    }
  }

  , create_objective : function(audit) {
    return new CMS.Models.Objective({
      title : "Generic request"
      , context : audit.context
      , owner : audit.owner || CMS.Models.Person.model(GGRC.current_user)
    }).save();
  }

  , map_objective_to_program : function(program, objective) {
    return new CMS.Models.ObjectObjective({
      objectiveable : program.stub()
      , objective : objective.stub()
      , context : objective.context
    }).save();
  }

  , create_request : function(audit, objective) {
    return new CMS.Models.Request({
      assignee : audit.owner || CMS.Models.Person.model(GGRC.current_user)
      , audit : audit.stub()
      , start_at : new Date()
      , end_at : moment().add(30, "days").toDate()
      , objective : objective.stub()
      , request_type : "documentation"
      , description : "Please provide evidence of the type requested to demonstrate this Objective has been met."
      , context : audit.context
    }).save();
  }

  , update_objective_title : function(request) {
    var objective = request.objective.reify();
    objective.refresh().then(function() {
      objective.attr("title", objective.title + " " + request.attr("id")).save();
    });
    return request;
  }

  , create_response : function(request) {
    return new CMS.Models[request.response_model_class()]({
      pbc_response : "Generic response"
      , request : request.stub()
      , context : request.context
      , owner : request.assignee
    }).save();
  }

});

$(function() {
  $(document.body).ggrc_controllers_pbc_workflows();
});

})(this.can, this.can.$);<commit_msg>Story 58022046: Now using program's extended_related_objectives binding to choose objectives for making PBC requests<commit_after>/*
 * Copyright (C) 2013 Google Inc., authors, and contributors <see AUTHORS file>
 * Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
 * Created By: brad@reciprocitylabs.com
 * Maintained By: brad@reciprocitylabs.com
 */

(function(can, $) {

can.Control("GGRC.Controllers.PbcWorkflows", {

}, {

  "{CMS.Models.Audit} created" : function(model, ev, instance) {
    var refresh_queue
    , that = this
    if(instance instanceof CMS.Models.Audit) {
      if(instance.auto_generate) {
        refresh_queue = new RefreshQueue();

        instance.program.reify().refresh()
        .then(function(program) {
          return program.get_binding("extended_related_objectives").refresh_instances()
        }).then(function(objective_mappings) {
          can.each(objective_mappings, function(objective_mapping) {
            that.create_request(instance, objective_mapping.instance)
            .then(that.proxy("create_response"));
          });
        });
      } else {
        this.create_objective(instance)
        .then(this.proxy("map_objective_to_program", instance.program.reify()))
        .then(function(object_objective) {
          return object_objective.objective.reify();
        })
        .then(that.proxy("create_request", instance))
        .done(that.proxy("update_objective_title"))
        .done(this.proxy("create_response"));
      }
    }
  }

  , create_objective : function(audit) {
    return new CMS.Models.Objective({
      title : "Generic request"
      , context : audit.context
      , owner : audit.owner || CMS.Models.Person.model(GGRC.current_user)
    }).save();
  }

  , map_objective_to_program : function(program, objective) {
    return new CMS.Models.ObjectObjective({
      objectiveable : program.stub()
      , objective : objective.stub()
      , context : objective.context
    }).save();
  }

  , create_request : function(audit, objective) {
    return new CMS.Models.Request({
      assignee : audit.owner || CMS.Models.Person.model(GGRC.current_user)
      , audit : audit.stub()
      , start_at : new Date()
      , end_at : moment().add(30, "days").toDate()
      , objective : objective.stub()
      , request_type : "documentation"
      , description : "Please provide evidence of the type requested to demonstrate this Objective has been met."
      , context : audit.context
    }).save();
  }

  , update_objective_title : function(request) {
    var objective = request.objective.reify();
    objective.refresh().then(function() {
      objective.attr("title", objective.title + " " + request.attr("id")).save();
    });
    return request;
  }

  , create_response : function(request) {
    return new CMS.Models[request.response_model_class()]({
      pbc_response : "Generic response"
      , request : request.stub()
      , context : request.context
      , owner : request.assignee
    }).save();
  }

});

$(function() {
  $(document.body).ggrc_controllers_pbc_workflows();
});

})(this.can, this.can.$);
<|endoftext|>


<commit_before>var pomelo = require('pomelo');
var channelUtil = require('../../util/channelUtil');
var MessageService = require('../../services/messageService');
var WYMusicService = require('../../services/wyMusicService.js');
var ProxyService = require('../../services/proxyService.js');

var studio = function (opts) {
    this.studioId = opts.id;

    this.users = {};
    this.playerList = {};
    this.songs = [];
    this.playingSong = null;
    this.channel = null;
}

module.exports = studio;

studio.prototype.getChannel = function () {
    if (!this.channel) {
        var channelName = channelUtil.getStudioChannelName(this.studioId);
        this.channel = pomelo.app.get('channelService').getChannel(channelName, true);
    }

    return this.channel;
};

studio.prototype.pushByUserName = function (userName, route, msg, cb) {
    var user = this.getUser(userName);
    if (!user) {
        cb(new Error('user[' + userName + '] is not in this studio'));
        return;
    }
    MessageService.pushMessageToUser({ uid: user.name, sid: user.serverId }, route, msg);
    pomelo.app.get('channelService').pushMessageByUids(route, msg, [{ uid: user.userName, sid: user.serverId }], cb);
};

studio.prototype.getPlayerMusicList = function () {
    var playerList = this.playerList;
    var songs = this.songs;
    var list = [];
    for (var n in songs) {
        list.unshift(playerList[songs[n].id]);
    }
    return list;
};

studio.prototype.getMusic = function (id, cb) {
    var music = this.playerList[id];
    if (!music) {
        cb('music not exist! ');
        return;
    }
    return music;
};

studio.prototype.addMusicById = function (id, userName, cb) {
    var self = this;
    if (pomelo.app.get('appConfig').useProxy) {
        ProxyService.getIp(function (err, ip) {
            WYMusicService.getMusicById(id, ip, self.addMusic.bind(self, userName, cb));
        });
    }
    else {
        WYMusicService.getMusicById(id, null, self.addMusic.bind(self, userName, cb));
    }
}

studio.prototype.addMusicByUrl = function (url, userName, cb) {
    var self = this;
    if (pomelo.app.get('appConfig').useProxy) {
        ProxyService.getIp(function (err, ip) {
            WYMusicService.getMusicByUrl(url, ip, self.addMusic.bind(self, userName, cb));
        });
    }
    else {
        WYMusicService.getMusicByUrl(url, null, self.addMusic.bind(self, userName, cb));
    }
};

studio.prototype.addMusic = function (userName, cb, err, music) {
    var self = this;
    if (!!err) {
        cb('get music by url error! ');
        return;
    }
    music.orderer = userName;
    if (!self.playerList[music.id]) {
        self.songs.push(music);
        self.playerList[music.id] = music;
        self.getChannel().pushMessage('onMusicAdd', music, cb);
    }
    else {
        if (music.resourceUrl) {
            if (!self.playerList[music.id].resourceUrl || self.playerList[music.id].resourceUrl == "") {
                self.playerList[music.id] = music;
                self.getChannel().pushMessage('onMusicAdd', music, cb);
            }
            else {
                cb('music already existed! ');
                return;
            }
        } else {
            self.getChannel().pushMessage('onMusicAdd', null, cb);
        }
    }
}

studio.prototype.importMusic = function (list, userName, cb) {
    var self = this;
    var playerList = this.playerList;
    var songs = this.songs;
    for (var n in list) {
        if (!list[n].resourceUrl || list[n].resourceUrl == "") {
            self.addMusicById(list[n].id, userName, cb);
        }
        else {
            if (!playerList[list[n].id]) {
                songs.push(list[n]);
                playerList[list[n].id] = list[n];
                self.getChannel().pushMessage('onMusicAdd', list[n], cb);
            }
            else {
                playerList[list[n].id] = list[n];
            }
        }
    }
}

studio.prototype.playMusic = function (id, userName, cb) {
    var music = this.playerList[id];
    if (!music) {
        cb('music not exist! ');
        return;
    }
    music.playby = userName;
    this.playingSong = music;
    this.getChannel().pushMessage('onMusicPlay', music, cb);
};

studio.prototype.removeMusic = function (id, userName, cb) {
    var songs = this.songs;
    delete this.playerList[id];
    for (var n in songs) {
        if (songs[n].id == id) {
            songs.splice(n, 1);
            break;
        }
    }
    if (this.playingSong) {
        if (this.playingSong.id == id) {
            this.playingSong = null;
        }
    }
    this.getChannel().pushMessage('onMusicRemove', { id: id, deleter: userName }, cb);
};

studio.prototype.getUser = function (userName) {
    return this.users[userName];
};

studio.prototype.addUser = function (user) {
    this.users[user.name] = user;
    this.getChannel().add(user.name, user.serverId);
};

studio.prototype.removeUser = function (userName) {
    var user = this.users[userName];
    this.getChannel().leave(user.name, user.serverId);
    delete this.users[userName];
};

studio.prototype.getPlayingSong = function () {
    return this.playingSong;
}
<commit_msg>update remove music object<commit_after>var pomelo = require('pomelo');
var channelUtil = require('../../util/channelUtil');
var MessageService = require('../../services/messageService');
var WYMusicService = require('../../services/wyMusicService.js');
var ProxyService = require('../../services/proxyService.js');

var studio = function (opts) {
    this.studioId = opts.id;

    this.users = {};
    this.playerList = {};
    this.songs = [];
    this.playingSong = null;
    this.channel = null;
}

module.exports = studio;

studio.prototype.getChannel = function () {
    if (!this.channel) {
        var channelName = channelUtil.getStudioChannelName(this.studioId);
        this.channel = pomelo.app.get('channelService').getChannel(channelName, true);
    }

    return this.channel;
};

studio.prototype.pushByUserName = function (userName, route, msg, cb) {
    var user = this.getUser(userName);
    if (!user) {
        cb(new Error('user[' + userName + '] is not in this studio'));
        return;
    }
    MessageService.pushMessageToUser({ uid: user.name, sid: user.serverId }, route, msg);
    pomelo.app.get('channelService').pushMessageByUids(route, msg, [{ uid: user.userName, sid: user.serverId }], cb);
};

studio.prototype.getPlayerMusicList = function () {
    var playerList = this.playerList;
    var songs = this.songs;
    var list = [];
    for (var n in songs) {
        list.unshift(playerList[songs[n].id]);
    }
    return list;
};

studio.prototype.getMusic = function (id, cb) {
    var music = this.playerList[id];
    if (!music) {
        cb('music not exist! ');
        return;
    }
    return music;
};

studio.prototype.addMusicById = function (id, userName, cb) {
    var self = this;
    if (pomelo.app.get('appConfig').useProxy) {
        ProxyService.getIp(function (err, ip) {
            WYMusicService.getMusicById(id, ip, self.addMusic.bind(self, userName, cb));
        });
    }
    else {
        WYMusicService.getMusicById(id, null, self.addMusic.bind(self, userName, cb));
    }
}

studio.prototype.addMusicByUrl = function (url, userName, cb) {
    var self = this;
    if (pomelo.app.get('appConfig').useProxy) {
        ProxyService.getIp(function (err, ip) {
            WYMusicService.getMusicByUrl(url, ip, self.addMusic.bind(self, userName, cb));
        });
    }
    else {
        WYMusicService.getMusicByUrl(url, null, self.addMusic.bind(self, userName, cb));
    }
};

studio.prototype.addMusic = function (userName, cb, err, music) {
    var self = this;
    if (!!err) {
        cb('get music by url error! ');
        return;
    }
    music.orderer = userName;
    if (!self.playerList[music.id]) {
        self.songs.push(music);
        self.playerList[music.id] = music;
        self.getChannel().pushMessage('onMusicAdd', music, cb);
    }
    else {
        if (music.resourceUrl) {
            if (!self.playerList[music.id].resourceUrl || self.playerList[music.id].resourceUrl == "") {
                self.playerList[music.id] = music;
                self.getChannel().pushMessage('onMusicAdd', music, cb);
            }
            else {
                cb('music already existed! ');
                return;
            }
        } else {
            self.getChannel().pushMessage('onMusicAdd', null, cb);
        }
    }
}

studio.prototype.importMusic = function (list, userName, cb) {
    var self = this;
    var playerList = this.playerList;
    var songs = this.songs;
    for (var n in list) {
        if (!list[n].resourceUrl || list[n].resourceUrl == "") {
            self.addMusicById(list[n].id, userName, cb);
        }
        else {
            if (!playerList[list[n].id]) {
                songs.push(list[n]);
                playerList[list[n].id] = list[n];
                self.getChannel().pushMessage('onMusicAdd', list[n], cb);
            }
            else {
                playerList[list[n].id] = list[n];
            }
        }
    }
}

studio.prototype.playMusic = function (id, userName, cb) {
    var music = this.playerList[id];
    if (!music) {
        cb('music not exist! ');
        return;
    }
    music.playby = userName;
    this.playingSong = music;
    this.getChannel().pushMessage('onMusicPlay', music, cb);
};

studio.prototype.removeMusic = function (id, userName, cb) {
    var songs = this.songs;
    var music = this.playerList[id];
    delete this.playerList[id];
    for (var n in songs) {
        if (songs[n].id == id) {
            songs.splice(n, 1);
            break;
        }
    }
    if (this.playingSong) {
        if (this.playingSong.id == id) {
            this.playingSong = null;
        }
    }
    music.deleter = userName;
    this.getChannel().pushMessage('onMusicRemove', music, cb);
};

studio.prototype.getUser = function (userName) {
    return this.users[userName];
};

studio.prototype.addUser = function (user) {
    this.users[user.name] = user;
    this.getChannel().add(user.name, user.serverId);
};

studio.prototype.removeUser = function (userName) {
    var user = this.users[userName];
    this.getChannel().leave(user.name, user.serverId);
    delete this.users[userName];
};

studio.prototype.getPlayingSong = function () {
    return this.playingSong;
}
<|endoftext|>


<commit_before>// rock paper scissor game mechanics

var user = undefined;
var outcome = undefined;

function playGame(choice){
  var computer = Math.random();
  user = choice;

  if (computer<0.34 && computer>0){
    computer = "quarantine";
  }
  else if(computer<=0.67 && computer>=0.34){
    computer = "cure";
  }
  else if(computer>.67){
    computer = "rescue";
  }

  result = compare(user, computer);
  document.getElementById("result").innerHTML =
    "You: " + user + " Enemy: " + computer + "
" + result;
}

function compare(userSelection, computerSelection){
  if (userSelection == computerSelection){
    outcome = 0;
    if (userSelection == "quarantine"){
      return "You tried to herd some of the infected towards a quarantine zone, " +
             "but your technique was ineffective and they scattered. Try again.";
    }
    else if (userSelection == "cure"){
      return "You tried to inject the antidote into a nearby infected person, " +
             "but your aim was off- try again.";
    }
    else if(userSelection == "rescue"){
      return "You attempted to get to the healthy person, "+
             "but too many infected got in your way, obstructing your path. Try again."
    }
	}
  if (userSelection == "rock"){
    if (computerSelection == "scissors"){
      outcome = 1;
      return "You win! Infection rate down.";
    }
    else {
      outcome = -1;
      return "You lost. Infection rate up.";
		}
  }
  else if (userSelection == "paper"){
    if (computerSelection == "rock"){
      outcome = 1;
      return "You win! Infection rate down." ;
    }
    else if("scissors"){
      outcome = -1;
      return "You lost. Infection rate up." ;
    }
  }
  else if (userSelection == "scissors"){
    if (computerSelection == "rock"){
      outcome = -1;
      return "You lost. Infection rate up.";
    }
    else {
      outcome = 1;
      return "You win! Infection rate down.";
    }
	}
}
<commit_msg>fully modified game text output to fit story<commit_after>// Modified rock paper scissors; Quarantine = Rock, Cure = Paper, Rescue = Scissors

var user = undefined;
var outcome = undefined;

function playGame(choice){
  var computer = Math.random();
  user = choice;

  if (computer<0.34 && computer>0){
    computer = "quarantine";
  }
  else if(computer<=0.67 && computer>=0.34){
    computer = "cure";
  }
  else if(computer>.67){
    computer = "rescue";
  }

  result = compare(user, computer);
  document.getElementById("result").innerHTML =
    "You: " + user + " Enemy: " + computer + "
" + result;
}

function compare(userSelection, computerSelection){
  if (userSelection == computerSelection){
    outcome = 0;
    if (userSelection == "quarantine"){
      return "You tried to herd some of the infected towards a " +
             "quarantine zone, but your technique was ineffective " +
             "and they scattered. Try again.";
    }
    else if (userSelection == "cure"){
      return "You tried to inject the antidote into a nearby infected person," +
             " but your aim was off- try again.";
    }
    else if(userSelection == "rescue"){
      return "You attempted to get to the healthy person, but too many " +
             "infected got in your way, obstructing your path. Try again."
    }
	}
  if (userSelection == "quarantine"){
    if (computerSelection == "rescue"){
      outcome = 1;
      return "You successfully lured some infected into a vehicle " +
             "to transport them to a quarantine zone! Infection rate down.";
    }
    else {
      outcome = -1;
      return "While trying to attract the attention of one of the infected, " +
             "one of your colleagues was dragged away by a different group " +
             "of the infected. Infection rate up.";
		}
  }
  else if (userSelection == "cure"){
    if (computerSelection == "quarantine"){
      outcome = 1;
      return "You successfully injected the antidote into one of the infected!"+
             " Infection rate down.";
    }
    else{
      outcome = -1;
      return "You tried to inject the antidote into one of the infected, " +
             "but it saw you coming. A colleague comes to defend you but " +
             "gets bitten! Infection rate up." ;
    }
  }
  else if (userSelection == "rescue"){
    if (computerSelection == "quarantine"){
      outcome = 1;
      return "You successfully brought a healthy person into the safe zone! " +
             "Infection rate down.";
    }
    else {
      outcome = -1;
      return "You heard a scream as someone was overtaken by a group of " +
             "infected. Infection rate up.";
    }
  }
}
<|endoftext|>


<commit_before>import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { getTranslate } from 'react-localize-redux';
import { LIMIT_ORDER_CONFIG } from "../../services/constants";
import * as limitOrderActions from "../../actions/limitOrderActions";

@connect((store, props) => {
  return {
    translate: getTranslate(store.locale),
    limitOrder: store.limitOrder
  }
})
export default class Pagination extends React.Component {
  range = (start, end) => {
    const arr = [];
    for (let i = start; i < end; i++) {
      arr.push(i)
    }
    return arr;
  }

  generatePageRange = (start, end, activePage) => {
    const component = this.range(start, end).map(item => {
      return <div className={`Pagination__page-item ${item == activePage ? "Pagination__page-item--selected theme__button-2" : ""}`}
        key={item}
        onClick={(e) => this.validatePageIndex(item)}
      >
        {item}

    });

    return component;
  }

  listPage = () => {
    const { filterMode, ordersCount } = this.props.limitOrder;

    const totalPage = filterMode === "client" ? this.props.pages : Math.ceil(ordersCount / LIMIT_ORDER_CONFIG.pageSize);
    const activePage = filterMode === "client" ? this.props.page + 1 : this.props.limitOrder.pageIndex;

    if (totalPage >= 10) {
      if (1 <= activePage && activePage <= 3) {
        const pageRange = this.generatePageRange(1, 4, activePage);

        return (
          <React.Fragment>
            {pageRange}
            ...
            <div className={`Pagination__page-item ${activePage == totalPage ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={totalPage}
              onClick={(e) => this.validatePageIndex(totalPage)}>
              {totalPage}

          </React.Fragment>
        )
      } else if (totalPage - 2 <= activePage && activePage <= totalPage) {
        const pageRange = this.generatePageRange(totalPage - 2, totalPage + 1, activePage);

        return (
          <React.Fragment>
            <div className={`Pagination__page-item ${activePage == 1 ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={1}
              onClick={(e) => this.validatePageIndex(1)}>
              {1}

            ...
            {pageRange}
          </React.Fragment>
        )
      } else {
        const pageRange = this.generatePageRange(activePage - 1, activePage + 2, activePage);

        return (
          <React.Fragment>
            <div className={`Pagination__page-item ${activePage == 1 ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={1}
              onClick={(e) => this.validatePageIndex(1)}>
              {1}

            ...
              {pageRange}
            ...
            <div className={`Pagination__page-item ${activePage == totalPage ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={totalPage}
              onClick={(e) => this.validatePageIndex(totalPage)}>
              {totalPage}

          </React.Fragment>
        )
      }
    } else {
      return this.generatePageRange(1, totalPage + 1, activePage);
    }
  }

  validatePageIndex = (page) => {
    const { filterMode, ordersCount, pageIndex } = this.props.limitOrder;

    if (filterMode === "client") {
      if (page < 1 || page > this.props.pages || page === this.props.page + 1) return;
      this.props.onPageChange(page - 1);
    } else {
      const totalPage = Math.ceil(ordersCount / LIMIT_ORDER_CONFIG.pageSize);
      if (page < 1 || page > totalPage || page === pageIndex) return;
      this.props.dispatch(limitOrderActions.getOrdersByFilter({
        pageIndex: page
      }));
    }
  }

  render() {
    const { totalCount } = this.props;
    const { filterMode, ordersCount } = this.props.limitOrder;

    let pageIndex = 1;
    if (filterMode === "client") {
      pageIndex = this.props.page + 1;
    } else {
      pageIndex = this.props.limitOrder.pageIndex;
    }
    console.log("010101 ",this.props)
    return (
      <div className={`Pagination__container ${this.props.screen}`}>
        {/* Total count */}
        {`Total ${filterMode === "client" ? totalCount : ordersCount} orders`}
        {/* Previous button */}

 this.validatePageIndex(pageIndex - 1)}>

          {/* Page selection */}

            {this.listPage()}

          {/* Next button */}

 this.validatePageIndex(pageIndex + 1)}>

    )
  }
}

Pagination.propTypes = {
  pages: PropTypes.number,
  page: PropTypes.number,
  onPageChange: PropTypes.func,
}<commit_msg>remove unused code<commit_after>import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { getTranslate } from 'react-localize-redux';
import { LIMIT_ORDER_CONFIG } from "../../services/constants";
import * as limitOrderActions from "../../actions/limitOrderActions";

@connect((store, props) => {
  return {
    translate: getTranslate(store.locale),
    limitOrder: store.limitOrder
  }
})
export default class Pagination extends React.Component {
  range = (start, end) => {
    const arr = [];
    for (let i = start; i < end; i++) {
      arr.push(i)
    }
    return arr;
  }

  generatePageRange = (start, end, activePage) => {
    const component = this.range(start, end).map(item => {
      return <div className={`Pagination__page-item ${item == activePage ? "Pagination__page-item--selected theme__button-2" : ""}`}
        key={item}
        onClick={(e) => this.validatePageIndex(item)}
      >
        {item}

    });

    return component;
  }

  listPage = () => {
    const { filterMode, ordersCount } = this.props.limitOrder;

    const totalPage = filterMode === "client" ? this.props.pages : Math.ceil(ordersCount / LIMIT_ORDER_CONFIG.pageSize);
    const activePage = filterMode === "client" ? this.props.page + 1 : this.props.limitOrder.pageIndex;

    if (totalPage >= 10) {
      if (1 <= activePage && activePage <= 3) {
        const pageRange = this.generatePageRange(1, 4, activePage);

        return (
          <React.Fragment>
            {pageRange}
            ...
            <div className={`Pagination__page-item ${activePage == totalPage ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={totalPage}
              onClick={(e) => this.validatePageIndex(totalPage)}>
              {totalPage}

          </React.Fragment>
        )
      } else if (totalPage - 2 <= activePage && activePage <= totalPage) {
        const pageRange = this.generatePageRange(totalPage - 2, totalPage + 1, activePage);

        return (
          <React.Fragment>
            <div className={`Pagination__page-item ${activePage == 1 ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={1}
              onClick={(e) => this.validatePageIndex(1)}>
              {1}

            ...
            {pageRange}
          </React.Fragment>
        )
      } else {
        const pageRange = this.generatePageRange(activePage - 1, activePage + 2, activePage);

        return (
          <React.Fragment>
            <div className={`Pagination__page-item ${activePage == 1 ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={1}
              onClick={(e) => this.validatePageIndex(1)}>
              {1}

            ...
              {pageRange}
            ...
            <div className={`Pagination__page-item ${activePage == totalPage ? "Pagination__page-item--selected theme__button-2" : ""}`}
              key={totalPage}
              onClick={(e) => this.validatePageIndex(totalPage)}>
              {totalPage}

          </React.Fragment>
        )
      }
    } else {
      return this.generatePageRange(1, totalPage + 1, activePage);
    }
  }

  validatePageIndex = (page) => {
    const { filterMode, ordersCount, pageIndex } = this.props.limitOrder;

    if (filterMode === "client") {
      if (page < 1 || page > this.props.pages || page === this.props.page + 1) return;
      this.props.onPageChange(page - 1);
    } else {
      const totalPage = Math.ceil(ordersCount / LIMIT_ORDER_CONFIG.pageSize);
      if (page < 1 || page > totalPage || page === pageIndex) return;
      this.props.dispatch(limitOrderActions.getOrdersByFilter({
        pageIndex: page
      }));
    }
  }

  render() {
    const { totalCount } = this.props;
    const { filterMode, ordersCount } = this.props.limitOrder;

    let pageIndex = 1;
    if (filterMode === "client") {
      pageIndex = this.props.page + 1;
    } else {
      pageIndex = this.props.limitOrder.pageIndex;
    }
    return (
      <div className={`Pagination__container ${this.props.screen}`}>
        {/* Total count */}
        {`Total ${filterMode === "client" ? totalCount : ordersCount} orders`}
        {/* Previous button */}

 this.validatePageIndex(pageIndex - 1)}>

          {/* Page selection */}

            {this.listPage()}

          {/* Next button */}

 this.validatePageIndex(pageIndex + 1)}>

    )
  }
}

Pagination.propTypes = {
  pages: PropTypes.number,
  page: PropTypes.number,
  onPageChange: PropTypes.func,
}<|endoftext|>


<commit_before>'use strict';

(function() {
  angular
      .module('bitcoincom.controllers')
      .controller('buyBitcoinSuccessController', successController);

  function successController(
    $ionicHistory,
    $scope,
    $state,
    ) {
    var vm = this;
    vm.onDone = onDone;
    vm.onMakeAnotherPurchase = onMakeAnotherPurchase;

    $scope.$on('$ionicView.beforeEnter', _onBeforeEnter);

    function _initVariables() {
      vm.purchasedAmount = 0.15384976;
      vm.purchasedCurrency = 'bch';
      vm.walletName = 'Business Wallet';
    }

    function _onBeforeEnter() {
      console.log('_onBeforeEnter()');
      _initVariables();
    }

    function onDone() {
      $ionicHistory.nextViewOptions({
        disableAnimation: true,
        historyRoot: true
      });
      $state.go('tabs.home').then(
        function() {
          $state.go('tabs.buybitcoin');
        }
      );
    }

    function onMakeAnotherPurchase() {
      $ionicHistory.nextViewOptions({
        disableAnimation: true,
        historyRoot: true
      });
      $state.go('tabs.home').then(
        function() {
          $state.go('tabs.buybitcoin').then(
            function () {
              $state.go('tabs.buybitcoin-amount');
            }
          );
        }
      );
    }

  }
})();
<commit_msg>Starting to get default wallet in Success screen.<commit_after>'use strict';

(function() {
  angular
      .module('bitcoincom.controllers')
      .controller('buyBitcoinSuccessController', successController);

  function successController(
    $ionicHistory,
    moonPayService,
    $scope,
    $state,
    ) {
    var vm = this;
    vm.onDone = onDone;
    vm.onMakeAnotherPurchase = onMakeAnotherPurchase;

    $scope.$on('$ionicView.beforeEnter', _onBeforeEnter);

    function _initVariables() {
      vm.purchasedAmount = 0.15384976;
      vm.purchasedCurrency = 'bch';
      vm.walletName = 'Business Wallet';
    }

    function _onBeforeEnter() {
      console.log('_onBeforeEnter()');
      _initVariables();

      moonPayService.getDefaultWalletId().then(
        function onGetDefaultWalletIdSuccess(walletId) {

        },
        function onGetDefaultWalletIdError(err) {

        }
      );
    }

    function onDone() {
      $ionicHistory.nextViewOptions({
        disableAnimation: true,
        historyRoot: true
      });
      $state.go('tabs.home').then(
        function() {
          $state.go('tabs.buybitcoin');
        }
      );
    }

    function onMakeAnotherPurchase() {
      $ionicHistory.nextViewOptions({
        disableAnimation: true,
        historyRoot: true
      });
      $state.go('tabs.home').then(
        function() {
          $state.go('tabs.buybitcoin').then(
            function () {
              $state.go('tabs.buybitcoin-amount');
            }
          );
        }
      );
    }

  }
})();
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {BaseLayer} from '../../../lib';
import earcut from 'earcut';
import flattenDeep from 'lodash.flattendeep';
import normalize from 'geojson-normalize';
import {Model, Program, Geometry, glGetDebugInfo} from 'luma.gl';
const glslify = require('glslify');

const DEFAULT_COLOR = [0, 0, 255];

const defaultGetColor = feature => feature.properties.color;

export default class ChoroplethLayer extends BaseLayer {
  /**
   * @classdesc
   * ChoroplethLayer
   *
   * @class
   * @param {object} props
   * @param {bool} props.drawContour - ? drawContour : drawArea
   * @param {function} props.onChoroplethHovered - provide proerties of the
   * selected choropleth, together with the mouse event when mouse hovered
   * @param {function} props.onChoroplethClicked - provide proerties of the
   * selected choropleth, together with the mouse event when mouse clicked
   */
  constructor(props) {
    super({
      strokeWidth: 1,
      opacity: 1,
      getColor: defaultGetColor,
      ...props
    });
  }

  initializeState() {
    const {gl, attributeManager} = this.state;

    const model = this.getModel(gl);
    model.userData.strokeWidth = this.props.strokeWidth;

    attributeManager.addDynamic({
      // Primtive attributes
      indices: {size: 1, update: this.calculateIndices, isIndexed: true},
      positions: {size: 3, update: this.calculatePositions},
      colors: {size: 3, update: this.calculateColors},
      // Instanced attributes
      pickingColors:
        {size: 3, update: this.calculatePickingColors, noAlloc: true}
    });

    const indexType = gl.getExtension('OES_element_index_uint') ?
      Uint32Array : Uint16Array;

    this.setUniforms({opacity: this.props.opacity});
    this.setState({
      numInstances: 0,
      indexType,
      model
    });

    this.extractChoropleths();
  }

  willReceiveProps(oldProps, newProps) {
    super.willReceiveProps(oldProps, newProps);

    const {dataChanged, attributeManager} = this.state;
    if (dataChanged) {
      this.extractChoropleths();

      attributeManager.invalidateAll();
    }

    if (oldProps.opacity !== newProps.opacity) {
      this.setUniforms({opacity: newProps.opacity});
    }
    this.state.model.userData.strokeWidth = newProps.strokeWidth;
  }

  getModel(gl) {
    let IntelDef = '';

    if (glGetDebugInfo(gl) !== null) {
      if (glGetDebugInfo(gl).vendor.match(/Intel/)) {
        IntelDef += '#define INTEL_WORKAROUND 1\n';
      }
    }

    return new Model({
      program: new Program(gl, {
        vs: IntelDef + glslify('./choropleth-layer-vertex.glsl'),
        fs: glslify('./choropleth-layer-fragment.glsl'),
        id: 'choropleth'
      }),
      geometry: new Geometry({
        id: this.props.id,
        drawMode: this.props.drawContour ? 'LINES' : 'TRIANGLES'
      }),
      vertexCount: 0,
      isIndexed: true,
      onBeforeRender() {
        this.userData.oldStrokeWidth = gl.getParameter(gl.LINE_WIDTH);
        this.program.gl.lineWidth(this.userData.strokeWidth || 1);
      },
      onAfterRender() {
        this.program.gl.lineWidth(this.userData.oldStrokeWidth || 1);
      }
    });
  }

  extractChoropleths() {
    const {data} = this.props;
    const normalizedGeojson = normalize(data);

    this.state.choropleths = [];

    normalizedGeojson.features.map((feature, featureIndex) => {
      const {properties, geometry} = feature;
      const {coordinates, type} = geometry;
      if (type === 'MultiPolygon') {
        const choropleths = coordinates.map(coords => ({
          coordinates: coords,
          featureIndex
        }));
        this.state.choropleths.push(...choropleths);
      } else if (type === 'Polygon') {
        this.state.choropleths.push({coordinates, featureIndex});
      }
    });

    this.state.groupedVertices = this.state.choropleths.map(
      choropleth => {
        return choropleth.coordinates.map(
          polygon => polygon.map(
            coordinate => [coordinate[0], coordinate[1], 0]
          )
        );
      }
    );

  }

  calculateContourIndices(vertices) {
    let offset = 0;

    return vertices.reduce((acc, polygon) => {
      const numVertices = polygon.length;

      // use vertex pairs for gl.LINES => [0, 1, 1, 2, 2, ..., n-1, n-1, 0]
      let indices = [...acc, offset];
      for (let i = 1; i < numVertices - 1; i++) {
        indices.push(i + offset, i + offset);
      }
      indices.push(offset);

      offset += numVertices;
      return indices;
    }, []);
  }

  calculateSurfaceIndices(vertices) {
    let holes = null;

    if (vertices.length > 1) {
      holes = vertices.reduce(
        (acc, polygon) => [...acc, acc[acc.length - 1] + polygon.length],
        [0]
      ).slice(1, vertices.length);
    }

    return earcut(flattenDeep(vertices), holes, 3);
  }

  calculateIndices(attribute) {
    // adjust index offset for multiple choropleths
    const offsets = this.state.groupedVertices.reduce(
      (acc, vertices) => [...acc, acc[acc.length - 1] +
        vertices.reduce((count, polygon) => count + polygon.length, 0)],
      [0]
    );
    const {indexType} = this.state;
    if(indexType === Uint16Array && offsets[offsets.length - 1] > 65535) {
      throw new Error('Vertex count exceeds browser\'s limit');
    }

    const indices = this.state.groupedVertices.map(
      (vertices, choroplethIndex) => this.props.drawContour ?
        // 1. get sequentially ordered indices of each choropleth contour
        // 2. offset them by the number of indices in previous choropleths
        this.calculateContourIndices(vertices).map(
          index => index + offsets[choroplethIndex]
        ) :
        // 1. get triangulated indices for the internal areas
        // 2. offset them by the number of indices in previous choropleths
        this.calculateSurfaceIndices(vertices).map(
          index => index + offsets[choroplethIndex]
        )
    );

    attribute.value = new indexType(flattenDeep(indices));
    attribute.target = this.state.gl.ELEMENT_ARRAY_BUFFER;
    this.state.model.setVertexCount(attribute.value.length / attribute.size);
  }

  calculatePositions(attribute) {
    const vertices = flattenDeep(this.state.groupedVertices);
    attribute.value = new Float32Array(vertices);
  }

  calculateColors(attribute) {
    const {data: {features}, getColor} = this.props;

    const colors = this.state.groupedVertices.map(
      (vertices, choroplethIndex) => {
        const choropleth = this.state.choropleths[choroplethIndex];
        const feature = features[choropleth.featureIndex];
        const color = getColor(feature) || DEFAULT_COLOR;
        return vertices.map(polygon => polygon.map(vertex => color));
      }
    );

    attribute.value = new Float32Array(flattenDeep(nestedColors));
  }

  // Override the default picking colors calculation
  calculatePickingColors(attribute) {

    const colors = this.state.groupedVertices.map(
      (vertices, choroplethIndex) => {
        const choropleth = this.state.choropleths[choroplethIndex];
        const {featureIndex} = choropleth;
        const color = this.props.drawContour ? [-1, -1, -1] : [
          (featureIndex + 1) % 256,
          Math.floor((featureIndex + 1) / 256) % 256,
          Math.floor((featureIndex + 1) / 256 / 256) % 256];
        return vertices.map(polygon => polygon.map(vertex => color));
      }
    );

    attribute.value = new Float32Array(flattenDeep(colors));
  }

  onHover(info) {
    const {color} = info;
    const index = color[0] + color[1] * 256 + color[2] * 256 * 256 - 1;
    const {data} = this.props;
    const feature = data.features[index];
    this.props.onHover({...info, feature});
  }

  onClick(info) {
    const {color} = info;
    const index = color[0] + color[1] * 256 + color[2] * 256 * 256 - 1;
    const {data} = this.props;
    const feature = data.features[index];
    this.props.onClick({...info, feature});
  }

}
<commit_msg>lint fix<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {BaseLayer} from '../../../lib';
import earcut from 'earcut';
import flattenDeep from 'lodash.flattendeep';
import normalize from 'geojson-normalize';
import {Model, Program, Geometry, glGetDebugInfo} from 'luma.gl';
const glslify = require('glslify');

const DEFAULT_COLOR = [0, 0, 255];

const defaultGetColor = feature => feature.properties.color;

export default class ChoroplethLayer extends BaseLayer {
  /**
   * @classdesc
   * ChoroplethLayer
   *
   * @class
   * @param {object} props
   * @param {bool} props.drawContour - ? drawContour : drawArea
   * @param {function} props.onChoroplethHovered - provide proerties of the
   * selected choropleth, together with the mouse event when mouse hovered
   * @param {function} props.onChoroplethClicked - provide proerties of the
   * selected choropleth, together with the mouse event when mouse clicked
   */
  constructor(props) {
    super({
      strokeWidth: 1,
      opacity: 1,
      getColor: defaultGetColor,
      ...props
    });
  }

  initializeState() {
    const {gl, attributeManager} = this.state;

    const model = this.getModel(gl);
    model.userData.strokeWidth = this.props.strokeWidth;

    attributeManager.addDynamic({
      // Primtive attributes
      indices: {size: 1, update: this.calculateIndices, isIndexed: true},
      positions: {size: 3, update: this.calculatePositions},
      colors: {size: 3, update: this.calculateColors},
      // Instanced attributes
      pickingColors:
        {size: 3, update: this.calculatePickingColors, noAlloc: true}
    });

    const indexType = gl.getExtension('OES_element_index_uint') ?
      Uint32Array : Uint16Array;

    this.setUniforms({opacity: this.props.opacity});
    this.setState({
      numInstances: 0,
      indexType,
      model
    });

    this.extractChoropleths();
  }

  willReceiveProps(oldProps, newProps) {
    super.willReceiveProps(oldProps, newProps);

    const {dataChanged, attributeManager} = this.state;
    if (dataChanged) {
      this.extractChoropleths();

      attributeManager.invalidateAll();
    }

    if (oldProps.opacity !== newProps.opacity) {
      this.setUniforms({opacity: newProps.opacity});
    }
    this.state.model.userData.strokeWidth = newProps.strokeWidth;
  }

  getModel(gl) {
    let IntelDef = '';

    if (glGetDebugInfo(gl) !== null) {
      if (glGetDebugInfo(gl).vendor.match(/Intel/)) {
        IntelDef += '#define INTEL_WORKAROUND 1\n';
      }
    }

    return new Model({
      program: new Program(gl, {
        vs: IntelDef + glslify('./choropleth-layer-vertex.glsl'),
        fs: glslify('./choropleth-layer-fragment.glsl'),
        id: 'choropleth'
      }),
      geometry: new Geometry({
        id: this.props.id,
        drawMode: this.props.drawContour ? 'LINES' : 'TRIANGLES'
      }),
      vertexCount: 0,
      isIndexed: true,
      onBeforeRender() {
        this.userData.oldStrokeWidth = gl.getParameter(gl.LINE_WIDTH);
        this.program.gl.lineWidth(this.userData.strokeWidth || 1);
      },
      onAfterRender() {
        this.program.gl.lineWidth(this.userData.oldStrokeWidth || 1);
      }
    });
  }

  extractChoropleths() {
    const {data} = this.props;
    const normalizedGeojson = normalize(data);

    this.state.choropleths = [];

    normalizedGeojson.features.map((feature, featureIndex) => {
      const {properties, geometry} = feature;
      const {coordinates, type} = geometry;
      if (type === 'MultiPolygon') {
        const choropleths = coordinates.map(coords => ({
          coordinates: coords,
          featureIndex
        }));
        this.state.choropleths.push(...choropleths);
      } else if (type === 'Polygon') {
        this.state.choropleths.push({coordinates, featureIndex});
      }
    });

    this.state.groupedVertices = this.state.choropleths.map(
      choropleth => {
        return choropleth.coordinates.map(
          polygon => polygon.map(
            coordinate => [coordinate[0], coordinate[1], 0]
          )
        );
      }
    );

  }

  calculateContourIndices(vertices) {
    let offset = 0;

    return vertices.reduce((acc, polygon) => {
      const numVertices = polygon.length;

      // use vertex pairs for gl.LINES => [0, 1, 1, 2, 2, ..., n-1, n-1, 0]
      const indices = [...acc, offset];
      for (let i = 1; i < numVertices - 1; i++) {
        indices.push(i + offset, i + offset);
      }
      indices.push(offset);

      offset += numVertices;
      return indices;
    }, []);
  }

  calculateSurfaceIndices(vertices) {
    let holes = null;

    if (vertices.length > 1) {
      holes = vertices.reduce(
        (acc, polygon) => [...acc, acc[acc.length - 1] + polygon.length],
        [0]
      ).slice(1, vertices.length);
    }

    return earcut(flattenDeep(vertices), holes, 3);
  }

  calculateIndices(attribute) {
    // adjust index offset for multiple choropleths
    const offsets = this.state.groupedVertices.reduce(
      (acc, vertices) => [...acc, acc[acc.length - 1] +
        vertices.reduce((count, polygon) => count + polygon.length, 0)],
      [0]
    );
    const {indexType} = this.state;
    if(indexType === Uint16Array && offsets[offsets.length - 1] > 65535) {
      throw new Error('Vertex count exceeds browser\'s limit');
    }

    const indices = this.state.groupedVertices.map(
      (vertices, choroplethIndex) => this.props.drawContour ?
        // 1. get sequentially ordered indices of each choropleth contour
        // 2. offset them by the number of indices in previous choropleths
        this.calculateContourIndices(vertices).map(
          index => index + offsets[choroplethIndex]
        ) :
        // 1. get triangulated indices for the internal areas
        // 2. offset them by the number of indices in previous choropleths
        this.calculateSurfaceIndices(vertices).map(
          index => index + offsets[choroplethIndex]
        )
    );

    attribute.value = new indexType(flattenDeep(indices));
    attribute.target = this.state.gl.ELEMENT_ARRAY_BUFFER;
    this.state.model.setVertexCount(attribute.value.length / attribute.size);
  }

  calculatePositions(attribute) {
    const vertices = flattenDeep(this.state.groupedVertices);
    attribute.value = new Float32Array(vertices);
  }

  calculateColors(attribute) {
    const {data: {features}, getColor} = this.props;

    const colors = this.state.groupedVertices.map(
      (vertices, choroplethIndex) => {
        const choropleth = this.state.choropleths[choroplethIndex];
        const feature = features[choropleth.featureIndex];
        const color = getColor(feature) || DEFAULT_COLOR;
        return vertices.map(polygon => polygon.map(vertex => color));
      }
    );

    attribute.value = new Float32Array(flattenDeep(nestedColors));
  }

  // Override the default picking colors calculation
  calculatePickingColors(attribute) {

    const colors = this.state.groupedVertices.map(
      (vertices, choroplethIndex) => {
        const choropleth = this.state.choropleths[choroplethIndex];
        const {featureIndex} = choropleth;
        const color = this.props.drawContour ? [-1, -1, -1] : [
          (featureIndex + 1) % 256,
          Math.floor((featureIndex + 1) / 256) % 256,
          Math.floor((featureIndex + 1) / 256 / 256) % 256];
        return vertices.map(polygon => polygon.map(vertex => color));
      }
    );

    attribute.value = new Float32Array(flattenDeep(colors));
  }

  onHover(info) {
    const {color} = info;
    const index = color[0] + color[1] * 256 + color[2] * 256 * 256 - 1;
    const {data} = this.props;
    const feature = data.features[index];
    this.props.onHover({...info, feature});
  }

  onClick(info) {
    const {color} = info;
    const index = color[0] + color[1] * 256 + color[2] * 256 * 256 - 1;
    const {data} = this.props;
    const feature = data.features[index];
    this.props.onClick({...info, feature});
  }

}
<|endoftext|>


<commit_before>// NG2
import { Component, ElementRef, EventEmitter, Input, Output } from '@angular/core';
// APP
import { OutsideClick } from './../../utils/outside-click/OutsideClick';
import { KeyCodes } from './../../utils/key-codes/KeyCodes';
import { Helpers } from './../../utils/Helpers';

@Component({
    selector: 'novo-category-dropdown',
    template: `
        <ng-content select="button"></ng-content>

                <input type="text" [placeholder]="search.placeholder || 'SEARCH'" [value]="_query" (input)="queryCategories($event.target.value)"/>

            <novo-nav theme="white" [outlet]="novoCategoryDropdownOutlet" direction="vertical">
                <novo-tab *ngFor="let category of _categories">
                    {{ category }} ({{ _categoryMap[category].length }})
                </novo-tab>
            </novo-nav>
            <novo-nav-outlet #novoCategoryDropdownOutlet>
                <novo-nav-content *ngFor="let category of _categories">
                    <novo-list direction="vertical">
                        <novo-list-item *ngFor="let item of _categoryMap[category]" (click)="select($event, item)">
                            <item-content>{{ item.label }}</item-content>
                            <item-end class="novo-category-dropdown-hover" *ngIf="item.hoverText && !item.selected">{{ item.hoverText }}</item-end>
                            <item-end class="novo-category-dropdown-hover" *ngIf="item.hoverIcon && !item.selected"></item-end>
                            <item-end *ngIf="item.selected"></item-end>
                        </novo-list-item>
                        <novo-list-item *ngIf="_categoryMap[category].length === 0 && search" class="novo-category-dropdown-empty-item">
                            <item-content>{{ search.emptyMessage || 'There are no items.' }}</item-content>
                        </novo-list-item>
                    </novo-list>
                </novo-nav-content>
            </novo-nav-outlet>

                {{ link.label }}

    `,
    host: {
        '(keydown)': 'onKeyDown($event)',
        '[class.active]': 'active'
    }
})
export class NovoCategoryDropdownElement extends OutsideClick {
    _query:string = '';
    _categoryMap:any = {};
    _categories:string[] = [];
    // Boolean to keep the selection persist when closing the dropdown
    @Input() persistSelection:boolean = false;
    // Boolean to close the dropdown on selection
    @Input() closeOnSelect:boolean = false;
    // Search Config
    // {
    //   placeholder: 'STRING' // defaults to "SEARCH" - placeholder for search input
    //   emptyMessage: 'STRING' // defaults to "There are no items." - empty message when there are no items in the category
    //   debounce: 'NUMBER (in MS)' // defaults to 300ms - debounce time for the search
    //   compare: 'FUNCTION' // default to simple indexOf - compare function for category search, should accept (query, item) and return true/false
    // }
    @Input() search:any;
    // Footer config
    // {
    //   align: 'STRING' // defaults to "right" - alignment of the links
    //   links: 'ARRAY' // array of links to go into the footer, be away of spacing - { label, callback } for the object inside
    // }
    @Input() footer:any;
    // Event that is emitted whenever an item is selected
    @Output('itemSelected') _select:EventEmitter = new EventEmitter();

    @Input()
    set categories(categories:any) {
        this._masterCategoryMap = Object.assign({}, categories);
        this._categoryMap = Object.assign({}, categories);
        this._categories = Object.keys(categories);
    }

    constructor(element:ElementRef) {
        super(element);
        this.clickHandler = this.toggleActive.bind(this);
    }

    ngOnInit() {
        let button = this.element.nativeElement.querySelector('button');
        button.addEventListener('click', this.clickHandler);
    }

    ngOnDestroy() {
        let button = this.element.nativeElement.querySelector('button');
        button.removeEventListener('click', this.clickHandler);
    }

    onKeyDown(event) {
        if (this.active && (event.keyCode === KeyCodes.ESC || event.keyCode === KeyCodes.ENTER)) {
            this.toggleActive();
        }
    }

    clearSelection() {
        this._categories.forEach(category => {
            this._categoryMap[category].forEach(item => {
                item.selected = false;
            });
        });
    }

    select(event, item) {
        Helpers.swallowEvent(event);
        // If we persist the selection, clear and show a check
        if (this.persistSelection) {
            this.clearSelection();
            item.selected = true;
        }
        // Emit the item
        this._select.emit(item);
        // Close, if input is set
        if (this.closeOnSelect) {
            this.toggleActive();
        }
    }

    clearQuery(event) {
        Helpers.swallowEvent(event);
        this._query = '';
        // Reset the categories
        this._categories.forEach(category => {
            this._categoryMap[category] = this._masterCategoryMap[category];
        });
    }

    queryCategories(query) {
        // Save the query
        this._query = query;
        // Check timeout
        if (this._queryTimeout) {
            clearTimeout(this._queryTimeout);
        }
        // Store a timeout, to debounce user input
        this._queryTimeout = setTimeout(() => {
            this._categories.forEach(category => {
                if (this.search.compare) {
                    this._categoryMap[category] = this._masterCategoryMap[category].filter(item => this.search.compare(query, item));
                } else {
                    this._categoryMap[category] = this._masterCategoryMap[category].filter(item => ~item.label.toLowerCase().indexOf(query.toLowerCase()));
                }
            });
        }, this.search.debounce || 300);
    }
}
<commit_msg>chore(categorydropdown): Adding data-automation-ids<commit_after>// NG2
import { Component, ElementRef, EventEmitter, Input, Output } from '@angular/core';
// APP
import { OutsideClick } from './../../utils/outside-click/OutsideClick';
import { KeyCodes } from './../../utils/key-codes/KeyCodes';
import { Helpers } from './../../utils/Helpers';

@Component({
    selector: 'novo-category-dropdown',
    template: `
        <ng-content select="button"></ng-content>

                <input type="text" [placeholder]="search.placeholder || 'SEARCH'" [value]="_query" (input)="queryCategories($event.target.value)"/>

            <novo-nav theme="white" [outlet]="novoCategoryDropdownOutlet" direction="vertical">
                <novo-tab *ngFor="let category of _categories" [attr.data-automation-id]="category">
                    {{ category }} ({{ _categoryMap[category].length }})
                </novo-tab>
            </novo-nav>
            <novo-nav-outlet #novoCategoryDropdownOutlet>
                <novo-nav-content *ngFor="let category of _categories">
                    <novo-list direction="vertical">
                        <novo-list-item *ngFor="let item of _categoryMap[category]" (click)="select($event, item)" [attr.data-automation-id]="item.label">
                            <item-content>{{ item.label }}</item-content>
                            <item-end class="novo-category-dropdown-hover" *ngIf="item.hoverText && !item.selected">{{ item.hoverText }}</item-end>
                            <item-end class="novo-category-dropdown-hover" *ngIf="item.hoverIcon && !item.selected"></item-end>
                            <item-end *ngIf="item.selected"></item-end>
                        </novo-list-item>
                        <novo-list-item *ngIf="_categoryMap[category].length === 0 && search" class="novo-category-dropdown-empty-item">
                            <item-content>{{ search.emptyMessage || 'There are no items.' }}</item-content>
                        </novo-list-item>
                    </novo-list>
                </novo-nav-content>
            </novo-nav-outlet>

                {{ link.label }}

    `,
    host: {
        '(keydown)': 'onKeyDown($event)',
        '[class.active]': 'active'
    }
})
export class NovoCategoryDropdownElement extends OutsideClick {
    _query:string = '';
    _categoryMap:any = {};
    _categories:string[] = [];
    // Boolean to keep the selection persist when closing the dropdown
    @Input() persistSelection:boolean = false;
    // Boolean to close the dropdown on selection
    @Input() closeOnSelect:boolean = false;
    // Search Config
    // {
    //   placeholder: 'STRING' // defaults to "SEARCH" - placeholder for search input
    //   emptyMessage: 'STRING' // defaults to "There are no items." - empty message when there are no items in the category
    //   debounce: 'NUMBER (in MS)' // defaults to 300ms - debounce time for the search
    //   compare: 'FUNCTION' // default to simple indexOf - compare function for category search, should accept (query, item) and return true/false
    // }
    @Input() search:any;
    // Footer config
    // {
    //   align: 'STRING' // defaults to "right" - alignment of the links
    //   links: 'ARRAY' // array of links to go into the footer, be away of spacing - { label, callback } for the object inside
    // }
    @Input() footer:any;
    // Event that is emitted whenever an item is selected
    @Output('itemSelected') _select:EventEmitter = new EventEmitter();

    @Input()
    set categories(categories:any) {
        this._masterCategoryMap = Object.assign({}, categories);
        this._categoryMap = Object.assign({}, categories);
        this._categories = Object.keys(categories);
    }

    constructor(element:ElementRef) {
        super(element);
        this.clickHandler = this.toggleActive.bind(this);
    }

    ngOnInit() {
        let button = this.element.nativeElement.querySelector('button');
        button.addEventListener('click', this.clickHandler);
    }

    ngOnDestroy() {
        let button = this.element.nativeElement.querySelector('button');
        button.removeEventListener('click', this.clickHandler);
    }

    onKeyDown(event) {
        if (this.active && (event.keyCode === KeyCodes.ESC || event.keyCode === KeyCodes.ENTER)) {
            this.toggleActive();
        }
    }

    clearSelection() {
        this._categories.forEach(category => {
            this._categoryMap[category].forEach(item => {
                item.selected = false;
            });
        });
    }

    select(event, item) {
        Helpers.swallowEvent(event);
        // If we persist the selection, clear and show a check
        if (this.persistSelection) {
            this.clearSelection();
            item.selected = true;
        }
        // Emit the item
        this._select.emit(item);
        // Close, if input is set
        if (this.closeOnSelect) {
            this.toggleActive();
        }
    }

    clearQuery(event) {
        Helpers.swallowEvent(event);
        this._query = '';
        // Reset the categories
        this._categories.forEach(category => {
            this._categoryMap[category] = this._masterCategoryMap[category];
        });
    }

    queryCategories(query) {
        // Save the query
        this._query = query;
        // Check timeout
        if (this._queryTimeout) {
            clearTimeout(this._queryTimeout);
        }
        // Store a timeout, to debounce user input
        this._queryTimeout = setTimeout(() => {
            this._categories.forEach(category => {
                if (this.search.compare) {
                    this._categoryMap[category] = this._masterCategoryMap[category].filter(item => this.search.compare(query, item));
                } else {
                    this._categoryMap[category] = this._masterCategoryMap[category].filter(item => ~item.label.toLowerCase().indexOf(query.toLowerCase()));
                }
            });
        }, this.search.debounce || 300);
    }
}
<|endoftext|>