uccser/cs-field-guide

View on GitHub
csfieldguide/static/interactives/big-number-calculator/js/big-number-calculator.js

Summary

Maintainability
B
5 hrs
Test Coverage
"use strict";

const Big = require('big.js');
// Dictionary key is the number of digits the base (x) has.
// Dictionary value is the maximum value that the exponent (y) can have for the given number of digits.
// E.g for bases that have a value in the range 10 - 99 (2 digits), the exponent value must not exceed 15,000.
const MAX_EXPONENT_VALUES = {
  '1': 50000,
  '2': 15000,
  '3': 7500,
  '4': 6000,
  '5': 5000,
  '6': 999,
  '7': 99
};
const TIMEOUT_MS = 10;

$(document).ready(function () {
  $('#interactive-big-number-calculator button').on('click', function(){
    var button_type = $(this).attr('id').split('-').pop();
    var x = getXValue();
    var y = getYValue();

    if (x && button_type == 'factorial') {
      factorial(x);
    } else if (x && y) {
      if (button_type == 'addition') {
        updateResult(x.add(y).toFixed(), true);
      }
      else if (button_type == 'subtraction') {
        updateResult(x.minus(y).toFixed(), true);
      }
      else if (button_type == 'multiply') {
        if (x.toString().length > 5000 || y.toString().length > 5000) {
          updateResult(gettext("The result of this calculation could be massive! We won't try calculating this number as it's so big!"), false);
        } else {
          updateResult(x.times(y).toFixed(), true);
        }
      }
      else if (button_type == 'division') {
        try {
          updateResult(x.div(y).toFixed(), true);
        } catch (exception) {
          if (y == 0) {
            updateResult(gettext("You can't divide by zero!"), false);
          } else {
            updateResult(exception, false);
          }
        }
      }
      else if (button_type == 'power') {
        try {
          var y = parseInt(document.getElementById('interactive-big-number-calculator-y').value.replace(/[\,\s]/g, ""));

          var x_length = x.toString().length;
          var y_length = y.toString().length;
          // big.js cannot calculate powers when the exponent is greater than 1 million
          // however we don't want to throw an error if the base is 1
          if (x == 1) {
            updateResult(1, true);
          } else if (y_length == 1 || y_length == 2) {
            disableButtons();
            setTimeout(function() {
              updateResult(x.pow(y).toFixed(0), true);
            }, TIMEOUT_MS);
          } else if ((x_length + y_length) < 10) {
            // check if the exponent is less than the maximum allowed value
            if (y <= MAX_EXPONENT_VALUES[x_length]) {
              disableButtons();
              setTimeout(function() {
                updateResult(x.pow(y).toFixed(0), true);
              }, TIMEOUT_MS);
            } else {
              throw false;
            }
          } else {
            throw false;
          }
        } catch (exception) {
          updateResult(gettext("The result of this calculation could be massive! We won't try calculating this number as it's so big!"), false);
        }
      }
    } else if (x === undefined) {
      updateResult(gettext("Error! Your X value is not a valid number."), false);
    } else if (y === undefined) {
      updateResult(gettext("Error! Your Y value is not a valid number."), false);
    }

  });

  // On 'Clear' click
  $('#interactive-big-number-calculator-clear').on('click', function(){
    $('#interactive-big-number-calculator-x').val('').trigger('autoresize');
    $('#interactive-big-number-calculator-y').val('').trigger('autoresize');
    $('#interactive-big-number-calculator-result').text('');
  });
});


function factorial(value) {
  if (value < 1800) {
    var total = new Big(value);
    for (var i = value - 1; i > 0; i--) {
        total = total.times(i);
    }
    updateResult(total.toFixed(), true);
  } else {
    updateResult(gettext("The result of this factorial could be massive, over 5000 digits long! We won't try calculating this number as it's so big!"), false);
  }
};


function getXValue() {
  var x;
  try {
    x = new Big(document.getElementById('interactive-big-number-calculator-x').value.replace(/[\,\s]/g, ""));
  } catch (e) {
    x = undefined;
  } finally {
    return x;
  }
};


function getYValue() {
  var y;
  try {
    y = new Big(document.getElementById('interactive-big-number-calculator-y').value.replace(/[\,\s]/g, ""));
  } catch (e) {
    y = undefined;
  } finally {
    return y;
  }
};


function updateResult(string, is_success) {
  $('#interactive-big-number-calculator button').removeClass('disabled');
  var $result = $('#interactive-big-number-calculator-result');
  $result.text(string);
  if (is_success == true) {
    $result.removeClass('calculating').removeClass('error');
  } else {
    $result.removeClass('calculating').addClass('error');
  }
};

function disableButtons() {
  $('#interactive-big-number-calculator button').addClass('disabled');
  $('#interactive-big-number-calculator-result')
  .removeClass('error')
  .addClass('calculating')
  .text(gettext('Calculating...'));
};