<!DOCTYPE html>
<html>

  <head>
    <link data-require="bootstrap-css@3.2.0" data-semver="3.2.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js" data-semver="1.2.17" data-require="angular.js@1.2.17"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="angular-fcsa-number.js"></script>
    <script src="main-ctrl.js"></script>
  </head>

  <body ng-app="app" ng-controller="MainCtrl as ctrl">
    <h2>FCSA Number Demo</h2>
    <p><a href="https://github.com/FCSAmerica/angular-fcsa-number">https://github.com/FCSAmerica/angular-fcsa-number</a></p>
    <p>Here is a quick demo of the FCSA Number Angular directive that validates and formats numbers in text boxes. The text box will turn red when an invalid number is entered.</p>
    <form name="form">
      <div class="form-group" ng-class="{ 'has-error': form.numberNoOptions.$invalid }">
        <label>Default Functionality with No Options</label>
        <input type="text" fcsa-number="{  }" ng-model="ctrl.model.numberNoOptions" name="numberNoOptions" class="form-control" />
        <p class="help-block">
          Notice how the comma is removed when you enter the text box, and then it's re-added when you leave the textbox.
        </p>
      </div>
      <div class="form-group" ng-class="{ 'has-error': form.numberMax.$invalid }">
        <label>fcsa-number="{ max: 10 }"</label>
        <input type="text" fcsa-number="{ max: 10 }" ng-model="ctrl.model.numberMax" name="numberMax" class="form-control" />
        <p class="help-block">
          Any number above 10 will be marked invalid
        </p>
      </div>
      <div class="form-group" ng-class="{ 'has-error': form.numberMin.$invalid }">
        <label>fcsa-number="{ min: 0 }"</label>
        <input type="text" fcsa-number="{ min: 0 }" ng-model="ctrl.model.numberMin" name="numberMin" class="form-control" />
        <p class="help-block">
          Any number below 0 will be marked invalid
        </p>
      </div>
      <div class="form-group" ng-class="{ 'has-error': form.numberMaxDecimals.$invalid }">
        <label>fcsa-number="{ maxDecimals: 2 }"</label>
        <input type="text" fcsa-number="{ maxDecimals: 2 }" ng-model="ctrl.model.numberMaxDecimals" name="numberMaxDecimals" class="form-control" />
        <p class="help-block">
          Any number with more than 2 decimals will be marked invalid
        </p>
      </div>
      <div class="form-group" ng-class="{ 'has-error': form.numberMaxDigits.$invalid }">
        <label>fcsa-number="{ maxDigits: 3 }"</label>
        <input type="text" fcsa-number="{ maxDigits: 3 }" ng-model="ctrl.model.numberMaxDigits" name="numberMaxDigits" class="form-control" />
        <p class="help-block">
          Any number with more than 3 digits will be marked invalid
        </p>
      </div>
      <div class="form-group" ng-class="{ 'has-error': form.numberPrepend.$invalid }">
        <label>fcsa-number="{ prepend: '$' }"</label>
        <input type="text" fcsa-number="{ prepend: '$' }" ng-model="ctrl.model.numberPrepend" name="numberPrepend" class="form-control" />
        <p class="help-block">
          The '$' will be prepended to the number in the text box
        </p>
      </div>
      <div class="form-group" ng-class="{ 'has-error': form.numberAppend.$invalid }">
        <label>fcsa-number="{ append: '%' }"</label>
        <input type="text" fcsa-number="{ append: '%' }" ng-model="ctrl.model.numberAppend" name="numberPrepend" class="form-control" />
        <p class="help-block">
          The '%' will be appended to the number in the text box
        </p>
      </div>
      <div class="form-group" ng-class="{ 'has-error': form.numberMultipleOptions.$invalid }">
        <label>fcsa-number="{ max: 10, min: 0, maxDecimals: 2 }"</label>
        <input type="text" fcsa-number="{ max: 10, min: 0, maxDecimals: 2 }" ng-model="ctrl.model.numberMultipleOptions" name="numberMultipleOptions" class="form-control" />
        <p class="help-block">
          It's possible to set multiple options
        </p>
      </div>
    </form>
  </body>

</html>
// Code goes here

/* Styles go here */

body {
  padding: 20px;
}
(function() {
  angular.module('fcsa-number', []).directive('fcsaNumber', function() {
    var addCommasToInteger, controlKeys, hasMultipleDecimals, isNotControlKey, isNotDigit, isNumber, makeIsValid, makeMaxDecimals, makeMaxDigits, makeMaxNumber, makeMinNumber;
    isNumber = function(val) {
      return !isNaN(parseFloat(val)) && isFinite(val);
    };
    isNotDigit = function(which) {
      return which < 45 || which > 57 || which === 47;
    };
    controlKeys = [0, 8, 13];
    isNotControlKey = function(which) {
      return controlKeys.indexOf(which) === -1;
    };
    hasMultipleDecimals = function(val) {
      return (val != null) && val.toString().split('.').length > 2;
    };
    makeMaxDecimals = function(maxDecimals) {
      var regexString, validRegex;
      if (maxDecimals > 0) {
        regexString = "^-?\\d*\\.?\\d{0," + maxDecimals + "}$";
      } else {
        regexString = "^-?\\d*$";
      }
      validRegex = new RegExp(regexString);
      return function(val) {
        return validRegex.test(val);
      };
    };
    makeMaxNumber = function(maxNumber) {
      return function(val, number) {
        return number <= maxNumber;
      };
    };
    makeMinNumber = function(minNumber) {
      return function(val, number) {
        return number >= minNumber;
      };
    };
    makeMaxDigits = function(maxDigits) {
      var validRegex;
      validRegex = new RegExp("^-?\\d{0," + maxDigits + "}(\\.\\d*)?$");
      return function(val) {
        return validRegex.test(val);
      };
    };
    makeIsValid = function(options) {
      var validations;
      validations = [];
      if (options.maxDecimals != null) {
        validations.push(makeMaxDecimals(options.maxDecimals));
      }
      if (options.max != null) {
        validations.push(makeMaxNumber(options.max));
      }
      if (options.min != null) {
        validations.push(makeMinNumber(options.min));
      }
      if (options.maxDigits != null) {
        validations.push(makeMaxDigits(options.maxDigits));
      }
      return function(val) {
        var i, number, _i, _ref;
        if (!isNumber(val)) {
          return false;
        }
        if (hasMultipleDecimals(val)) {
          return false;
        }
        number = Number(val);
        for (i = _i = 0, _ref = validations.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
          if (!validations[i](val, number)) {
            return false;
          }
        }
        return true;
      };
    };
    addCommasToInteger = function(val) {
      var commas, decimals, wholeNumbers;
      decimals = val.indexOf('.') == -1 ? '' : val.replace(/^\d+(?=\.)/, '');
      wholeNumbers = val.replace(/(\.\d+)$/, '');
      commas = wholeNumbers.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
      return "" + commas + decimals;
    };
    return {
      restrict: 'A',
      require: 'ngModel',
      scope: {
        options: '@fcsaNumber'
      },
      link: function(scope, elem, attrs, ngModelCtrl) {
        var isValid, options;
        options = {};
        if (scope.options != null) {
          options = scope.$eval(scope.options);
        }
        isValid = makeIsValid(options);
        ngModelCtrl.$parsers.unshift(function(viewVal) {
          var noCommasVal;
          noCommasVal = viewVal.replace(/,/g, '');
          if (isValid(noCommasVal) || !noCommasVal) {
            ngModelCtrl.$setValidity('fcsaNumber', true);
            return noCommasVal;
          } else {
            ngModelCtrl.$setValidity('fcsaNumber', false);
            return void 0;
          }
        });
        ngModelCtrl.$formatters.push(function(val) {
          if ((options.nullDisplay != null) && (!val || val === '')) {
            return options.nullDisplay;
          }
          if ((val == null) || !isValid(val)) {
            return val;
          }
          ngModelCtrl.$setValidity('fcsaNumber', true);
          val = addCommasToInteger(val.toString());
          if (options.prepend != null) {
            val = "" + options.prepend + val;
          }
          if (options.append != null) {
            val = "" + val + options.append;
          }
          return val;
        });
        elem.on('blur', function() {
          var formatter, viewValue, _i, _len, _ref;
          viewValue = ngModelCtrl.$modelValue;
          if ((viewValue == null) || !isValid(viewValue)) {
            return;
          }
          _ref = ngModelCtrl.$formatters;
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            formatter = _ref[_i];
            viewValue = formatter(viewValue);
          }
          ngModelCtrl.$viewValue = viewValue;
          return ngModelCtrl.$render();
        });
        elem.on('focus', function() {
          var val;
          val = elem.val();
          if (options.prepend != null) {
            val = val.replace(options.prepend, '');
          }
          if (options.append != null) {
            val = val.replace(options.append, '');
          }
          elem.val(val.replace(/,/g, ''));
          return elem[0].select();
        });
        if (options.preventInvalidInput === true) {
          return elem.on('keypress', function(e) {
            if (isNotDigit(e.which && isNotControlKey(e.which))) {
              return e.preventDefault();
            }
          });
        }
      }
    };
  });

}).call(this);
angular.module('app', ['fcsa-number']).controller('MainCtrl', function() {
  this.model = {
    numberNoOptions: 1000,
    numberMax: 9,
    numberMin: 1,
    numberMaxDecimals: 9.87,
    numberMaxDigits: 393,
    numberPrepend: 5.97,
    numberAppend: 100,
    numberMultipleOptions: 1.25
  };
});