<!doctype html>
<html ng-app="formlyExample">
  <head>
    <!-- script src="https://cdn.rawgit.com/zhaber/datetimepicker/master/datetimepicker.js"></script -->
    <link href="https://cdn.rawgit.com/zhaber/datetimepicker/master/datetimepicker.css" type="text/css" rel="stylesheet"> 
   
    <!-- Twitter bootstrap -->
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
   
    <!-- apiCheck is used by formly to validate its api -->
    <script src="//npmcdn.com/api-check"></script>
   
    <!-- This is the latest version of angular (at the time this template was created) -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
   
    <!-- Angular-UI Bootstrap has tabs directive we want -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.1/ui-bootstrap-tpls.min.js"></script>
   
    <!-- This is the latest version of formly core. -->
    <script src="//npmcdn.com/angular-formly"></script>
   
    <!-- This is the latest version of formly bootstrap templates -->
    <script src="//npmcdn.com/angular-formly-templates-bootstrap"></script>
    
    <!-- This is the latest version of angular-js-bootstrap-datetimepicker (at the time this template was created) -->
    <script src="datetimepicker.js"></script>

    <script src="example.js"></script> 

    <title>Angular Formly Example</title>
  </head>

  <body ng-controller="MainCtrl as vm">
    <div class="container">
      <h1>angular-formly example: {{vm.exampleTitle}}</h1>
      <div>
        This is a very basic example of how to use the <a hreh="angular-js-bootstrap-datetimepicker">UI Boostrap date and time picker</a> with angular-formly.
      </div>
      <hr />
      <form ng-submit="vm.onSubmit()" name="vm.form" novalidate>
        <formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form">
          <button type="submit" class="btn btn-primary submit-button" ng-disabled="vm.form.$invalid">Submit</button>
          <button type="button" class="btn btn-default" ng-click="vm.options.resetModel()">Reset</button>
        </formly-form>
      </form>
      <hr />
      <h2>Model Value</h2>
      <pre>{{vm.model | json}}</pre>
      <h2>Fields <small>(note, functions are not shown)</small></h2>
      <pre>{{vm.originalFields | json}}</pre>
      <h2>Form</h2>
      <pre>{{vm.form | json}}</pre>
    </div>

    <div style="margin-top:30px">
      <small>
        This is an example for the
        <a href="https://github.com/formly-js/angular-formly">angular-formly</a>
        project made with ♥ by
        <strong>
          <span ng-if="!vm.author.name || !vm.author.url">
            {{vm.author.name || 'anonymous'}}
          </span>
          <a ng-if="vm.author.url" ng-href="{{::vm.author.url}}">
            {{vm.author.name}}
          </a>
        </strong>
        <br />
        This example is running angular version "{{vm.env.angularVersion}}" and formly version "{{vm.env.formlyVersion}}"
      </small>
    </div>
  </body>

</html>
/* global angular */
(function() {
  'use strict';
  console.clear();

  var app = angular.module('formlyExample', ['formly', 'formlyBootstrap', 'ui.bootstrap', 'ui.bootstrap.datetimepicker']);

  app.run(function(formlyConfig) {
    var ngModelAttrs = {};

    var attributes = [
      'ng-model',
      'min-date',
      'max-date',
      'date-disabled',
      'day-format',
      'month-format',
      'year-format',
      'year-range',
      'day-header-format',
      'day-title-format',
      'month-title-format',
      'date-format',
      'date-options',
      'hour-step',
      'minute-step',
      'show-meridian',
      'meridians',
      'readonly-time',
      'readonly-date',
      'hidden-time',
      'hidden-date',
      'mousewheel',
      'show-spinners',
      'current-text',
      'clear-text',
      'close-text'
    ];

    var bindings = [
      'ng-model',
      'min-date',
      'max-date',
      'date-disabled',
      'day-format',
      'month-format',
      'year-format',
      'year-range',
      'day-header-format',
      'day-title-format',
      'month-title-format',
      'date-format',
      'date-options',
      'hour-step',
      'minute-step',
      'show-meridian',
      'readonly-time',
      'readonly-date',
      'hidden-time',
      'hidden-date'
    ];

    angular.forEach(attributes, function(attr) {
      ngModelAttrs[camelize(attr)] = {
        attribute: attr
      };
    });

    angular.forEach(bindings, function(binding) {
      ngModelAttrs[camelize(binding)] = {
        bound: binding
      };
    });

    function camelize(string) {
      string = string.replace(/[\-_\s]+(.)?/g,

        function(match, chr) {
          return chr ? chr.toUpperCase() : '';
        });
      // Ensure 1st char is always lowercase
      return string.replace(/^([A-Z])/, function(match, chr) {
        return chr ? chr.toLowerCase() : '';
      });
    }

    formlyConfig.setType({
      name: 'datetimepicker',
      template: '<br><datetimepicker ng-model="model[options.key]" show-spinners="true" date-format="M/d/yyyy" date-options="dateOptions"></datetimepicker>',
      wrapper: ['bootstrapLabel'],
      defaultOptions: {
        ngModelAttrs: ngModelAttrs,
        templateOptions: {
          label: 'Time',
          minDate: '04/01/2016'
        }
      }
    });
  });

  
  app.controller('MainCtrl', function MainCtrl(formlyVersion) {
    var vm = this;
    // funcation assignment
    vm.onSubmit = onSubmit;

    // variable assignment
    vm.author = { // optionally fill in your info below :-)
      name: 'Mark Ostrander',
      url: 'https://github.com/ostranme'
    };
    vm.exampleTitle = 'Angular UI Bootstrap Date Time Picker'; // add this
    vm.env = {
      angularVersion: angular.version.full,
      formlyVersion: formlyVersion
    };


    vm.model = {};
    vm.options = {};

    vm.fields = [
      {
        key: 'datetime',
        type: 'datetimepicker',
        templateOptions: {
          label: 'Date and Time Picker',
          datepickerPopup: 'dd-MMMM-yyyy'
        },
        expressionProperties: {
            'templateOptions.hiddenTime': 'model.hide_time',
            'templateOptions.hiddenDate': 'model.hide_date'
          }
      }, {
        key: 'hide_time',
        type: 'checkbox',
        'templateOptions': {
          label: 'Hide Time'
        }
      },{
        key: 'hide_date',
        type: 'checkbox',
        'templateOptions': {
          label: 'Hide Date'
        }
      }
    ];

    vm.originalFields = angular.copy(vm.fields);

    // function definition
    function onSubmit() {
      vm.options.updateInitialValue();
      alert(JSON.stringify(vm.model), null, 2);
    }
  });

})();
angular.module('ui.bootstrap.datetimepicker', ["ui.bootstrap.dateparser", "ui.bootstrap.datepicker", "ui.bootstrap.timepicker"])
  .directive('datepickerPopup', function () {
    return {
      restrict: 'EAC',
      require: 'ngModel',
      link: function (scope, element, attr, controller) {
        //remove the default formatter from the input directive to prevent conflict
        controller.$formatters.shift();
      }
    }
  })
  .directive('datetimepicker', [
    function () {
      if (angular.version.full < '1.4.0') {
        return {
          restrict: 'EA',
          template: "<div class=\"alert alert-danger\">Angular 1.4.0 or above is required for datetimepicker to work correctly</div>"
        };
      }
      return {
        restrict: 'EA',
        require: 'ngModel',
        scope: {
          ngModel: '=',
          dayFormat: "=",
          monthFormat: "=",
          yearFormat: "=",
          dayHeaderFormat: "=",
          dayTitleFormat: "=",
          monthTitleFormat: "=",
          yearRange: "=",
          minDate: "=",
          maxDate: "=",
          dateOptions: "=?",
          dateDisabled: "&",
          dateNgClick: "&",
          hourStep: "=",
          dateOpened: "=",
          minuteStep: "=",
          showMeridian: "=",
          meredians: "=",
          mousewheel: "=",
          readonlyTime: "=",
          readonlyDate: "=",
          hiddenTime: "=",
          hiddenDate: "="
        },
        template: function (elem, attrs) {
          function dashCase(name) {
            return name.replace(/[A-Z]/g, function (letter, pos) {
              return (pos ? '-' : '') + letter.toLowerCase();
            });
          }

          function createAttr(innerAttr, dateTimeAttrOpt) {
            var dateTimeAttr = angular.isDefined(dateTimeAttrOpt) ? dateTimeAttrOpt : innerAttr;
            if (attrs[dateTimeAttr]) {
              return dashCase(innerAttr) + "=\"" + dateTimeAttr + "\" ";
            } else {
              return '';
            }
          }

          function createFuncAttr(innerAttr, funcArgs, dateTimeAttrOpt, defaultImpl) {
            var dateTimeAttr = angular.isDefined(dateTimeAttrOpt) ? dateTimeAttrOpt : innerAttr;
            if (attrs[dateTimeAttr]) {
              return dashCase(innerAttr) + "=\"" + dateTimeAttr + "({" + funcArgs + "})\" ";
            } else {
              return angular.isDefined(defaultImpl) ? dashCase(innerAttr) + "=\"" + defaultImpl + "\"" : "";
            }
          }

          function createEvalAttr(innerAttr, dateTimeAttrOpt) {
            var dateTimeAttr = angular.isDefined(dateTimeAttrOpt) ? dateTimeAttrOpt : innerAttr;
            if (attrs[dateTimeAttr]) {
              return dashCase(innerAttr) + "=\"" + attrs[dateTimeAttr] + "\" ";
            } else {
              return dashCase(innerAttr) + " ";
            }
          }

          function createAttrConcat(previousAttrs, attr) {
            return previousAttrs + createAttr.apply(null, attr)
          }

          var dateTmpl = "<div class=\"datetimepicker-wrapper\">" +
            "<input class=\"form-control\" type=\"text\" " +
            "name=\"datepicker\"" +
            "ng-change=\"date_change($event)\" " +
            "is-open=\"innerDateOpened\" " +
            "datepicker-options=\"dateOptions\" " + 
            "uib-datepicker-popup=\"{{dateFormat}}\"" +
            "ng-model=\"ngModel\" " + [
              ["dayFormat"],
              ["monthFormat"],
              ["yearFormat"],
              ["dayHeaderFormat"],
              ["dayTitleFormat"],
              ["monthTitleFormat"],
              ["yearRange"],
              ["ngHide", "hiddenDate"],
              ["ngDisabled", "readonlyDate"]
            ].reduce(createAttrConcat, '') +
            createFuncAttr("ngClick",
              "$event: $event, opened: opened",
              "dateNgClick",
              "open($event)") +
            createEvalAttr("currentText", "currentText") +
            createEvalAttr("clearText", "clearText") +
            createEvalAttr("datepickerAppendToBody", "datepickerAppendToBody") +
            createEvalAttr("closeText", "closeText") +
            createEvalAttr("placeholder", "placeholder") +
            "/>\n" +
            "</div>\n";
          var timeTmpl = "<div class=\"datetimepicker-wrapper\" name=\"timepicker\" ng-model=\"time\" ng-change=\"time_change()\" style=\"display:inline-block\">\n" +
            "<uib-timepicker " + [
              ["hourStep"],
              ["minuteStep"],
              ["showMeridian"],
              ["meredians"], 
              ["mousewheel"],
              ["min", "minDate"],
              ["max", "maxDate"],
              ["ngHide", "hiddenTime"],
              ["ngDisabled", "readonlyTime"]
            ].reduce(createAttrConcat, '') +
            createEvalAttr("showSpinners", "showSpinners") +
            "></timepicker>\n" +
            "</div>";
          // form is isolated so the directive is registered as one component in the parent form (not date and time)
          var tmpl = "<ng-form name=\"datetimepickerForm\" isolate-form>" + dateTmpl + timeTmpl + "</ng-form>";
          return tmpl;
        },
        controller: ['$scope', '$attrs',
          function ($scope, $attrs) {
            $scope.createDateOptionsWatch = function(dateAttr) {
              $scope.$watch(dateAttr, function (value) {
                var date = new Date(value);
                $scope.dateOptions[dateAttr] = date;
                if ($scope[dateAttr]) {
                  $scope[dateAttr] = date;
                } 
              }, true); 
            } 
            $scope.date_change = function () {
              // If we changed the date only, set the time (h,m) on it.
              // This is important in case the previous date was null.
              // This solves the issue when the user set a date and time, cleared the date, and chose another date,
              // and then, the time was cleared too - which is unexpected
              var time = $scope.time;
              if ($scope.ngModel && $scope.time) { // if ngModel is null, that's because the user cleared the date field
                $scope.ngModel.setHours(time.getHours(), time.getMinutes(), 0, 0);
              }
            };
            $scope.time_change = function () {
              
              if ($scope.ngModel && $scope.time) {
                // convert from ISO format to Date
                if (!($scope.ngModel instanceof Date)) $scope.ngModel = new Date($scope.ngModel);
                $scope.ngModel.setHours($scope.time.getHours(), $scope.time.getMinutes(), 0, 0);
              } else {
                $scope.ngModel = new Date();
              }
            };
            $scope.open = function ($event) {
              $event.preventDefault();
              $event.stopPropagation();
              $scope.innerDateOpened = true;
            };
            $attrs.$observe('dateFormat', function(newDateFormat, oldValue) {
              $scope.dateFormat = newDateFormat;
            });
            $scope.dateOptions = angular.isDefined($scope.dateOptions) ? $scope.dateOptions : {};
            $scope.createDateOptionsWatch('minDate');
            $scope.createDateOptionsWatch('maxDate'); 
            $scope.dateOptions.dateDisabled = $scope.dateDisabled;
          }
        ],
        link: function (scope, element, attrs, ctrl) {
          var firstTimeAssign = true;
 
          scope.$watch(function () {
            return scope.ngModel;
          }, function (newTime) {
            var timeElement = document.evaluate(".//*[@ng-model='time']",
              element[0], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

            // if a time element is focused, updating its model will cause hours/minutes to be formatted by padding with leading zeros
            if (timeElement && !timeElement.contains(document.activeElement)) {

              if (newTime === null || newTime === '') { // if the newTime is not defined
                if (firstTimeAssign) { // if it's the first time we assign the time value
                  // create a new default time where the hours, minutes, seconds and milliseconds are set to 0.
                  newTime = new Date();
                  newTime.setHours(0, 0, 0, 0);
                } else { // clear the time
                  scope.time = null;
                  return;
                }
              }
              // Update timepicker (watch on ng-model in timepicker does not use object equality),
              // also if the ngModel was not a Date, convert it to date
              newTime = new Date(newTime);

              if (isNaN(newTime.getTime()) === false) {
                scope.time = newTime; // change the time in timepicker
                if (firstTimeAssign) {
                  firstTimeAssign = false;
                }
              }
            }
          }, true);

          scope.$watch(function () {
            return scope.datetimepickerForm.$error;
          }, function (errors) {
            Object.keys(ctrl.$error).forEach(function (error) {
              ctrl.$setValidity(error, true);
            });
            Object.keys(errors).forEach(function (error) {
              ctrl.$setValidity(error, false);
            });
          }, true);
          
          scope.$watch(function () {
            return scope.datetimepickerForm.timepicker.$touched || scope.datetimepickerForm.datepicker.$touched;
          }, function (touched) {
            if (touched) {
              ctrl.$setTouched();
            }
          });
          
          scope.$watch(function () {
            return scope.datetimepickerForm.$dirty;
          }, function (dirty) {
            if (dirty) {
              ctrl.$setDirty();
            }
          });
          
          scope.$watch('dateOpened', function (value) {
            scope.innerDateOpened = value;
          }); 
          scope.$watch('innerDateOpened', function (value) {
            if (angular.isDefined(scope.dateOpened)) {
              scope.dateOpened = value;
            } 
          });   
        }
      }
    }
  ]).directive('isolateForm', [function () {
  return {
    restrict: 'A',
    require: '?form',
    link: function (scope, elm, attrs, ctrl) {
      if (!ctrl) {
        return;
      }
      // Do a copy of the controller
      var ctrlCopy = {};
      angular.copy(ctrl, ctrlCopy);

      // Get the parent of the form
      var parent = elm.parent().controller('form');
      if (!parent) {
        return;
      }
      // Remove parent link to the controller
      parent.$removeControl(ctrl);

      // Replace form controller with an "isolated form"
      var isolatedFormCtrl = {
        $setValidity: function (validationToken, isValid, control) {
          ctrlCopy.$setValidity(validationToken, isValid, control);
          parent.$setValidity(validationToken, true, ctrl);
        }
      };
      angular.extend(ctrl, isolatedFormCtrl);
    }
  };
}]);