diff --git a/angularjs-datetime-picker.js b/angularjs-datetime-picker.js
index e86247d..88c14ea 100644
--- a/angularjs-datetime-picker.js
+++ b/angularjs-datetime-picker.js
@@ -1,346 +1,443 @@
-(function() {
- 'use strict';
-
- angular.module('angularjs-datetime-picker', []);
-
- var getTimezoneOffset = function(date) {
- (typeof date == 'string') && (date = new Date(date));
- var jan = new Date(date.getFullYear(), 0, 1);
- var jul = new Date(date.getFullYear(), 6, 1);
- var stdTimezoneOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
- var isDST = date.getTimezoneOffset() < stdTimezoneOffset;
- var offset = isDST ? stdTimezoneOffset - 60 : stdTimezoneOffset;
- var diff = offset >=0 ? '-' : '+';
- return diff +
- ("0"+ (offset / 60)).slice(-2) + ':' +
- ("0"+ (offset % 60)).slice(-2);
- };
-
- var DatetimePicker = function($compile, $document, $controller){
- var datetimePickerCtrl = $controller('DatetimePickerCtrl'); //directive controller
- return {
- open: function(options) {
- datetimePickerCtrl.openDatetimePicker(options);
- },
- close: function() {
- datetimePickerCtrl.closeDatetimePicker();
- }
- };
- };
- DatetimePicker.$inject = ['$compile', '$document', '$controller'];
- angular.module('angularjs-datetime-picker').factory('DatetimePicker', DatetimePicker);
-
- var DatetimePickerCtrl = function($compile, $document) {
- var datetimePickerEl;
- var _this = this;
- var removeEl = function(el) {
- el && el.remove();
- $document[0].body.removeEventListener('click', _this.closeDatetimePicker);
- };
-
- this.openDatetimePicker = function(options) {
- this.closeDatetimePicker();
- var div = angular.element('
');
- options.dateFormat && div.attr('date-format', options.dateFormat);
- options.ngModel && div.attr('ng-model', options.ngModel);
- options.year && div.attr('year', parseInt(options.year));
- options.month && div.attr('month', parseInt(options.month));
- options.day && div.attr('day', parseInt(options.day));
- options.hour && div.attr('hour', parseInt(options.hour));
- options.minute && div.attr('minute', parseInt(options.minute));
- if (options.dateOnly === '' || options.dateOnly === true) {
- div.attr('date-only', 'true');
- }
- if (options.closeOnSelect === 'false') {
- div.attr('close-on-select', 'false');
- }
-
- var triggerEl = options.triggerEl;
- options.scope = options.scope || angular.element(triggerEl).scope();
- datetimePickerEl = $compile(div)(options.scope)[0];
- datetimePickerEl.triggerEl = options.triggerEl;
-
- $document[0].body.appendChild(datetimePickerEl);
-
- //show datetimePicker below triggerEl
- var bcr = triggerEl.getBoundingClientRect();
-
-
- options.scope.$apply();
-
- var datePickerElBcr = datetimePickerEl.getBoundingClientRect();
-
- datetimePickerEl.style.position='absolute';
- if(bcr.width > datePickerElBcr.width){
- datetimePickerEl.style.left= (bcr.left + bcr.width - datePickerElBcr.width + window.scrollX) + 'px';
- } else {
- datetimePickerEl.style.left= (bcr.left + window.scrollX) + 'px';
- }
-
- if (bcr.top < 300 || window.innerHeight - bcr.bottom > 300) {
- datetimePickerEl.style.top = (bcr.bottom + window.scrollY) + 'px';
- } else {
- datetimePickerEl.style.top = (bcr.top - datePickerElBcr.height + window.scrollY) + 'px';
- }
-
- $document[0].body.addEventListener('click', this.closeDatetimePicker);
- };
-
- this.closeDatetimePicker = function(evt) {
- var target = evt && evt.target;
- var popupEl = $document[0].querySelector('div[datetime-picker-popup]');
- if (evt && target) {
- if (target.hasAttribute('datetime-picker')) { // element with datetimePicker behaviour
- // do nothing
- } else if (popupEl && popupEl.contains(target)) { // datetimePicker itself
- // do nothing
- } else {
- removeEl(popupEl);
- }
- } else {
- removeEl(popupEl);
- }
- }
- };
- DatetimePickerCtrl.$inject = ['$compile', '$document'];
- angular.module('angularjs-datetime-picker').controller('DatetimePickerCtrl', DatetimePickerCtrl);
-
- var tmpl = [
- '' ,
- '
',
- ' ',
- ' {{months[mv.month].shortName}} {{mv.year}}',
- ' ',
- '
',
- '
',
- '
{{::dayOfWeek.firstLetter}}
',
- '
{{::day}}
',
- '
',
- ' {{::day}}',
- '
',
- '
{{::day}}
',
- '
',
- '
',
- ' {{("0"+inputHour).slice(-2)}} : {{("0"+inputMinute).slice(-2)}}
',
- ' ',
- ' ',
- '
',
- '
'].join("\n");
-
- var datetimePickerPopup = function($locale, dateFilter){
- var days, months, daysOfWeek, firstDayOfWeek;
-
- var initVars = function() {
- days =[], months=[]; daysOfWeek=[], firstDayOfWeek=0;
- for (var i = 1; i <= 31; i++) {
- days.push(i);
- }
-
- for (var i = 0; i < 12; i++) { //jshint ignore:line
- months.push({
- fullName: $locale.DATETIME_FORMATS.MONTH[i],
- shortName: $locale.DATETIME_FORMATS.SHORTMONTH[i]
- });
- }
-
- for (var i = 0; i < 7; i++) { //jshint ignore:line
- var day = $locale.DATETIME_FORMATS.DAY[(i + firstDayOfWeek) % 7];
-
- daysOfWeek.push({
- fullName: day,
- firstLetter: day.substr(0, 1)
- });
- }
- firstDayOfWeek = 0;
- };
-
- var getMonthView = function(year, month) {
- if (month>11) {
- year++;
- } else if (month < 0) {
- year--;
- }
- month = (month + 12) % 12;
- var firstDayOfMonth = new Date(year, month, 1),
- lastDayOfMonth = new Date(year, month + 1, 0),
- lastDayOfPreviousMonth = new Date(year, month, 0),
- daysInMonth = lastDayOfMonth.getDate(),
- daysInLastMonth = lastDayOfPreviousMonth.getDate(),
- dayOfWeek = firstDayOfMonth.getDay(),
- leadingDays = (dayOfWeek - firstDayOfWeek + 7) % 7 || 7, // Ensure there are always leading days to give context
- trailingDays = days.slice(0, 6 * 7 - (leadingDays + daysInMonth));
- if (trailingDays.length > 7) {
- trailingDays = trailingDays.slice(0, trailingDays.length-7);
- }
-
- return {
- year: year,
- month: month,
- days: days.slice(0, daysInMonth),
- leadingDays: days.slice(- leadingDays - (31 - daysInLastMonth), daysInLastMonth),
- trailingDays: trailingDays
- };
- };
-
- var linkFunc = function(scope, element, attrs, ctrl) { //jshint ignore:line
- initVars(); //initialize days, months, daysOfWeek, and firstDayOfWeek;
- var dateFormat = attrs.dateFormat || 'short';
- scope.months = months;
- scope.daysOfWeek = daysOfWeek;
- scope.inputHour;
- scope.inputMinute;
-
- if (scope.dateOnly === true){
- element[0].querySelector('#adp-time').style.display = 'none';
- }
-
- scope.$applyAsync( function() {
- ctrl.triggerEl = angular.element(element[0].triggerEl);
- if (attrs.ngModel) { // need to parse date string
- var dateStr = ''+ctrl.triggerEl.scope().$eval(attrs.ngModel);
- if (dateStr) {
- if (!dateStr.match(/[0-9]{2}:/)) { // if no time is given, add 00:00:00 at the end
- dateStr += " 00:00:00";
- }
- dateStr = dateStr.replace(/([0-9]{2}-[0-9]{2})-([0-9]{4})/,'$2-$1'); //mm-dd-yyyy to yyyy-mm-dd
- dateStr = dateStr.replace(/([\/-][0-9]{2,4})\ ([0-9]{2}\:[0-9]{2}\:)/,'$1T$2'); //reformat for FF
- dateStr = dateStr.replace(/EDT|EST|CDT|CST|MDT|PDT|PST|UT|GMT/g,''); //remove timezone
- dateStr = dateStr.replace(/\s*\(\)\s*/,''); //remove timezone
- dateStr = dateStr.replace(/[\-\+][0-9]{2}:?[0-9]{2}$/,''); //remove timezone
- dateStr += getTimezoneOffset(dateStr);
- var d = new Date(dateStr);
- scope.selectedDate = new Date(
- d.getFullYear(),
- d.getMonth(),
- d.getDate(),
- d.getHours(),
- d.getMinutes(),
- d.getSeconds()
- );
- }
- }
-
- if (!scope.selectedDate || isNaN(scope.selectedDate.getTime())) { // no predefined date
- var today = new Date();
- var year = scope.year || today.getFullYear();
- var month = scope.month ? (scope.month-1) : today.getMonth();
- var day = scope.day || today.getDate();
- var hour = scope.hour || today.getHours();
- var minute = scope.minute || today.getMinutes();
- scope.selectedDate = new Date(year, month, day, hour, minute, 0);
- }
- scope.inputHour = scope.selectedDate.getHours();
- scope.inputMinute = scope.selectedDate.getMinutes();
-
- // Default to current year and month
- scope.mv = getMonthView(scope.selectedDate.getFullYear(), scope.selectedDate.getMonth());
- scope.today = dateFilter(new Date(), 'yyyy-M-d');
- if (scope.mv.year == scope.selectedDate.getFullYear() && scope.mv.month == scope.selectedDate.getMonth()) {
- scope.selectedDay = scope.selectedDate.getDate();
- } else {
- scope.selectedDay = null;
- }
- });
-
- scope.addMonth = function (amount) {
- scope.mv = getMonthView(scope.mv.year, scope.mv.month + amount);
- };
-
- scope.setDate = function (evt) {
- var target = angular.element(evt.target)[0];
- if (target.className.indexOf('selectable') !== -1) {
- scope.updateNgModel(parseInt(target.innerHTML));
- if (scope.closeOnSelect !== false) {
- ctrl.closeDatetimePicker();
- }
- }
- };
-
- scope.updateNgModel = function(day) {
- day = day ? day : scope.selectedDate.getDate();
- scope.selectedDate = new Date(
- scope.mv.year, scope.mv.month, day, scope.inputHour, scope.inputMinute, 0
- );
- scope.selectedDay = scope.selectedDate.getDate();
- if (attrs.ngModel) {
- //console.log('attrs.ngModel',attrs.ngModel);
- var elScope = ctrl.triggerEl.scope(), dateValue;
- if (elScope.$eval(attrs.ngModel) && elScope.$eval(attrs.ngModel).constructor.name === 'Date') {
- dateValue = new Date(dateFilter(scope.selectedDate, dateFormat));
- } else {
- dateValue = dateFilter(scope.selectedDate, dateFormat);
- }
- elScope.$eval(attrs.ngModel + '= date', {date: dateValue});
- }
- };
-
- scope.$on('$destroy', ctrl.closeDatetimePicker);
- };
-
- return {
- restrict: 'A',
- template: tmpl,
- controller: 'DatetimePickerCtrl',
- replace: true,
- scope: {
- year: '=',
- month: '=',
- day: '=',
- hour: '=',
- minute: '=',
- dateOnly: '=',
- closeOnSelect: '='
- },
- link: linkFunc
- };
- };
- datetimePickerPopup.$inject = ['$locale', 'dateFilter'];
- angular.module('angularjs-datetime-picker').directive('datetimePickerPopup', datetimePickerPopup);
-
- var datetimePicker = function($parse, DatetimePicker) {
- return {
- // An ngModel is required to get the controller argument
- require: 'ngModel',
- link: function(scope, element, attrs, ctrl) {
- // Attach validation watcher
- scope.$watch(attrs.ngModel, function(value) {
- if( !value || value == '' ){
- return;
- }
- // The value has already been cleaned by the above code
- var date = new Date(value);
- ctrl.$setValidity('date', !date? false : true);
- var now = new Date();
- if( attrs.hasOwnProperty('futureOnly') ){
- ctrl.$setValidity('future-only', date < now? false : true);
- }
- });
-
- element[0].addEventListener('click', function() {
- DatetimePicker.open({
- triggerEl: element[0],
- dateFormat: attrs.dateFormat,
- ngModel: attrs.ngModel,
- year: attrs.year,
- month: attrs.month,
- day: attrs.day,
- hour: attrs.hour,
- minute: attrs.minute,
- dateOnly: attrs.dateOnly,
- futureOnly: attrs.futureOnly,
- closeOnSelect: attrs.closeOnSelect
- });
- });
- }
- };
- };
- datetimePicker.$inject=['$parse', 'DatetimePicker'];
- angular.module('angularjs-datetime-picker').directive('datetimePicker', datetimePicker);
-
+(function () {
+ 'use strict';
+
+ var tmpl = [
+ '',
+ '
',
+ ' ',
+ ' {{months[mv.month].shortName}} {{mv.year}}',
+ ' ',
+ '
',
+ '
',
+ '
{{::dayOfWeek.firstLetter}}
',
+ '
{{::day}}
',
+ '
',
+ ' {{::day}}',
+ '
',
+ '
{{::day}}
',
+ '
',
+ '
',
+ ' {{("0"+inputHour).slice(-2)}} : {{("0"+inputMinute).slice(-2)}}
',
+ ' ',
+ ' ',
+ '
',
+ '
',
+ ' ',
+ '
',
+ '
'].join("\n");
+
+ angular.module('angularjs-datetime-picker', [])
+ .factory('DatetimePicker', DatetimePickerFactory)
+ .directive('datetimePickerPopup', datetimePickerPopup)
+ .directive('datetimePicker', datetimePicker);
+
+ DatetimePickerFactory.$inject = ['$compile', '$document'];
+ function DatetimePickerFactory($compile, $document) {
+ return DatetimePicker;
+
+ function DatetimePicker(_options) {
+ var self = this;
+ var popupElement;
+ var triggerElement;
+ var scope;
+ var options;
+ var isOpened = false;
+
+ self.triggerElementScope = null;
+
+ self.open = open;
+ self.close = close;
+
+ activate();
+
+ function activate() {
+ options = _options;
+ triggerElement = options.triggerEl;
+ self.triggerElementScope = angular.element(triggerElement).scope();
+
+ scope = options.scope || self.triggerElementScope;
+ scope.popupCtrl = self;
+ }
+
+ function close() {
+ $document[0].body.removeEventListener('mousedown', onMousedownOutside);
+
+ if (popupElement) {
+ popupElement.removeEventListener('mousedown', onMousedown);
+ popupElement.parentElement.removeChild(popupElement);
+ popupElement = null;
+ }
+
+ isOpened = false;
+ }
+
+ function open() {
+ if (isOpened)
+ return;
+
+ var div = angular.element('');
+
+ options.dateFormat && div.attr('date-format', options.dateFormat);
+ options.ngModel && div.attr('ng-model', options.ngModel);
+ options.year && div.attr('year', parseInt(options.year));
+ options.month && div.attr('month', parseInt(options.month));
+ options.day && div.attr('day', parseInt(options.day));
+ options.hour && div.attr('hour', parseInt(options.hour));
+ options.minute && div.attr('minute', parseInt(options.minute));
+
+ if (options.dateOnly === '' || options.dateOnly === true) {
+ div.attr('date-only', 'true');
+ }
+ if (options.closeOnSelect === 'false') {
+ div.attr('close-on-select', 'false');
+ }
+
+ popupElement = $compile(div)(scope)[0];
+ popupElement.triggerEl = triggerElement;
+
+ $document[0].body.appendChild(popupElement);
+
+ //show datetimePicker below triggerEl
+ var bcr = triggerElement.getBoundingClientRect();
+
+ scope.$apply();
+
+ var datePickerElBcr = popupElement.getBoundingClientRect();
+
+ popupElement.style.position = 'absolute';
+ if (bcr.width > datePickerElBcr.width) {
+ popupElement.style.left = (bcr.left + bcr.width - datePickerElBcr.width + window.pageXOffset) + 'px';
+ } else {
+ popupElement.style.left = (bcr.left + window.pageXOffset) + 'px';
+ }
+
+ if (bcr.top < 300 || window.innerHeight - bcr.bottom > 300) {
+ popupElement.style.top = (bcr.bottom + window.pageYOffset) + 'px';
+ } else {
+ popupElement.style.top = (bcr.top - datePickerElBcr.height + window.pageYOffset) + 'px';
+ }
+
+ $document[0].body.addEventListener('mousedown', onMousedownOutside);
+ popupElement.addEventListener('mousedown', onMousedown);
+
+ isOpened = true;
+ }
+
+ function onMousedownOutside(e) {
+ if (!popupElement)
+ return;
+
+ var target = e && e.target;
+ if (target.hasAttribute('datetime-picker'))
+ return;
+
+ close(popupElement);
+ }
+
+ function onMousedown(e) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+ }
+ }
+
+ datetimePickerPopup.$inject = ['$locale', 'dateFilter'];
+ function datetimePickerPopup($locale, dateFilter) {
+ var days, months, daysOfWeek, firstDayOfWeek;
+
+ return {
+ restrict: 'A',
+ template: tmpl,
+ replace: true,
+ scope: {
+ year: '=',
+ month: '=',
+ day: '=',
+ hour: '=',
+ minute: '=',
+ dateOnly: '=',
+ closeOnSelect: '=',
+ popupCtrl: '='
+ },
+ link: link
+ };
+
+ function link(scope, element, attrs) { //jshint ignore:line
+ initVars(); //initialize days, months, daysOfWeek, and firstDayOfWeek;
+
+ var dateFormat = attrs.dateFormat || 'short';
+ scope.dateFormat = dateFormat;
+ scope.months = months;
+ scope.daysOfWeek = daysOfWeek;
+ scope.inputHour = null;
+ scope.inputMinute = null;
+
+ if (scope.dateOnly === true) {
+ element[0].querySelector('#adp-time').style.display = 'none';
+ }
+
+ scope.$applyAsync(function () {
+ if (attrs.ngModel) { // need to parse date string
+ var dateStr = '' + (scope.popupCtrl.triggerElementScope.$eval(attrs.ngModel) || '');
+
+ if (dateStr)
+ scope.selectedDate = parseDate(dateStr, dateFormat);
+ }
+
+ if (!scope.selectedDate || isNaN(scope.selectedDate.getTime())) { // no predefined date
+ var today = new Date();
+ var year = scope.year || today.getFullYear();
+ var month = scope.month ? (scope.month - 1) : today.getMonth();
+ var day = scope.day || today.getDate();
+ var hour = scope.hour || today.getHours();
+ var minute = scope.minute || today.getMinutes();
+ scope.selectedDate = new Date(year, month, day, hour, minute, 0);
+ }
+ scope.inputHour = scope.selectedDate.getHours();
+ scope.inputMinute = scope.selectedDate.getMinutes();
+
+ // Default to current year and month
+ scope.mv = getMonthView(scope.selectedDate.getFullYear(), scope.selectedDate.getMonth());
+ scope.today = dateFilter(new Date(), 'yyyy-M-d');
+ if (scope.mv.year == scope.selectedDate.getFullYear() && scope.mv.month == scope.selectedDate.getMonth()) {
+ scope.selectedDay = scope.selectedDate.getDate();
+ } else {
+ scope.selectedDay = null;
+ }
+ });
+
+ scope.addMonth = addMonth;
+ scope.clearDate = clearDate;
+ scope.setDate = setDate;
+ scope.updateNgModel = updateNgModel;
+
+ scope.$on('$destroy', scope.popupCtrl.close);
+
+ function addMonth(amount) {
+ scope.mv = getMonthView(scope.mv.year, scope.mv.month + amount);
+ }
+
+ function clearDate() {
+ scope.selectedDate = null;
+
+ var elScope = scope.popupCtrl.triggerElementScope;
+ elScope.$eval(attrs.ngModel + '= date', { date: null });
+ scope.$emit('datetime-picker-changed', null);
+ }
+
+ function setDate(evt) {
+ var target = angular.element(evt.target)[0];
+ if (target.className.indexOf('selectable') !== -1) {
+ scope.updateNgModel(parseInt(target.innerHTML));
+ if (scope.closeOnSelect !== false) {
+ scope.popupCtrl.close();
+ }
+ }
+ }
+
+ function updateNgModel(day) {
+ day = day ? day : scope.selectedDate.getDate();
+ scope.selectedDate = new Date(
+ scope.mv.year,
+ scope.mv.month,
+ day,
+ scope.inputHour,
+ scope.inputMinute,
+ 0
+ );
+ scope.selectedDay = scope.selectedDate.getDate();
+ if (attrs.ngModel) {
+ var elScope = scope.popupCtrl.triggerElementScope, dateValue;
+ if (elScope.$eval(attrs.ngModel) && elScope.$eval(attrs.ngModel).constructor.name === 'Date') {
+ dateValue = new Date(dateFilter(scope.selectedDate, scope.dateFormat));
+ } else {
+ dateValue = dateFilter(scope.selectedDate, scope.dateFormat);
+ }
+ elScope.$eval(attrs.ngModel + '= date', { date: dateValue });
+
+ scope.$emit('datetime-picker-changed', dateValue);
+ }
+ }
+ }
+
+ function initVars() {
+ days = [], months = []; daysOfWeek = [], firstDayOfWeek = 0;
+ for (var i = 1; i <= 31; i++) {
+ days.push(i);
+ }
+
+ for (var i = 0; i < 12; i++) { //jshint ignore:line
+ months.push({
+ fullName: $locale.DATETIME_FORMATS.MONTH[i],
+ shortName: $locale.DATETIME_FORMATS.SHORTMONTH[i]
+ });
+ }
+
+ for (var i = 0; i < 7; i++) { //jshint ignore:line
+ var day = $locale.DATETIME_FORMATS.DAY[(i + firstDayOfWeek) % 7];
+
+ daysOfWeek.push({
+ fullName: day,
+ firstLetter: day.substr(0, 1)
+ });
+ }
+ firstDayOfWeek = 0;
+ }
+
+ function getMonthView(year, month) {
+ if (month > 11) {
+ year++;
+ } else if (month < 0) {
+ year--;
+ }
+ month = (month + 12) % 12;
+ var firstDayOfMonth = new Date(year, month, 1),
+ lastDayOfMonth = new Date(year, month + 1, 0),
+ lastDayOfPreviousMonth = new Date(year, month, 0),
+ daysInMonth = lastDayOfMonth.getDate(),
+ daysInLastMonth = lastDayOfPreviousMonth.getDate(),
+ dayOfWeek = firstDayOfMonth.getDay(),
+ leadingDays = (dayOfWeek - firstDayOfWeek + 7) % 7 || 7, // Ensure there are always leading days to give context
+ trailingDays = days.slice(0, 6 * 7 - (leadingDays + daysInMonth));
+ if (trailingDays.length > 7) {
+ trailingDays = trailingDays.slice(0, trailingDays.length - 7);
+ }
+
+ return {
+ year: year,
+ month: month,
+ days: days.slice(0, daysInMonth),
+ leadingDays: days.slice(-leadingDays - (31 - daysInLastMonth), daysInLastMonth),
+ trailingDays: trailingDays
+ };
+ }
+ }
+
+ datetimePicker.$inject = ['$parse', 'DatetimePicker', '$timeout'];
+ function datetimePicker($parse, DatetimePicker, $timeout) {
+ return {
+ // An ngModel is required to get the controller argument
+ require: 'ngModel',
+ link: function (scope, element, attrs, ctrl) {
+ var datetimePicker = new DatetimePicker({
+ triggerEl: element[0],
+ dateFormat: attrs.dateFormat,
+ ngModel: attrs.ngModel,
+ year: attrs.year,
+ month: attrs.month,
+ day: attrs.day,
+ hour: attrs.hour,
+ minute: attrs.minute,
+ dateOnly: attrs.dateOnly,
+ futureOnly: attrs.futureOnly,
+ closeOnSelect: attrs.closeOnSelect
+ });
+
+ // Attach validation watcher
+ scope.$watch(attrs.ngModel, function (value) {
+ if (!value || value == '') {
+ return;
+ }
+ // The value has already been cleaned by the above code
+ var date = new Date(value);
+ ctrl.$setValidity('date', !date ? false : true);
+ var now = new Date();
+ if (attrs.hasOwnProperty('futureOnly')) {
+ ctrl.$setValidity('future-only', date < now ? false : true);
+ }
+ });
+
+ element[0].addEventListener('click', function () {
+ datetimePicker.open();
+ });
+
+ element[0].addEventListener('blur', function () {
+ $timeout(function() {
+ datetimePicker.close();
+ }, 0);
+ });
+ }
+ };
+ };
+
+ function getTimezoneOffset(date) {
+ (typeof date == 'string') && (date = new Date(date));
+ var jan = new Date(date.getFullYear(), 0, 1);
+ var jul = new Date(date.getFullYear(), 6, 1);
+ var stdTimezoneOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
+ var isDST = date.getTimezoneOffset() < stdTimezoneOffset;
+ var offset = isDST ? stdTimezoneOffset - 60 : stdTimezoneOffset;
+ var diff = offset >= 0 ? '-' : '+';
+
+ return diff + pad(Math.abs(offset) / 60, 2) + ':' + pad(Math.abs(offset % 60), 2);
+ }
+
+ function parseDate(src, format) {
+ var dateStr = '';
+ format = format.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ var formatRegex = format;
+
+ var dateParts = [
+ { key: 'day', spec: 'd', s: 0, e: 0 },
+ { key: 'month', spec: 'M', s: 0, e: 0 },
+ { key: 'year', spec: 'y', s: 0, e: 0 }
+ ];
+
+ for (var i = 0; i < dateParts.length; i++) {
+ var matcher = new RegExp('[' + dateParts[i].spec + ']+');
+
+ var match = matcher.exec(format);
+
+ dateParts[i].s = match.index;
+ dateParts[i].e = match.index + match[0].length;
+ }
+ dateParts = dateParts
+ .sort(function (one, other) { return other.s - one.s; });
+
+ for (var i = 0; i < dateParts.length; i++)
+ formatRegex = formatRegex.slice(0, dateParts[i].s) + '([0-9]+)' + formatRegex.slice(dateParts[i].e);
+
+ formatRegex = new RegExp(formatRegex);
+
+ var matches = formatRegex.exec(src);
+ dateParts.reverse();
+ var date = {
+ day: 0,
+ month: 0,
+ year: 0
+ }
+ for (var i = 0; i < dateParts.length; i++)
+ date[dateParts[i].key] = +matches[i + 1];
+
+ dateStr = pad(date.year, 4) + '-' + pad(date.month, 2) + '-' + pad(date.day, 2);
+
+ if (!dateStr.match(/[0-9]{2}:/)) // if no time is given, add 00:00:00 at the end
+ dateStr += " 00:00:00";
+
+ dateStr = dateStr.replace(/([0-9]{2}-[0-9]{2})-([0-9]{4})/, '$2-$1'); //mm-dd-yyyy to yyyy-mm-dd
+ dateStr = dateStr.replace(/([\/-][0-9]{2,4})\ ([0-9]{2}\:[0-9]{2}\:)/, '$1T$2'); //reformat for FF
+ dateStr = dateStr.replace(/EDT|EST|CDT|CST|MDT|PDT|PST|UT|GMT/g, ''); //remove timezone
+ dateStr = dateStr.replace(/\s*\(\)\s*/, ''); //remove timezone
+ dateStr = dateStr.replace(/[\-\+][0-9]{2}:?[0-9]{2}$/, ''); //remove timezone
+ dateStr += getTimezoneOffset(dateStr);
+ var d = new Date(dateStr);
+ return new Date(
+ d.getFullYear(),
+ d.getMonth(),
+ d.getDate(),
+ d.getHours(),
+ d.getMinutes(),
+ d.getSeconds()
+ );
+ }
+
+ function pad(val, nchar) {
+ return ('0000' + val).slice(-nchar);
+ }
})();
diff --git a/angularjs-datetime-picker.min.js b/angularjs-datetime-picker.min.js
index fe3a6bc..5aa707e 100644
--- a/angularjs-datetime-picker.min.js
+++ b/angularjs-datetime-picker.min.js
@@ -1 +1 @@
-!function(){"use strict";angular.module("angularjs-datetime-picker",[]);var e=function(e){"string"==typeof e&&(e=new Date(e));var t=new Date(e.getFullYear(),0,1),a=new Date(e.getFullYear(),6,1),n=Math.max(t.getTimezoneOffset(),a.getTimezoneOffset()),l=e.getTimezoneOffset()=0?"-":"+";return r+("0"+i/60).slice(-2)+":"+("0"+i%60).slice(-2)},t=function(e,t,a){var n=a("DatetimePickerCtrl");return{open:function(e){n.openDatetimePicker(e)},close:function(){n.closeDatetimePicker()}}};t.$inject=["$compile","$document","$controller"],angular.module("angularjs-datetime-picker").factory("DatetimePicker",t);var a=function(e,t){var a,n=this,l=function(e){e&&e.remove(),t[0].body.removeEventListener("click",n.closeDatetimePicker)};this.openDatetimePicker=function(n){this.closeDatetimePicker();var l=angular.element("");n.dateFormat&&l.attr("date-format",n.dateFormat),n.ngModel&&l.attr("ng-model",n.ngModel),n.year&&l.attr("year",parseInt(n.year)),n.month&&l.attr("month",parseInt(n.month)),n.day&&l.attr("day",parseInt(n.day)),n.hour&&l.attr("hour",parseInt(n.hour)),n.minute&&l.attr("minute",parseInt(n.minute)),(""===n.dateOnly||n.dateOnly===!0)&&l.attr("date-only","true"),"false"===n.closeOnSelect&&l.attr("close-on-select","false");var i=n.triggerEl;n.scope=n.scope||angular.element(i).scope(),a=e(l)(n.scope)[0],a.triggerEl=n.triggerEl,t[0].body.appendChild(a);var r=i.getBoundingClientRect();n.scope.$apply();var o=a.getBoundingClientRect();a.style.position="absolute",a.style.left=r.width>o.width?r.left+r.width-o.width+window.scrollX+"px":r.left+window.scrollX+"px",a.style.top=r.top<300||window.innerHeight-r.bottom>300?r.bottom+window.scrollY+"px":r.top-o.height+window.scrollY+"px",t[0].body.addEventListener("click",this.closeDatetimePicker)},this.closeDatetimePicker=function(e){var a=e&&e.target,n=t[0].querySelector("div[datetime-picker-popup]");e&&a?a.hasAttribute("datetime-picker")||n&&n.contains(a)||l(n):l(n)}};a.$inject=["$compile","$document"],angular.module("angularjs-datetime-picker").controller("DatetimePickerCtrl",a);var n=['','
',' ',' {{months[mv.month].shortName}} {{mv.year}}',' ',"
",'
','
{{::dayOfWeek.firstLetter}}
','
{{::day}}
','
'," {{::day}}","
",'
{{::day}}
',"
",'
',' {{("0"+inputHour).slice(-2)}} : {{("0"+inputMinute).slice(-2)}}
',' ',' ',"
","
"].join("\n"),l=function(t,a){var l,i,r,o,d=function(){l=[],i=[],r=[],o=0;for(var e=1;31>=e;e++)l.push(e);for(var e=0;12>e;e++)i.push({fullName:t.DATETIME_FORMATS.MONTH[e],shortName:t.DATETIME_FORMATS.SHORTMONTH[e]});for(var e=0;7>e;e++){var a=t.DATETIME_FORMATS.DAY[(e+o)%7];r.push({fullName:a,firstLetter:a.substr(0,1)})}o=0},s=function(e,t){t>11?e++:0>t&&e--,t=(t+12)%12;var a=new Date(e,t,1),n=new Date(e,t+1,0),i=new Date(e,t,0),r=n.getDate(),d=i.getDate(),s=a.getDay(),c=(s-o+7)%7||7,u=l.slice(0,42-(c+r));return u.length>7&&(u=u.slice(0,u.length-7)),{year:e,month:t,days:l.slice(0,r),leadingDays:l.slice(-c-(31-d),d),trailingDays:u}},c=function(t,n,l,o){d();var c=l.dateFormat||"short";t.months=i,t.daysOfWeek=r,t.inputHour,t.inputMinute,t.dateOnly===!0&&(n[0].querySelector("#adp-time").style.display="none"),t.$applyAsync(function(){if(o.triggerEl=angular.element(n[0].triggerEl),l.ngModel){var i=""+o.triggerEl.scope().$eval(l.ngModel);if(i){i.match(/[0-9]{2}:/)||(i+=" 00:00:00"),i=i.replace(/([0-9]{2}-[0-9]{2})-([0-9]{4})/,"$2-$1"),i=i.replace(/([\/-][0-9]{2,4})\ ([0-9]{2}\:[0-9]{2}\:)/,"$1T$2"),i=i.replace(/EDT|EST|CDT|CST|MDT|PDT|PST|UT|GMT/g,""),i=i.replace(/\s*\(\)\s*/,""),i=i.replace(/[\-\+][0-9]{2}:?[0-9]{2}$/,""),i+=e(i);var r=new Date(i);t.selectedDate=new Date(r.getFullYear(),r.getMonth(),r.getDate(),r.getHours(),r.getMinutes(),r.getSeconds())}}if(!t.selectedDate||isNaN(t.selectedDate.getTime())){var d=new Date,c=t.year||d.getFullYear(),u=t.month?t.month-1:d.getMonth(),m=t.day||d.getDate(),g=t.hour||d.getHours(),p=t.minute||d.getMinutes();t.selectedDate=new Date(c,u,m,g,p,0)}t.inputHour=t.selectedDate.getHours(),t.inputMinute=t.selectedDate.getMinutes(),t.mv=s(t.selectedDate.getFullYear(),t.selectedDate.getMonth()),t.today=a(new Date,"yyyy-M-d"),t.selectedDay=t.mv.year==t.selectedDate.getFullYear()&&t.mv.month==t.selectedDate.getMonth()?t.selectedDate.getDate():null}),t.addMonth=function(e){t.mv=s(t.mv.year,t.mv.month+e)},t.setDate=function(e){var a=angular.element(e.target)[0];-1!==a.className.indexOf("selectable")&&(t.updateNgModel(parseInt(a.innerHTML)),t.closeOnSelect!==!1&&o.closeDatetimePicker())},t.updateNgModel=function(e){if(e=e?e:t.selectedDate.getDate(),t.selectedDate=new Date(t.mv.year,t.mv.month,e,t.inputHour,t.inputMinute,0),t.selectedDay=t.selectedDate.getDate(),l.ngModel){var n,i=o.triggerEl.scope();n=i.$eval(l.ngModel)&&"Date"===i.$eval(l.ngModel).constructor.name?new Date(a(t.selectedDate,c)):a(t.selectedDate,c),i.$eval(l.ngModel+"= date",{date:n})}},t.$on("$destroy",o.closeDatetimePicker)};return{restrict:"A",template:n,controller:"DatetimePickerCtrl",replace:!0,scope:{year:"=",month:"=",day:"=",hour:"=",minute:"=",dateOnly:"=",closeOnSelect:"="},link:c}};l.$inject=["$locale","dateFilter"],angular.module("angularjs-datetime-picker").directive("datetimePickerPopup",l);var i=function(e,t){return{require:"ngModel",link:function(e,a,n,l){e.$watch(n.ngModel,function(e){if(e&&""!=e){var t=new Date(e);l.$setValidity("date",t?!0:!1);var a=new Date;n.hasOwnProperty("futureOnly")&&l.$setValidity("future-only",a>t?!1:!0)}}),a[0].addEventListener("click",function(){t.open({triggerEl:a[0],dateFormat:n.dateFormat,ngModel:n.ngModel,year:n.year,month:n.month,day:n.day,hour:n.hour,minute:n.minute,dateOnly:n.dateOnly,futureOnly:n.futureOnly,closeOnSelect:n.closeOnSelect})})}}};i.$inject=["$parse","DatetimePicker"],angular.module("angularjs-datetime-picker").directive("datetimePicker",i)}();
\ No newline at end of file
+!function(){"use strict";function e(e,t){function a(a){function n(){u=a,s=u.triggerEl,p.triggerElementScope=angular.element(s).scope(),c=u.scope||p.triggerElementScope,c.popupCtrl=p}function l(){t[0].body.removeEventListener("mousedown",o),d&&(d.removeEventListener("mousedown",i),d.parentElement.removeChild(d),d=null),g=!1}function r(){if(!g){var a=angular.element('');u.dateFormat&&a.attr("date-format",u.dateFormat),u.ngModel&&a.attr("ng-model",u.ngModel),u.year&&a.attr("year",parseInt(u.year)),u.month&&a.attr("month",parseInt(u.month)),u.day&&a.attr("day",parseInt(u.day)),u.hour&&a.attr("hour",parseInt(u.hour)),u.minute&&a.attr("minute",parseInt(u.minute)),""!==u.dateOnly&&u.dateOnly!==!0||a.attr("date-only","true"),"false"===u.closeOnSelect&&a.attr("close-on-select","false"),d=e(a)(c)[0],d.triggerEl=s,t[0].body.appendChild(d);var n=s.getBoundingClientRect();c.$apply();var l=d.getBoundingClientRect();d.style.position="absolute",n.width>l.width?d.style.left=n.left+n.width-l.width+window.pageXOffset+"px":d.style.left=n.left+window.pageXOffset+"px",n.top<300||window.innerHeight-n.bottom>300?d.style.top=n.bottom+window.pageYOffset+"px":d.style.top=n.top-l.height+window.pageYOffset+"px",t[0].body.addEventListener("mousedown",o),d.addEventListener("mousedown",i),g=!0}}function o(e){if(d){var t=e&&e.target;t.hasAttribute("datetime-picker")||l(d)}}function i(e){e.preventDefault(),e.stopImmediatePropagation()}var d,s,c,u,p=this,g=!1;p.triggerElementScope=null,p.open=r,p.close=l,n()}return a}function t(e,t){function a(e,a,o){function i(t){e.mv=r(e.mv.year,e.mv.month+t)}function c(){e.selectedDate=null;var t=e.popupCtrl.triggerElementScope;t.$eval(o.ngModel+"= date",{date:null}),e.$emit("datetime-picker-changed",null)}function u(t){var a=angular.element(t.target)[0];a.className.indexOf("selectable")!==-1&&(e.updateNgModel(parseInt(a.innerHTML)),e.closeOnSelect!==!1&&e.popupCtrl.close())}function p(a){if(a=a?a:e.selectedDate.getDate(),e.selectedDate=new Date(e.mv.year,e.mv.month,a,e.inputHour,e.inputMinute,0),e.selectedDay=e.selectedDate.getDate(),o.ngModel){var n,l=e.popupCtrl.triggerElementScope;n=l.$eval(o.ngModel)&&"Date"===l.$eval(o.ngModel).constructor.name?new Date(t(e.selectedDate,e.dateFormat)):t(e.selectedDate,e.dateFormat),l.$eval(o.ngModel+"= date",{date:n}),e.$emit("datetime-picker-changed",n)}}n();var g=o.dateFormat||"short";e.dateFormat=g,e.months=d,e.daysOfWeek=s,e.inputHour=null,e.inputMinute=null,e.dateOnly===!0&&(a[0].querySelector("#adp-time").style.display="none"),e.$applyAsync(function(){if(o.ngModel){var a=""+(e.popupCtrl.triggerElementScope.$eval(o.ngModel)||"");a&&(e.selectedDate=l(a,g))}if(!e.selectedDate||isNaN(e.selectedDate.getTime())){var n=new Date,i=e.year||n.getFullYear(),d=e.month?e.month-1:n.getMonth(),s=e.day||n.getDate(),c=e.hour||n.getHours(),u=e.minute||n.getMinutes();e.selectedDate=new Date(i,d,s,c,u,0)}e.inputHour=e.selectedDate.getHours(),e.inputMinute=e.selectedDate.getMinutes(),e.mv=r(e.selectedDate.getFullYear(),e.selectedDate.getMonth()),e.today=t(new Date,"yyyy-M-d"),e.mv.year==e.selectedDate.getFullYear()&&e.mv.month==e.selectedDate.getMonth()?e.selectedDay=e.selectedDate.getDate():e.selectedDay=null}),e.addMonth=i,e.clearDate=c,e.setDate=u,e.updateNgModel=p,e.$on("$destroy",e.popupCtrl.close)}function n(){i=[],d=[],s=[],c=0;for(var t=1;t<=31;t++)i.push(t);for(var t=0;t<12;t++)d.push({fullName:e.DATETIME_FORMATS.MONTH[t],shortName:e.DATETIME_FORMATS.SHORTMONTH[t]});for(var t=0;t<7;t++){var a=e.DATETIME_FORMATS.DAY[(t+c)%7];s.push({fullName:a,firstLetter:a.substr(0,1)})}c=0}function r(e,t){t>11?e++:t<0&&e--,t=(t+12)%12;var a=new Date(e,t,1),n=new Date(e,t+1,0),l=new Date(e,t,0),r=n.getDate(),o=l.getDate(),d=a.getDay(),s=(d-c+7)%7||7,u=i.slice(0,42-(s+r));return u.length>7&&(u=u.slice(0,u.length-7)),{year:e,month:t,days:i.slice(0,r),leadingDays:i.slice(-s-(31-o),o),trailingDays:u}}var i,d,s,c;return{restrict:"A",template:o,replace:!0,scope:{year:"=",month:"=",day:"=",hour:"=",minute:"=",dateOnly:"=",closeOnSelect:"=",popupCtrl:"="},link:a}}function a(e,t,a){return{require:"ngModel",link:function(e,n,l,r){var o=new t({triggerEl:n[0],dateFormat:l.dateFormat,ngModel:l.ngModel,year:l.year,month:l.month,day:l.day,hour:l.hour,minute:l.minute,dateOnly:l.dateOnly,futureOnly:l.futureOnly,closeOnSelect:l.closeOnSelect});e.$watch(l.ngModel,function(e){if(e&&""!=e){var t=new Date(e);r.$setValidity("date",!!t);var a=new Date;l.hasOwnProperty("futureOnly")&&r.$setValidity("future-only",!(t=0?"-":"+";return i+r(Math.abs(o)/60,2)+":"+r(Math.abs(o%60),2)}function l(e,t){var a="";t=t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");for(var l=t,o=[{key:"day",spec:"d",s:0,e:0},{key:"month",spec:"M",s:0,e:0},{key:"year",spec:"y",s:0,e:0}],i=0;i',' ',' ',' {{months[mv.month].shortName}} {{mv.year}}',' ',"
",' ','
{{::dayOfWeek.firstLetter}}
','
{{::day}}
','
'," {{::day}}","
",'
{{::day}}
',"
",' ',' {{("0"+inputHour).slice(-2)}} : {{("0"+inputMinute).slice(-2)}}
',' ',' ',"
",' ',' ',"
",""].join("\n");angular.module("angularjs-datetime-picker",[]).factory("DatetimePicker",e).directive("datetimePickerPopup",t).directive("datetimePicker",a),e.$inject=["$compile","$document"],t.$inject=["$locale","dateFilter"],a.$inject=["$parse","DatetimePicker","$timeout"]}();
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 6702b38..0a6b6c2 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,7 @@
{
"name": "angularjs-datetime-picker",
"main": "angularjs-datetime-picker.js",
- "version": "0.1.20",
+ "version": "0.1.21",
"homepage": "https://github.com/kineticsocial/angularjs-datetime-picker",
"authors": [
"Allen Kim "
diff --git a/package.json b/package.json
index 64fac8b..060ddca 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "angularjs-datetime-picker",
- "version": "0.1.20",
+ "version": "0.1.21",
"description": "Very Lightweight AngularJS DateTime Picker Without Using jQuery, bootStrap, or moment",
"main": "angularjs-datetime-picker.js",
"scripts": {
@@ -29,6 +29,6 @@
"gulp-strip-debug": "^1.0.2",
"gulp-uglify": "^1.2.0",
"run-sequence": "^1.1.0"
- }
+ },
"homepage": "https://github.com/kineticsocial/angularjs-datetime-picker"
}