<!DOCTYPE html>
<html>

  <head>
    <link data-require="jasmine@*" data-semver="1.3.1" rel="stylesheet" href="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css" />
    <script data-require="jasmine@*" data-semver="1.3.1" src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
    <script data-require="jasmine@*" data-semver="1.3.1" src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
    <script data-require="angular.js@1.1.5" data-semver="1.1.5" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
    <script data-require="angular-mocks@1.1.5" data-semver="1.1.5" src="http://code.angularjs.org/1.1.5/angular-mocks.js"></script>
    <script data-require="json2@*" data-semver="0.0.2012100-8" src="//cdnjs.cloudflare.com/ajax/libs/json2/20121008/json2.js"></script>
    <script data-require="underscore.js@1.4.3" data-semver="1.4.3" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore.js"></script>
    <script data-require="moment.js@2.3.1" data-semver="2.3.1" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.3.1/moment.js"></script>
    <script src="script.js"></script>
    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <title>Jasmine Spec Runner</title>
    <script src="spec.js"></script>
  </body>

</html>
myApp = angular.module("myApp", []);
    
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("myValidateBirthDate", function(dataSet){
  
  var 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;
  }
  
  var 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);
  }
  
  var 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');
};

  
  // 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){
     
      // set the validation to false until user fills in
      ctrl.$setValidity('incompleteDateOfBirth', false);
     
      // Constants to initialize where we get months, days and years
      angular.extend(scope, dataSet);

      // Watch if whole model has changed
      scope.$watch(attrs.ngModel, function(newVal){
        
        if(angular.isDefined(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 */

body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }

#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }

#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }

describe("myValidateBirthDate", function() {
  
  beforeEach( function(){
    module('myApp')
  });
    
  beforeEach(inject(function($compile, $rootScope){
    $scope = $rootScope;
    var element = angular.element(
      '<form name="form">'
        +'<div my-validate-birth-date data-ng-model="dateOfBirth">' 
          + 'Date Of Birth:<select data-ng-model="dateOfBirth.month" required name="monthOfBirth" data-ng-options="month as month for month in months">'
          + '<option value="" selected="">Month</option></select>'
          + '<select data-ng-model="dateOfBirth.day" required name="dayOfBirth" data-ng-options="day as day for day in days">'
          + '<option value="" selected="">Day</option></select>'
          + '<select data-ng-model="dateOfBirth.year" required name="yearOfBirth" data-ng-options="year as year for year in years">'
          + '<option value="" selected="">Year</option></select>'
        +'</div>'
      + '</form>'
    );
    $scope.model = { dateOfBirth: null};
    $compile(element)($scope);
    $scope.$digest();
    form = $scope.form;
  }));
  
  describe("incompleteDateOfBirth", function(){
    
      it('should be defined and initially set valid to false', function() {
        expect(form.$error.incompleteDateOfBirth[0].$valid).toBeFalsy();
      });  
      
      it('should be set to invalid if user has only entered day', function(){
        form.monthOfBirth.$setViewValue("2-Feb");
        $scope.$digest();
        expect(form.$error.incompleteDateOfBirth[0].$valid).toBeFalsy();
      });
      
      it('should be false when user has entered day, month and year', function(){
        form.monthOfBirth.$setViewValue("2-Feb");
        form.dayOfBirth.$setViewValue(16);
        form.yearOfBirth.$setViewValue(1970);
        $scope.$digest();
        expect(form.$error.incompleteDateOfBirth).toBeFalsy();
      });
    
    });
    
    describe("invalidDateOfBith", function(){
      
      it("should be initially undefined", function(){
        expect(form.$error.invalidDateOfBith).not.toBeDefined();
      });
      
      it("should be invalid when day, month and year results in an invalid combination such as 31 Feb", function(){
        form.monthOfBirth.$setViewValue("2-Feb");
        form.dayOfBirth.$setViewValue(31);
        form.yearOfBirth.$setViewValue(1970);
        $scope.$digest();
        expect(form.$error.invalidDateOfBith[0].$valid).toBeFalsy()
      });
      
      it("should be false when day, month and year results in an valid combination such as 11 Feb", function(){
        form.monthOfBirth.$setViewValue("2-Feb");
        form.dayOfBirth.$setViewValue(11);
        form.yearOfBirth.$setViewValue(1970);
        $scope.$digest();
        expect(form.$error.invalidDateOfBith).toBeFalsy();
      })
    
    });
    
    describe("minorDateOfBirth", function(){
      
      it("should be initially undefined", function(){
        expect(form.$error.minorDateOfBirth).not.toBeDefined();
      });
      
      it("should be invalid when day, month and year results in a combination combination where the year difference with current year is lower than 15 such as 1998", function(){
        form.monthOfBirth.$setViewValue("2-Feb");
        form.dayOfBirth.$setViewValue(11);
        form.yearOfBirth.$setViewValue(2001);
        $scope.$digest();
        expect(form.$error.minorDateOfBirth[0].$valid).toBeFalsy();
      });
      
      it("should be valid when day, month and year results in a combination combination where the year difference with current year is higher than 15 such as 1998", function(){
        form.monthOfBirth.$setViewValue("2-Feb");
        form.dayOfBirth.$setViewValue(11);
        form.yearOfBirth.$setViewValue(1970);
        $scope.$digest();
        expect(form.$error.minorDateOfBirth).toBeFalsy();
      });
      
    });
    
  });

var NOT_IMPLEMENTED = undefined;

// load jasmine htmlReporter
(function() {
  var env = jasmine.getEnv();
  env.addReporter(new jasmine.HtmlReporter());
  env.execute();
}());