<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@1.1.4" data-semver="1.1.4" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.js"></script>
    <script data-require="underscore.js@1.5.1" data-semver="1.5.1" src="//cdn.jsdelivr.net/underscorejs/1.5.1/underscore-min.js"></script>
    <script data-require="moment.js@*" data-semver="2.5.1" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-app="myApp" ng-controller="searchCtrl">

    <div class="errors">
      <!-- We use submitted to hide all error msgs until its actually submitted-->
      <ul ng-show="searchForm.submitted">
        <li data-ng-show="searchForm.leaving_from.$invalid">Departure Airport is invalid</li>
        <li data-ng-show="searchForm.leaving_from.$error.required">Departure Airport is required</li>
        <li data-ng-show="searchForm.leaving_from.$error.minlength">Departure Airport should be at least 3 charachters</li>
        <li data-ng-show="searchForm.leaving_from.$error.invalidAiportCode">Airport Code should start with letter A</li>
        <li ng-show="searchForm.$error.incompleteDateOfBirth">Incomplete Date of Birth</li>
        <li ng-show="searchForm.$error.invalidDateOfBith">Invalid Date of Birth</li>
        <li ng-show="searchForm.$error.minorDateOfBirth">Must be an adult</li>
      </ul>
    </div>
    
    <form name="searchForm" novalidate="" role="form">
      <!-- Required: out of the box validation -->
      <label for="leaving_from" data-ng-class="{'error-req': searchForm.submitted &amp;&amp; searchForm.leaving_from.$invalid}">From<span class="ada-hide"> City or airport</span>*</label>
      <input type="text" placeholder="City or airport" id="leaving_from" name="leaving_from" 
        data-ng-model="search.leavingFrom" required data-ng-minlength="3"
        my-validate-airport-code>
      <br />
      
      <!-- Applying the directive to the model holding all parts of the date -->
      <div data-my-birth-date data-my-validate-birth-date data-ng-model="search.dateOfBirth"></div>

      <br />
      
      <button type="submit" data-ng-click="submitSearch()">submit</button>
      <button data-ng-click="reset()">reset</button>
    </form>
    <pre>{{search | json}}</pre>
  </body>

</html>
// Code goes here

myApp = angular.module("myApp", []);
    
myApp.controller("searchCtrl", function($scope){
  
  // This is to hold the validation until we submit the form.
  $scope.submitSearch = function(){
    if($scope.searchForm.$valid) {
      console.log("form sent");
    }else{
      // If for is invalid, show errors
      $scope.searchForm.submitted = true;
    }
  }
  
  // This is to reset the search model and all errors from screen.
  $scope.reset = function(){
    $scope.search = {}
    $scope.searchForm.submitted = false;
  }

});

myApp.directive("myValidateAirportCode", function(){
  // requires an isloated model
  return {
   // restrict to an attribute type.
   restrict: 'A',
  // element must have ng-model attribute.
   require: 'ngModel', 
   link: function(scope, ele, attrs, ctrl){

      // add a parser that will process each time the value is 
      // parsed into the model when the user updates it.
      ctrl.$parsers.unshift(function(value) {
        if(value){
          // test and set the validity after update.
          var valid = value.charAt(0) == 'A' || value.charAt(0) == 'a';
          ctrl.$setValidity('invalidAiportCode', valid);
        }
          
        // if it's valid, return the value to the model, 
        // otherwise return undefined.
        return valid ? value : undefined;
      });
      
   }
  }
});

myApp.constant("dataSet", {
  months: ["1-Jan", "2-Feb", "3-Mar", "4-Apr", "5-May", "6-Jun", "7-Jul", "8-Aug", "9-Sep", "10-Oct", "11-Nov", "12-Dec"],
  years: _.range(new Date().getFullYear(), new Date().getFullYear() - 116, -1),
  days: _.range(1, 32)
});

myApp.directive("myBirthDate", function(dataSet){
   return {
     // restrict to an attribute type.
     restrict: 'A',
     scope: {
       date: '=ngModel'
     },
     link: function(scope){
       // Constants
       angular.extend(scope, dataSet);
     },
     template: "Date Of Birth:" + 
          "<select ng-model='date.month' required name='monthOfBirth' data-ng-options='month as month for month in months'>" +
          "  <option value='' selected=''>Month</option> "+
          " </select>" +
          " <select ng-model='date.day' required name='dayOfBirth' data-ng-options='day as day for day in days'> " +
          "  <option value='' selected=''>Day</option> " +
          " </select> " +
          " <select ng-model='date.year'' required name='yearOfBirth'' data-ng-options='year as year for year in years'> " +
          " <option value='' selected=''>Year</option> " +
          " </select> "
    }
});

myApp.directive("myValidateBirthDate", function(){
  
  isDateOfBirthComplete = function(dateOfBirth){
    isValidMonth = typeof dateOfBirth.month != 'undefined' && dateOfBirth.month.toString().length > 0 ;
    isValidDay = typeof dateOfBirth.day != 'undefined'  && dateOfBirth.day.toString().length > 0 ;
    isValidYear = typeof dateOfBirth.year != 'undefined' && dateOfBirth.year.toString().length > 0 ;
    return isValidMonth && isValidDay && isValidYear;
  }
  
  isDateOfBirthValid = function(dateOfBirth){
    var daysInMonth, month, parsedDateOfBirth, today, yearMonth;
    month = dateOfBirth.month.split("-")[0];
    yearMonth = dateOfBirth.year + "-" + month;
    daysInMonth = moment(yearMonth, "YYYY-MM").daysInMonth();
    parsedDateOfBirth = moment(new Date(dateOfBirth.year, month - 1, dateOfBirth.day));
    today = moment(new Date());
    return dateOfBirth.day <= daysInMonth && parsedDateOfBirth.isBefore(today);
  }
  
  isAdult = function(dateOfBirth) {
    var fifteenYearsAgo, momentOfBirth, month, today;
    today = new Date();
    fifteenYearsAgo = today.setFullYear(today.getFullYear() - 15);
    month = dateOfBirth.month.split("-")[0];
    momentOfBirth = moment("" + dateOfBirth.year + "-" + month + "-" + dateOfBirth.day, "YYYY-MM-DD");
    return momentOfBirth.isBefore(fifteenYearsAgo) || momentOfBirth.isSame(fifteenYearsAgo, 'day');
};

  return {
   // restrict to an attribute type.
   restrict: 'A',
   // requires the controller
   require: 'ngModel',
   link: function(scope, ele, attrs, ctrl){

      // set the validation to false until use fills in
      ctrl.$setValidity('incompleteDateOfBirth', false);

      // Watch if model inside the directive has changed
      scope.$watch('date', function(newVal, oldVal){
        
        if(newVal){
          dateOfBirth = newVal
          dateOfBirthComplete = isDateOfBirthComplete(dateOfBirth);
          
          ctrl.$setValidity('incompleteDateOfBirth', dateOfBirthComplete);
          
          if(dateOfBirthComplete){
            ctrl.$setValidity("invalidDateOfBith", isDateOfBirthValid(dateOfBirth));
            ctrl.$setValidity("minorDateOfBirth", isAdult(dateOfBirth));
          }
        
        }
        
      }, true); 
     
   }
  }
});
/* Styles go here */
.error-req {
  color: red;
}