<!DOCTYPE html>
<html>
    <head>
        <title>ngComboDatePicker</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

		    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.6/angular.js"></script>
        <script type="text/javascript" src="ngCombo.js"></script>
        <script type="text/javascript" src="https://npmcdn.com/moment@2.13.0"></script>

        <script type="text/javascript">
            var app = angular.module('myApp',['ngComboDatePicker']);

            app.controller('myCtrl', function ($scope) {
              $scope.todaysdate = moment (new Date());
              $scope.currentYear = new Date().getFullYear();
            });
        </script>
    </head>
    <body ng-app="myApp">
      <h2>Place holders</h2>
      <form name="form">
          <p>Choose a date: 
          <ng-combo-date-picker
            ng-required="true"
            ng-model="placeholder"
            ng-order="mdy"
            ng-max-date="{{ currentYear + '-12-31' }}"
            ng-attrs-date="{'class': 'dayField'}"
            ng-attrs-month="{'class': 'monthField'}"
            ng-attrs-year="{'class': 'yearField'}"
            ng-months="January,February,March,April,May,June,July,August,September,October,November,December"
            ng-placeholder="-Year-,-Month-,-Day-"
          ></ng-combo-date-picker></p>
          <p>Current selection is: <strong>{{ placeholder }}</strong>
          <br />
          {{form}}
          <br />
          <br />
          <br />
  form.$name: {{form.$name}}<br />
  form.$dirty: {{form.$dirty}}<br />
  form.$pristine: {{form.$pristine}}<br />
  form.$valid: {{form.$valid}}<br />
  form.$invalid: {{form.$invalid}}<br />
  form.$submitted: {{form.$submitted}}
      </form>
    </body>
</html>
/*
 * ngComboDatePicker v1.2.1
 * http://github.com/jfmdev/ngComboDatePicker
 * «Copyright 2015 Jose F. Maldonado»
 * License: LGPLv3 (http://www.gnu.org/licenses/lgpl-3.0.html)
 */

// Declare module.
angular.module("ngComboDatePicker", [])

// Create directive.
.directive('ngComboDatePicker', function() {
    return {
        restrict: 'AEC',
        scope: {
            ngModel: '=',
            ngDate : '@',
            ngMinDate : '@',
            ngMaxDate : '@',
            ngMonths : '@',
            ngOrder: '@',
            ngAttrsDate: '@',
            ngAttrsMonth: '@',
            ngAttrsYear: '@',
            ngYearOrder: '@',
            ngTimezone: '@',
            ngPlaceholder: '@'
        },
        controller: ['$scope', function($scope) {
            // Define function for parse dates.
            function parseDate(myDate, myTimezone) {
                var res = null;
                if(myDate !== undefined && myDate !== null) {
                    if(myDate instanceof Date) {
                        res = myDate;
                    } else {
                        if(typeof myDate == 'number' || typeof myDate == 'string') {
                            // Parse date.
                            res = new Date(isNaN(myDate)? myDate : parseInt(myDate, 10));
                            
                            // Adjust timezone.
                            res = adjustTimezone(res, myTimezone);
                        }
                    }
                }
                return res;
            };

            // Define function for adjust timezone.
            function adjustTimezone(myDate, myTimezone) {
                var offset = isNaN(myTimezone)? new Date().getTimezoneOffset() : parseFloat(myTimezone)*60;
                return new Date(myDate.getTime() + offset*60*1000);                           
            }
            
            // Define fuction for getting the maximum date for a month.
            function maxDate(month, year) {
                var res = 31;
                if(month != null) {
                    if(month == 4 || month == 6 || month == 9 || month == 11) {
                        res = 30;
                    }
                    if(year != null && month == 2) {
                        res = year % 4 == 0 && year % 100 != 0? 29 : 28;
                    }
                }
                return res;
            }

            // Function to parse a JSON object.
            function parseJsonPlus(jsonObj) {
                var res = null;
                if(jsonObj != null) {
                    try{ res = JSON.parse(jsonObj); }catch(ex) {}
                    if(res == null) try{ res = JSON.parse(jsonObj.replace(/'/g, '"')); }catch(ex) {}
                }
                return res;
            }
            
            // Initialize model.  
            $scope.ngModel = parseDate($scope.ngModel, $scope.ngTimezone);

            // Initialize attributes variables.
            $scope.ngAttrsDate = parseJsonPlus($scope.ngAttrsDate);
            $scope.ngAttrsMonth = parseJsonPlus($scope.ngAttrsMonth);
            $scope.ngAttrsYear = parseJsonPlus($scope.ngAttrsYear);
            
            // Verify if initial date was defined.
            var initDate = parseDate($scope.ngDate, $scope.ngTimezone);
            if(initDate != null) $scope.ngModel = initDate;

            // Initialize order.
            if(typeof $scope.ngOrder != 'string') {
                $scope.ngOrder = 'dmy';
            } else {
                $scope.ngOrder = $scope.ngOrder.toLowerCase();
            }

            // Initialize minimal and maximum values.
            $scope.minDate = parseDate($scope.ngMinDate, $scope.ngTimezone);
            if($scope.ngMinDate == null) {
                var now = adjustTimezone(new Date(), $scope.ngTimezone);
                $scope.minDate = new Date(now.getFullYear()-100, now.getMonth(), now.getDate(),
                                          now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds());
            }
            $scope.maxDate = parseDate($scope.ngMaxDate, $scope.ngTimezone);
            if($scope.maxDate == null) {
                $scope.maxDate = adjustTimezone(new Date(), $scope.ngTimezone);
            }

            // Verify if selected date is in the valid range.
            if($scope.ngModel < $scope.minDate) $scope.ngModel = $scope.minDate;
            if($scope.ngModel > $scope.maxDate) $scope.ngModel = $scope.maxDate;

            // Initialize place holders.
            var placeHolders = null;
            if($scope.ngPlaceholder !== undefined && $scope.ngPlaceholder !== null && (typeof $scope.ngPlaceholder == 'string' || Array.isArray($scope.ngPlaceholder))) {
                var holders = typeof $scope.ngPlaceholder == 'string'? $scope.ngPlaceholder.split(',') : $scope.ngPlaceholder;
                if(holders.length == 3) {
                    placeHolders = [];
                    for(var h=0; h<holders.length; h++) {
                        placeHolders.push({value:'', name:holders[h], disabled:false});
                    }
                }
            }

            // Initialize list of years.
            $scope.years = [];
            if(placeHolders) $scope.years.push(placeHolders[0]);
            for(var i=$scope.minDate.getFullYear(); i<=$scope.maxDate.getFullYear(); i++) {
                $scope.years.push({value:i, name:i});
            }

            // Verify if the order of the years must be reversed.
            if(typeof $scope.ngYearOrder == 'string' && $scope.ngYearOrder.indexOf('des') == 0) {
                $scope.years.reverse();
            }

            // Initialize list of months names.
            var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
            if($scope.ngMonths !== undefined && $scope.ngMonths !== null) {
                if(typeof $scope.ngMonths == 'string') {
                    var months = $scope.ngMonths.split(',');
                    if(months.length == 12) monthNames = months;
                }
                if(Array.isArray($scope.ngMonths) && $scope.ngMonths.length == 12) {
                     monthNames = $scope.ngMonths;
                }
            }
            
            // Update list of months.
            $scope.updateMonthList = function() {
                // Some months can not be choosed if the year matchs with the year of the minimum or maximum dates.
                var start = $scope.ngModel != null && $scope.ngModel.getFullYear() == $scope.minDate.getFullYear()? $scope.minDate.getMonth() : 0;
                var end = $scope.ngModel != null && $scope.ngModel.getFullYear() == $scope.maxDate.getFullYear()? $scope.maxDate.getMonth() : 11;

                // Generate list.
                $scope.months = [];
                if(placeHolders) $scope.months.push(placeHolders[1]);
                for(var i=start; i<=end; i++) {
                    $scope.months.push({value:i + 1, name:monthNames[i]});
                }
            };

            // Initialize list of days.
            $scope.updateDateList = function() {
                // Start date is 1, unless the selected month and year matchs the minimum date.
                var start = 1;
                if($scope.ngModel != null && $scope.ngModel.getMonth() == $scope.minDate.getMonth() && $scope.ngModel.getFullYear() == $scope.minDate.getFullYear()) {
                    start = $scope.minDate.getDate();
                }

                // End date is 30 or 31 (28 or 29 in February), unless the selected month and year matchs the maximum date.
                var end = $scope.ngModel != null? maxDate($scope.ngModel.getMonth()+1, $scope.ngModel.getFullYear()) : maxDate(null, null);
                if($scope.ngModel != null && $scope.ngModel.getMonth() == $scope.maxDate.getMonth() && $scope.ngModel.getFullYear() == $scope.maxDate.getFullYear()) {
                    end = $scope.maxDate.getDate();
                }

                // Generate list.
                $scope.dates = [];
                if(placeHolders) $scope.dates.push(placeHolders[2]);
                for(var i=start; i<=end; i++) {
                    $scope.dates.push({value:i, name:i});
                }
            };

            // When the model is updated, update the combo boxes.
            $scope.modelUpdated = function() {
                // Update combo boxes.
                $scope.date = $scope.ngModel != null? $scope.ngModel.getDate() : '';
                $scope.month = $scope.ngModel != null? $scope.ngModel.getMonth() : '';
                $scope.year = $scope.ngModel != null? $scope.ngModel.getFullYear() : '';

                // Hide or show days and months according to the min and max dates.
                $scope.updateMonthList();
                $scope.updateDateList();
            }
            // Watch for changes in the model.
            $scope.$watch('ngModel', function() {
                $scope.modelUpdated();
            });

            // When a combo box is changed, update the model and verify which values in the combo boxes for dates and months can be show.
            $scope.onChange = function(part) {
                // Update model.                
                if($scope.date != null && $scope.month != null && $scope.year != null && $scope.date != '' && $scope.month != '' && $scope.year != '') {
                    var maxDay = maxDate($scope.month+1, $scope.year);
                    
                    var hours = 0, minutes = 0, seconds = 0, milliseconds = 0;
                    if($scope.ngModel != null) {
                        hours = $scope.ngModel.getHours();
                        minutes = $scope.ngModel.getMinutes();
                        seconds = $scope.ngModel.getSeconds();
                        milliseconds = $scope.ngModel.getMilliseconds();
                    }
                    
                    $scope.ngModel = new Date($scope.year, $scope.month, $scope.date > maxDay? maxDay : $scope.date, hours, minutes, seconds, milliseconds);
                }
                
                // Disable placeholders after selecting a value.
                if(placeHolders) {
                    if($scope.year != '') placeHolders[0].disabled = true;
                    if($scope.month != '') placeHolders[1].disabled = true;
                    if($scope.date != '') placeHolders[2].disabled = true;
                }
                
                // Hide or show days and months according to the min and max dates.
                $scope.updateMonthList();
                $scope.updateDateList();
            };

            $scope.getSomething = function() { return 'color:red;'; };
        } ],
        link: function(scope, element, attrs) {
            // Initialize variables.
            var jqLite = angular.element;
            var children = jqLite(element[0]).children();
            var order = scope.ngOrder.split('');

            // Add attributes.
            if(scope.ngAttrsDate != null) jqLite(children[0]).attr( scope.ngAttrsDate );
            if(scope.ngAttrsMonth != null) jqLite(children[1]).attr( scope.ngAttrsMonth );
            if(scope.ngAttrsYear != null) jqLite(children[2]).attr( scope.ngAttrsYear );

            // Reorder elements.
            for(var i=0; i<order.length; i++) {
                if(order[i] == 'd') jqLite(element[0]).append(children[0]);
                if(order[i] == 'm') jqLite(element[0]).append(children[1]);
                if(order[i] == 'y') jqLite(element[0]).append(children[2]);
            }
        },
        template: function() {
            var html =
                '<select ng-model="date" ng-change="onChange(\'date\')" ng-options="date.value as date.name disable when date.disabled for date in dates"></select>' +
                '<select ng-model="month" ng-change="onChange(\'month\')" ng-options="month.value as month.name disable when month.disabled for month in months"></select>' +
                '<select ng-model="year" ng-change="onChange(\'year\')" ng-options="year.value as year.name disable when year.disabled for year in years"></select>'
            ;

            return html;
        }
    }
});