2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
25 var getTimezoneOffset = function(date) {
26 (typeof date == 'string') && (date = new Date(date));
27 var jan = new Date(date.getFullYear(), 0, 1);
28 var jul = new Date(date.getFullYear(), 6, 1);
29 var stdTimezoneOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
30 var isDST = date.getTimezoneOffset() < stdTimezoneOffset;
31 var offset = isDST ? stdTimezoneOffset - 60 : stdTimezoneOffset;
32 var diff = offset >=0 ? '-' : '+';
34 ("0"+ (offset / 60)).slice(-2) + ':' +
35 ("0"+ (offset % 60)).slice(-2);
38 var DatetimePicker = function($compile, $document, $controller){
39 var datetimePickerCtrl = $controller('DatetimePickerCtrl'); //directive controller
41 open: function(options) {
42 datetimePickerCtrl.openDatetimePicker(options);
45 datetimePickerCtrl.closeDatetimePicker();
49 DatetimePicker.$inject = ['$compile', '$document', '$controller'];
50 appDS2.factory('DatetimePicker', DatetimePicker);
52 var DatetimePickerCtrl = function($compile, $document) {
55 var removeEl = function(el) {
57 $document[0].body.removeEventListener('click', _this.closeDatetimePicker);
60 this.openDatetimePicker = function(options) {
61 this.closeDatetimePicker();
62 var div = angular.element('<div datetime-picker-popup ng-cloak></div>');
63 options.dateFormat && div.attr('date-format', options.dateFormat);
64 options.ngModel && div.attr('ng-model', options.ngModel);
65 options.year && div.attr('year', parseInt(options.year));
66 options.month && div.attr('month', parseInt(options.month));
67 options.day && div.attr('day', parseInt(options.day));
68 options.hour && div.attr('hour', parseInt(options.hour));
69 options.minute && div.attr('minute', parseInt(options.minute));
70 if (options.dateOnly === '' || options.dateOnly === true) {
71 div.attr('date-only', 'true');
73 if (options.closeOnSelect === 'false') {
74 div.attr('close-on-select', 'false');
77 var triggerEl = options.triggerEl;
78 options.scope = options.scope || angular.element(triggerEl).scope();
79 datetimePickerEl = $compile(div)(options.scope)[0];
80 datetimePickerEl.triggerEl = options.triggerEl;
82 $document[0].body.appendChild(datetimePickerEl);
84 //show datetimePicker below triggerEl
85 var bcr = triggerEl.getBoundingClientRect();
88 options.scope.$apply();
90 var datePickerElBcr = datetimePickerEl.getBoundingClientRect();
92 datetimePickerEl.style.position='absolute';
93 if(bcr.width > datePickerElBcr.width){
94 datetimePickerEl.style.left= (bcr.left + bcr.width - datePickerElBcr.width + window.scrollX) + 'px';
96 datetimePickerEl.style.left= (bcr.left + window.scrollX) + 'px';
99 if (bcr.top < 300 || window.innerHeight - bcr.bottom > 300) {
100 datetimePickerEl.style.top = (bcr.bottom + window.scrollY) + 'px';
102 datetimePickerEl.style.top = (bcr.top - datePickerElBcr.height + window.scrollY) + 'px';
105 $document[0].body.addEventListener('click', this.closeDatetimePicker);
108 this.closeDatetimePicker = function(evt) {
109 var target = evt && evt.target;
110 var popupEl = $document[0].querySelector('div[datetime-picker-popup]');
112 if (target.hasAttribute('datetime-picker')) { // element with datetimePicker behaviour
114 } else if (popupEl && popupEl.contains(target)) { // datetimePicker itself
124 DatetimePickerCtrl.$inject = ['$compile', '$document'];
125 appDS2.controller('DatetimePickerCtrl', DatetimePickerCtrl);
128 '<div class="angularjs-datetime-picker">' ,
129 ' <div class="adp-month">',
130 ' <button type="button" class="adp-prev" ng-click="addMonth(-1)">«</button>',
131 ' <span title="{{months[mv.month].fullName}}">{{months[mv.month].shortName}}</span> {{mv.year}}',
132 ' <button type="button" class="adp-next" ng-click="addMonth(1)">»</button>',
134 ' <div class="adp-days" ng-click="setDate($event)">',
135 ' <div class="adp-day-of-week" ng-repeat="dayOfWeek in ::daysOfWeek" title="{{dayOfWeek.fullName}}">{{::dayOfWeek.firstLetter}}</div>',
136 ' <div class="adp-day" ng-show="mv.leadingDays.length < 7" ng-repeat="day in mv.leadingDays">{{::day}}</div>',
137 ' <div class="adp-day selectable" ng-repeat="day in mv.days" ',
138 ' today="{{today}}" d2="{{mv.year + \'-\' + (mv.month + 1) + \'-\' + day}}"',
140 ' selected: (day == selectedDay),',
141 ' today: (today == (mv.year + \'-\' + (mv.month + 1) + \'-\' + day)),',
142 ' weekend: (mv.leadingDays.length + day)%7 == 1 || (mv.leadingDays.length + day)%7 == 0',
146 ' <div class="adp-day" ng-show="mv.trailingDays.length < 7" ng-repeat="day in mv.trailingDays">{{::day}}</div>',
148 ' <div class="adp-days" id="adp-time"> ',
149 ' <label class="timeLabel">Time:</label> <span class="timeValue">{{("0"+inputHour).slice(-2)}} : {{("0"+inputMinute).slice(-2)}}</span><br/>',
150 ' <label class="hourLabel">Hour:</label> <input class="hourInput" type="range" min="0" max="23" ng-model="inputHour" ng-change="updateNgModel()" />',
151 ' <label class="minutesLabel">Min:</label> <input class="minutesInput" type="range" min="0" max="59" ng-model="inputMinute" ng-change="updateNgModel()"/> ',
153 '</div>'].join("\n");
155 var datetimePickerPopup = function($locale, dateFilter){
156 var days, months, daysOfWeek, firstDayOfWeek;
158 var initVars = function() {
159 days =[], months=[]; daysOfWeek=[], firstDayOfWeek=0;
160 for (var i = 1; i <= 31; i++) {
164 for (var i = 0; i < 12; i++) { //jshint ignore:line
166 fullName: $locale.DATETIME_FORMATS.MONTH[i],
167 shortName: $locale.DATETIME_FORMATS.SHORTMONTH[i]
171 for (var i = 0; i < 7; i++) { //jshint ignore:line
172 var day = $locale.DATETIME_FORMATS.DAY[(i + firstDayOfWeek) % 7];
176 firstLetter: day.substr(0, 1)
182 var getMonthView = function(year, month) {
185 } else if (month < 0) {
188 month = (month + 12) % 12;
189 var firstDayOfMonth = new Date(year, month, 1),
190 lastDayOfMonth = new Date(year, month + 1, 0),
191 lastDayOfPreviousMonth = new Date(year, month, 0),
192 daysInMonth = lastDayOfMonth.getDate(),
193 daysInLastMonth = lastDayOfPreviousMonth.getDate(),
194 dayOfWeek = firstDayOfMonth.getDay(),
195 leadingDays = (dayOfWeek - firstDayOfWeek + 7) % 7 || 7, // Ensure there are always leading days to give context
196 trailingDays = days.slice(0, 6 * 7 - (leadingDays + daysInMonth));
197 if (trailingDays.length > 7) {
198 trailingDays = trailingDays.slice(0, trailingDays.length-7);
204 days: days.slice(0, daysInMonth),
205 leadingDays: days.slice(- leadingDays - (31 - daysInLastMonth), daysInLastMonth),
206 trailingDays: trailingDays
210 var linkFunc = function(scope, element, attrs, ctrl) { //jshint ignore:line
211 initVars(); //initialize days, months, daysOfWeek, and firstDayOfWeek;
212 var dateFormat = attrs.dateFormat || 'short';
213 scope.months = months;
214 scope.daysOfWeek = daysOfWeek;
218 if (scope.dateOnly === true){
219 element[0].querySelector('#adp-time').style.display = 'none';
222 scope.$applyAsync( function() {
223 ctrl.triggerEl = angular.element(element[0].triggerEl);
224 if (attrs.ngModel) { // need to parse date string
225 var dateStr = ''+ctrl.triggerEl.scope().$eval(attrs.ngModel);
227 if (!dateStr.match(/[0-9]{2}:/)) { // if no time is given, add 00:00:00 at the end
228 dateStr += " 00:00:00";
230 dateStr = dateStr.replace(/([0-9]{2}-[0-9]{2})-([0-9]{4})/,'$2-$1'); //mm-dd-yyyy to yyyy-mm-dd
231 dateStr = dateStr.replace(/([\/-][0-9]{2,4})\ ([0-9]{2}\:[0-9]{2}\:)/,'$1T$2'); //reformat for FF
232 dateStr = dateStr.replace(/EDT|EST|CDT|CST|MDT|PDT|PST|UT|GMT/g,''); //remove timezone
233 dateStr = dateStr.replace(/\s*\(\)\s*/,''); //remove timezone
234 dateStr = dateStr.replace(/[\-\+][0-9]{2}:?[0-9]{2}$/,''); //remove timezone
235 dateStr += getTimezoneOffset(dateStr);
236 var d = new Date(dateStr);
237 scope.selectedDate = new Date(
248 if (!scope.selectedDate || isNaN(scope.selectedDate.getTime())) { // no predefined date
249 var today = new Date();
250 var year = scope.year || today.getFullYear();
251 var month = scope.month ? (scope.month-1) : today.getMonth();
252 var day = scope.day || today.getDate();
253 var hour = scope.hour || today.getHours();
254 var minute = scope.minute || today.getMinutes();
255 scope.selectedDate = new Date(year, month, day, hour, minute, 0);
257 scope.inputHour = scope.selectedDate.getHours();
258 scope.inputMinute = scope.selectedDate.getMinutes();
260 // Default to current year and month
261 scope.mv = getMonthView(scope.selectedDate.getFullYear(), scope.selectedDate.getMonth());
262 scope.today = dateFilter(new Date(), 'yyyy-M-d');
263 if (scope.mv.year == scope.selectedDate.getFullYear() && scope.mv.month == scope.selectedDate.getMonth()) {
264 scope.selectedDay = scope.selectedDate.getDate();
266 scope.selectedDay = null;
270 /* disable previous months.not current months
271 scope.addMonth = function (amount) {
272 var today = new Date();
273 if((amount==-1) && (scope.mv.month==today.getMonth())){
279 scope.mv = getMonthView(scope.mv.year, scope.mv.month + amount);
283 scope.addMonth = function (amount) {
284 scope.mv = getMonthView(scope.mv.year, scope.mv.month + amount);
287 scope.setDate = function (evt) {
289 var target = angular.element(evt.target)[0];
290 if (target.className.indexOf('selectable') !== -1) {
291 scope.updateNgModel(parseInt(target.innerHTML));
292 if (scope.closeOnSelect !== false) {
293 ctrl.closeDatetimePicker();
298 scope.updateNgModel = function(day) {
299 day = day ? day : scope.selectedDate.getDate();
300 scope.selectedDate = new Date(
301 scope.mv.year, scope.mv.month, day, scope.inputHour, scope.inputMinute, 0
303 scope.selectedDay = scope.selectedDate.getDate();
305 //console.log('attrs.ngModel',attrs.ngModel);
306 var elScope = ctrl.triggerEl.scope(), dateValue;
307 if (elScope.$eval(attrs.ngModel) && elScope.$eval(attrs.ngModel).constructor.name === 'Date') {
308 dateValue = new Date(dateFilter(scope.selectedDate, dateFormat));
310 dateValue = dateFilter(scope.selectedDate, dateFormat);
312 elScope.$eval(attrs.ngModel + '= date', {date: dateValue}); // this line for have the date in the format of mm/dd/yyyy
313 // elScope.$eval(attrs.ngModel + '= date', {date: scope.selectedDate});// this line in the format of Thu Jul 20 2017 23:59:00 GMT-0400 (Eastern Daylight Time)
317 scope.$on('$destroy', ctrl.closeDatetimePicker);
323 controller: 'DatetimePickerCtrl',
338 datetimePickerPopup.$inject = ['$locale', 'dateFilter'];
339 appDS2.directive('datetimePickerPopup', datetimePickerPopup);
341 var datetimePicker = function($parse, DatetimePicker) {
343 // An ngModel is required to get the controller argument
345 link: function(scope, element, attrs, ctrl) {
346 // Attach validation watcher
347 scope.$watch(attrs.ngModel, function(value) {
348 if( !value || value == '' ){
351 // The value has already been cleaned by the above code
352 var date = new Date(value);
353 ctrl.$setValidity('date', !date? false : true);
354 var now = new Date();
355 if( attrs.hasOwnProperty('futureOnly') ){
356 ctrl.$setValidity('future-only', date < now? false : true);
360 element[0].addEventListener('click', function() {
361 DatetimePicker.open({
362 triggerEl: element[0],
363 dateFormat: attrs.dateFormat,
364 ngModel: attrs.ngModel,
369 minute: attrs.minute,
370 dateOnly: attrs.dateOnly,
371 futureOnly: attrs.futureOnly,
372 closeOnSelect: attrs.closeOnSelect
378 datetimePicker.$inject=['$parse', 'DatetimePicker'];
379 appDS2.directive('datetimePicker', datetimePicker);