(function () { 'use strict'; var app = angular.module('sysMgrApp'); app.directive('smDateTimePicker', [function () { return { require: 'ngModel', restrict: 'E', scope: { ngModel: '=',//the date being saved to the database compareDate: '=',//either the start or end date of the range for validation (isDateRange attr must be set to true) form: '=',//the name of the form (if a subform, this will be parent.subform), used for setting the form to dirty and validation name: '@',//the input's name, used to manually outline invalid fields in red placeholder: '@',//text of placeholder on input field, showBod: '=',//if true, we display the Beginning of Day button (pickTime must be true) showEod: '=',//if true, we display the End of Day button (pickTime must be true) showNow: '=',//if true, we display the Now button setBod: '&',//function defined in the directive that is called when clicking on the BOD button setEod: '&',//function defined in the directive that is called when clicking on the EOD button setNow: '&',//function defined in the directive that is called when clicking on the Now button validStartDateRange: '&',//function defined in the directive that determines whether to show validation message validEndDateRange: '&', validRequired: '&', validFutureDate: '&', validPastDate: '&' }, templateUrl: "/app/templates/smDateTimePicker", link: function (scope, element, attrs, controller) { if (!controller) return undefined; var updateModel, onblur, datetimepicker, options; var pickTime = attrs.pickTime === "true" ? true : false; var isDateRange = attrs.isDateRange === "true" ? true : false; //if true, we do range validation on start and end dates var isRequired = attrs.isRequired === "true" ? true : false; //if true, we do required validation scope.showBod = (scope.showBod && pickTime); //only display showBod button if pickTime is true scope.showEod = (scope.showEod && pickTime); //only display showEod button if pickTime is true var onlyFutureDates = attrs.onlyFutureDates === "true" ? true : false; var onlyPastDates = attrs.onlyPastDates === "true" ? true : false; var isEndDate = attrs.isEndDate === "true" ? true : false; var format = function (date) { if (date) { if (!pickTime) { if (isEndDate && !onlyFutureDates) { date = date.hours(23).minutes(59).seconds(59).milliseconds(997); } else { date = date.hours(0).minutes(0).seconds(0).milliseconds(0); } } date = date.format("YYYY-MM-DD HH:mm:ss.SSS").toString(); } return date; }; var setStartDateLimit = function () { if (onlyFutureDates) { return moment().add('days', 1); } return moment("1753 01 01", "YYYY MM DD"); }; var setEndDateLimit = function () { if (onlyPastDates) { return moment().subtract('days', 1); } return moment("9999 12 31", "YYYY MM DD"); }; var setCalendarPickerStartDate = function () { if (onlyFutureDates) { return moment().add('days', 1); } else if (onlyPastDates) { return moment().subtract('days', 1); } else { return moment(); } }; var init = function () { options = { icons: { time: "fa fa-clock-o icon-1x", date: "fa fa-calendar icon-1x", up: "fa fa-arrow-up", down: "fa fa-arrow-down" }, //pickDate: true, //pickTime: pickTime, format: pickTime ? 'MM/DD/YYYY hh:mm:ss a' : 'MM/DD/YYYY', //language: 'en', locale: 'en', //startDate: format(setStartDateLimit()), //endDate: format(setEndDateLimit()), minDate: format(setStartDateLimit()), maxDate: format(setEndDateLimit()), useStrict: true }; datetimepicker = element.find('.input-group').datetimepicker(options); }; init(); controller.$render = function () { var viewValue = controller.$viewValue; if (viewValue) { var date = moment(viewValue); if (angular.isDefined(date) && date != null && moment.isMoment(date)) { datetimepicker.data('DateTimePicker').date(date); } } else { datetimepicker.data('DateTimePicker').date(setCalendarPickerStartDate()); datetimepicker.data('DateTimePicker').date(); } return controller.$viewValue; }; updateModel = function () { //if (datetimepicker.data("DateTimePicker").minViewMode === datetimepicker.data("DateTimePicker").viewMode) { datetimepicker.blur(); //} }; onblur = function () { var newValue = datetimepicker.data("DateTimePicker").date(); return scope.$evalAsync(function () { scope.form.$setDirty(); if (newValue) { return controller.$setViewValue(format(moment(newValue))); } else { return controller.$setViewValue(); } }); }; scope.$watch('compareDate', function () { validate(); }); scope.$watch('ngModel', function () { validate(); datetimepicker.on('dp.change', updateModel).on('blur', onblur); }); scope.setBod = function () { var viewValue = controller.$viewValue; var bod = format(moment(viewValue).hours(0).minutes(0).seconds(0).milliseconds(0)); datetimepicker.data("datetimepicker").date(bod); scope.form.$setDirty(); scope.ngModel = bod; }; scope.setEod = function () { var viewValue = controller.$viewValue; var eod = format(moment(viewValue).hours(23).minutes(59).seconds(59).milliseconds(997)); datetimepicker.data("DateTimePicker").date(eod); scope.form.$setDirty(); scope.ngModel = eod; }; scope.setNow = function () { var now = format(moment()); datetimepicker.data("DateTimePicker").date(now); scope.form.$setDirty(); scope.ngModel = now; }; //NOTE: We have to manually set these valid/invalid classes and required validation //because we're not putting ng-model on the input field. //We're not putting ng-model on the input field //because two-way data-binding would prevent the user //from being able to type in a date. var validate = function () { if (controller.$dirty || scope.form.$dirty) { var input = $("#" + scope.name); validateRequired(scope.ngModel); validateFutureDate(input, scope.ngModel); validatePastDate(input, scope.ngModel); validateDateRange(scope.ngModel, scope.compareDate); if (controller.$error.daterange || controller.$error.required || controller.$error.futuredate || controller.$error.pastdate) { setInValid(input); } else { setValid(input); } } }; var setValid = function (input) { input.removeClass("ng-invalid"); input.addClass("ng-valid"); }; var setInValid = function (input) { input.removeClass("ng-valid"); input.addClass("ng-invalid"); }; var validateFutureDate = function (input, date) { if (onlyFutureDates) { var inputValue = element.find('input').val(); if (!inputValue) { setFutureDateValidity(true); } else if (moment(date) <= moment()) { setFutureDateValidity(false); } else { setFutureDateValidity(true); } } }; var validatePastDate = function (input, date) { if (onlyPastDates) { var inputValue = element.find('input').val(); if (!inputValue) { setPastDateValidity(true); } else if (moment(date) >= moment()) { setPastDateValidity(false); } else { setPastDateValidity(true); } } }; var validateDateRange = function (ngModel, compareDate) { if (isDateRange && !controller.$error.futuredate && !controller.$error.pastdate) { var inputValue = element.find('input').val(); if (!ngModel && !compareDate) { setDateRangeValidity(true); } else if ((!ngModel || !compareDate) && (ngModel || compareDate) && !inputValue) {//here we need to check the input value if we can setDateRangeValidity(false); } else { var newStartDate; var newEndDate; if (isEndDate) { newEndDate = moment(ngModel); newStartDate = moment(compareDate); } else { newStartDate = moment(ngModel); newEndDate = moment(compareDate); } if (newStartDate.isBefore(newEndDate) || newStartDate.isSame(newEndDate)) { setDateRangeValidity(true); } else { setDateRangeValidity(false); } } } else { setDateRangeValidity(true); } }; var validateRequired = function (input, date) { if (isRequired) { if (date) { setRequiredValidity(true); } else { setRequiredValidity(false); } } }; scope.validStartDateRange = function () { if (isDateRange) { return (!(controller.$error.daterange && (controller.$dirty || scope.form.$dirty) && !isEndDate)); } return true; }; scope.validEndDateRange = function () { if (isDateRange) { return (!(controller.$error.daterange && (controller.$dirty || scope.form.$dirty) && isEndDate)); } return true; }; scope.validRequired = function () { if (isRequired) { return (!(controller.$error.required && controller.$dirty)); } return true; }; scope.validFutureDate = function () { if (onlyFutureDates) { return (!(controller.$error.futuredate && controller.$dirty)); } return true; }; scope.validPastDate = function () { if (onlyPastDates) { return (!(controller.$error.pastdate && controller.$dirty)); } return true; }; var setRequiredValidity = function (valid) { controller.$setValidity('required', valid); }; var setFutureDateValidity = function (valid) { controller.$setValidity('futuredate', valid); }; var setPastDateValidity = function (valid) { controller.$setValidity('pastdate', valid); }; var setDateRangeValidity = function (valid) { controller.$setValidity('daterange', valid); }; var hasInputValue = function (input) { return input.length > 0; }; } }; }]); })();