<commit_before>import store from 'store';
import web3Utils from 'web3-utils';
import {
  networkSymbols,
  BASE_CURRENCY,
  swapNotificationStatuses
} from '../partnersConfig';
import { utils } from '../helpers';
import {
  getRates,
  openOrder,
  getStatus,
  getExitRates,
  loginWithPhone,
  sendReceivedSmsCode,
  buildCyptoToFiatOrderData,
  getCyptoToFiatOrderDetails,
  getStatusFiat
} from './bity-calls';
import {
  bityStatuses,
  BityCurrencies,
  bityFiatCurrencies,
  PROVIDER_NAME,
  TIME_SWAP_VALID,
  BITY_MAX,
  BITY_MIN,
  BITY_DECIMALS,
  LOCAL_STORAGE_KEY
} from './config';

function disabledPairing(currencyList, symbol, invalid, side) {
  if (currencyList[symbol]) {
    if (side === 'from') {
      if (currencyList[symbol].invalidFrom) {
        return !currencyList[symbol].invalidFrom.includes(invalid);
      }
      return true;
    } else if (side === 'to') {
      if (currencyList[symbol].invalidTo) {
        return currencyList[symbol].invalidTo.includes(invalid);
      }
      return true;
    }
    return true;
  }
}

export default class BitySwap {
  constructor(props = {}) {
    this.name = BitySwap.getName();
    this.network = props.network || networkSymbols.ETH;
    this.decimals = BITY_DECIMALS;
    this.hasRates = 0;
    this.validStatus = ['RCVE', 'FILL', 'CONF', 'EXEC'];
    this.invalidStatus = ['CANC'];
    this.mainPairs = ['REP', 'ETH'];
    this.minValue = BITY_MIN;
    this.maxValue = BITY_MAX;
    this.fiatCurrencies = Object.keys(bityFiatCurrencies);
    this.rates = new Map();

    this.phoneSha = undefined;
    this.userDetails = this.loadStoredCredentials();
    // this.phoneToken =
    //   'OPWip5M2mbpidCtoYZZPJ1zxrDnOinWHok9TLyVfE1mQDOIp0dU6orrP+Gxm3Qrm';
    this.retrieveRates();
  }

  static getName() {
    return PROVIDER_NAME;
  }

  static isDex() {
    return false;
  }

  get phoneToken() {
    return this.userDetails[this.phoneSha].phone_token;
  }

  get isValidNetwork() {
    return this.network === networkSymbols.ETH;
  }

  get currencies() {
    if (this.isValidNetwork) {
      return BityCurrencies;
    }
    return {};
  }

  async retrieveRates() {
    try {
      if (!this.isValidNetwork) return;
      const exitRates = await getExitRates();
      const exitData = exitRates.pairs;
      const rates = await getRates();
      const data = rates.objects;
      exitData.forEach(entry => {
        if (entry.enabled) {
          data.forEach(rateEntry => {
            if (rateEntry.pair === entry.input + entry.output) {
              this.rates.set(
                `${entry.input}/${entry.output}`,
                parseFloat(rateEntry.rate_we_buy)
              );
            }
          });
        }
      });

      data.forEach(pair => {
        if (~this.mainPairs.indexOf(pair.pair.substring(3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_sell)
            );
          }
        } else if (~this.mainPairs.indexOf(pair.pair.substring(0, 3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_buy)
            );
          }
        }
      });
      this.hasRates = data.length > 0 ? this.hasRates + 1 : 0;
    } catch (e) {
      throw e;
    }
  }

  _getRate(fromToken, toToken) {
    if (this.rates.has(`${fromToken}/${toToken}`)) {
      return this.rates.get(`${fromToken}/${toToken}`);
    }
    return -1;
  }

  async getRate(fromCurrency, toCurrency) {
    const rate = this._getRate(fromCurrency, toCurrency);
    return {
      fromCurrency,
      toCurrency,
      provider: this.name,
      rate: rate,
      minValue: this.minValue,
      maxValue: this.getBtcEquivalent(fromCurrency)
    };
  }

  validSwap(fromCurrency, toCurrency) {
    if (this.isValidNetwork) {
      return this.rates.has(`${fromCurrency}/${toCurrency}`);
    }
    return false;
  }

  minCheck(fromCurrency, fromValue, toCurrency, toValue) {
    return (
      this.validityCheck(fromCurrency, fromValue, toCurrency, toValue) !==
      'lessThanMin'
    );
  }

  maxCheck(fromCurrency, fromValue, toCurrency, toValue) {
    return (
      this.validityCheck(fromCurrency, fromValue, toCurrency, toValue) !==
      'greaterThanMax'
    );
  }

  getBtcEquivalent(currency) {
    if (currency === 'BTC') {
      return this.maxValue;
    }
    const btcRate = this._getRate(currency, 'BTC');
    return this.maxValue / btcRate;
  }

  validityCheck(fromCurrency, fromValue, toCurrency, toValue) {
    if (toValue < this.minValue || fromValue < this.minValue)
      return 'lessThanMin';
    else if (
      (toCurrency == 'BTC' && toValue > this.maxValue) ||
      (fromCurrency == 'BTC' && fromValue > this.maxValue)
    )
      return 'greaterThanMax';
    else if (
      (toCurrency == 'ETH' &&
        toValue * this._getRate('ETH', 'BTC') > this.maxValue) ||
      (fromCurrency == 'ETH' &&
        fromValue * this._getRate('ETH', 'BTC') > this.maxValue)
    )
      return 'greaterThanMax';
    else if (
      (toCurrency == 'REP' &&
        toValue * this._getRate('REP', 'BTC') > this.maxValue) ||
      (fromCurrency == 'REP' &&
        fromValue * this._getRate('REP', 'BTC') > this.maxValue)
    )
      return 'greaterThanMax';
    return 'noErrors';
  }

  setNetwork(network) {
    this.network = network;
  }

  getInitialCurrencyEntries(collectMapFrom, collectMapTo) {
    for (const prop in this.currencies) {
      if (this.currencies[prop]) {
        collectMapTo.set(prop, {
          symbol: prop,
          name: this.currencies[prop].name
        });
        if (!this.fiatCurrencies.includes(prop)) {
          collectMapFrom.set(prop, {
            symbol: prop,
            name: this.currencies[prop].name
          });
        }
      }
    }
  }

  getUpdatedFromCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (disabledPairing(this.currencies, value.symbol, prop, 'from')) {
          if (this.currencies[prop] && !this.fiatCurrencies.includes(prop)) {
            collectMap.set(prop, {
              symbol: prop,
              name: this.currencies[prop].name
            });
          }
        }
      }
    }
  }

  getUpdatedToCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (this.currencies[prop])
          collectMap.set(prop, {
            symbol: prop,
            name: this.currencies[prop].name
          });
      }
    }
  }

  checkIfExit(swapDetails) {
    return this.fiatCurrencies.includes(swapDetails.toCurrency);
  }

  async startSwap(swapDetails) {
    if (this.checkIfExit(swapDetails) && !swapDetails.bypass) {
      if (swapDetails.exitFromAddress === '') return {};
      swapDetails.dataForInitialization = false;
      swapDetails.isExitToFiat = true;
      return swapDetails;
      // throw Error('Exit to Fiat not yet implemented');
    } else if (this.checkIfExit(swapDetails) && swapDetails.bypass) {
      const preOrder = await this.buildExitOrder(swapDetails);
      if (preOrder.created) {
        swapDetails.dataForInitialization = await getCyptoToFiatOrderDetails({
          pair: swapDetails.fromCurrency + swapDetails.toCurrency,
          detailsUrl: preOrder.status_address,
          phoneToken: this.phoneToken
        });
        if (swapDetails.dataForInitialization) {
          swapDetails.providerReceives =
            swapDetails.dataForInitialization.input.amount;
          swapDetails.providerSends =
            swapDetails.dataForInitialization.output.amount;
          swapDetails.parsed = BitySwap.parseExitOrder(
            swapDetails.dataForInitialization
          );
          swapDetails.providerAddress =
            swapDetails.dataForInitialization.payment_address;
          swapDetails.isDex = BitySwap.isDex();
        }
      }
    } else if (!this.checkIfExit(swapDetails)) {
      swapDetails.dataForInitialization = await this.buildOrder(
        swapDetails.fromCurrency === BASE_CURRENCY,
        swapDetails
      );
      swapDetails.providerReceives =
        swapDetails.dataForInitialization.input.amount;
      swapDetails.providerSends =
        swapDetails.dataForInitialization.output.amount;
      swapDetails.parsed = BitySwap.parseOrder(
        swapDetails.dataForInitialization
      );
      swapDetails.providerAddress =
        swapDetails.dataForInitialization.payment_address;
      swapDetails.isDex = BitySwap.isDex();
    }

    return swapDetails;
  }

  async buildOrder({
    fromCurrency,
    toCurrency,
    fromValue,
    toValue,
    toAddress
  }) {
    if (
      this.minCheck(fromCurrency, fromValue, toCurrency, toValue) &&
      this.maxCheck(fromCurrency, fromValue, toCurrency, toValue)
    ) {
      const order = {
        amount: fromValue,
        mode: 0,
        pair: fromCurrency + toCurrency,
        destAddress: toAddress
      };

      return await openOrder(order);
    }
  }

  loadStoredCredentials() {
    const userDetails = store.get(LOCAL_STORAGE_KEY);
    if (userDetails !== null && userDetails !== undefined) {
      return userDetails;
    }
    return {};
  }

  setStoredCredentials(phoneSha, phoneToken, verified = false) {
    let userDetails = store.get(LOCAL_STORAGE_KEY);
    if (userDetails === null || userDetails === undefined) {
      userDetails = {};
    }
    userDetails[phoneSha] = {
      phone_token: phoneToken,
      verified: verified
    };
    store.set(LOCAL_STORAGE_KEY, userDetails);
    this.userDetails = userDetails;
    return userDetails;
  }

  async registerUser(initData) {
    this.phoneSha = web3Utils.sha3(initData.phoneNumber);
    if (this.userDetails[this.phoneSha] === undefined) {
      await this.getPhoneToken(initData);
      return false;
    } else if (!this.userDetails[this.phoneSha].verified) {
      await this.getPhoneToken(initData);
      return false;
    }
    return true;
  }

  async getPhoneToken(initData) {
    const initializeData = {
      pair: initData.fromCurrency + initData.toCurrency,
      phoneNumber: initData.phoneNumber
    };
    const result = await loginWithPhone(initializeData);
    this.setStoredCredentials(this.phoneSha, result.phone_token);
  }

  async verifyUser(verifyData) {
    const verificationData = {
      pair: verifyData.fromCurrency + verifyData.toCurrency,
      phoneToken: this.phoneToken,
      tan: verifyData.tan
    };
    // returns {success: true} if successful
    const result = await sendReceivedSmsCode(verificationData);
    this.setStoredCredentials(this.phoneSha, this.phoneToken, result.success);
    return result;
  }

  async buildExitOrder({ fromCurrency, toCurrency, orderDetails }) {
    try {
      const orderData = {
        pair: fromCurrency + toCurrency,
        phoneToken: this.phoneToken,
        orderDetails: { ...orderDetails } // details //{ ...orderDetails }
      };
      return buildCyptoToFiatOrderData(orderData);
    } catch (e) {
      console.error(e);
    }
  }

  async getExitOrderDetails(detailsUrl) {
    return getCyptoToFiatOrderDetails(detailsUrl);
  }

  async startSpecial() {}

  static parseOrder(order) {
    return {
      orderId: order.id,
      statusId: order.reference,
      sendToAddress: order.payment_address,
      recValue: order.output.amount,
      sendValue: order.payment_amount,
      status: order.status,
      timestamp: order.timestamp_created,
      validFor: order.validFor || TIME_SWAP_VALID
    };
  }

  static parseExitOrder(order) {
    return {
      orderId: order.id,
      statusId: order.id,
      sendToAddress: order.payment_address,
      recValue: order.amount,
      sendValue: order.payment_amount,
      status: order.status,
      timestamp: order.timestamp_created,
      validFor: order.validFor || TIME_SWAP_VALID
    };
  }

  static async getOrderStatus(noticeDetails) {
    if (Object.keys(bityFiatCurrencies).includes(noticeDetails.toCurrency)) {
      return BitySwap.getOrderStatusFiat(noticeDetails);
    }
    return BitySwap.getOrderStatusCrypto(noticeDetails);
  }

  static async getOrderStatusCrypto(noticeDetails) {
    try {
      const data = await getStatus(noticeDetails.statusId);
      if (data.status === bityStatuses.EXEC) {
        return swapNotificationStatuses.COMPLETE;
      }
      if (data.input.status !== bityStatuses.FILL) {
        switch (data.input.status) {
          case bityStatuses.OPEN:
            return swapNotificationStatuses.NEW;
          case bityStatuses.RCVE:
          case bityStatuses.CONF:
            return swapNotificationStatuses.PENDING;
          case bityStatuses.CANC:
            return swapNotificationStatuses.CANCELLED;
        }
      } else {
        switch (data.output.status) {
          case bityStatuses.FILL:
            return swapNotificationStatuses.COMPLETE;
          case bityStatuses.CANC:
            return swapNotificationStatuses.CANCELLED;
          default:
            return swapNotificationStatuses.PENDING;
        }
      }
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error(e);
      return swapNotificationStatuses.PENDING;
    }
  }

  /*
  *
  *     status: response.legacy_status,
    timestamp_created: response.timestamp_created + "Z",
    fromAddress: response.input.crypto_address,
    payment_address: response.payment_details.crypto_address,
    payment_amount: response.input.amount,
    input: {
      amount: response.input.amount,
      currency: response.input.currency
    },
    output: {
      amount: response.output.amount,
      currency: response.output.currency
    }
  * */

  static async getOrderStatusFiat(noticeDetails) {
    try {
      const data = await getStatusFiat(
        noticeDetails.statusId,
        noticeDetails.special
      );
      if (!utils.isJson(data)) return swapNotificationStatuses.PENDING;

      // Since the status cannot be relied upon, we are going to assume the order went through after 10 min, if their was no error with the eth transaction.
      // Cannot make the same assumption with regards to chains other than ethereum.
      const timeSinceOrder =
        (new Date().getTime() - new Date(data.timestamp_created).getTime()) /
        1000;
      if (
        timeSinceOrder > 600 &&
        data.status === bityStatuses.OPEN &&
        data.input.currency === 'ETH'
      ) {
        return swapNotificationStatuses.COMPLETE;
      }
      switch (data.status) {
        // The endPoint does not seem to be updating the order.
        //case bityStatuses.OPEN:
        // return 'new';
        case bityStatuses.OPEN:
        case bityStatuses.RCVE:
        case bityStatuses.CONF:
          return swapNotificationStatuses.PENDING;
        case bityStatuses.FILL:
          return swapNotificationStatuses.COMPLETE;
        case bityStatuses.CANC:
          return swapNotificationStatuses.CANCELLED;
      }
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error(e);
      return swapNotificationStatuses.PENDING;
    }
  }
}
<commit_msg>fix failing build<commit_after>import store from 'store';
import web3Utils from 'web3-utils';
import {
  networkSymbols,
  BASE_CURRENCY,
  swapNotificationStatuses
} from '../partnersConfig';
import { utils } from '../helpers';
import {
  getRates,
  openOrder,
  getStatus,
  getExitRates,
  loginWithPhone,
  sendReceivedSmsCode,
  buildCyptoToFiatOrderData,
  getCyptoToFiatOrderDetails,
  getStatusFiat
} from './bity-calls';
import {
  bityStatuses,
  BityCurrencies,
  bityFiatCurrencies,
  PROVIDER_NAME,
  TIME_SWAP_VALID,
  BITY_MAX,
  BITY_MIN,
  BITY_DECIMALS,
  LOCAL_STORAGE_KEY
} from './config';

function disabledPairing(currencyList, symbol, invalid, side) {
  if (currencyList[symbol]) {
    if (side === 'from') {
      if (currencyList[symbol].invalidFrom) {
        return !currencyList[symbol].invalidFrom.includes(invalid);
      }
      return true;
    } else if (side === 'to') {
      if (currencyList[symbol].invalidTo) {
        return currencyList[symbol].invalidTo.includes(invalid);
      }
      return true;
    }
    return true;
  }
}

export default class BitySwap {
  constructor(props = {}) {
    this.name = BitySwap.getName();
    this.network = props.network || networkSymbols.ETH;
    this.decimals = BITY_DECIMALS;
    this.hasRates = 0;
    this.validStatus = ['RCVE', 'FILL', 'CONF', 'EXEC'];
    this.invalidStatus = ['CANC'];
    this.mainPairs = ['REP', 'ETH'];
    this.minValue = BITY_MIN;
    this.maxValue = BITY_MAX;
    this.fiatCurrencies = Object.keys(bityFiatCurrencies);
    this.rates = new Map();

    this.phoneSha = undefined;
    this.userDetails = this.loadStoredCredentials();
    // this.phoneToken =
    //   'OPWip5M2mbpidCtoYZZPJ1zxrDnOinWHok9TLyVfE1mQDOIp0dU6orrP+Gxm3Qrm';
    this.retrieveRates();
  }

  static getName() {
    return PROVIDER_NAME;
  }

  static isDex() {
    return false;
  }

  get phoneToken() {
    return this.userDetails[this.phoneSha].phone_token;
  }

  get isValidNetwork() {
    return this.network === networkSymbols.ETH;
  }

  get currencies() {
    if (this.isValidNetwork) {
      return BityCurrencies;
    }
    return {};
  }

  async retrieveRates() {
    try {
      if (!this.isValidNetwork) return;
      const exitRates = await getExitRates();
      const exitData = exitRates.pairs;
      const rates = await getRates();
      const data = rates.objects;
      exitData.forEach(entry => {
        if (entry.enabled) {
          data.forEach(rateEntry => {
            if (rateEntry.pair === entry.input + entry.output) {
              this.rates.set(
                `${entry.input}/${entry.output}`,
                parseFloat(rateEntry.rate_we_buy)
              );
            }
          });
        }
      });

      data.forEach(pair => {
        if (~this.mainPairs.indexOf(pair.pair.substring(3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_sell)
            );
          }
        } else if (~this.mainPairs.indexOf(pair.pair.substring(0, 3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_buy)
            );
          }
        }
      });
      this.hasRates = data.length > 0 ? this.hasRates + 1 : 0;
    } catch (e) {
      throw e;
    }
  }

  _getRate(fromToken, toToken) {
    if (this.rates.has(`${fromToken}/${toToken}`)) {
      return this.rates.get(`${fromToken}/${toToken}`);
    }
    return -1;
  }

  async getRate(fromCurrency, toCurrency) {
    const rate = this._getRate(fromCurrency, toCurrency);
    return {
      fromCurrency,
      toCurrency,
      provider: this.name,
      rate: rate,
      minValue: this.minValue,
      maxValue: this.getBtcEquivalent(fromCurrency)
    };
  }

  validSwap(fromCurrency, toCurrency) {
    if (this.isValidNetwork) {
      return this.rates.has(`${fromCurrency}/${toCurrency}`);
    }
    return false;
  }

  minCheck(fromCurrency, fromValue, toCurrency, toValue) {
    return (
      this.validityCheck(fromCurrency, fromValue, toCurrency, toValue) !==
      'lessThanMin'
    );
  }

  maxCheck(fromCurrency, fromValue, toCurrency, toValue) {
    return (
      this.validityCheck(fromCurrency, fromValue, toCurrency, toValue) !==
      'greaterThanMax'
    );
  }

  getBtcEquivalent(currency) {
    if (currency === 'BTC') {
      return this.maxValue;
    }
    const btcRate = this._getRate(currency, 'BTC');
    return this.maxValue / btcRate;
  }

  validityCheck(fromCurrency, fromValue, toCurrency, toValue) {
    if (toValue < this.minValue || fromValue < this.minValue)
      return 'lessThanMin';
    else if (
      (toCurrency == 'BTC' && toValue > this.maxValue) ||
      (fromCurrency == 'BTC' && fromValue > this.maxValue)
    )
      return 'greaterThanMax';
    else if (
      (toCurrency == 'ETH' &&
        toValue * this._getRate('ETH', 'BTC') > this.maxValue) ||
      (fromCurrency == 'ETH' &&
        fromValue * this._getRate('ETH', 'BTC') > this.maxValue)
    )
      return 'greaterThanMax';
    else if (
      (toCurrency == 'REP' &&
        toValue * this._getRate('REP', 'BTC') > this.maxValue) ||
      (fromCurrency == 'REP' &&
        fromValue * this._getRate('REP', 'BTC') > this.maxValue)
    )
      return 'greaterThanMax';
    return 'noErrors';
  }

  setNetwork(network) {
    this.network = network;
  }

  getInitialCurrencyEntries(collectMapFrom, collectMapTo) {
    for (const prop in this.currencies) {
      if (this.currencies[prop]) {
        collectMapTo.set(prop, {
          symbol: prop,
          name: this.currencies[prop].name
        });
        if (!this.fiatCurrencies.includes(prop)) {
          collectMapFrom.set(prop, {
            symbol: prop,
            name: this.currencies[prop].name
          });
        }
      }
    }
  }

  getUpdatedFromCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (disabledPairing(this.currencies, value.symbol, prop, 'from')) {
          if (this.currencies[prop] && !this.fiatCurrencies.includes(prop)) {
            collectMap.set(prop, {
              symbol: prop,
              name: this.currencies[prop].name
            });
          }
        }
      }
    }
  }

  getUpdatedToCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (this.currencies[prop])
          collectMap.set(prop, {
            symbol: prop,
            name: this.currencies[prop].name
          });
      }
    }
  }

  checkIfExit(swapDetails) {
    return this.fiatCurrencies.includes(swapDetails.toCurrency);
  }

  async startSwap(swapDetails) {
    if (this.checkIfExit(swapDetails) && !swapDetails.bypass) {
      if (swapDetails.exitFromAddress === '') return {};
      swapDetails.dataForInitialization = false;
      swapDetails.isExitToFiat = true;
      return swapDetails;
      // throw Error('Exit to Fiat not yet implemented');
    } else if (this.checkIfExit(swapDetails) && swapDetails.bypass) {
      const preOrder = await this.buildExitOrder(swapDetails);
      if (preOrder.created) {
        swapDetails.dataForInitialization = await getCyptoToFiatOrderDetails({
          pair: swapDetails.fromCurrency + swapDetails.toCurrency,
          detailsUrl: preOrder.status_address,
          phoneToken: this.phoneToken
        });
        if (swapDetails.dataForInitialization) {
          swapDetails.providerReceives =
            swapDetails.dataForInitialization.input.amount;
          swapDetails.providerSends =
            swapDetails.dataForInitialization.output.amount;
          swapDetails.parsed = BitySwap.parseExitOrder(
            swapDetails.dataForInitialization
          );
          swapDetails.providerAddress =
            swapDetails.dataForInitialization.payment_address;
          swapDetails.isDex = BitySwap.isDex();
        }
      }
    } else if (!this.checkIfExit(swapDetails)) {
      swapDetails.dataForInitialization = await this.buildOrder(
        swapDetails.fromCurrency === BASE_CURRENCY,
        swapDetails
      );
      swapDetails.providerReceives =
        swapDetails.dataForInitialization.input.amount;
      swapDetails.providerSends =
        swapDetails.dataForInitialization.output.amount;
      swapDetails.parsed = BitySwap.parseOrder(
        swapDetails.dataForInitialization
      );
      swapDetails.providerAddress =
        swapDetails.dataForInitialization.payment_address;
      swapDetails.isDex = BitySwap.isDex();
    }

    return swapDetails;
  }

  async buildOrder({
    fromCurrency,
    toCurrency,
    fromValue,
    toValue,
    toAddress
  }) {
    if (
      this.minCheck(fromCurrency, fromValue, toCurrency, toValue) &&
      this.maxCheck(fromCurrency, fromValue, toCurrency, toValue)
    ) {
      const order = {
        amount: fromValue,
        mode: 0,
        pair: fromCurrency + toCurrency,
        destAddress: toAddress
      };

      return await openOrder(order);
    }
  }

  loadStoredCredentials() {
    const userDetails = store.get(LOCAL_STORAGE_KEY);
    if (userDetails !== null && userDetails !== undefined) {
      return userDetails;
    }
    return {};
  }

  setStoredCredentials(phoneSha, phoneToken, verified = false) {
    let userDetails = store.get(LOCAL_STORAGE_KEY);
    if (userDetails === null || userDetails === undefined) {
      userDetails = {};
    }
    userDetails[phoneSha] = {
      phone_token: phoneToken,
      verified: verified
    };
    store.set(LOCAL_STORAGE_KEY, userDetails);
    this.userDetails = userDetails;
    return userDetails;
  }

  async registerUser(initData) {
    this.phoneSha = web3Utils.sha3(initData.phoneNumber);
    if (this.userDetails[this.phoneSha] === undefined) {
      await this.getPhoneToken(initData);
      return false;
    } else if (!this.userDetails[this.phoneSha].verified) {
      await this.getPhoneToken(initData);
      return false;
    }
    return true;
  }

  async getPhoneToken(initData) {
    const initializeData = {
      pair: initData.fromCurrency + initData.toCurrency,
      phoneNumber: initData.phoneNumber
    };
    const result = await loginWithPhone(initializeData);
    this.setStoredCredentials(this.phoneSha, result.phone_token);
  }

  async verifyUser(verifyData) {
    const verificationData = {
      pair: verifyData.fromCurrency + verifyData.toCurrency,
      phoneToken: this.phoneToken,
      tan: verifyData.tan
    };
    // returns {success: true} if successful
    const result = await sendReceivedSmsCode(verificationData);
    this.setStoredCredentials(this.phoneSha, this.phoneToken, result.success);
    return result;
  }

  async buildExitOrder({ fromCurrency, toCurrency, orderDetails }) {
    try {
      const orderData = {
        pair: fromCurrency + toCurrency,
        phoneToken: this.phoneToken,
        orderDetails: { ...orderDetails } // details //{ ...orderDetails }
      };
      return buildCyptoToFiatOrderData(orderData);
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error(e);
    }
  }

  async getExitOrderDetails(detailsUrl) {
    return getCyptoToFiatOrderDetails(detailsUrl);
  }

  async startSpecial() {}

  static parseOrder(order) {
    return {
      orderId: order.id,
      statusId: order.reference,
      sendToAddress: order.payment_address,
      recValue: order.output.amount,
      sendValue: order.payment_amount,
      status: order.status,
      timestamp: order.timestamp_created,
      validFor: order.validFor || TIME_SWAP_VALID
    };
  }

  static parseExitOrder(order) {
    return {
      orderId: order.id,
      statusId: order.id,
      sendToAddress: order.payment_address,
      recValue: order.amount,
      sendValue: order.payment_amount,
      status: order.status,
      timestamp: order.timestamp_created,
      validFor: order.validFor || TIME_SWAP_VALID
    };
  }

  static async getOrderStatus(noticeDetails) {
    if (Object.keys(bityFiatCurrencies).includes(noticeDetails.toCurrency)) {
      return BitySwap.getOrderStatusFiat(noticeDetails);
    }
    return BitySwap.getOrderStatusCrypto(noticeDetails);
  }

  static async getOrderStatusCrypto(noticeDetails) {
    try {
      const data = await getStatus(noticeDetails.statusId);
      if (data.status === bityStatuses.EXEC) {
        return swapNotificationStatuses.COMPLETE;
      }
      if (data.input.status !== bityStatuses.FILL) {
        switch (data.input.status) {
          case bityStatuses.OPEN:
            return swapNotificationStatuses.NEW;
          case bityStatuses.RCVE:
          case bityStatuses.CONF:
            return swapNotificationStatuses.PENDING;
          case bityStatuses.CANC:
            return swapNotificationStatuses.CANCELLED;
        }
      } else {
        switch (data.output.status) {
          case bityStatuses.FILL:
            return swapNotificationStatuses.COMPLETE;
          case bityStatuses.CANC:
            return swapNotificationStatuses.CANCELLED;
          default:
            return swapNotificationStatuses.PENDING;
        }
      }
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error(e);
      return swapNotificationStatuses.PENDING;
    }
  }

  static async getOrderStatusFiat(noticeDetails) {
    try {
      const data = await getStatusFiat(
        noticeDetails.statusId,
        noticeDetails.special
      );
      if (!utils.isJson(data)) return swapNotificationStatuses.PENDING;

      // Since the status cannot be relied upon, we are going to assume the order went through after 10 min, if their was no error with the eth transaction.
      // Cannot make the same assumption with regards to chains other than ethereum.
      const timeSinceOrder =
        (new Date().getTime() - new Date(data.timestamp_created).getTime()) /
        1000;
      if (
        timeSinceOrder > 600 &&
        data.status === bityStatuses.OPEN &&
        data.input.currency === 'ETH'
      ) {
        return swapNotificationStatuses.COMPLETE;
      }
      switch (data.status) {
        // The endPoint does not seem to be updating the order.
        //case bityStatuses.OPEN:
        // return 'new';
        case bityStatuses.OPEN:
        case bityStatuses.RCVE:
        case bityStatuses.CONF:
          return swapNotificationStatuses.PENDING;
        case bityStatuses.FILL:
          return swapNotificationStatuses.COMPLETE;
        case bityStatuses.CANC:
          return swapNotificationStatuses.CANCELLED;
      }
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error(e);
      return swapNotificationStatuses.PENDING;
    }
  }
}
<|endoftext|>


<commit_before>import { networkSymbols, BASE_CURRENCY } from '../partnersConfig';
import {
  getRates,
  openOrder,
  getStatus,
  getExitRates,
  loginWithPhone,
  sendReceivedSmsCode,
  buildCyptoToFiatOrderData,
  getStatusFiat
} from './bity-calls';
import {
  bityStatuses,
  BityCurrencies,
  bityFiatCurrencies,
  PROVIDER_NAME,
  TIME_SWAP_VALID,
  BITY_MAX,
  BITY_MIN,
  BITY_DECIMALS
} from './config';

function disabledPairing(currencyList, symbol, invalid, side) {
  if (currencyList[symbol]) {
    if (side === 'from') {
      if (currencyList[symbol].invalidFrom) {
        return !currencyList[symbol].invalidFrom.includes(invalid);
      }
      return true;
    } else if (side === 'to') {
      if (currencyList[symbol].invalidTo) {
        return !currencyList[symbol].invalidTo.includes(invalid);
      }
      return true;
    }
    return true;
  }
}

export default class BitySwap {
  constructor(props = {}) {
    this.name = BitySwap.getName();
    this.network = props.network || networkSymbols.ETH;
    this.decimals = BITY_DECIMALS;
    this.hasRates = 0;
    this.validStatus = ['RCVE', 'FILL', 'CONF', 'EXEC'];
    this.invalidStatus = ['CANC'];
    this.mainPairs = ['REP', 'ETH'];
    this.minValue = BITY_MIN;
    this.maxValue = BITY_MAX;
    this.fiatCurrencies = Object.keys(bityFiatCurrencies);
    this.rates = new Map();

    this.retrieveRates();
  }

  static getName() {
    return PROVIDER_NAME;
  }

  static isDex() {
    return false;
  }

  get isValidNetwork() {
    return this.network === networkSymbols.ETH;
  }

  get currencies() {
    if (this.isValidNetwork) {
      return BityCurrencies;
    }
    return {};
  }

  async retrieveRates() {
    try {
      if (!this.isValidNetwork) return;
      const exitRates = await getExitRates();
      const exitData = exitRates.pairs;
      const rates = await getRates();
      const data = rates.objects;

      exitData.forEach(entry => {
        if (this.fiatCurrencies.includes(entry.output)) {
          this.rates.set(
            `${entry.input}/${entry.output}`,
            parseFloat(entry.price)
          );
        }
      });

      data.forEach(pair => {
        if (~this.mainPairs.indexOf(pair.pair.substring(3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_sell)
            );
          }
        } else if (~this.mainPairs.indexOf(pair.pair.substring(0, 3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_buy)
            );
          }
        }
      });
      this.hasRates = data.length > 0 ? this.hasRates + 1 : 0;
    } catch (e) {
      throw e;
    }
  }

  _getRate(fromToken, toToken) {
    if (this.rates.has(`${fromToken}/${toToken}`)) {
      return this.rates.get(`${fromToken}/${toToken}`);
    }
    return -1;
  }

  async getRate(fromCurrency, toCurrency) {
    const rate = this._getRate(fromCurrency, toCurrency);
    return {
      fromCurrency,
      toCurrency,
      provider: this.name,
      rate: rate,
      minValue: this.minValue,
      maxValue: this.maxValue
    };
  }

  validSwap(fromCurrency, toCurrency) {
    if (this.isValidNetwork) {
      return this.rates.has(`${fromCurrency}/${toCurrency}`);
    }
    return false;
  }

  minCheck(fromCurrency, fromValue, toCurrency, toValue) {
    return toValue > this.minValue || fromValue > this.minValue;
  }

  maxCheck(fromCurrency, fromValue, toCurrency, toValue) {
    const overMax =
      (toCurrency === 'BTC' && toValue > this.maxValue) ||
      (fromCurrency === 'BTC' && fromValue > this.maxValue);
    const overMaxETH =
      (toCurrency === 'ETH' && toValue > this.maxValue) ||
      (fromCurrency === 'ETH' &&
        fromValue * this._getRate(toCurrency, fromCurrency) > this.maxValue);
    const overMaxREP =
      (toCurrency === 'REP' && toValue > this.maxValue) ||
      (fromCurrency === 'REP' &&
        fromValue * this._getRate(fromCurrency, toCurrency) > this.maxValue);
    if (toCurrency === 'BTC' && overMax) {
      return false;
    } else if (toCurrency === 'ETH' && overMaxETH) {
      return false;
    } else if (toCurrency === 'REP' && overMaxREP) {
      return false;
    }
    return true;
  }

  setNetwork(network) {
    this.network = network;
  }

  getInitialCurrencyEntries(collectMapFrom, collectMapTo) {
    for (const prop in this.currencies) {
      if (this.currencies[prop]) {
        collectMapTo.set(prop, {
          symbol: prop,
          name: this.currencies[prop].name
        });
        if (!this.fiatCurrencies.includes(prop)) {
          collectMapFrom.set(prop, {
            symbol: prop,
            name: this.currencies[prop].name
          });
        }
      }
    }
  }

  getUpdatedFromCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (
          prop !== value.symbol &&
          disabledPairing(this.currencies, value.symbol, prop, 'from')
        ) {
          if (this.currencies[prop] && !this.fiatCurrencies.includes(prop)) {
            collectMap.set(prop, {
              symbol: prop,
              name: this.currencies[prop].name
            });
          }
        }
      }
    }
  }

  getUpdatedToCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (
          prop !== value.symbol &&
          disabledPairing(this.currencies, value.symbol, prop, 'to')
        ) {
          if (this.currencies[prop])
            collectMap.set(prop, {
              symbol: prop,
              name: this.currencies[prop].name
            });
        }
      }
    }
  }

  checkIfExit(swapDetails) {
    return this.fiatCurrencies.includes(swapDetails.toCurrency);
  }

  async startSwap(swapDetails) {
    if (this.checkIfExit(swapDetails) && !swapDetails.bypass) {
      if (swapDetails.exitFromAddress === '') return {};
      swapDetails.dataForInitialization = false;
      swapDetails.isExitToFiat = true;
      return swapDetails;
      // throw Error('Exit to Fiat not yet implemented');
    } else if (this.checkIfExit(swapDetails) && swapDetails.bypass) {
      swapDetails.dataForInitialization = await this.buildExitOrder(
        swapDetails
      );
      // receiving: {"jsonrpc":"2.0","error":{"code":-12345,"message":"{\"errors\":[{\"code\":\"invalid_order\",\"message\":\"Your order can not be placed at this moment, please try again later.\"}]}","data":null},"id":""}
      console.log(swapDetails); // todo remove dev item
    } else if (!this.checkIfExit(swapDetails)) {
      swapDetails.dataForInitialization = await this.buildOrder(
        swapDetails.fromCurrency === BASE_CURRENCY,
        swapDetails
      );
      swapDetails.providerReceives =
        swapDetails.dataForInitialization.input.amount;
      swapDetails.providerSends =
        swapDetails.dataForInitialization.output.amount;
      swapDetails.parsed = BitySwap.parseOrder(
        swapDetails.dataForInitialization
      );
      swapDetails.providerAddress =
        swapDetails.dataForInitialization.payment_address;
      swapDetails.isDex = BitySwap.isDex();
    }

    return swapDetails;
  }

  async buildOrder(
    isFrom,
    { fromCurrency, toCurrency, fromValue, toValue, toAddress }
  ) {
    if (
      this.minCheck(fromCurrency, fromValue, toCurrency, toValue) &&
      this.maxCheck(fromCurrency, fromValue, toCurrency, toValue)
    ) {
      const order = {
        amount: fromValue,
        mode: isFrom ? 0 : 1, // check how I should handle this now
        pair: fromCurrency + toCurrency,
        destAddress: toAddress
      };

      const bityOrder = await openOrder(order);

      if (!bityOrder.error) {
        return bityOrder.data;
      }
      throw Error('error creating bity order');
    }
  }

  async initializeUser(initData) {
    const initializeData = {
      pair: initData.fromCurrency + initData.toCurrency,
      phoneNumber: initData.phoneNumber
    };
    const result = await loginWithPhone(initializeData);
    this.phoneToken = result.phone_token;
  }

  async verifyUser(verifyData) {
    const verificationData = {
      pair: verifyData.fromCurrency + verifyData.toCurrency,
      phoneToken: this.phoneToken,
      tan: verifyData.tan
    };
    // returns {success: true} if successful
    return sendReceivedSmsCode(verificationData);
  }

  async buildExitOrder({ fromCurrency, toCurrency, orderDetails }) {
    const orderData = {
      pair: fromCurrency + toCurrency,
      phoneToken: this.phoneToken,
      orderDetails: orderDetails
    };
    console.log(orderData); // todo remove dev item
    return buildCyptoToFiatOrderData(orderData);
  }

  async startSpecial() {}

  static parseOrder(order) {
    return {
      orderId: order.id,
      statusId: order.reference,
      sendToAddress: order.payment_address,
      recValue: order.output.amount,
      sendValue: order.input.amount,
      status: order.status,
      timestamp: order.timestamp_created,
      validFor: order.validFor || TIME_SWAP_VALID
    };
  }

  static async getOrderStatus(noticeDetails) {
    let data;
    if (Object.keys(bityFiatCurrencies).includes(noticeDetails.toCurrency)) {
      data = await getStatusFiat({ orderid: noticeDetails.statusId });
    } else {
      data = await getStatus({ orderid: noticeDetails.statusId });
    }
    switch (data.status) {
      case bityStatuses.OPEN:
        return 'new';
      case bityStatuses.RCVE:
      case bityStatuses.CONF:
        return 'pending';
      case bityStatuses.FILL:
        return 'complete';
      case bityStatuses.CANC:
        return 'cancelled';
    }
  }
}
<commit_msg>correct data passed in exit to fiat operation. ops except create ok.<commit_after>import { networkSymbols, BASE_CURRENCY } from '../partnersConfig';
import {
  getRates,
  openOrder,
  getStatus,
  getExitRates,
  loginWithPhone,
  sendReceivedSmsCode,
  buildCyptoToFiatOrderData,
  getStatusFiat
} from './bity-calls';
import {
  bityStatuses,
  BityCurrencies,
  bityFiatCurrencies,
  PROVIDER_NAME,
  TIME_SWAP_VALID,
  BITY_MAX,
  BITY_MIN,
  BITY_DECIMALS
} from './config';

function disabledPairing(currencyList, symbol, invalid, side) {
  if (currencyList[symbol]) {
    if (side === 'from') {
      if (currencyList[symbol].invalidFrom) {
        return !currencyList[symbol].invalidFrom.includes(invalid);
      }
      return true;
    } else if (side === 'to') {
      if (currencyList[symbol].invalidTo) {
        return !currencyList[symbol].invalidTo.includes(invalid);
      }
      return true;
    }
    return true;
  }
}

export default class BitySwap {
  constructor(props = {}) {
    this.name = BitySwap.getName();
    this.network = props.network || networkSymbols.ETH;
    this.decimals = BITY_DECIMALS;
    this.hasRates = 0;
    this.validStatus = ['RCVE', 'FILL', 'CONF', 'EXEC'];
    this.invalidStatus = ['CANC'];
    this.mainPairs = ['REP', 'ETH'];
    this.minValue = BITY_MIN;
    this.maxValue = BITY_MAX;
    this.fiatCurrencies = Object.keys(bityFiatCurrencies);
    this.rates = new Map();

    this.retrieveRates();
  }

  static getName() {
    return PROVIDER_NAME;
  }

  static isDex() {
    return false;
  }

  get isValidNetwork() {
    return this.network === networkSymbols.ETH;
  }

  get currencies() {
    if (this.isValidNetwork) {
      return BityCurrencies;
    }
    return {};
  }

  async retrieveRates() {
    try {
      if (!this.isValidNetwork) return;
      const exitRates = await getExitRates();
      const exitData = exitRates.pairs;
      const rates = await getRates();
      const data = rates.objects;

      exitData.forEach(entry => {
        if (this.fiatCurrencies.includes(entry.output)) {
          this.rates.set(
            `${entry.input}/${entry.output}`,
            parseFloat(entry.price)
          );
        }
      });

      data.forEach(pair => {
        if (~this.mainPairs.indexOf(pair.pair.substring(3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_sell)
            );
          }
        } else if (~this.mainPairs.indexOf(pair.pair.substring(0, 3))) {
          if (pair.is_enabled && !this.fiatCurrencies.includes(pair.source)) {
            this.rates.set(
              `${pair.source}/${pair.target}`,
              parseFloat(pair.rate_we_buy)
            );
          }
        }
      });
      this.hasRates = data.length > 0 ? this.hasRates + 1 : 0;
    } catch (e) {
      throw e;
    }
  }

  _getRate(fromToken, toToken) {
    if (this.rates.has(`${fromToken}/${toToken}`)) {
      return this.rates.get(`${fromToken}/${toToken}`);
    }
    return -1;
  }

  async getRate(fromCurrency, toCurrency) {
    const rate = this._getRate(fromCurrency, toCurrency);
    return {
      fromCurrency,
      toCurrency,
      provider: this.name,
      rate: rate,
      minValue: this.minValue,
      maxValue: this.maxValue
    };
  }

  validSwap(fromCurrency, toCurrency) {
    if (this.isValidNetwork) {
      return this.rates.has(`${fromCurrency}/${toCurrency}`);
    }
    return false;
  }

  minCheck(fromCurrency, fromValue, toCurrency, toValue) {
    return toValue > this.minValue || fromValue > this.minValue;
  }

  maxCheck(fromCurrency, fromValue, toCurrency, toValue) {
    const overMax =
      (toCurrency === 'BTC' && toValue > this.maxValue) ||
      (fromCurrency === 'BTC' && fromValue > this.maxValue);
    const overMaxETH =
      (toCurrency === 'ETH' && toValue > this.maxValue) ||
      (fromCurrency === 'ETH' &&
        fromValue * this._getRate(toCurrency, fromCurrency) > this.maxValue);
    const overMaxREP =
      (toCurrency === 'REP' && toValue > this.maxValue) ||
      (fromCurrency === 'REP' &&
        fromValue * this._getRate(fromCurrency, toCurrency) > this.maxValue);
    if (toCurrency === 'BTC' && overMax) {
      return false;
    } else if (toCurrency === 'ETH' && overMaxETH) {
      return false;
    } else if (toCurrency === 'REP' && overMaxREP) {
      return false;
    }
    return true;
  }

  setNetwork(network) {
    this.network = network;
  }

  getInitialCurrencyEntries(collectMapFrom, collectMapTo) {
    for (const prop in this.currencies) {
      if (this.currencies[prop]) {
        collectMapTo.set(prop, {
          symbol: prop,
          name: this.currencies[prop].name
        });
        if (!this.fiatCurrencies.includes(prop)) {
          collectMapFrom.set(prop, {
            symbol: prop,
            name: this.currencies[prop].name
          });
        }
      }
    }
  }

  getUpdatedFromCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (
          prop !== value.symbol &&
          disabledPairing(this.currencies, value.symbol, prop, 'from')
        ) {
          if (this.currencies[prop] && !this.fiatCurrencies.includes(prop)) {
            collectMap.set(prop, {
              symbol: prop,
              name: this.currencies[prop].name
            });
          }
        }
      }
    }
  }

  getUpdatedToCurrencyEntries(value, collectMap) {
    if (this.currencies[value.symbol]) {
      for (const prop in this.currencies) {
        if (
          prop !== value.symbol &&
          disabledPairing(this.currencies, value.symbol, prop, 'to')
        ) {
          if (this.currencies[prop])
            collectMap.set(prop, {
              symbol: prop,
              name: this.currencies[prop].name
            });
        }
      }
    }
  }

  checkIfExit(swapDetails) {
    return this.fiatCurrencies.includes(swapDetails.toCurrency);
  }

  async startSwap(swapDetails) {
    if (this.checkIfExit(swapDetails) && !swapDetails.bypass) {
      if (swapDetails.exitFromAddress === '') return {};
      swapDetails.dataForInitialization = false;
      swapDetails.isExitToFiat = true;
      return swapDetails;
      // throw Error('Exit to Fiat not yet implemented');
    } else if (this.checkIfExit(swapDetails) && swapDetails.bypass) {
      swapDetails.dataForInitialization = await this.buildExitOrder(
        swapDetails
      );
      // receiving:
      // {"jsonrpc":"2.0","error":{"code":-12345,"message":"{\"errors\":[{\"code\":\"invalid_order\",\"message\":\"Your order can not be placed at this moment, please try again later.\"}]}","data":null},"id":""}
      console.log(swapDetails); // todo remove dev item
    } else if (!this.checkIfExit(swapDetails)) {
      swapDetails.dataForInitialization = await this.buildOrder(
        swapDetails.fromCurrency === BASE_CURRENCY,
        swapDetails
      );
      swapDetails.providerReceives =
        swapDetails.dataForInitialization.input.amount;
      swapDetails.providerSends =
        swapDetails.dataForInitialization.output.amount;
      swapDetails.parsed = BitySwap.parseOrder(
        swapDetails.dataForInitialization
      );
      swapDetails.providerAddress =
        swapDetails.dataForInitialization.payment_address;
      swapDetails.isDex = BitySwap.isDex();
    }

    return swapDetails;
  }

  async buildOrder(
    isFrom,
    { fromCurrency, toCurrency, fromValue, toValue, toAddress }
  ) {
    if (
      this.minCheck(fromCurrency, fromValue, toCurrency, toValue) &&
      this.maxCheck(fromCurrency, fromValue, toCurrency, toValue)
    ) {
      const order = {
        amount: fromValue,
        mode: isFrom ? 0 : 1, // check how I should handle this now
        pair: fromCurrency + toCurrency,
        destAddress: toAddress
      };

      const bityOrder = await openOrder(order);

      if (!bityOrder.error) {
        return bityOrder.data;
      }
      throw Error('error creating bity order');
    }
  }

  async initializeUser(initData) {
    const initializeData = {
      pair: initData.fromCurrency + initData.toCurrency,
      phoneNumber: initData.phoneNumber
    };
    const result = await loginWithPhone(initializeData);
    this.phoneToken = result.phone_token;
  }

  async verifyUser(verifyData) {
    const verificationData = {
      pair: verifyData.fromCurrency + verifyData.toCurrency,
      phoneToken: this.phoneToken,
      tan: verifyData.tan
    };
    // returns {success: true} if successful
    return sendReceivedSmsCode(verificationData);
  }

  async buildExitOrder({ fromCurrency, toCurrency, orderDetails }) {
    const orderData = {
      pair: fromCurrency + toCurrency,
      phoneToken: this.phoneToken,
      orderDetails: orderDetails
    };
    console.log(orderData); // todo remove dev item
    return buildCyptoToFiatOrderData(orderData);
  }

  async startSpecial() {}

  static parseOrder(order) {
    return {
      orderId: order.id,
      statusId: order.reference,
      sendToAddress: order.payment_address,
      recValue: order.output.amount,
      sendValue: order.input.amount,
      status: order.status,
      timestamp: order.timestamp_created,
      validFor: order.validFor || TIME_SWAP_VALID
    };
  }

  static async getOrderStatus(noticeDetails) {
    let data;
    if (Object.keys(bityFiatCurrencies).includes(noticeDetails.toCurrency)) {
      data = await getStatusFiat({ orderid: noticeDetails.statusId });
    } else {
      data = await getStatus({ orderid: noticeDetails.statusId });
    }
    switch (data.status) {
      case bityStatuses.OPEN:
        return 'new';
      case bityStatuses.RCVE:
      case bityStatuses.CONF:
        return 'pending';
      case bityStatuses.FILL:
        return 'complete';
      case bityStatuses.CANC:
        return 'cancelled';
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

CurveLocation = Base.extend({
	/** @lends CurveLocation# */

	beans: true,

	/**
	 * Creates a new CurveLocation object.
	 *
	 * @name CurveLocation
	 * @constructor
	 * @param {Curve} curve
	 * @param {number} parameter
	 * @param {Point} point
	 *
	 * @class CurveLocation objects describe a location on {@Curve} objects, as
	 * defined by the curve {@link #parameter}, a value between {@code 0}
	 * (beginning of the curve) and {@code 1} (end of the curve). If the curve is
	 * part of a {@link Path} item, its {@link #index} inside the
	 * {@link Path#curves} array is also provided.
	 *
	 * The class is in use in many places, such as {@link Path#getLocationAt(length)},
	 * {@link Path#getLength(CurveLocation)}, {@link Path#getPoint(length)},
	 * {@link Path#split(CurveLocation)},
	 * {@link PathItem#getIntersections(PathItem)}, etc.
	 */
	initialize: function(curve, parameter, point) {
		this._curve = curve;
		this._parameter = parameter;
		this._point = point;
	},

	/**
	 * The segment of the curve which is closer to the described location.
	 *
	 * @type Segment
	 * @bean
	 */
	getSegment: function() {
		if (!this._segment) {
			var parameter = this.getParameter();
			if (parameter == 0) {
				this._segment = curve._segment1;
			} else if (parameter == 1) {
				this._segment = curve._segment2;
			} else if (parameter == null) {
				return null;
			} else {
				// Determine the closest segment by comparing curve lengths
				this._segment = curve.getLength(0, parameter)
					< curve.getLength(parameter, 1)
						? curve._segment1
						: curve._segment2;
			}
		}
		return this._segment;
	},

	/**
	 * The curve by which the location is defined.
	 *
	 * @type Curve
	 * @bean
	 */
	getCurve: function() {
		return this._curve;
	},

	/**
	 * The item this curve belongs to, if any.
	 *
	 * @type Item
	 * @bean
	 */
	getItem: function() {
		return this._curve && this._curve._path;
	},

	/**
	 * The index of the curve within the {@link Path#getCurves()} list, if the
	 * curve is part of a {@link Path} item.
	 *
	 * @type Index
	 * @bean
	 */
	getIndex: function() {
		return this._curve && this._curve.getIndex();
	},

	/**
	 * The length of the path from its beginning up to the location described
	 * by this object.
	 *
	 * @type number
	 * @bean
	 */
	getOffset: function() {
		var path = this._curve && this._curve._path;
		return path && path._getOffset(this);
	},

	/**
	 * The length of the curve from its beginning up to the location described
	 * by this object.
	 *
	 * @type number
	 * @bean
	 */
	getCurveOffset: function() {
		var parameter = this._curve && this.getParameter();
		return parameter != null ? this._curve.getLength(0, parameter) : null;
	},

	/**
	 * The curve parameter, as used by various bezier curve calculations. It is
	 * value between {@code 0} (beginning of the curve) and {@code 1} (end of
	 * the curve).
	 *
	 * @type number
	 * @bean
	 */
	getParameter: function() {
		if (this._parameter == null && this._point)
			this._parameter = this._curve.getParameter(this._point);
		return this._parameter;
	},

	/**
	 * The point which is defined by the {@link #curve} and
	 * {@link #parameter}.
	 *
	 * @type Point
	 * @bean
	 */
	getPoint: function() {
		if (!this._point && this._curve) {
			var parameter = this.getParameter();
			if (parameter != null)
				this._point = this._curve.getPoint(parameter);
		}
		return this._point;
	},

	/**
	 * The tangential vector to the {@link #curve} at the given location.
	 *
	 * @type Point
	 * @bean
	 */
	getTangent: function() {
		var parameter = this.getParameter();
		return parameter != null && this._curve
				&& this._curve.getTangent(parameter);
	},

	/**
	 * The normal vector to the {@link #curve} at the given location.
	 *
	 * @type Point
	 * @bean
	 */
	getNormal: function() {
		var parameter = this.getParameter();
		return parameter != null && this._curve
				&& this._curve.getNormal(parameter);
	},

	/**
	 * @return {string} A string representation of the curve location.
	 */
	toString: function() {
		var parts = [],
			point = this.getPoint();
		if (point)
			parts.push('point: ' + point);
		var index = this.getIndex();
		if (index != null)
			parts.push('index: ' + index);
		var parameter = this.getParameter();
		if (parameter != null)
			parts.push('parameter: ' + Base.formatNumber(parameter));
		return '{ ' + parts.join(', ') + ' }';
	}
});
<commit_msg>Documentation: remove part of the constructor explanation of CurveLocation until the mentioned functions have been added.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

CurveLocation = Base.extend({
	/** @lends CurveLocation# */

	beans: true,

	// DOCS: CurveLocation class description: add this comment back when the
	// mentioned functioned have been added:
	// The class is in use in many places, such as {@link Path#getLocationAt(offset)},
	// {@link Path#getLength(CurveLocation)}, {@link Path#getPoint(length)},
	// {@link Path#split(CurveLocation)},
	// {@link PathItem#getIntersections(PathItem)}, etc.
	/**
	 * Creates a new CurveLocation object.
	 *
	 * @name CurveLocation
	 * @constructor
	 * @param {Curve} curve
	 * @param {number} parameter
	 * @param {Point} point
	 *
	 * @class CurveLocation objects describe a location on {@link Curve}
	 * objects, as defined by the curve {@link #parameter}, a value between
	 * {@code 0} (beginning of the curve) and {@code 1} (end of the curve). If
	 * the curve is part of a {@link Path} item, its {@link #index} inside the
	 * {@link Path#curves} array is also provided.
	 */
	initialize: function(curve, parameter, point) {
		this._curve = curve;
		this._parameter = parameter;
		this._point = point;
	},

	/**
	 * The segment of the curve which is closer to the described location.
	 *
	 * @type Segment
	 * @bean
	 */
	getSegment: function() {
		if (!this._segment) {
			var parameter = this.getParameter();
			if (parameter == 0) {
				this._segment = curve._segment1;
			} else if (parameter == 1) {
				this._segment = curve._segment2;
			} else if (parameter == null) {
				return null;
			} else {
				// Determine the closest segment by comparing curve lengths
				this._segment = curve.getLength(0, parameter)
					< curve.getLength(parameter, 1)
						? curve._segment1
						: curve._segment2;
			}
		}
		return this._segment;
	},

	/**
	 * The curve by which the location is defined.
	 *
	 * @type Curve
	 * @bean
	 */
	getCurve: function() {
		return this._curve;
	},

	/**
	 * The item this curve belongs to, if any.
	 *
	 * @type Item
	 * @bean
	 */
	getItem: function() {
		return this._curve && this._curve._path;
	},

	/**
	 * The index of the curve within the {@link Path#curves} list, if the
	 * curve is part of a {@link Path} item.
	 *
	 * @type Index
	 * @bean
	 */
	getIndex: function() {
		return this._curve && this._curve.getIndex();
	},

	/**
	 * The length of the path from its beginning up to the location described
	 * by this object.
	 *
	 * @type number
	 * @bean
	 */
	getOffset: function() {
		var path = this._curve && this._curve._path;
		return path && path._getOffset(this);
	},

	/**
	 * The length of the curve from its beginning up to the location described
	 * by this object.
	 *
	 * @type number
	 * @bean
	 */
	getCurveOffset: function() {
		var parameter = this._curve && this.getParameter();
		return parameter != null ? this._curve.getLength(0, parameter) : null;
	},

	/**
	 * The curve parameter, as used by various bezier curve calculations. It is
	 * value between {@code 0} (beginning of the curve) and {@code 1} (end of
	 * the curve).
	 *
	 * @type number
	 * @bean
	 */
	getParameter: function() {
		if (this._parameter == null && this._point)
			this._parameter = this._curve.getParameter(this._point);
		return this._parameter;
	},

	/**
	 * The point which is defined by the {@link #curve} and
	 * {@link #parameter}.
	 *
	 * @type Point
	 * @bean
	 */
	getPoint: function() {
		if (!this._point && this._curve) {
			var parameter = this.getParameter();
			if (parameter != null)
				this._point = this._curve.getPoint(parameter);
		}
		return this._point;
	},

	/**
	 * The tangential vector to the {@link #curve} at the given location.
	 *
	 * @type Point
	 * @bean
	 */
	getTangent: function() {
		var parameter = this.getParameter();
		return parameter != null && this._curve
				&& this._curve.getTangent(parameter);
	},

	/**
	 * The normal vector to the {@link #curve} at the given location.
	 *
	 * @type Point
	 * @bean
	 */
	getNormal: function() {
		var parameter = this.getParameter();
		return parameter != null && this._curve
				&& this._curve.getNormal(parameter);
	},

	/**
	 * @return {string} A string representation of the curve location.
	 */
	toString: function() {
		var parts = [],
			point = this.getPoint();
		if (point)
			parts.push('point: ' + point);
		var index = this.getIndex();
		if (index != null)
			parts.push('index: ' + index);
		var parameter = this.getParameter();
		if (parameter != null)
			parts.push('parameter: ' + Base.formatNumber(parameter));
		return '{ ' + parts.join(', ') + ' }';
	}
});
<|endoftext|>


<commit_before>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
 * http://scratchdisk.com/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name CurveLocation
 *
 * @class CurveLocation objects describe a location on {@link Curve}
 * objects, as defined by the curve {@link #parameter}, a value between
 * {@code 0} (beginning of the curve) and {@code 1} (end of the curve). If
 * the curve is part of a {@link Path} item, its {@link #index} inside the
 * {@link Path#curves} array is also provided.
 *
 * The class is in use in many places, such as
 * {@link Path#getLocationAt(offset, isParameter)},
 * {@link Path#getLocationOf(point)},
 * {@link Path#getNearestLocation(point),
 * {@link PathItem#getIntersections(path)},
 * etc.
 */
var CurveLocation = Base.extend(/** @lends CurveLocation# */{
    _class: 'CurveLocation',
    // Enforce creation of beans, as bean getters have hidden parameters.
    // See #getSegment() below.
    beans: true,

    // DOCS: CurveLocation class description: add these back when the  mentioned
    // functioned have been added: {@link Path#split(location)}
    /**
     * Creates a new CurveLocation object.
     *
     * @param {Curve} curve
     * @param {Number} parameter
     * @param {Point} point
     */
    initialize: function CurveLocation(curve, parameter, point, _curve2,
            _parameter2, _point2, _distance) {
        // Define this CurveLocation's unique id.
        this._id = CurveLocation._id = (CurveLocation._id || 0) + 1;
        this._curve = curve;
        // Also store references to segment1 and segment2, in case path
        // splitting / dividing is going to happen, in which case the segments
        // can be used to determine the new curves, see #getCurve(true)
        this._segment1 = curve._segment1;
        this._segment2 = curve._segment2;
        this._parameter = parameter;
        this._point = point;
        this._curve2 = _curve2;
        this._parameter2 = _parameter2;
        this._point2 = _point2;
        this._distance = _distance;
    },

    /**
     * The segment of the curve which is closer to the described location.
     *
     * @type Segment
     * @bean
     */
    getSegment: function(_preferFirst) {
        if (!this._segment) {
            var curve = this.getCurve(),
                parameter = this.getParameter();
            if (parameter === 1) {
                this._segment = curve._segment2;
            } else if (parameter === 0 || _preferFirst) {
                this._segment = curve._segment1;
            } else if (parameter == null) {
                return null;
            } else {
                // Determine the closest segment by comparing curve lengths
                this._segment = curve.getPartLength(0, parameter)
                    < curve.getPartLength(parameter, 1)
                        ? curve._segment1
                        : curve._segment2;
            }
        }
        return this._segment;
    },

    /**
     * The curve that this location belongs to.
     *
     * @type Curve
     * @bean
     */
    getCurve: function(_uncached) {
        if (!this._curve || _uncached) {
            // If we're asked to get the curve uncached, access current curve
            // objects through segment1 / segment2. Since path splitting or
            // dividing might have happened in the meantime, try segment1's
            // curve, and see if _point lies on it still, otherwise assume it's
            // the curve before segment2.
            this._curve = this._segment1.getCurve();
            if (this._curve.getParameterOf(this._point) == null)
                this._curve = this._segment2.getPrevious().getCurve();
        }
        return this._curve;
    },

    /**
     * The curve location on the intersecting curve, if this location is the
     * result of a call to {@link PathItem#getIntersections(path)} /
     * {@link Curve#getIntersections(curve)}.
     *
     * @type CurveLocation
     * @bean
     */
    getIntersection: function() {
        var intersection = this._intersection;
        if (!intersection && this._curve2) {
            var param = this._parameter2;
            // If we have the parameter on the other curve use that for
            // intersection rather than the point.
            this._intersection = intersection = new CurveLocation(
                    this._curve2, param, this._point2 || this._point, this);
            intersection._intersection = this;
        }
        return intersection;
    },

    /**
     * The path this curve belongs to, if any.
     *
     * @type Item
     * @bean
     */
    getPath: function() {
        var curve = this.getCurve();
        return curve && curve._path;
    },

    /**
     * The index of the curve within the {@link Path#curves} list, if the
     * curve is part of a {@link Path} item.
     *
     * @type Index
     * @bean
     */
    getIndex: function() {
        var curve = this.getCurve();
        return curve && curve.getIndex();
    },

    /**
     * The length of the path from its beginning up to the location described
     * by this object. If the curve is not part of a path, then the length
     * within the curve is returned instead.
     *
     * @type Number
     * @bean
     */
    getOffset: function() {
        var path = this.getPath();
        return path ? path._getOffset(this) : this.getCurveOffset();
    },

    /**
     * The length of the curve from its beginning up to the location described
     * by this object.
     *
     * @type Number
     * @bean
     */
    getCurveOffset: function() {
        var curve = this.getCurve(),
            parameter = this.getParameter();
        return parameter != null && curve && curve.getPartLength(0, parameter);
    },

    /**
     * The curve parameter, as used by various bezier curve calculations. It is
     * value between {@code 0} (beginning of the curve) and {@code 1} (end of
     * the curve).
     *
     * @type Number
     * @bean
     */
    getParameter: function(_uncached) {
        if ((this._parameter == null || _uncached) && this._point) {
            var curve = this.getCurve(_uncached && this._point);
            this._parameter = curve && curve.getParameterOf(this._point);
        }
        return this._parameter;
    },

    /**
     * The point which is defined by the {@link #curve} and
     * {@link #parameter}.
     *
     * @type Point
     * @bean
     */
    getPoint: function(_uncached) {
        if ((!this._point || _uncached) && this._parameter != null) {
            var curve = this.getCurve();
            this._point = curve && curve.getPointAt(this._parameter, true);
        }
        return this._point;
    },

    /**
     * The tangential vector to the {@link #curve} at the given location.
     *
     * @name Item#tangent
     * @type Point
     */

    /**
     * The normal vector to the {@link #curve} at the given location.
     *
     * @name Item#normal
     * @type Point
     */

    /**
     * The curvature of the {@link #curve} at the given location.
     *
     * @name Item#curvature
     * @type Number
     */

    /**
     * The distance from the queried point to the returned location.
     *
     * @type Number
     * @bean
     */
    getDistance: function() {
        return this._distance;
    },

    divide: function() {
        var curve = this.getCurve(true);
        return curve && curve.divide(this.getParameter(true), true);
    },

    split: function() {
        var curve = this.getCurve(true);
        return curve && curve.split(this.getParameter(true), true);
    },

    /**
     * Checks whether tow CurveLocation objects are describing the same location
     * on a path, by applying the same tolerances as elsewhere when dealing with
     * curve time parameters.
     *
     * @param {CurveLocation} location
     * @return {Boolean} {@true if the locations are equal}
     */
    equals: function(loc) {
        var isZero = Numerical.isZero;
        return this === loc
                || loc
                    && this._curve === loc._curve
                    && this._curve2 === loc._curve2
                    && isZero(this._parameter - loc._parameter)
                    && isZero(this._parameter2 - loc._parameter2)
                || false;
    },

    /**
     * @return {String} a string representation of the curve location
     */
    toString: function() {
        var parts = [],
            point = this.getPoint(),
            f = Formatter.instance;
        if (point)
            parts.push('point: ' + point);
        var index = this.getIndex();
        if (index != null)
            parts.push('index: ' + index);
        var parameter = this.getParameter();
        if (parameter != null)
            parts.push('parameter: ' + f.number(parameter));
        if (this._distance != null)
            parts.push('distance: ' + f.number(this._distance));
        return '{ ' + parts.join(', ') + ' }';
    }
}, Base.each(['Tangent', 'Normal', 'Curvature'],
    // Produce getters for #getTangent() / #getNormal() / #getCurvature()
    function(name) {
        var get = 'get' + name + 'At';
        this['get' + name] = function() {
            var parameter = this.getParameter(),
                curve = this.getCurve();
            return parameter != null && curve && curve[get](parameter, true);
        };
    }, {}
));
<commit_msg>Streamline CurveLocation#getTangent/Normal/Curvature getter definitions to align with definitions in Curve, Path and PathIterator.<commit_after>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
 * http://scratchdisk.com/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name CurveLocation
 *
 * @class CurveLocation objects describe a location on {@link Curve}
 * objects, as defined by the curve {@link #parameter}, a value between
 * {@code 0} (beginning of the curve) and {@code 1} (end of the curve). If
 * the curve is part of a {@link Path} item, its {@link #index} inside the
 * {@link Path#curves} array is also provided.
 *
 * The class is in use in many places, such as
 * {@link Path#getLocationAt(offset, isParameter)},
 * {@link Path#getLocationOf(point)},
 * {@link Path#getNearestLocation(point),
 * {@link PathItem#getIntersections(path)},
 * etc.
 */
var CurveLocation = Base.extend(/** @lends CurveLocation# */{
    _class: 'CurveLocation',
    // Enforce creation of beans, as bean getters have hidden parameters.
    // See #getSegment() below.
    beans: true,

    // DOCS: CurveLocation class description: add these back when the  mentioned
    // functioned have been added: {@link Path#split(location)}
    /**
     * Creates a new CurveLocation object.
     *
     * @param {Curve} curve
     * @param {Number} parameter
     * @param {Point} point
     */
    initialize: function CurveLocation(curve, parameter, point, _curve2,
            _parameter2, _point2, _distance) {
        // Define this CurveLocation's unique id.
        this._id = CurveLocation._id = (CurveLocation._id || 0) + 1;
        this._curve = curve;
        // Also store references to segment1 and segment2, in case path
        // splitting / dividing is going to happen, in which case the segments
        // can be used to determine the new curves, see #getCurve(true)
        this._segment1 = curve._segment1;
        this._segment2 = curve._segment2;
        this._parameter = parameter;
        this._point = point;
        this._curve2 = _curve2;
        this._parameter2 = _parameter2;
        this._point2 = _point2;
        this._distance = _distance;
    },

    /**
     * The segment of the curve which is closer to the described location.
     *
     * @type Segment
     * @bean
     */
    getSegment: function(_preferFirst) {
        if (!this._segment) {
            var curve = this.getCurve(),
                parameter = this.getParameter();
            if (parameter === 1) {
                this._segment = curve._segment2;
            } else if (parameter === 0 || _preferFirst) {
                this._segment = curve._segment1;
            } else if (parameter == null) {
                return null;
            } else {
                // Determine the closest segment by comparing curve lengths
                this._segment = curve.getPartLength(0, parameter)
                    < curve.getPartLength(parameter, 1)
                        ? curve._segment1
                        : curve._segment2;
            }
        }
        return this._segment;
    },

    /**
     * The curve that this location belongs to.
     *
     * @type Curve
     * @bean
     */
    getCurve: function(_uncached) {
        if (!this._curve || _uncached) {
            // If we're asked to get the curve uncached, access current curve
            // objects through segment1 / segment2. Since path splitting or
            // dividing might have happened in the meantime, try segment1's
            // curve, and see if _point lies on it still, otherwise assume it's
            // the curve before segment2.
            this._curve = this._segment1.getCurve();
            if (this._curve.getParameterOf(this._point) == null)
                this._curve = this._segment2.getPrevious().getCurve();
        }
        return this._curve;
    },

    /**
     * The curve location on the intersecting curve, if this location is the
     * result of a call to {@link PathItem#getIntersections(path)} /
     * {@link Curve#getIntersections(curve)}.
     *
     * @type CurveLocation
     * @bean
     */
    getIntersection: function() {
        var intersection = this._intersection;
        if (!intersection && this._curve2) {
            var param = this._parameter2;
            // If we have the parameter on the other curve use that for
            // intersection rather than the point.
            this._intersection = intersection = new CurveLocation(
                    this._curve2, param, this._point2 || this._point, this);
            intersection._intersection = this;
        }
        return intersection;
    },

    /**
     * The path this curve belongs to, if any.
     *
     * @type Item
     * @bean
     */
    getPath: function() {
        var curve = this.getCurve();
        return curve && curve._path;
    },

    /**
     * The index of the curve within the {@link Path#curves} list, if the
     * curve is part of a {@link Path} item.
     *
     * @type Index
     * @bean
     */
    getIndex: function() {
        var curve = this.getCurve();
        return curve && curve.getIndex();
    },

    /**
     * The length of the path from its beginning up to the location described
     * by this object. If the curve is not part of a path, then the length
     * within the curve is returned instead.
     *
     * @type Number
     * @bean
     */
    getOffset: function() {
        var path = this.getPath();
        return path ? path._getOffset(this) : this.getCurveOffset();
    },

    /**
     * The length of the curve from its beginning up to the location described
     * by this object.
     *
     * @type Number
     * @bean
     */
    getCurveOffset: function() {
        var curve = this.getCurve(),
            parameter = this.getParameter();
        return parameter != null && curve && curve.getPartLength(0, parameter);
    },

    /**
     * The curve parameter, as used by various bezier curve calculations. It is
     * value between {@code 0} (beginning of the curve) and {@code 1} (end of
     * the curve).
     *
     * @type Number
     * @bean
     */
    getParameter: function(_uncached) {
        if ((this._parameter == null || _uncached) && this._point) {
            var curve = this.getCurve(_uncached && this._point);
            this._parameter = curve && curve.getParameterOf(this._point);
        }
        return this._parameter;
    },

    /**
     * The point which is defined by the {@link #curve} and
     * {@link #parameter}.
     *
     * @type Point
     * @bean
     */
    getPoint: function(_uncached) {
        if ((!this._point || _uncached) && this._parameter != null) {
            var curve = this.getCurve();
            this._point = curve && curve.getPointAt(this._parameter, true);
        }
        return this._point;
    },

    /**
     * The tangential vector to the {@link #curve} at the given location.
     *
     * @name Item#tangent
     * @type Point
     */

    /**
     * The normal vector to the {@link #curve} at the given location.
     *
     * @name Item#normal
     * @type Point
     */

    /**
     * The curvature of the {@link #curve} at the given location.
     *
     * @name Item#curvature
     * @type Number
     */

    /**
     * The distance from the queried point to the returned location.
     *
     * @type Number
     * @bean
     */
    getDistance: function() {
        return this._distance;
    },

    divide: function() {
        var curve = this.getCurve(true);
        return curve && curve.divide(this.getParameter(true), true);
    },

    split: function() {
        var curve = this.getCurve(true);
        return curve && curve.split(this.getParameter(true), true);
    },

    /**
     * Checks whether tow CurveLocation objects are describing the same location
     * on a path, by applying the same tolerances as elsewhere when dealing with
     * curve time parameters.
     *
     * @param {CurveLocation} location
     * @return {Boolean} {@true if the locations are equal}
     */
    equals: function(loc) {
        var isZero = Numerical.isZero;
        return this === loc
                || loc
                    && this._curve === loc._curve
                    && this._curve2 === loc._curve2
                    && isZero(this._parameter - loc._parameter)
                    && isZero(this._parameter2 - loc._parameter2)
                || false;
    },

    /**
     * @return {String} a string representation of the curve location
     */
    toString: function() {
        var parts = [],
            point = this.getPoint(),
            f = Formatter.instance;
        if (point)
            parts.push('point: ' + point);
        var index = this.getIndex();
        if (index != null)
            parts.push('index: ' + index);
        var parameter = this.getParameter();
        if (parameter != null)
            parts.push('parameter: ' + f.number(parameter));
        if (this._distance != null)
            parts.push('distance: ' + f.number(this._distance));
        return '{ ' + parts.join(', ') + ' }';
    }
}, Base.each(['getTangent', 'getNormal', 'getCurvature'], function(name) {
    // Produce getters for #getTangent() / #getNormal() / #getCurvature()
    var get = name + 'At';
    this[name] = function() {
        var parameter = this.getParameter(),
            curve = this.getCurve();
        return parameter != null && curve && curve[get](parameter, true);
    };
}, {}));
<|endoftext|>