<commit_before>var util = require('util'),
    webutil = require('../util/web'),
    Tab = require('../client/tab').Tab,
    Amount = ripple.Amount,
    Base = ripple.Base,
    RippleError = ripple.RippleError;

var SendTab = function ()
{
  Tab.call(this);
};

util.inherits(SendTab, Tab);

SendTab.prototype.tabName = 'send';
SendTab.prototype.mainMenu = 'send';

SendTab.prototype.angularDeps = Tab.prototype.angularDeps.concat(['federation', 'keychain']);

SendTab.prototype.generateHtml = function ()
{
  return require('../../jade/tabs/send.jade')();
};

SendTab.prototype.angular = function (module)
{
  module.controller('SendCtrl', ['$scope', '$timeout', '$routeParams', 'rpId',
                                 'rpNetwork', 'rpFederation', 'rpTracker',
                                 'rpKeychain',
                                 function ($scope, $timeout, $routeParams, $id,
                                           $network, $federation, $rpTracker,
                                           keychain)
  {
    if (!$id.loginStatus) return $id.goId();

    var timer;

    // XRP currency object.
    // {name: "XRP - Ripples", order: 146, value: "XRP"}
    $scope.xrp = _.where($scope.currencies_all, {value: "XRP"})[0];

    $scope.$watch('send.recipient', function(){
      // raw address without any parameters
      var address = webutil.stripRippleAddress($scope.send.recipient);

      $scope.contact = webutil.getContact($scope.userBlob.data.contacts, address);

      // Sets
      // send.recipient, send.recipient_name, send.recipient_address, send.dt.
      if ($scope.contact) {
        if ($scope.send.recipient === $scope.contact.address) {
          $scope.send.recipient = $scope.contact.name;
        }
        $scope.send.recipient_name = $scope.contact.name;
        $scope.send.recipient_address = $scope.contact.address;

        if ($scope.contact.dt) {
          $scope.send.dt = $scope.contact.dt;
        }
      }
      else {
        $scope.send.recipient_name = '';
        $scope.send.recipient_address = address;
      }

      $scope.update_destination();
    }, true);

    $scope.$watch('send.currency', function () {
      var currency = ripple.Currency.from_json($scope.send.currency);
      if ($scope.send.currency !== '' && currency.is_valid()) {
        $scope.send.currency_code = currency.to_human().toUpperCase();
      } else {
        $scope.send.currency_code = '';
      }
      $scope.update_currency();
    }, true);

    $scope.$watch('send.amount', function () {
      $scope.update_amount();
    }, true);

    $scope.$watch('send.extra_fields', function () {
      $scope.update_amount();
    }, true);

    // When the send form is invalid, path finding won't trigger. So if the form
    // is changed by one of the update_* handlers and becomes valid during the
    // next digest, we need to manually trigger another update_amount.
    $scope.$watch('sendForm.$valid', function () {
      $scope.update_amount();
    });

    var destUpdateTimeout;

    // Reset everything that depends on the destination
    $scope.reset_destination_deps = function() {
      var send = $scope.send;
      send.self = false;
      send.quote_url = false;
      send.federation = false;
      send.fund_status = "none";
      send.extra_fields = [];

      // Reset federation address validity status
      if ($scope.sendForm && $scope.sendForm.send_destination)
        $scope.sendForm.send_destination.$setValidity("federation", true);

      // Now starting to work on resolving the recipient
      send.recipient_resolved = false;

      $scope.reset_currency_deps();
    };

    $scope.check_dt_visibility = function () {
      var send = $scope.send;

      send.show_dt_field = ($routeParams.dt
        || send.dt
        || send.recipient_info.dest_tag_required)
          && !send.federation;
    };

    $scope.update_destination = function () {
      var send = $scope.send;
      var recipient = send.recipient_address;

      if (recipient === send.last_recipient) return;

      // Trying to send to a Bitcoin address
      if (!isNaN(Base.decode_check([0, 5], recipient, 'bitcoin'))) {
        if (Options.bridge.out.bitcoin) { // And there is a default bridge
          recipient += '@' + Options.bridge.out.bitcoin
          send.recipient_address = recipient
        }
      }

      send.last_recipient = recipient;

      $scope.reset_destination_deps();

      // Trying to send XRP to self.
      // This is used to disable 'Send XRP' button
      send.self = recipient === $scope.address;

      // Trying to send to a Ripple name
      send.rippleName = webutil.isRippleName(recipient);

      // Trying to send to an email/federation address
      send.federation = ("string" === typeof recipient) && ~recipient.indexOf('@');

      // Check destination tag visibility
      $scope.check_dt_visibility();

      if (destUpdateTimeout) $timeout.cancel(destUpdateTimeout);
      destUpdateTimeout = $timeout($scope.update_destination_remote, 500);
    };

    $scope.update_destination_remote = function () {
      var send = $scope.send;
      var recipient = send.recipient_address;

      // Reset federation address validity status
      if ($scope.sendForm && $scope.sendForm.send_destination)
        $scope.sendForm.send_destination.$setValidity("federation", true);

      // If there was a previous federation request, we need to clean it up here.
      if (send.federation_record) {
        send.federation_record = null;
        send.dt = null;
      }

      if (send.federation) {
        send.path_status = "fed-check";
        $federation.check_email(recipient)
          .then(function (result) {
            // Check if this request is still current, exit if not
            var now_recipient = send.recipient_actual || send.recipient_address;
            if (recipient !== now_recipient) return;

            send.federation_record = result;

            if (result.extra_fields) {
              send.extra_fields = result.extra_fields;
            }

            send.dt = ("number" === typeof result.dt) ? result.dt : undefined;

            if (result.destination_address) {
              // Federation record specifies destination
              send.recipient_name = recipient;
              send.recipient_address = result.destination_address;

              $scope.check_destination();
            } else if (result.quote_url) {
              // Federation destination requires us to request a quote
              send.quote_url = result.quote_url;
              send.quote_destination = result.destination;
              send.path_status = "waiting";
              $scope.update_currency_constraints();
            } else {
              // Invalid federation result
              send.path_status = "waiting";
              $scope.sendForm.send_destination.$setValidity("federation", false);
              // XXX Show specific error message
            }
          }, function () {
            // Check if this request is still current, exit if not
            var now_recipient = send.recipient_actual || send.recipient_address;
            if (recipient !== now_recipient) return;

            send.path_status = "waiting";
            $scope.sendForm.send_destination.$setValidity("federation", false);
          })
        ;
      }
      else if (send.rippleName) {
        ripple.AuthInfo.get(Options.domain,send.recipient,function(err, response) {
          $scope.$apply(function(){
            send.recipient_name = '~' + response.username;
            send.recipient_address = response.address;
          });

          $scope.check_destination();
        })
      }
      else {
        $scope.check_destination();
      }
    };

    // Check destination for XRP sufficiency and flags
    $scope.check_destination = function () {
      var send = $scope.send;
      var recipient = send.recipient_actual || send.recipient_address;

      if (!ripple.UInt160.is_valid(recipient)) return;

      var account = $network.remote.account(recipient);

      send.path_status = 'checking';
      account.entry(function (e, data) {
        $scope.$apply(function () {
          // Check if this request is still current, exit if not
          var now_recipient = send.recipient_actual || send.recipient_address;
          if (recipient !== now_recipient) return;

          // If we get this far, we have a Ripple address resolved
          send.recipient_resolved = true;

          if (e) {
            if (e.remote.error === "actNotFound") {
              send.recipient_info = {
                'loaded': true,
                'exists': false,
                'Balance': "0"
              };
              $scope.update_currency_constraints();
            } else {
              // XXX Actual error
            }
          } else {
            send.recipient_info = {
              'loaded': true,
              'exists': true,
              'Balance': data.account_data.Balance,

              // Flags
              'disallow_xrp': data.account_data.Flags & ripple.Remote.flags.account_root.DisallowXRP,
              'dest_tag_required': data.account_data.Flags & ripple.Remote.flags.account_root.RequireDestTag
            };

            // Check destination tag visibility
            $scope.check_dt_visibility();

            if (!$scope.account || !$scope.account.reserve_base) return;

            var reserve_base = $scope.account.reserve_base;
            send.xrp_deficiency = reserve_base.subtract(data.account_data.Balance);

            send.recipient_lines = false;
            $scope.update_currency_constraints();
          }
        });
      });
    };

    /**
     * Update any constraints on what currencies the user can select.
     *
     * In many modes, the user is restricted in terms of what they can send.
     * For example, when sending to a Bitcoin address, they can only send BTC.
     *
     * This function checks those conditions and updates the UI.
     */
    $scope.update_currency_constraints = function () {
      var send = $scope.send;

      // Reset constraints
      send.currency_choices = $scope.currencies_all;
      send.currency_force = false;

      send.currency_choices_constraints = {};

      // Federation response can specific a fixed amount
      if (send.federation_record &&
          "undefined" !== typeof send.federation_record.amount) {
        send.force_amount = Amount.from_json(send.federation_record.amount);
        send.amount = send.force_amount.to_text();
        send.currency_choices_constraints.federation = [send.force_amount.currency().to_json()];

      // Apply federation currency restrictions
      } else if (send.federation_record &&
          $.isArray(send.federation_record.currencies) &&
          send.federation_record.currencies.length >= 1 &&
          "object" === typeof send.federation_record.currencies[0] &&
          "string" === typeof send.federation_record.currencies[0].currency) {
        // XXX Do some validation on this
        send.currency_choices_constraints.federation = [];
        $.each(send.federation_record.currencies, function () {
          send.currency_choices_constraints.federation.push(this.currency);
        });
      }

      if (!send.recipient_info.loaded) return;

      if (send.recipient_info.exists) {
        // Check allowed currencies for this address
        var requestedRecipientAddress = send.recipient_address;
        send.currency_choices_constraints.accountLines = 'pending';
        $network.remote.request_account_currencies(requestedRecipientAddress)
          .on('success', function (data) {
            $scope.$apply(function () {
              if (data.receive_currencies &&
                  // We need to make sure the destination account hasn't changed
                  send.recipient_address === requestedRecipientAddress) {
                send.currency_choices_constraints.accountLines = data.receive_currencies;

                // add XRP if it's allowed
                if (!$scope.send.recipient_info.disallow_xrp) {
                  send.currency_choices_constraints.accountLines.unshift('XRP');
                }

                $scope.update_currency_choices();
              }
            });
          })
          .on('error', function () {})
          .request();
      } else if (send.recipient_info.exists) {
        // Their account exists, but we couldn't grab their trust lines,
        // probably because their owner directory is too large. So, we'll
        // just show a default selection of currencies.

        // If we do nothing here, we'll be showing the default currency list

        // Do nothing
      } else {
        // If the account doesn't exist, we can only send XRP
        send.currency_choices_constraints.accountLines = ["XRP"];
      }

      $scope.update_currency_choices();
    };

    // Generate list of accepted currencies<commit_msg>[FIX] Resolve issues with bridges that require quotes.<commit_after>var util = require('util'),
    webutil = require('../util/web'),
    Tab = require('../client/tab').Tab,
    Amount = ripple.Amount,
    Base = ripple.Base,
    RippleError = ripple.RippleError;

var SendTab = function ()
{
  Tab.call(this);
};

util.inherits(SendTab, Tab);

SendTab.prototype.tabName = 'send';
SendTab.prototype.mainMenu = 'send';

SendTab.prototype.angularDeps = Tab.prototype.angularDeps.concat(['federation', 'keychain']);

SendTab.prototype.generateHtml = function ()
{
  return require('../../jade/tabs/send.jade')();
};

SendTab.prototype.angular = function (module)
{
  module.controller('SendCtrl', ['$scope', '$timeout', '$routeParams', 'rpId',
                                 'rpNetwork', 'rpFederation', 'rpTracker',
                                 'rpKeychain',
                                 function ($scope, $timeout, $routeParams, $id,
                                           $network, $federation, $rpTracker,
                                           keychain)
  {
    if (!$id.loginStatus) return $id.goId();

    var timer;

    // XRP currency object.
    // {name: "XRP - Ripples", order: 146, value: "XRP"}
    $scope.xrp = _.where($scope.currencies_all, {value: "XRP"})[0];

    $scope.$watch('send.recipient', function(){
      // raw address without any parameters
      var address = webutil.stripRippleAddress($scope.send.recipient);

      $scope.contact = webutil.getContact($scope.userBlob.data.contacts, address);

      // Sets
      // send.recipient, send.recipient_name, send.recipient_address, send.dt.
      if ($scope.contact) {
        if ($scope.send.recipient === $scope.contact.address) {
          $scope.send.recipient = $scope.contact.name;
        }
        $scope.send.recipient_name = $scope.contact.name;
        $scope.send.recipient_address = $scope.contact.address;

        if ($scope.contact.dt) {
          $scope.send.dt = $scope.contact.dt;
        }
      }
      else {
        $scope.send.recipient_name = '';
        $scope.send.recipient_address = address;
      }

      $scope.update_destination();
    }, true);

    $scope.$watch('send.currency', function () {
      var currency = ripple.Currency.from_json($scope.send.currency);
      if ($scope.send.currency !== '' && currency.is_valid()) {
        $scope.send.currency_code = currency.to_human().toUpperCase();
      } else {
        $scope.send.currency_code = '';
      }
      $scope.update_currency();
    }, true);

    $scope.$watch('send.amount', function () {
      $scope.update_amount();
    }, true);

    $scope.$watch('send.extra_fields', function () {
      $scope.update_amount();
    }, true);

    // When the send form is invalid, path finding won't trigger. So if the form
    // is changed by one of the update_* handlers and becomes valid during the
    // next digest, we need to manually trigger another update_amount.
    $scope.$watch('sendForm.$valid', function () {
      $scope.update_amount();
    });

    var destUpdateTimeout;

    // Reset everything that depends on the destination
    $scope.reset_destination_deps = function() {
      var send = $scope.send;
      send.self = false;
      send.quote_url = false;
      send.federation = false;
      send.fund_status = "none";
      send.extra_fields = [];

      // Reset federation address validity status
      if ($scope.sendForm && $scope.sendForm.send_destination)
        $scope.sendForm.send_destination.$setValidity("federation", true);

      // Now starting to work on resolving the recipient
      send.recipient_resolved = false;

      $scope.reset_currency_deps();
    };

    $scope.check_dt_visibility = function () {
      var send = $scope.send;

      send.show_dt_field = ($routeParams.dt
        || send.dt
        || send.recipient_info.dest_tag_required)
          && !send.federation;
    };

    $scope.update_destination = function () {
      var send = $scope.send;
      var recipient = send.recipient_address;

      if (recipient === send.last_recipient) return;

      // Trying to send to a Bitcoin address
      if (!isNaN(Base.decode_check([0, 5], recipient, 'bitcoin'))) {
        if (Options.bridge.out.bitcoin) { // And there is a default bridge
          recipient += '@' + Options.bridge.out.bitcoin
          send.recipient_address = recipient
        }
      }

      send.last_recipient = recipient;

      $scope.reset_destination_deps();

      // Trying to send XRP to self.
      // This is used to disable 'Send XRP' button
      send.self = recipient === $scope.address;

      // Trying to send to a Ripple name
      send.rippleName = webutil.isRippleName(recipient);

      // Trying to send to an email/federation address
      send.federation = ("string" === typeof recipient) && ~recipient.indexOf('@');

      // Check destination tag visibility
      $scope.check_dt_visibility();

      if (destUpdateTimeout) $timeout.cancel(destUpdateTimeout);
      destUpdateTimeout = $timeout($scope.update_destination_remote, 500);
    };

    $scope.update_destination_remote = function () {
      var send = $scope.send;
      var recipient = send.recipient_address;

      // Reset federation address validity status
      if ($scope.sendForm && $scope.sendForm.send_destination)
        $scope.sendForm.send_destination.$setValidity("federation", true);

      // If there was a previous federation request, we need to clean it up here.
      if (send.federation_record) {
        send.federation_record = null;
        send.dt = null;
      }

      if (send.federation) {
        send.path_status = "fed-check";
        $federation.check_email(recipient)
          .then(function (result) {
            // Check if this request is still current, exit if not
            var now_recipient = send.recipient_address;
            if (recipient !== now_recipient) return;

            send.federation_record = result;

            if (result.extra_fields) {
              send.extra_fields = result.extra_fields;
            }

            send.dt = ("number" === typeof result.dt) ? result.dt : undefined;

            if (result.destination_address) {
              // Federation record specifies destination
              send.recipient_name = recipient;
              send.recipient_address = result.destination_address;

              $scope.check_destination();
            } else if (result.quote_url) {
              // Federation destination requires us to request a quote
              send.quote_url = result.quote_url;
              send.quote_destination = result.destination;
              send.path_status = "waiting";
              $scope.update_currency_constraints();
            } else {
              // Invalid federation result
              send.path_status = "waiting";
              $scope.sendForm.send_destination.$setValidity("federation", false);
              // XXX Show specific error message
            }
          }, function () {
            // Check if this request is still current, exit if not
            var now_recipient = send.recipient_actual || send.recipient_address;
            if (recipient !== now_recipient) return;

            send.path_status = "waiting";
            $scope.sendForm.send_destination.$setValidity("federation", false);
          })
        ;
      }
      else if (send.rippleName) {
        ripple.AuthInfo.get(Options.domain,send.recipient,function(err, response) {
          $scope.$apply(function(){
            send.recipient_name = '~' + response.username;
            send.recipient_address = response.address;
          });

          $scope.check_destination();
        })
      }
      else {
        $scope.check_destination();
      }
    };

    // Check destination for XRP sufficiency and flags
    $scope.check_destination = function () {
      var send = $scope.send;
      var recipient = send.recipient_actual || send.recipient_address;

      if (!ripple.UInt160.is_valid(recipient)) return;

      var account = $network.remote.account(recipient);

      send.path_status = 'checking';
      send.recipient_info = null;
      account.entry(function (e, data) {
        $scope.$apply(function () {
          // Check if this request is still current, exit if not
          var now_recipient = send.recipient_actual || send.recipient_address;
          if (recipient !== now_recipient) return;

          // If we get this far, we have a Ripple address resolved
          send.recipient_resolved = true;

          if (e) {
            if (e.remote.error === "actNotFound") {
              send.recipient_info = {
                'loaded': true,
                'exists': false,
                'Balance': "0"
              };
              $scope.update_currency_constraints();
            } else {
              // XXX Actual error
            }
          } else {
            send.recipient_info = {
              'loaded': true,
              'exists': true,
              'Balance': data.account_data.Balance,

              // Flags
              'disallow_xrp': data.account_data.Flags & ripple.Remote.flags.account_root.DisallowXRP,
              'dest_tag_required': data.account_data.Flags & ripple.Remote.flags.account_root.RequireDestTag
            };

            // Check destination tag visibility
            $scope.check_dt_visibility();

            if (!$scope.account || !$scope.account.reserve_base) return;

            var reserve_base = $scope.account.reserve_base;
            send.xrp_deficiency = reserve_base.subtract(data.account_data.Balance);

            send.recipient_lines = false;
            $scope.update_currency_constraints();
          }
        });
      });
    };

    /**
     * Update any constraints on what currencies the user can select.
     *
     * In many modes, the user is restricted in terms of what they can send.
     * For example, when sending to a Bitcoin address, they can only send BTC.
     *
     * This function checks those conditions and updates the UI.
     */
    $scope.update_currency_constraints = function () {
      var send = $scope.send;

      // Reset constraints
      send.currency_choices = $scope.currencies_all;
      send.currency_force = false;

      send.currency_choices_constraints = {};

      // Federation response can specific a fixed amount
      if (send.federation_record &&
          "undefined" !== typeof send.federation_record.amount) {
        send.force_amount = Amount.from_json(send.federation_record.amount);
        send.amount = send.force_amount.to_text();
        send.currency_choices_constraints.federation = [send.force_amount.currency().to_json()];

      // Apply federation currency restrictions
      } else if (send.federation_record &&
          $.isArray(send.federation_record.currencies) &&
          send.federation_record.currencies.length >= 1 &&
          "object" === typeof send.federation_record.currencies[0] &&
          "string" === typeof send.federation_record.currencies[0].currency) {
        // XXX Do some validation on this
        send.currency_choices_constraints.federation = [];
        $.each(send.federation_record.currencies, function () {
          send.currency_choices_constraints.federation.push(this.currency);
        });
      }

      // If we don't have information about the recipient Ripple account yet,
      // we'll just return. We'll get back here once we have that information.
      //
      // Except: If this a bridge where we need a quote, we need to enter an
      // amount first, before we can even find out who the recipient is. So
      // if there is a quote_url, we want to pass this by.
      if (!send.recipient_info.loaded && !send.quote_url) return;

      if (send.recipient_info.loaded) {
        if (send.recipient_info.exists) {
          // Check allowed currencies for this address
          var requestedRecipientAddress = send.recipient_address;
          send.currency_choices_constraints.accountLines = 'pending';
          $network.remote.request_account_currencies(requestedRecipientAddress)
            .on('success', function (data) {
              $scope.$apply(function () {
                if (data.receive_currencies &&
                    // We need to make sure the destination account hasn't changed
                    send.recipient_address === requestedRecipientAddress) {
                  send.currency_choices_constraints.accountLines = data.receive_currencies;

                  // add XRP if it's allowed
                  if (!$scope.send.recipient_info.disallow_xrp) {
                    send.currency_choices_constraints.accountLines.unshift('XRP');
                  }

                  $scope.update_currency_choices();
                }
              });
            })
            .on('error', function () {})
            .request();
        } else {
          // If the account doesn't exist, we can only send XRP
          send.currency_choices_constraints.accountLines = ["XRP"];
        }
      }

      $scope.update_currency_choices();
    };

    // Generate list of accepted currencies
{'fraction_non_alphanumeric': 0.11474530831099196, 'fraction_numerical': 0.0012255840674071238, 'mean_word_length': 2.160372791091745, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '876417', 'n_tokens_mistral': 7477, 'n_tokens_neox': 7043, 'n_words': 1604}