var app = angular.module('plunker', ['ui.bootstrap.datetimepicker', 'ui.dateTimeInput', 'jcs-autoValidate'])
  .run([
    'bootstrap3ElementModifier',
    function(bootstrap3ElementModifier) {
      bootstrap3ElementModifier.enableValidationStateIcons(true);
    }
  ]);

app.controller('MainCtrl', function($scope) {
  $scope.dates = {};
  $scope.name = 'DateTimePicker(http://dalelotts.github.io/angular-bootstrap-datetimepicker/) and for datetime formats (https://github.com/dalelotts/angular-date-time-input)';
  moment.locale('en');
  $scope.dates.AlarmDate = "11/17/2014 13:22:00";
  //$scope.dates.ControlledDate = "11/18/2014 13:22:00";
  
  //alert(0.222>0);
  
  
  $scope.$watch('dates', function(newval, oldval) {
    $scope.difference = dateDiff($scope.dates.AlarmDate, $scope.dates.ControlledDate);
    if (dateDiff($scope.dates.AlarmDate, $scope.dates.ControlledDate) > 0) {
      $scope.dateCheckForm.controlleddate.$invalid = true;
    } else {
      $scope.dateCheckForm.controlleddate.$invalid = false;
    }
    $scope.formValid = $scope.dateCheckForm.$valid;
  }, true);

  function dateDiff(dtFrom, dtTo) {
    var dt1 = new Date(dtFrom);
    var dt2 = new Date(dtTo);
    var diff = dt2.getTime() - dt1.getTime();
    var days = diff / (1000 * 60 * 60 * 24);
    //alert(days);
    return (days);
  }
});
<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <link rel="stylesheet" href="datetimepicker.css" />
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
  <script data-require="angular.js@1.2.x" src="https://code.angularjs.org/1.2.25/angular.js" data-semver="1.2.25"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.js"></script>
  <script src="validate.js"></script>
  <script src="datetimeinput.js"></script>
  <script src="datetimepicker.js"></script>
  <script src="app.js"></script>
  <style>
    .datetimepicker {
      width: 400px;
    }
  </style>
</head>

<!-- 
* This works ;) 
* I see on init it doesnt ! - dontnow scope doesnt seem to know about startdate on init
* Aaah !
-->

<body ng-controller="MainCtrl">
  <samp class="red">Hello {{name}}!</samp>
  <form name="dateCheckForm" novalidate="novalidate">
    <hr/>
    <div class='label' ng-class="dateCheckForm.controlleddate.$invalid?'label-danger':'label-success'">Valid?</div>
    <kbd>Date Diff = {{difference}}</kbd>
    <div class="well">
            <p>Alarm Date: {{dates.AlarmDate | date: 'MM/dd/yyyy HH:mm:ss' }}</p>

            <div class="dropdown">
                <a class="dropdown-toggle" id="dropdown1" role="button" data-toggle="dropdown" data-target="#"
                   href="#">
                    <div class="input-group">
                        <input type="text" required data-date-time-input="MM/DD/YYYY HH:mm:ss" class="form-control" data-ng-model="dates.AlarmDate">
                        <span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
                    </div>
                </a>
                <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
                    <datetimepicker data-ng-model="dates.AlarmDate"
                                    data-datetimepicker-config="{ dropdownSelector: '#dropdown1' }"></datetimepicker>
                </ul>
            </div>
        </div>
        <hr/>
        <div class="well form-group">
                <p>Controlled Date: {{dates.ControlledDate | date: 'MM/dd/yyyy HH:mm:ss' }}</p>
    
                <div class="dropdown" ng-class="{ 'has-error' : dateCheckForm.controlleddate.$invalid}">
                    <a class="dropdown-toggle" id="dropdown1" role="button" data-toggle="dropdown" data-target="#"
                       href="#">
                        <div class="input-group">
                            <input type="text" name='controlleddate'  required data-date-time-input="MM/DD/YYYY HH:mm:ss" class="form-control" data-ng-model="dates.ControlledDate">
                            <span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
                        </div>
                    </a>
                    <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
                        <datetimepicker data-ng-model="dates.ControlledDate"
                                        data-datetimepicker-config="{ dropdownSelector: '#dropdown1' }"></datetimepicker>
                    </ul>
                </div>
            </div>
        </form>
  </body>

</html>
/* Put your css in here */

/**
 * @license angular-bootstrap-datetimepicker  version: 0.3.7
 * (c) 2013-2014 Knight Rider Consulting, Inc. http://www.knightrider.com
 * License: MIT
 */

.datetimepicker {
    padding: 4px;
    margin-top: 1px;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
    direction: ltr;
    width: 320px;
}

.datetimepicker.datetimepicker-rtl {
    direction: rtl;
}

.datetimepicker.datetimepicker-rtl table tr td span {
    float: right;
}

.datetimepicker-dropdown, .datetimepicker-dropdown-left {
    top: 0;
    left: 0;
}

[class*=" datetimepicker-dropdown"]:before {
    content: '';
    display: inline-block;
    border-left: 7px solid transparent;
    border-right: 7px solid transparent;
    border-bottom: 7px solid #ccc;
    border-bottom-color: rgba(0, 0, 0, 0.2);
    position: absolute;
}

[class*=" datetimepicker-dropdown"]:after {
    content: '';
    display: inline-block;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-bottom: 6px solid #ffffff;
    position: absolute;
}

[class*=" datetimepicker-dropdown-top"]:before {
    content: '';
    display: inline-block;
    border-left: 7px solid transparent;
    border-right: 7px solid transparent;
    border-top: 7px solid #ccc;
    border-top-color: rgba(0, 0, 0, 0.2);
    border-bottom: 0;
}

[class*=" datetimepicker-dropdown-top"]:after {
    content: '';
    display: inline-block;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-top: 6px solid #ffffff;
    border-bottom: 0;
}

.datetimepicker-dropdown-bottom-left:before {
    top: -7px;
    right: 6px;
}

.datetimepicker-dropdown-bottom-left:after {
    top: -6px;
    right: 7px;
}

.datetimepicker-dropdown-bottom-right:before {
    top: -7px;
    left: 6px;
}

.datetimepicker-dropdown-bottom-right:after {
    top: -6px;
    left: 7px;
}

.datetimepicker-dropdown-top-left:before {
    bottom: -7px;
    right: 6px;
}

.datetimepicker-dropdown-top-left:after {
    bottom: -6px;
    right: 7px;
}

.datetimepicker-dropdown-top-right:before {
    bottom: -7px;
    left: 6px;
}

.datetimepicker-dropdown-top-right:after {
    bottom: -6px;
    left: 7px;
}

.datetimepicker > div {
    display: none;
}

.datetimepicker.minutes div.datetimepicker-minutes {
    display: block;
}

.datetimepicker.hours div.datetimepicker-hours {
    display: block;
}

.datetimepicker.days div.datetimepicker-days {
    display: block;
}

.datetimepicker.months div.datetimepicker-months {
    display: block;
}

.datetimepicker.years div.datetimepicker-years {
    display: block;
}

.datetimepicker table {
    margin: 0;
}

.datetimepicker .table td,
.datetimepicker .table th {
    text-align: center;
    width: 14.6%;
    height: 20px;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
    border: none;
}
.datetimepicker .table-striped > tbody > tr:nth-child(odd) > td, .datetimepicker .table-striped > tbody > tr:nth-child(odd) > td,
.datetimepicker .table-striped > tbody > tr:nth-child(odd) > td, .datetimepicker .table-striped > tbody > tr:nth-child(odd) > th {
  background-color: transparent;
}
.datetimepicker table tr td.minute:hover {
    background: #eeeeee;
    cursor: pointer;
}

.datetimepicker table tr td.hour:hover {
    background: #eeeeee;
    cursor: pointer;
}

.datetimepicker table tr td.day:hover {
    background: #eeeeee;
    cursor: pointer;
}

.datetimepicker table tr td.past,
.datetimepicker table tr td.future {
    color: #999999;
}

.datetimepicker table tr td.disabled,
.datetimepicker table tr td.disabled:hover {
    background: none;
    color: #999999;
    cursor: default;
}

.datetimepicker table tr td.today,
.datetimepicker table tr td.today:hover,
.datetimepicker table tr td.today.disabled,
.datetimepicker table tr td.today.disabled:hover {
    background-color: #fde19a;
    background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
    background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
    background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
    background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
    background-image: linear-gradient(top, #fdd49a, #fdf59a);
    background-repeat: repeat-x;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
    border-color: #fdf59a #fdf59a #fbed50;
    border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}

.datetimepicker table tr td.today:hover,
.datetimepicker table tr td.today:hover:hover,
.datetimepicker table tr td.today.disabled:hover,
.datetimepicker table tr td.today.disabled:hover:hover,
.datetimepicker table tr td.today:active,
.datetimepicker table tr td.today:hover:active,
.datetimepicker table tr td.today.disabled:active,
.datetimepicker table tr td.today.disabled:hover:active,
.datetimepicker table tr td.today.active,
.datetimepicker table tr td.today:hover.active,
.datetimepicker table tr td.today.disabled.active,
.datetimepicker table tr td.today.disabled:hover.active,
.datetimepicker table tr td.today.disabled,
.datetimepicker table tr td.today:hover.disabled,
.datetimepicker table tr td.today.disabled.disabled,
.datetimepicker table tr td.today.disabled:hover.disabled,
.datetimepicker table tr td.today[disabled],
.datetimepicker table tr td.today:hover[disabled],
.datetimepicker table tr td.today.disabled[disabled],
.datetimepicker table tr td.today.disabled:hover[disabled] {
    background-color: #fdf59a;
}

.datetimepicker table tr td.today:active,
.datetimepicker table tr td.today:hover:active,
.datetimepicker table tr td.today.disabled:active,
.datetimepicker table tr td.today.disabled:hover:active,
.datetimepicker table tr td.today.active,
.datetimepicker table tr td.today:hover.active,
.datetimepicker table tr td.today.disabled.active,
.datetimepicker table tr td.today.disabled:hover.active {
    background-color: #fbf069 \9;
}

.datetimepicker table tr td.active,
.datetimepicker table tr td.active:hover,
.datetimepicker table tr td.active.disabled,
.datetimepicker table tr td.active.disabled:hover {
    background-color: #006dcc;
    background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
    background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
    background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
    background-image: -o-linear-gradient(top, #0088cc, #0044cc);
    background-image: linear-gradient(top, #0088cc, #0044cc);
    background-repeat: repeat-x;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
    border-color: #0044cc #0044cc #002a80;
    border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    color: #fff;
    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}

.datetimepicker table tr td.active:hover,
.datetimepicker table tr td.active:hover:hover,
.datetimepicker table tr td.active.disabled:hover,
.datetimepicker table tr td.active.disabled:hover:hover,
.datetimepicker table tr td.active:active,
.datetimepicker table tr td.active:hover:active,
.datetimepicker table tr td.active.disabled:active,
.datetimepicker table tr td.active.disabled:hover:active,
.datetimepicker table tr td.active.active,
.datetimepicker table tr td.active:hover.active,
.datetimepicker table tr td.active.disabled.active,
.datetimepicker table tr td.active.disabled:hover.active,
.datetimepicker table tr td.active.disabled,
.datetimepicker table tr td.active:hover.disabled,
.datetimepicker table tr td.active.disabled.disabled,
.datetimepicker table tr td.active.disabled:hover.disabled,
.datetimepicker table tr td.active[disabled],
.datetimepicker table tr td.active:hover[disabled],
.datetimepicker table tr td.active.disabled[disabled],
.datetimepicker table tr td.active.disabled:hover[disabled] {
    background-color: #0044cc;
}

.datetimepicker table tr td.active:active,
.datetimepicker table tr td.active:hover:active,
.datetimepicker table tr td.active.disabled:active,
.datetimepicker table tr td.active.disabled:hover:active,
.datetimepicker table tr td.active.active,
.datetimepicker table tr td.active:hover.active,
.datetimepicker table tr td.active.disabled.active,
.datetimepicker table tr td.active.disabled:hover.active {
    background-color: #003399 \9;
}

.datetimepicker table tr td span {
    display: block;
    width: 23%;
    height: 54px;
    line-height: 54px;
    float: left;
    margin: 1%;
    cursor: pointer;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
}

.datetimepicker span.hour {
    height: 26px;
    line-height: 26px;
}

.datetimepicker span.minute {
    height: 26px;
    line-height: 26px;
}

.datetimepicker table tr td span:hover {
    background: #eeeeee;
}

.datetimepicker table tr td span.disabled,
.datetimepicker table tr td span.disabled:hover {
    background: none;
    color: #999999;
    cursor: default;
}

.datetimepicker table tr td span.active,
.datetimepicker table tr td span.active:hover,
.datetimepicker table tr td span.active.disabled,
.datetimepicker table tr td span.active.disabled:hover {
    background-color: #006dcc;
    background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
    background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
    background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
    background-image: -o-linear-gradient(top, #0088cc, #0044cc);
    background-image: linear-gradient(top, #0088cc, #0044cc);
    background-repeat: repeat-x;
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
    border-color: #0044cc #0044cc #002a80;
    border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
    color: #fff;
    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}

.datetimepicker table tr td span.active:hover,
.datetimepicker table tr td span.active:hover:hover,
.datetimepicker table tr td span.active.disabled:hover,
.datetimepicker table tr td span.active.disabled:hover:hover,
.datetimepicker table tr td span.active:active,
.datetimepicker table tr td span.active:hover:active,
.datetimepicker table tr td span.active.disabled:active,
.datetimepicker table tr td span.active.disabled:hover:active,
.datetimepicker table tr td span.active.active,
.datetimepicker table tr td span.active:hover.active,
.datetimepicker table tr td span.active.disabled.active,
.datetimepicker table tr td span.active.disabled:hover.active,
.datetimepicker table tr td span.active.disabled,
.datetimepicker table tr td span.active:hover.disabled,
.datetimepicker table tr td span.active.disabled.disabled,
.datetimepicker table tr td span.active.disabled:hover.disabled,
.datetimepicker table tr td span.active[disabled],
.datetimepicker table tr td span.active:hover[disabled],
.datetimepicker table tr td span.active.disabled[disabled],
.datetimepicker table tr td span.active.disabled:hover[disabled] {
    background-color: #0044cc;
}

.datetimepicker table tr td span.active:active,
.datetimepicker table tr td span.active:hover:active,
.datetimepicker table tr td span.active.disabled:active,
.datetimepicker table tr td span.active.disabled:hover:active,
.datetimepicker table tr td span.active.active,
.datetimepicker table tr td span.active:hover.active,
.datetimepicker table tr td span.active.disabled.active,
.datetimepicker table tr td span.active.disabled:hover.active {
    background-color: #003399 \9;
}

.datetimepicker table tr td span.past,
.datetimepicker table tr td span.future {
    color: #999999;
}



.datetimepicker thead tr:first-child th,
.datetimepicker tfoot tr:first-child th {
    cursor: pointer;
}

.datetimepicker thead tr:first-child th:hover,
.datetimepicker tfoot tr:first-child th:hover {
    background: #eeeeee;
}
/*globals define, jQuery */
/*jslint vars:true */

/**
 * @license angular-bootstrap-datetimepicker  version: 0.3.7
 * (c) 2013-2014 Knight Rider Consulting, Inc. http://www.knightrider.com
 * License: MIT
 */

/**
 *
 *    @author        Dale "Ducky" Lotts
 *    @since        2013-Jul-8
 */

(function (factory) {
  'use strict';
  /* istanbul ignore if */
  if (typeof define === 'function'  && /* istanbul ignore next */ define.amd) {
    define(['angular', 'moment'], factory); // AMD
  } else {
    factory(window.angular, window.moment); // Browser global
  }
}(function (angular, moment) {
  'use strict';
  angular.module('ui.bootstrap.datetimepicker', [])
    .constant('dateTimePickerConfig', {
      dropdownSelector: null,
      minuteStep: 5,
      minView: 'minute',
      startView: 'day'
      
    })
    .directive('datetimepicker', ['$log', 'dateTimePickerConfig', function datetimepickerDirective($log, defaultConfig) {

      function DateObject() {

        this.dateValue = new Date().getTime();
        this.selectable = true;

        var validProperties = ['dateValue', 'display', 'active', 'selectable', 'past', 'future'];

        for (var prop in arguments[0]) {
          /* istanbul ignore else */
          //noinspection JSUnfilteredForInLoop
          if (validProperties.indexOf(prop) >= 0) {
            //noinspection JSUnfilteredForInLoop
            this[prop] = arguments[0][prop];
          }
        }
      }

      var validateConfiguration = function validateConfiguration(configuration) {
        var validOptions = ['startView', 'minView', 'minuteStep', 'dropdownSelector'];

        for (var prop in configuration) {
          //noinspection JSUnfilteredForInLoop
          if (validOptions.indexOf(prop) < 0) {
            throw ('invalid option: ' + prop);
          }
        }

        // Order of the elements in the validViews array is significant.
        var validViews = ['minute', 'hour', 'day', 'month', 'year'];

        if (validViews.indexOf(configuration.startView) < 0) {
          throw ('invalid startView value: ' + configuration.startView);
        }

        if (validViews.indexOf(configuration.minView) < 0) {
          throw ('invalid minView value: ' + configuration.minView);
        }

        if (validViews.indexOf(configuration.minView) > validViews.indexOf(configuration.startView)) {
          throw ('startView must be greater than minView');
        }

        if (!angular.isNumber(configuration.minuteStep)) {
          throw ('minuteStep must be numeric');
        }
        if (configuration.minuteStep <= 0 || configuration.minuteStep >= 60) {
          throw ('minuteStep must be greater than zero and less than 60');
        }
        if (configuration.dropdownSelector !== null && !angular.isString(configuration.dropdownSelector)) {
          throw ('dropdownSelector must be a string');
        }

        /* istanbul ignore next */
        if (configuration.dropdownSelector !== null && ((typeof jQuery === 'undefined') || (typeof jQuery().dropdown !== 'function'))) {
          $log.error('Please DO NOT specify the dropdownSelector option unless you are using jQuery AND Bootstrap.js. ' +
          'Please include jQuery AND Bootstrap.js, or write code to close the dropdown in the on-set-time callback. \n\n' +
          'The dropdownSelector configuration option is being removed because it will not function properly.');
          delete configuration.dropdownSelector;
        }
      };

      return {
        restrict: 'E',
        require: 'ngModel',
        template: '<div class="datetimepicker table-responsive">' +
        '<table class="table table-striped">' +
        '   <thead>' +
        '       <tr>' +
        '           <th class="left" data-ng-click="changeView(data.currentView, data.leftDate, $event)" data-ng-show="data.leftDate.selectable"><i class="glyphicon glyphicon-arrow-left"/></th>' +
        '           <th class="switch" colspan="5" data-ng-show="data.previousViewDate.selectable" data-ng-click="changeView(data.previousView, data.previousViewDate, $event)">{{ data.previousViewDate.display }}</th>' +
        '           <th class="right" data-ng-click="changeView(data.currentView, data.rightDate, $event)" data-ng-show="data.rightDate.selectable"><i class="glyphicon glyphicon-arrow-right"/></th>' +
        '       </tr>' +
        '       <tr>' +
        '           <th class="dow" data-ng-repeat="day in data.dayNames" >{{ day }}</th>' +
        '       </tr>' +
        '   </thead>' +
        '   <tbody>' +
        '       <tr data-ng-if="data.currentView !== \'day\'" >' +
        '           <td colspan="7" >' +
        '              <span    class="{{ data.currentView }}" ' +
        '                       data-ng-repeat="dateObject in data.dates"  ' +
        '                       data-ng-class="{active: dateObject.active, past: dateObject.past, future: dateObject.future, disabled: !dateObject.selectable}" ' +
        '                       data-ng-click="changeView(data.nextView, dateObject, $event)">{{ dateObject.display }}</span> ' +
        '           </td>' +
        '       </tr>' +
        '       <tr data-ng-if="data.currentView === \'day\'" data-ng-repeat="week in data.weeks">' +
        '           <td data-ng-repeat="dateObject in week.dates" ' +
        '               data-ng-click="changeView(data.nextView, dateObject, $event)"' +
        '               class="day" ' +
        '               data-ng-class="{active: dateObject.active, past: dateObject.past, future: dateObject.future, disabled: !dateObject.selectable}" >{{ dateObject.display }}</td>' +
        '       </tr>' +
        '   </tbody>' +
        '</table></div>',
        scope: {
          onSetTime: '&',
          beforeRender: '&'
        },
        replace: true,
        link: function link(scope, element, attrs, ngModelController) {

          var directiveConfig = {};

          if (attrs.datetimepickerConfig) {
            directiveConfig = scope.$parent.$eval(attrs.datetimepickerConfig);
          }

          var configuration = {};

          angular.extend(configuration, defaultConfig, directiveConfig);

          validateConfiguration(configuration);

          var startOfDecade = function startOfDecade(unixDate) {
            var startYear = (parseInt(moment.utc(unixDate).year() / 10, 10) * 10);
            return moment.utc(unixDate).year(startYear).startOf('year');
          };

          var dataFactory = {
            year: function year(unixDate) {
              var selectedDate = moment.utc(unixDate).startOf('year');
              // View starts one year before the decade starts and ends one year after the decade ends
              // i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020
              // Truncate the last digit from the current year and subtract 1 to get the start of the decade
              var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
              var startDate = moment.utc(startOfDecade(unixDate)).subtract(1, 'year').startOf('year');

              var activeYear = ngModelController.$modelValue ? moment(ngModelController.$modelValue).year() : 0;

              var result = {
                'currentView': 'year',
                'nextView': configuration.minView === 'year' ? 'setTime' : 'month',
                'previousViewDate': new DateObject({dateValue: null, display: startDecade + '-' + (startDecade + 9)}),
                'leftDate': new DateObject({dateValue: moment.utc(startDate).subtract(9, 'year').valueOf()}),
                'rightDate': new DateObject({dateValue: moment.utc(startDate).add(11, 'year').valueOf()}),
                'dates': []
              };

              for (var i = 0; i < 12; i += 1) {
                var yearMoment = moment.utc(startDate).add(i, 'years');
                var dateValue = {
                  'dateValue': yearMoment.valueOf(),
                  'display': yearMoment.format('YYYY'),
                  'past': yearMoment.year() < startDecade,
                  'future': yearMoment.year() > startDecade + 9,
                  'active': yearMoment.year() === activeYear
                };

                result.dates.push(new DateObject(dateValue));
              }

              return result;
            },

            month: function month(unixDate) {

              var startDate = moment.utc(unixDate).startOf('year');
              var previousViewDate = startOfDecade(unixDate);
              var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM') : 0;

              var result = {
                'previousView': 'year',
                'currentView': 'month',
                'nextView': configuration.minView === 'month' ? 'setTime' : 'day',
                'previousViewDate': new DateObject({
                  dateValue: previousViewDate.valueOf(),
                  display: startDate.format('YYYY')
                }),
                'leftDate': new DateObject({dateValue: moment.utc(startDate).subtract(1, 'year').valueOf()}),
                'rightDate': new DateObject({dateValue: moment.utc(startDate).add(1, 'year').valueOf()}),
                'dates': []
              };

              for (var i = 0; i < 12; i += 1) {
                var monthMoment = moment.utc(startDate).add(i, 'months');
                var dateValue = {
                  'dateValue': monthMoment.valueOf(),
                  'display': monthMoment.format('MMM'),
                  'active': monthMoment.format('YYYY-MMM') === activeDate
                };

                result.dates.push(new DateObject(dateValue));
              }

              return result;
            },

            day: function day(unixDate) {

              var selectedDate = moment.utc(unixDate);
              var startOfMonth = moment.utc(selectedDate).startOf('month');
              var previousViewDate = moment.utc(selectedDate).startOf('year');
              var endOfMonth = moment.utc(selectedDate).endOf('month');

              var startDate = moment.utc(startOfMonth).subtract(Math.abs(startOfMonth.weekday()), 'days');

              var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM-DD') : '';

              var result = {
                'previousView': 'month',
                'currentView': 'day',
                'nextView': configuration.minView === 'day' ? 'setTime' : 'hour',
                'previousViewDate': new DateObject({
                  dateValue: previousViewDate.valueOf(),
                  display: startOfMonth.format('YYYY-MMM')
                }),
                'leftDate': new DateObject({dateValue: moment.utc(startOfMonth).subtract(1, 'months').valueOf()}),
                'rightDate': new DateObject({dateValue: moment.utc(startOfMonth).add(1, 'months').valueOf()}),
                'dayNames': [],
                'weeks': []
              };


              for (var dayNumber = 0; dayNumber < 7; dayNumber += 1) {
                result.dayNames.push(moment.utc().weekday(dayNumber).format('dd'));
              }

              for (var i = 0; i < 6; i += 1) {
                var week = {dates: []};
                for (var j = 0; j < 7; j += 1) {
                  var monthMoment = moment.utc(startDate).add((i * 7) + j, 'days');
                  var dateValue = {
                    'dateValue': monthMoment.valueOf(),
                    'display': monthMoment.format('D'),
                    'active': monthMoment.format('YYYY-MMM-DD') === activeDate,
                    'past': monthMoment.isBefore(startOfMonth),
                    'future': monthMoment.isAfter(endOfMonth)
                  };
                  week.dates.push(new DateObject(dateValue));
                }
                result.weeks.push(week);
              }

              return result;
            },

            hour: function hour(unixDate) {
              var selectedDate = moment.utc(unixDate).startOf('day');
              var previousViewDate = moment.utc(selectedDate).startOf('month');

              var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H') : '';

              var result = {
                'previousView': 'day',
                'currentView': 'hour',
                'nextView': configuration.minView === 'hour' ? 'setTime' : 'minute',
                'previousViewDate': new DateObject({
                  dateValue: previousViewDate.valueOf(),
                  display: selectedDate.format('ll')
                }),
                'leftDate': new DateObject({dateValue: moment.utc(selectedDate).subtract(1, 'days').valueOf()}),
                'rightDate': new DateObject({dateValue: moment.utc(selectedDate).add(1, 'days').valueOf()}),
                'dates': []
              };

              for (var i = 0; i < 24; i += 1) {
                var hourMoment = moment.utc(selectedDate).add(i, 'hours');
                var dateValue = {
                  'dateValue': hourMoment.valueOf(),
                  'display': hourMoment.format('LT'),
                  'active': hourMoment.format('YYYY-MM-DD H') === activeFormat
                };

                result.dates.push(new DateObject(dateValue));
              }

              return result;
            },

            minute: function minute(unixDate) {
              var selectedDate = moment.utc(unixDate).startOf('hour');
              var previousViewDate = moment.utc(selectedDate).startOf('day');
              var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H:mm') : '';

              var result = {
                'previousView': 'hour',
                'currentView': 'minute',
                'nextView': 'setTime',
                'previousViewDate': new DateObject({
                  dateValue: previousViewDate.valueOf(),
                  display: selectedDate.format('lll')
                }),
                'leftDate': new DateObject({dateValue: moment.utc(selectedDate).subtract(1, 'hours').valueOf()}),
                'rightDate': new DateObject({dateValue: moment.utc(selectedDate).add(1, 'hours').valueOf()}),
                'dates': []
              };

              var limit = 60 / configuration.minuteStep;

              for (var i = 0; i < limit; i += 1) {
                var hourMoment = moment.utc(selectedDate).add(i * configuration.minuteStep, 'minute');
                var dateValue = {
                  'dateValue': hourMoment.valueOf(),
                  'display': hourMoment.format('LT'),
                  'active': hourMoment.format('YYYY-MM-DD H:mm') === activeFormat
                };

                result.dates.push(new DateObject(dateValue));
              }

              return result;
            },

            setTime: function setTime(unixDate) {
              var tempDate = new Date(unixDate);
              var newDate = new Date(tempDate.getTime() + (tempDate.getTimezoneOffset() * 60000));

              var oldDate = ngModelController.$modelValue;
              ngModelController.$setViewValue(newDate);

              if (configuration.dropdownSelector) {
                jQuery(configuration.dropdownSelector).dropdown('toggle');
              }

              scope.onSetTime({newDate: newDate, oldDate: oldDate});

              return dataFactory[configuration.startView](unixDate);
            }
          };

          var getUTCTime = function getUTCTime(modelValue) {
            var tempDate = (modelValue ? moment(modelValue).toDate() : new Date());
            return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
          };

          scope.changeView = function changeView(viewName, dateObject, event) {
            if (event) {
              event.stopPropagation();
              event.preventDefault();
            }

            if (viewName && (dateObject.dateValue > -Infinity) && dateObject.selectable && dataFactory[viewName]) {
              var result = dataFactory[viewName](dateObject.dateValue);

              var weekDates = [];
              if (result.weeks) {
                for (var i = 0; i < result.weeks.length; i += 1) {
                  var week = result.weeks[i];
                  for (var j = 0; j < week.dates.length; j += 1) {
                    var weekDate = week.dates[j];
                    weekDates.push(weekDate);
                  }
                }
              }

              scope.beforeRender({
                $view: result.currentView,
                $dates: result.dates || weekDates,
                $leftDate: result.leftDate,
                $upDate: result.previousViewDate,
                $rightDate: result.rightDate
              });

              scope.data = result;
            }
          };

          ngModelController.$render = function $render() {
            scope.changeView(configuration.startView, new DateObject({dateValue: getUTCTime(ngModelController.$viewValue)}));
          };
        }
      };
    }]);
}));
/*globals angular, moment, jQuery */
/*jslint vars:true */

/**
 * @license angular-date-time-input  v0.1.0
 * (c) 2013 Knight Rider Consulting, Inc. http://www.knightrider.com
 * License: MIT
 */

/**
 *
 *    @author Dale "Ducky" Lotts
 *    @since  2013-Sep-23
 */

angular.module('ui.dateTimeInput', []).directive('dateTimeInput',
  [
    function () {
      "use strict";
      return {
        require: 'ngModel',
        restrict: 'A',
        link: function (scope, element, attrs, controller) {

          if (!attrs.dateTimeInput) {
            throw ("dateTimeInput must specify a date format");
          }

          var validateFn = function (viewValue) {

            var result = viewValue;

            if (viewValue) {
              var momentValue = moment(viewValue);
              if (momentValue.isValid()) {
                controller.$setValidity(attrs.ngModel, true);
                result = momentValue.format();
              } else {
                controller.$setValidity(attrs.ngModel, false);
              }
            }

            return result;
          };


          var formatFn = function (modelValue) {
            var result = modelValue;
            if (modelValue && moment(modelValue).isValid()) {
              result = moment(modelValue).format(attrs.dateTimeInput);
            }
            return result;
          };

          controller.$parsers.unshift(validateFn);

          controller.$formatters.push(formatFn);

          element.bind('blur', function () {
            var viewValue = controller.$modelValue;
            angular.forEach(controller.$formatters, function (formatter) {
              viewValue = formatter(viewValue);
            });
            controller.$viewValue = viewValue;
            controller.$render();
          });
        }
      };
    }
  ]);
/*
 * angular-auto-validate - v1.10.21 - 2014-11-13
 * https://github.com/jonsamwell/angular-auto-validate
 * Copyright (c) 2014 Jon Samwell (http://www.jonsamwell.com)
 */
(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate', []);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate')
        .provider('validator', [

            function () {
                var elementStateModifiers = {},
                    enableValidElementStyling = true,
                    enableInvalidElementStyling = true,
                    validationEnabled = true,

                    toBoolean = function (value) {
                        var v;
                        if (value && value.length !== 0) {
                            v = value.toLowerCase();
                            value = !(v === 'f' || v === '0' || v === 'false');
                        } else {
                            value = false;
                        }

                        return value;
                    },

                    getAttributeValue = function (el, attrName) {
                        var val;

                        if (el !== undefined) {
                            val = el.attr(attrName) || el.attr('data-' + attrName);
                        }

                        return val;
                    },

                    getBooleanAttributeValue = function (el, attrName) {
                        return toBoolean(getAttributeValue(el, attrName));
                    },

                    validElementStylingEnabled = function (el) {
                        return enableValidElementStyling && !getBooleanAttributeValue(el, 'disable-valid-styling');
                    },

                    invalidElementStylingEnabled = function (el) {
                        return enableInvalidElementStyling && !getBooleanAttributeValue(el, 'disable-invalid-styling');
                    };

                /**
                 * @ngdoc function
                 * @name validator#enable
                 * @methodOf validator
                 *
                 * @description
                 * By default auto validate will validate all forms and elements with an ngModel directive on.  By
                 * setting enabled to false you will explicitly have to opt in to enable validation on forms and child
                 * elements.
                 *
                 * Note: this can be overridden by add the 'auto-validate-enabled="true/false' attribute to a form.
                 *
                 * Example:
                 * <pre>
                 *  app.config(function (validator) {
                 *    validator.enable(false);
                 *  });
                 * </pre>
                 *
                 * @param {Boolean} isEnabled true to enable, false to disable.
                 */
                this.enable = function (isEnabled) {
                    validationEnabled = isEnabled;
                };

                /**
                 * @ngdoc function
                 * @name validator#isEnabled
                 * @methodOf validator
                 *
                 * @description
                 * Returns true if the library is enabeld.
                 *
                 * @return {Boolean} true if enabled, otherwise false.
                 */
                this.isEnabled = function () {
                    return validationEnabled;
                };

                /**
                 * @ngdoc function
                 * @name validator#setDefaultElementModifier
                 * @methodOf validator
                 *
                 * @description
                 * Sets the default element modifier that will be used by the validator
                 * to change an elements UI state.  Please ensure the modifier has been registered
                 * before setting it as default.
                 *
                 * Note: this can be changed by setting the
                 * element modifier attribute on the input element 'data-element-modifier="myCustomModifier"'
                 *
                 * Example:
                 * <pre>
                 *  app.config(function (validator) {
                 *    validator.setDefaultElementModifier('myCustomModifier');
                 *  });
                 * </pre>
                 *
                 * @param {string} key The key name of the modifier.
                 */
                this.setDefaultElementModifier = function (key) {
                    if (elementStateModifiers[key] === undefined) {
                        throw new Error('Element modifier not registered: ' + key);
                    }

                    this.defaultElementModifier = key;
                };

                /**
                 * @ngdoc function
                 * @name validator#registerDomModifier
                 * @methodOf validator
                 *
                 * @description
                 * Registers an object that adheres to the elementModifier interface and is
                 * able to modifier an elements dom so that appears valid / invalid for a specific
                 * scenario i.e. the Twitter Bootstrap css framework, Foundation CSS framework etc.
                 *
                 * Example:
                 * <pre>
                 *  app.config(function (validator) {
                 *    validator.registerDomModifier('customDomModifier', {
                 *      makeValid: function (el) {
                 *          el.removeClass(el, 'invalid');
                 *          el.addClass(el, 'valid');
                 *      },
                 *      makeInvalid: function (el, err, domManipulator) {
                 *          el.removeClass(el, 'valid');
                 *          el.addClass(el, 'invalid');
                 *      }
                 *    });
                 *  });
                 * </pre>
                 *
                 * @param {string} key The key name of the modifier
                 * @param {object} modifier An object which implements the elementModifier interface
                 */
                this.registerDomModifier = function (key, modifier) {
                    elementStateModifiers[key] = modifier;
                };

                /**
                 * @ngdoc function
                 * @name validator#setErrorMessageResolver
                 * @methodOf validator
                 *
                 * @description
                 * Registers an object that adheres to the elementModifier interface and is
                 * able to modifier an elements dom so that appears valid / invalid for a specific
                 * scenario i.e. the Twitter Bootstrap css framework, Foundation CSS framework etc.
                 *
                 * Example:
                 * <pre>
                 *  app.config(function (validator) {
                 *    validator.setErrorMessageResolver(function (errorKey, el) {
                 *      var defer = $q.defer();
                 *      // resolve the correct error from the given key and resolve the returned promise.
                 *      return defer.promise();
                 *    });
                 *  });
                 * </pre>
                 *
                 * @param {function} resolver A method that returns a promise with the resolved error message in.
                 */
                this.setErrorMessageResolver = function (resolver) {
                    this.errorMessageResolver = resolver;
                };

                /**
                 * @ngdoc function
                 * @name validator#getErrorMessage
                 * @methodOf validator
                 *
                 * @description
                 * Resolves the error message for the given error type.
                 *
                 * @param {String} errorKey The error type.
                 * @param {Element} el The UI element that is the focus of the error.
                 * It is provided as the error message may need information from the element i.e. ng-min (the min allowed value).
                 */
                this.getErrorMessage = function (errorKey, el) {
                    if (this.errorMessageResolver === undefined) {
                        throw new Error('Please set an error message resolver via the setErrorMessageResolver function before attempting to resolve an error message.');
                    }

                    return this.errorMessageResolver(errorKey, el);
                };

                /**
                 * @ngdoc function
                 * @name validator#setValidElementStyling
                 * @methodOf validator
                 *
                 * @description
                 * Globally enables valid element visual styling.  This is enabled by default.
                 *
                 * @param {Boolean} enabled True to enable style otherwise false.
                 */
                this.setValidElementStyling = function (enabled) {
                    enableValidElementStyling = enabled;
                };


                /**
                 * @ngdoc function
                 * @name validator#setInvalidElementStyling
                 * @methodOf validator
                 *
                 * @description
                 * Globally enables invalid element visual styling.  This is enabled by default.
                 *
                 * @param {Boolean} enabled True to enable style otherwise false.
                 */
                this.setInvalidElementStyling = function (enabled) {
                    enableInvalidElementStyling = enabled;
                };

                this.getDomModifier = function (el) {
                    var modifierKey = (el !== undefined ? el.attr('element-modifier') : this.defaultElementModifier) ||
                        (el !== undefined ? el.attr('data-element-modifier') : this.defaultElementModifier) ||
                        this.defaultElementModifier;

                    if (modifierKey === undefined) {
                        throw new Error('Please set a default dom modifier via the setDefaultElementModifier method on the validator class.');
                    }

                    return elementStateModifiers[modifierKey];
                };

                this.makeValid = function (el) {
                    if (validElementStylingEnabled(el)) {
                        this.getDomModifier(el).makeValid(el);
                    } else {
                        this.makeDefault(el);
                    }
                };

                this.makeInvalid = function (el, errorMsg) {
                    if (invalidElementStylingEnabled(el)) {
                        this.getDomModifier(el).makeInvalid(el, errorMsg);
                    } else {
                        this.makeDefault(el);
                    }
                };

                this.makeDefault = function (el) {
                    var dm = this.getDomModifier(el);
                    if (dm.makeDefault) {
                        dm.makeDefault(el);
                    }
                };

                this.$get = [
                    function () {
                        return this;
                    }
                ];
            }
        ]);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate')
        .factory('bootstrap3ElementModifier', [

            function () {
                var reset = function (el) {
                        angular.forEach(el.find('span'), function (spanEl) {
                            spanEl = angular.element(spanEl);
                            if (spanEl.hasClass('error-msg') || spanEl.hasClass('form-control-feedback') || spanEl.hasClass('control-feedback')) {
                                spanEl.remove();
                            }
                        });

                        el.removeClass('has-success has-error has-feedback');
                    },
                    findWithClassElementAsc = function (el, klass) {
                        var parent = el;
                        for (var i = 0; i <= 3; i += 1) {
                            if (parent !== undefined && parent.hasClass(klass)) {
                                break;
                            } else if (parent !== undefined) {
                                parent = parent.parent();
                            }
                        }

                        return parent;
                    },

                    findWithClassElementDesc = function (el, klass) {
                        var child;
                        for (var i = 0; i < el.children.length; i += 1) {
                            child = el.children[i];
                            if (child !== undefined && angular.element(child).hasClass(klass)) {
                                break;
                            } else if (child.children !== undefined) {
                                child = findWithClassElementDesc(child, klass);
                                if (child.length > 0) {
                                    break;
                                }
                            }
                        }

                        return angular.element(child);
                    },

                    findFormGroupElement = function (el) {
                        return findWithClassElementAsc(el, 'form-group');
                    },

                    findInputGroupElement = function (el) {
                        return findWithClassElementDesc(el, 'input-group');
                    },

                    insertAfter = function (referenceNode, newNode) {
                        referenceNode[0].parentNode.insertBefore(newNode[0], referenceNode[0].nextSibling);
                    },

                    /**
                     * @ngdoc property
                     * @name bootstrap3ElementModifier#addValidationStateIcons
                     * @propertyOf bootstrap3ElementModifier
                     * @returns {bool} True if an state icon will be added to the element in the valid and invalid control
                     * states.  The default is false.
                     */
                    addValidationStateIcons = false,

                    /**
                     * @ngdoc function
                     * @name bootstrap3ElementModifier#enableValidationStateIcons
                     * @methodOf bootstrap3ElementModifier
                     *
                     * @description
                     * Makes an element appear invalid by apply an icon to the input element.
                     *
                     * @param {bool} enable - True to enable the icon otherwise false.
                     */
                    enableValidationStateIcons = function (enable) {
                        addValidationStateIcons = enable;
                    },

                    /**
                     * @ngdoc function
                     * @name bootstrap3ElementModifier#makeValid
                     * @methodOf bootstrap3ElementModifier
                     *
                     * @description
                     * Makes an element appear valid by apply bootstrap 3 specific styles and child elements. If the service
                     * property 'addValidationStateIcons' is true it will also append validation glyphicon to the element.
                     * See: http://getbootstrap.com/css/#forms-control-validation
                     *
                     * @param {Element} el - The input control element that is the target of the validation.
                     */
                    makeValid = function (el) {
                        var frmGroupEl = findFormGroupElement(el),
                            inputGroupEl = findInputGroupElement(frmGroupEl[0]);

                        reset(frmGroupEl);
                        frmGroupEl.addClass('has-success ' + (inputGroupEl.length > 0 ? '' : 'has-feedback'));
                        if (addValidationStateIcons) {
                            var iconElText = '<span class="glyphicon glyphicon-ok form-control-feedback"></span>';
                            if (inputGroupEl.length > 0) {
                                iconElText = iconElText.replace('form-', '');
                                iconElText = '<span class="input-group-addon control-feedback">' + iconElText + '</span';
                            }

                            insertAfter(el, angular.element(iconElText));
                        }
                    },

                    /**
                     * @ngdoc function
                     * @name bootstrap3ElementModifier#makeInvalid
                     * @methodOf bootstrap3ElementModifier
                     *
                     * @description
                     * Makes an element appear invalid by apply bootstrap 3 specific styles and child elements. If the service
                     * property 'addValidationStateIcons' is true it will also append validation glyphicon to the element.
                     * See: http://getbootstrap.com/css/#forms-control-validation
                     *
                     * @param {Element} el - The input control element that is the target of the validation.
                     */
                    makeInvalid = function (el, errorMsg) {
                        var frmGroupEl = findFormGroupElement(el),
                            inputGroupEl = findInputGroupElement(frmGroupEl[0]),
                            helpTextEl = angular.element('<span class="help-block has-error error-msg">' + errorMsg + '</span>');
                        reset(frmGroupEl, inputGroupEl);
                        frmGroupEl.addClass('has-error ' + (inputGroupEl.length > 0 ? '' : 'has-feedback'));
                        insertAfter(inputGroupEl.length > 0 ? inputGroupEl : el, helpTextEl);
                        if (addValidationStateIcons) {
                            var iconElText = '<span class="glyphicon glyphicon-remove form-control-feedback"></span>';
                            if (inputGroupEl.length > 0) {
                                iconElText = iconElText.replace('form-', '');
                                iconElText = '<span class="input-group-addon control-feedback">' + iconElText + '</span';
                            }

                            insertAfter(el, angular.element(iconElText));
                        }
                    },

                    /**
                     * @ngdoc function
                     * @name bootstrap3ElementModifier#makeDefault
                     * @methodOf bootstrap3ElementModifier
                     *
                     * @description
                     * Makes an element appear in its default visual state by apply bootstrap 3 specific styles and child elements.
                     *
                     * @param {Element} el - The input control element that is the target of the validation.
                     */
                    makeDefault = function (el) {
                        var frmGroupEl = findFormGroupElement(el);
                        reset(frmGroupEl);
                    };

                return {
                    makeValid: makeValid,
                    makeInvalid: makeInvalid,
                    makeDefault: makeDefault,
                    enableValidationStateIcons: enableValidationStateIcons,
                    key: 'bs3'
                };
            }
        ]);
}(angular));

(function (angular) {
    'use strict';

    /*
     * Taken from https://github.com/angular/angular.js/issues/2690#issue-14462164 (with added tests of course!)
     */
    angular.module('jcs-autoValidate').factory('jcs-debounce', [
        '$timeout',
        function ($timeout) {
            var debounce = function (fn, timeout, apply) {
                timeout = angular.isUndefined(timeout) ? 0 : timeout;
                apply = angular.isUndefined(apply) ? true : apply; // !!default is true! most suitable to my experience
                var nthCall = 0;
                return function () { // intercepting fn
                    var that = this;
                    var argz = arguments;
                    nthCall += 1;
                    var later = (function (version) {
                        return function () {
                            if (version === nthCall) {
                                return fn.apply(that, argz);
                            }
                        };
                    })(nthCall);

                    return $timeout(later, timeout, apply);
                };
            };

            return {
                debounce: debounce
            };
        }
    ]);
}(angular));

(function (String, angular) {
    'use strict';

    /**
     * Replaces string placeholders with corresponding template string
     */
    if (!('format' in String.prototype)) {
        String.prototype.format = function () {
            var args = arguments;
            return this.replace(/{(\d+)}/g, function (match, number) {
                return typeof args[number] !== undefined ? args[number] : match;
            });
        };
    }

    angular.autoValidate = angular.autoValidate || {
        errorMessages: {}
    };

    angular.autoValidate.errorMessages['en-us'] = angular.autoValidate.errorMessages['en-gb'] = {
        defaultMsg: 'Please add error message for {0}',
        email: 'Please enter a valid email address',
        minlength: 'Please enter at least {0} characters',
        maxlength: 'You have entered more than the maximum {0} characters',
        min: 'Please enter the minimum number of {0}',
        max: 'Please enter the maximum number of {0}',
        required: 'This field is required',
        date: 'Please enter a valid date',
        pattern: 'Please ensure the entered information adheres to this pattern {0}',
        number: 'Please enter a valid number',
        url: 'Please enter a valid URL in the format of http(s)://wwww.google.com'
    };

    angular.module('jcs-autoValidate')
        .factory('defaultErrorMessageResolver', [
            '$q',
            '$http',
            function ($q, $http) {
                var currentCulture = 'en-gb',

                    i18nFileRootPath = 'js/angular-auto-validate/dist/lang',

                    cultureRetrievalPromise,

                    loadRemoteCulture = function (culture) {
                        cultureRetrievalPromise = $http.get('{0}/jcs-auto-validate_{1}.json'.format(i18nFileRootPath, culture.toLowerCase()));
                        return cultureRetrievalPromise;
                    },

                    /**
                     * @ngdoc function
                     * @name defaultErrorMessageResolver#setI18nFileRootPath
                     * @methodOf defaultErrorMessageResolver
                     *
                     * @description
                     * Set the root path to the il8n files on the server
                     *
                     * @param {String} rootPath - The root path on the server to the il8n file - this defaults
                     * to 'js/angular-auto-validate/lang/'
                     */
                    setI18nFileRootPath = function (rootPath) {
                        i18nFileRootPath = rootPath;
                    },

                    /**
                     * @ngdoc function
                     * @name defaultErrorMessageResolver#setCulture
                     * @methodOf defaultErrorMessageResolver
                     *
                     * @description
                     * Set the culture for the error messages by loading an the correct culture resource file.
                     *
                     * @param {String} culture - The new culture in the format of 'en-gb' etc.
                     * @param {Function} cultureLoadingFn - A optional function to load the culture resolve which should
                     * return a promise which is resolved with the culture errorMessage object.  If a function is not specified
                     * the culture file is loaded from the **i18nFileRootPath**.
                     * @returns {Promise} - A promise which is resolved with the loaded culture error messages object.
                     */
                    setCulture = function (culture, cultureLoadingFn) {
                        var defer = $q.defer();
                        cultureLoadingFn = cultureLoadingFn || loadRemoteCulture;
                        currentCulture = culture.toLowerCase();
                        if (angular.autoValidate.errorMessages[currentCulture] === undefined) {
                            cultureRetrievalPromise = cultureLoadingFn(culture);
                            cultureRetrievalPromise.then(function (response) {
                                cultureRetrievalPromise = undefined;
                                angular.autoValidate.errorMessages[currentCulture] = response.data;
                                defer.resolve(angular.autoValidate.errorMessages[currentCulture]);
                            }, function (err) {
                                angular.autoValidate.errorMessages[currentCulture] = {
                                    defaultMsg: 'Loading culture failed!'
                                };
                                cultureRetrievalPromise = null;
                                defer.reject(err);
                            });
                        } else {
                            defer.resolve(angular.autoValidate.errorMessages[currentCulture]);
                        }

                        return defer.promise;
                    },

                    getErrorMessages = function (culture) {
                        var defer = $q.defer();
                        culture = culture === undefined ? currentCulture : culture.toLowerCase();
                        if (cultureRetrievalPromise !== undefined) {
                            cultureRetrievalPromise.then(function () {
                                defer.resolve(angular.autoValidate.errorMessages[culture]);
                            }, function (err) {
                                defer.reject(err);
                            });
                        } else {
                            defer.resolve(angular.autoValidate.errorMessages[culture]);
                        }
                        return defer.promise;
                    },

                    getMessageTypeOverride = function (errorType, el) {
                        var overrideKey;

                        if (el) {
                            // try and find an attribute which overrides the given error type in the form of errorType-err-type="someMsgKey"
                            errorType += '-err-type';


                            overrideKey = el.attr(errorType);
                            if (overrideKey === undefined) {
                                overrideKey = el.attr('data-ng-' + errorType) || el.attr('ng-' + errorType);
                            }

                            if (overrideKey) {
                                overrideKey = overrideKey.replace(/[\W]/g, '');
                            }
                        }

                        return overrideKey;
                    },

                    /**
                     * @ngdoc function
                     * @name defaultErrorMessageResolver#resolve
                     * @methodOf defaultErrorMessageResolver
                     *
                     * @description
                     * Resolves a validate error type into a user validation error message
                     *
                     * @param {String} errorType - The type of validation error that has occurred.
                     * @param {Element} el - The input element that is the source of the validation error.
                     * @returns {Promise} A promise that is resolved when the validation message has been produced.
                     */
                    resolve = function (errorType, el) {
                        var defer = $q.defer(),
                            errMsg,
                            parameters = [],
                            parameter,
                            messageTypeOverride;

                        if (cultureRetrievalPromise !== undefined) {
                            cultureRetrievalPromise.then(function () {
                                resolve(errorType, el).then(function (msg) {
                                    defer.resolve(msg);
                                });
                            });
                        } else {
                            errMsg = angular.autoValidate.errorMessages[currentCulture][errorType];
                            messageTypeOverride = getMessageTypeOverride(errorType, el);
                            if (messageTypeOverride) {
                                errMsg = angular.autoValidate.errorMessages[currentCulture][messageTypeOverride];
                            }

                            if (errMsg === undefined) {
                                errMsg = angular.autoValidate.errorMessages[currentCulture].defaultMsg.format(errorType);
                            }

                            if (el && el.attr) {
                                try {
                                    parameter = el.attr(errorType);
                                    if (parameter === undefined) {
                                        parameter = el.attr('data-ng-' + errorType) || el.attr('ng-' + errorType);
                                    }

                                    parameters.push(parameter || '');

                                    errMsg = errMsg.format(parameters);
                                } catch (e) {}
                            }

                            defer.resolve(errMsg);
                        }

                        return defer.promise;
                    };

                return {
                    setI18nFileRootPath: setI18nFileRootPath,
                    setCulture: setCulture,
                    getErrorMessages: getErrorMessages,
                    resolve: resolve
                };
            }
        ]);
}(String, angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate')
        .factory('foundation5ElementModifier', [

            function () {
                var reset = function (el, inputEl) {
                        angular.forEach(el.find('small'), function (smallEl) {
                            if (angular.element(smallEl).hasClass('error')) {
                                angular.element(smallEl).remove();
                            }
                        });

                        inputEl.removeClass('error');
                    },
                    findParentColumn = function (el) {
                        var parent = el;
                        for (var i = 0; i <= 3; i += 1) {
                            if (parent !== undefined && parent.hasClass('columns')) {
                                break;
                            } else if (parent !== undefined) {
                                parent = parent.parent();
                            }
                        }

                        return parent;
                    },

                    /**
                     * @ngdoc function
                     * @name foundation5ElementModifier#makeValid
                     * @methodOf foundation5ElementModifier
                     *
                     * @description
                     * Makes an element appear valid by apply Foundation 5 specific styles and child elements.
                     * See: http://foundation.zurb.com/docs/components/forms.html
                     *
                     * @param {Element} el - The input control element that is the target of the validation.
                     */
                    makeValid = function (el) {
                        var parentColumn = findParentColumn(el);
                        reset(parentColumn && parentColumn.length > 0 ? parentColumn : el, el);
                    },

                    /**
                     * @ngdoc function
                     * @name foundation5ElementModifier#makeInvalid
                     * @methodOf foundation5ElementModifier
                     *
                     * @description
                     * Makes an element appear invalid by apply Foundation 5 specific styles and child elements.
                     * See: http://foundation.zurb.com/docs/components/forms.html
                     *
                     * @param {Element} el - The input control element that is the target of the validation.
                     */
                    makeInvalid = function (el, errorMsg) {
                        var parentColumn = findParentColumn(el),
                            helpTextEl;
                        reset(parentColumn || el, el);
                        el.addClass('error');
                        if (parentColumn) {
                            helpTextEl = angular.element('<small class="error">' + errorMsg + '</small>');
                            parentColumn.append(helpTextEl);
                        }
                    },

                    /**
                     * @ngdoc function
                     * @name foundation5ElementModifier#makeDefault
                     * @methodOf foundation5ElementModifier
                     *
                     * @description
                     * Makes an element appear in its default visual state by apply foundation 5 specific styles and child elements.
                     *
                     * @param {Element} el - The input control element that is the target of the validation.
                     */
                    makeDefault = function (el) {
                        makeValid(el);
                    };

                return {
                    makeValid: makeValid,
                    makeInvalid: makeInvalid,
                    makeDefault: makeDefault,
                    key: 'foundation5'
                };
            }
        ]);
}(angular));

(function (angular) {
    'use strict';


    angular.module('jcs-autoValidate')
        .factory('jcs-elementUtils', [
            function () {
                var isElementVisible = function (el) {
                    return el[0].offsetWidth > 0 && el[0].offsetHeight > 0;
                };

                return {
                    isElementVisible: isElementVisible
                };
            }
        ]);

    angular.module('jcs-autoValidate')
        .factory('validationManager', [
            'validator',
            'jcs-elementUtils',
            function (validator, elementUtils) {
                var elementTypesToValidate = ['input', 'textarea', 'select', 'form'],

                    elementIsVisible = function (el) {
                        return elementUtils.isElementVisible(el);
                    },

                    shouldValidateElement = function (el) {
                        return el && el.length > 0 && elementIsVisible(el) && elementTypesToValidate.indexOf(el[0].nodeName.toLowerCase()) > -1;
                    },

                    /**
                     * @ngdoc validateElement
                     * @name validation#validateElement
                     * @param {object} modelCtrl holds the information about the element e.g. $invalid, $valid
                     * @param {Boolean} forceValidation if set to true forces the validation even if the element is pristine
                     * @description
                     * Validate the form element and make invalid/valid element model status.
                     */
                    validateElement = function (modelCtrl, el, forceValidation) {
                        var isValid = true,
                            needsValidation = modelCtrl.$pristine === false || forceValidation,
                            errorType,
                            findErrorType = function ($errors) {
                                var keepGoing = true,
                                    errorTypeToReturn;
                                angular.forEach($errors, function (status, errortype) {
                                    if (keepGoing && status) {
                                        keepGoing = false;
                                        errorTypeToReturn = errortype;
                                    }
                                });

                                return errorTypeToReturn;
                            };

                        if ((forceValidation || (shouldValidateElement(el) && modelCtrl && needsValidation))) {
                            isValid = !modelCtrl.$invalid;

                            if (isValid) {
                                validator.makeValid(el);
                            } else {
                                errorType = findErrorType(modelCtrl.$error);

                                if (errorType === undefined) {
                                    // we have a weird situation some users are encountering where a custom control
                                    // is valid but the ngModel is report it isn't and thus no valid error type can be found
                                    isValid = true;
                                } else {
                                    validator.getErrorMessage(errorType, el).then(function (errorMsg) {
                                        validator.makeInvalid(el, errorMsg);
                                    });
                                }
                            }
                        }

                        return isValid;
                    },

                    resetElement = function (element) {
                        validator.makeDefault(element);
                    },

                    resetForm = function (frmElement) {
                        angular.forEach(frmElement[0], function (element) {
                            var controller,
                                ctrlElement = angular.element(element);
                            controller = ctrlElement.controller('ngModel');

                            if (controller !== undefined) {
                                if (ctrlElement[0].nodeName.toLowerCase() === 'form') {
                                    // we probably have a sub form
                                    resetForm(ctrlElement);
                                } else {
                                    controller.$setPristine();
                                }
                            }
                        });
                    },

                    validateForm = function (frmElement) {
                        var frmValid = true,
                            frmCtrl = frmElement ? angular.element(frmElement).controller('form') : undefined,
                            processElement = function (ctrlElement, force) {
                                var controller, isValid;
                                ctrlElement = angular.element(ctrlElement);
                                controller = ctrlElement.controller('ngModel');

                                if (controller !== undefined && (force || shouldValidateElement(ctrlElement))) {
                                    if (ctrlElement[0].nodeName.toLowerCase() === 'form') {
                                        // we probably have a sub form
                                        validateForm(ctrlElement);
                                    } else {
                                        isValid = validateElement(controller, ctrlElement, true);
                                        frmValid = frmValid && isValid;
                                    }
                                }
                            };

                        if (frmElement === undefined || (frmCtrl !== undefined && frmCtrl.disableDynamicValidation)) {
                            return frmElement !== undefined;
                        }

                        angular.forEach(frmElement[0], function (ctrlElement) {
                            processElement(ctrlElement);
                        });

                        // If you have a custom form control that should be validated i.e.
                        // <my-custom-element>...</my-custom-element> it will not be part of the forms
                        // HTMLFormControlsCollection and thus won't be included in the above element iteration although
                        // it will be on the Angular FormController (if it has a name attribute).  So adding the directive
                        // register-custom-form-control="" to the control root and autoValidate will include it in this
                        // iteration.
                        if (frmElement[0].customHTMLFormControlsCollection) {
                            angular.forEach(frmElement[0].customHTMLFormControlsCollection, function (ctrlElement) {
                                // need to force the validation as the element might not be a known form input type
                                // so the normal validation process will ignore it.
                                processElement(ctrlElement, true);
                            });
                        }

                        return frmValid;
                    },

                    setElementValidationError = function (element, errorMsgKey, errorMsg) {
                        if (errorMsgKey) {
                            validator.getErrorMessage(errorMsgKey, element).then(function (msg) {
                                validator.makeInvalid(element, msg);
                            });
                        } else {
                            validator.makeInvalid(element, errorMsg);
                        }
                    };

                return {
                    setElementValidationError: setElementValidationError,
                    validateElement: validateElement,
                    validateForm: validateForm,
                    resetElement: resetElement,
                    resetForm: resetForm
                };
            }
        ]);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate').directive('form', [
        'validationManager',
        function (validationManager) {
            return {
                restrict: 'E',
                link: function (scope, el) {
                    el.on('reset', function () {
                        validationManager.resetForm(el);
                    });

                    scope.$on('$destroy', function () {
                        el.off('reset');
                    });
                }
            };
        }
    ]);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate').directive('registerCustomFormControl', [

        function () {
            var findParentForm = function (el) {
                var parent = el;
                for (var i = 0; i <= 10; i += 1) {
                    if (parent !== undefined && parent.nodeName.toLowerCase() === 'form') {
                        break;
                    } else if (parent !== undefined) {
                        parent = angular.element(parent).parent()[0];
                    }
                }

                return parent;
            };

            return {
                restrict: 'A',
                link: function (scope, element) {
                    var frmEl = findParentForm(element.parent()[0]);
                    if (frmEl) {
                        frmEl.customHTMLFormControlsCollection = frmEl.customHTMLFormControlsCollection || [];
                        frmEl.customHTMLFormControlsCollection.push(element[0]);
                    }
                }
            };
        }
    ]);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate').directive('form', [
        'validator',
        function (validator) {
            return {
                restrict: 'E',
                require: 'form',
                compile: function () {
                    return {
                        pre: function (scope, element, attrs, ctrl) {
                            ctrl.disableDynamicValidation = !validator.isEnabled();
                            if (attrs.disableDynamicValidation !== undefined) {
                                ctrl.disableDynamicValidation = attrs.disableDynamicValidation === undefined || attrs.disableDynamicValidation === '' || attrs.disableDynamicValidation === 'true';
                            }
                        }
                    };
                }
            };
        }
    ]);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate').config(['$provide',
        function ($provide) {
            $provide.decorator('ngSubmitDirective', [
                '$delegate',
                '$parse',
                'validationManager',
                function ($delegate, $parse, validationManager) {
                    $delegate[0].compile = function ($element, attr) {
                        var fn = $parse(attr.ngSubmit),
                            force = attr.ngSubmitForce === 'true';
                        return function (scope, element) {
                            element.on('submit', function (event) {
                                scope.$apply(function () {
                                    if (force === true || validationManager.validateForm(element)) {
                                        fn(scope, {
                                            $event: event
                                        });
                                    }
                                });
                            });
                        };
                    };

                    return $delegate;
                }
            ]);
        }
    ]);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate').config(['$provide',
        function ($provide) {
            $provide.decorator('ngModelDirective', [
                '$timeout',
                '$delegate',
                'validationManager',
                'jcs-debounce',
                function ($timeout, $delegate, validationManager, debounce) {
                    var directive = $delegate[0],
                        link = directive.link || directive.compile;

                    directive.compile = function (el) {
                        return function (scope, element, attrs, ctrls) {
                            var ngModelCtrl = ctrls[0],
                                frmCtrl = ctrls[1],
                                supportsNgModelOptions = angular.version.major >= 1 && angular.version.minor >= 3,
                                ngModelOptions = attrs.ngModelOptions === undefined ? undefined : scope.$eval(attrs.ngModelOptions),
                                setValidity = ngModelCtrl.$setValidity,
                                setPristine = ngModelCtrl.$setPristine,
                                setValidationState = debounce.debounce(function () {
                                    validationManager.validateElement(ngModelCtrl, element);
                                }, 100);

                            // in the RC of 1.3 there is no directive.link only the directive.compile which
                            // needs to be invoked to get at the link functions.
                            if (supportsNgModelOptions && angular.isFunction(link)) {
                                link = link(el);
                            }

                            if (link.pre) {
                                link.pre.apply(this, arguments);
                                ngModelOptions = ngModelCtrl.$options === undefined ? undefined : ngModelCtrl.$options;
                            }

                            if (attrs.formnovalidate === undefined || (frmCtrl !== undefined && frmCtrl.disableDynamicValidation === false)) {
                                if (supportsNgModelOptions || ngModelOptions === undefined || ngModelOptions.updateOn === undefined || ngModelOptions.updateOn === '') {
                                    ngModelCtrl.$setValidity = function (validationErrorKey, isValid) {
                                        setValidity.call(ngModelCtrl, validationErrorKey, isValid);
                                        setValidationState();
                                    };
                                } else {
                                    element.on(ngModelOptions.updateOn, function () {
                                        setValidationState();
                                    });

                                    scope.$on('$destroy', function () {
                                        element.off(ngModelOptions.updateOn);
                                    });
                                }

                                // We override this so we can reset the element state when it is called.
                                ngModelCtrl.$setPristine = function () {
                                    setPristine.call(ngModelCtrl);
                                    validationManager.resetElement(element);
                                };

                                ngModelCtrl.autoValidated = true;
                            }

                            if (link.post) {
                                link.post.apply(this, arguments);
                            } else {
                                link.apply(this, arguments);
                            }

                            ngModelCtrl.setExternalValidation = function (errorMsgKey, errorMessage, addToModelErrors) {
                                if (addToModelErrors) {
                                    if (ngModelCtrl.$error) {
                                        ngModelCtrl.$error[errorMsgKey] = false;
                                    } else {
                                        ngModelCtrl.$errors[errorMsgKey] = false;
                                    }
                                }

                                validationManager.setElementValidationError(element, errorMsgKey, errorMessage);
                            };

                            ngModelCtrl.removeExternalValidation = function (errorMsgKey, addToModelErrors) {
                                if (addToModelErrors) {
                                    if (ngModelCtrl.$error) {
                                        ngModelCtrl.$error[errorMsgKey] = true;
                                    } else {
                                        ngModelCtrl.$errors[errorMsgKey] = true;
                                    }
                                }

                                validationManager.resetElement(element);
                            };

                            if (frmCtrl) {
                                frmCtrl.setExternalValidation = function (modelProperty, errorMsgKey, errorMessageOverride, addToModelErrors) {
                                    var success = false;
                                    if (frmCtrl[modelProperty]) {
                                        frmCtrl[modelProperty].setExternalValidation(errorMsgKey, errorMessageOverride, addToModelErrors);
                                        success = true;
                                    }

                                    return success;
                                };

                                frmCtrl.removeExternalValidation = function (modelProperty, errorMsgKey, errorMessageOverride, addToModelErrors) {
                                    var success = false;
                                    if (frmCtrl[modelProperty]) {
                                        frmCtrl[modelProperty].removeExternalValidation(errorMsgKey, addToModelErrors);
                                        success = true;
                                    }

                                    return success;
                                };
                            }
                        };
                    };

                    return $delegate;
                }
            ]);
        }
    ]);
}(angular));

(function (angular) {
    'use strict';

    angular.module('jcs-autoValidate')
        .run([
            'validator',
            'defaultErrorMessageResolver',
            'bootstrap3ElementModifier',
            'foundation5ElementModifier',
            function (validator, defaultErrorMessageResolver, bootstrap3ElementModifier, foundation5ElementModifier) {
                validator.setErrorMessageResolver(defaultErrorMessageResolver.resolve);
                validator.registerDomModifier(bootstrap3ElementModifier.key, bootstrap3ElementModifier);
                validator.registerDomModifier(foundation5ElementModifier.key, foundation5ElementModifier);
                validator.setDefaultElementModifier(bootstrap3ElementModifier.key);
            }
        ]);
}(angular));