contract JarvisPlusTokenCrowdsale is
Crowdsale,
CappedCrowdsale,
TimedCrowdsale,
WhitelistCrowdsale,
PostDeliveryCrowdsale,
Ownable
{
  // Track investor contributions
  uint256 public investorMinCap  = 500000000000000000;      // 0.5 ether
  uint256 public investorHardCap = 10000000000000000000;    // 10 ether
  uint256 _rate = 7200;
  mapping(address => uint256) public contributions;

  constructor(
    IERC20 _token,
    uint256 _cap,
    uint256 _openingTime,
    uint256 _closingTime,
    address[] memory _operators
  )
    Crowdsale(_rate, msg.sender, _token)
    CappedCrowdsale(_cap)
    TimedCrowdsale(_openingTime, _closingTime)
    public
  {
    for (uint i = 0; i < _operators.length; i++) {
      _addWhitelisted(_operators[i]);
    }
  }

  /**
  * @dev Returns the amount contributed so far by a sepecific user.
  * @param _beneficiary Address of contributor
  * @return User contribution so far
  */
  function getContribution(address _beneficiary)
    public view returns (uint256)
  {
    return contributions[_beneficiary];
  }

  /**
  * @dev Owner could redeem surplus tokens from the crowdsale contract address
  */
  function redeem() public onlyOwner
  {
    IERC20 token = token();
    uint256 balance = token.balanceOf(address(this));
    require(balance > 0, "JarvisPlusTokenCrowdsale: no tokens to redeem");
    token.safeTransfer(owner(), balance);
  }

  /**
  * @dev Extend parent behavior requiring purchase to respect the beneficiary's funding cap.
  * @param beneficiary Token purchaser
  * @param weiAmount Amount of wei contributed
  */
  function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
      super._preValidatePurchase(beneficiary, weiAmount);
      // solhint-disable-next-line max-line-length
      uint256 newContribution = contributions[beneficiary].add(weiAmount);
      require(newContribution <= investorHardCap, "JarvisPlusTokenCrowdsale: beneficiary's max cap exceeded");
      require(newContribution >= investorMinCap, "JarvisPlusTokenCrowdsale: beneficiary's min cap not reached");
  }

  /**
  * @dev Extend parent behavior to update beneficiary contributions.
  * @param beneficiary Token purchaser
  * @param weiAmount Amount of wei contributed
  */
  function _updatePurchasingState(address beneficiary, uint256 weiAmount) internal {
      super._updatePurchasingState(beneficiary, weiAmount);
      contributions[beneficiary] = contributions[beneficiary].add(weiAmount);
  }

}
function redeem() public onlyOwner
{
  IERC20 token = token();
  uint256 balance = token.balanceOf(address(this));
  require(balance > 0, "JarvisPlusTokenCrowdsale: no tokens to redeem");
  token.safeTransfer(owner(), balance);
}

/**
 * @dev Owner could redeem surplus tokens from the crowdsale contract address
 */