549 lines
20 KiB
JavaScript
549 lines
20 KiB
JavaScript
/*global angular navigator*/
|
|
|
|
(function withAngular(angular) {
|
|
|
|
'use strict';
|
|
|
|
angular.module('720kb.datepicker', [])
|
|
.directive('datepicker', ['$window', '$compile', '$locale', '$filter', '$interpolate', function manageDirective($window, $compile, $locale, $filter, $interpolate) {
|
|
|
|
var A_DAY_IN_MILLISECONDS = 86400000;
|
|
return {
|
|
'restrict': 'AEC',
|
|
'scope': {
|
|
'dateSet': '@',
|
|
'dateMinLimit': '@',
|
|
'dateMaxLimit': '@'
|
|
},
|
|
'link': function linkingFunction($scope, element, attr) {
|
|
//get child input
|
|
var selector = attr.selector
|
|
, thisInput = angular.element(selector ? element[0].querySelector('.' + selector) : element[0].children[0])
|
|
, theCalendar
|
|
, defaultPrevButton = '<b class="datepicker-default-button">⟨</b>'
|
|
, defaultNextButton = '<b class="datepicker-default-button">⟩</b>'
|
|
, prevButton = attr.buttonPrev || defaultPrevButton
|
|
, nextButton = attr.buttonNext || defaultNextButton
|
|
, dateFormat = attr.dateFormat
|
|
, dateMinLimit
|
|
, dateMaxLimit
|
|
, date = new Date()
|
|
, isMouseOn = false
|
|
, isMouseOnInput = false
|
|
, datetime = $locale.DATETIME_FORMATS
|
|
, pageDatepickers
|
|
, htmlTemplate = '<div class="_720kb-datepicker-calendar" ng-blur="hideCalendar()">' +
|
|
//month+year header
|
|
'<div class="_720kb-datepicker-calendar-header" ng-hide="isMobile()">' +
|
|
'<div class="_720kb-datepicker-calendar-header-left">' +
|
|
'<a href="javascript:void(0)" ng-click="prevMonth()">' + prevButton + '</a>' +
|
|
'</div>' +
|
|
'<div class="_720kb-datepicker-calendar-header-middle _720kb-datepicker-calendar-month">' +
|
|
'{{month}} <a href="javascript:void(0)" ng-click="showYearsPagination = !showYearsPagination"><span>{{year}} <i ng-if="!showYearsPagination">▾</i> <i ng-if="showYearsPagination">◹</i> </span> </a>' +
|
|
'</div>' +
|
|
'<div class="_720kb-datepicker-calendar-header-right">' +
|
|
'<a href="javascript:void(0)" ng-click="nextMonth()">' + nextButton + '</a>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
//Mobile month+year pagination
|
|
'<div class="_720kb-datepicker-calendar-header" ng-show="isMobile()">' +
|
|
'<div class="_720kb-datepicker-calendar-header-middle _720kb-datepicker-mobile-item _720kb-datepicker-calendar-month">' +
|
|
'<select ng-model="month" ng-change="selectedMonthHandle(month)">' +
|
|
'<option ng-repeat="item in months" ng-selected="month === item" ng-disabled=\'!isSelectableMaxDate(item + " " + day + ", " + year) || !isSelectableMinDate(item + " " + day + ", " + year)\' ng-value="item">{{item}}</option>' +
|
|
'</select>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'<div class="_720kb-datepicker-calendar-header" ng-show="isMobile()">' +
|
|
'<div class="_720kb-datepicker-calendar-header-middle _720kb-datepicker-mobile-item _720kb-datepicker-calendar-month">' +
|
|
'<select ng-model="mobileYear" ng-change="setNewYear(mobileYear)">' +
|
|
'<option ng-repeat="item in paginationYears" ng-selected="year === item" ng-value="item" ng-disabled="!isSelectableMinYear(item) || !isSelectableMaxYear(item)">{{item}}</option>' +
|
|
'</select>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
//years pagination header
|
|
'<div class="_720kb-datepicker-calendar-header" ng-show="showYearsPagination">' +
|
|
'<div class="_720kb-datepicker-calendar-years-pagination">' +
|
|
'<a ng-class="{\'_720kb-datepicker-active\': y === year, \'_720kb-datepicker-disabled\': !isSelectableMaxYear(y) || !isSelectableMinYear(y)}" href="javascript:void(0)" ng-click="setNewYear(y)" ng-repeat="y in paginationYears">{{y}}</a>' +
|
|
'</div>' +
|
|
'<div class="_720kb-datepicker-calendar-years-pagination-pages">' +
|
|
'<a href="javascript:void(0)" ng-click="paginateYears(paginationYears[0])" ng-class="{\'_720kb-datepicker-item-hidden\': paginationYearsPrevDisabled}">' + prevButton + '</a>' +
|
|
'<a href="javascript:void(0)" ng-click="paginateYears(paginationYears[paginationYears.length -1 ])" ng-class="{\'_720kb-datepicker-item-hidden\': paginationYearsNextDisabled}">' + nextButton + '</a>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
//days column
|
|
'<div class="_720kb-datepicker-calendar-days-header">' +
|
|
'<div ng-repeat="d in daysInString"> {{d}} </div> ' +
|
|
'</div>' +
|
|
//days
|
|
'<div class="_720kb-datepicker-calendar-body">' +
|
|
'<a href="javascript:void(0)" ng-repeat="px in prevMonthDays" class="_720kb-datepicker-calendar-day _720kb-datepicker-disabled">{{px}}</a>' +
|
|
'<a href="javascript:void(0)" ng-repeat="item in days" ng-click="setDatepickerDay(item)" ng-class="{\'_720kb-datepicker-active\': day === item, \'_720kb-datepicker-disabled\': !isSelectableMinDate(year + \'/\' + monthNumber + \'/\' + item ) || !isSelectableMaxDate(year + \'/\' + monthNumber + \'/\' + item)}" class="_720kb-datepicker-calendar-day">{{item}}</a>' +
|
|
'<a href="javascript:void(0)" ng-repeat="nx in nextMonthDays" class="_720kb-datepicker-calendar-day _720kb-datepicker-disabled">{{nx}}</a>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'</div>';
|
|
|
|
// Respect previously configured interpolation symbols.
|
|
htmlTemplate = htmlTemplate.replace(/{{/g, $interpolate.startSymbol())
|
|
.replace(/}}/g, $interpolate.endSymbol());
|
|
$scope.$watch('dateSet', function dateSetWatcher(value) {
|
|
if (value) {
|
|
date = new Date(value);
|
|
$scope.month = $filter('date')(date, 'MMMM');//December-November like
|
|
$scope.monthNumber = Number($filter('date')(date, 'MM')); // 01-12 like
|
|
$scope.day = Number($filter('date')(date, 'dd')); //01-31 like
|
|
$scope.year = Number($filter('date')(date, 'yyyy'));//2014 like
|
|
$scope.setDaysInMonth($scope.monthNumber, $scope.year);
|
|
}
|
|
});
|
|
|
|
$scope.$watch('dateMinLimit', function dateMinLimitWatcher(value) {
|
|
if (value) {
|
|
dateMinLimit = value;
|
|
}
|
|
});
|
|
|
|
$scope.$watch('dateMaxLimit', function dateMaxLimitWatcher(value) {
|
|
if (value) {
|
|
dateMaxLimit = value;
|
|
}
|
|
});
|
|
|
|
$scope.month = $filter('date')(date, 'MMMM');//December-November like
|
|
$scope.monthNumber = Number($filter('date')(date, 'MM')); // 01-12 like
|
|
$scope.day = Number($filter('date')(date, 'dd')); //01-31 like
|
|
if ($scope.dateMaxLimit) {
|
|
$scope.year = Number($filter('date')(new Date($scope.dateMaxLimit), 'yyyy'));//2014 like
|
|
} else {
|
|
$scope.year = Number($filter('date')(date, 'yyyy'));//2014 like
|
|
}
|
|
$scope.months = datetime.MONTH;
|
|
$scope.daysInString = ['0', '1', '2', '3', '4', '5', '6'].map(function mappingFunc(el) {
|
|
|
|
return $filter('date')(new Date(new Date('06/08/2014').valueOf() + A_DAY_IN_MILLISECONDS * el), 'EEE');
|
|
});
|
|
|
|
//create the calendar holder
|
|
thisInput.after($compile(angular.element(htmlTemplate))($scope));
|
|
|
|
//get the calendar as element
|
|
theCalendar = element[0].querySelector('._720kb-datepicker-calendar');
|
|
//some tricky dirty events to fire if click is outside of the calendar and show/hide calendar when needed
|
|
thisInput.bind('focus click', function onFocusAndClick() {
|
|
|
|
isMouseOnInput = true;
|
|
|
|
$scope.showCalendar();
|
|
});
|
|
|
|
thisInput.bind('focusout blur', function onBlurAndFocusOut() {
|
|
|
|
isMouseOnInput = false;
|
|
});
|
|
|
|
angular.element(theCalendar).bind('mouseenter', function onMouseEnter() {
|
|
|
|
isMouseOn = true;
|
|
});
|
|
|
|
angular.element(theCalendar).bind('mouseleave', function onMouseLeave() {
|
|
|
|
isMouseOn = false;
|
|
});
|
|
|
|
angular.element(theCalendar).bind('focusin', function onCalendarFocus() {
|
|
|
|
isMouseOn = true;
|
|
});
|
|
|
|
angular.element($window).bind('click focus', function onClickOnWindow() {
|
|
|
|
if (!isMouseOn &&
|
|
!isMouseOnInput && theCalendar) {
|
|
|
|
$scope.hideCalendar();
|
|
}
|
|
});
|
|
|
|
$scope.isMobile = function isMobile() {
|
|
|
|
if (navigator.userAgent && (navigator.userAgent.match(/Android/i)
|
|
|| navigator.userAgent.match(/webOS/i)
|
|
|| navigator.userAgent.match(/iPhone/i)
|
|
|| navigator.userAgent.match(/iPad/i)
|
|
|| navigator.userAgent.match(/iPod/i)
|
|
|| navigator.userAgent.match(/BlackBerry/i)
|
|
|| navigator.userAgent.match(/Windows Phone/i))){
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
$scope.resetToMinDate = function manageResetToMinDate() {
|
|
|
|
$scope.month = $filter('date')(new Date(dateMinLimit), 'MMMM');
|
|
$scope.monthNumber = Number($filter('date')(new Date(dateMinLimit), 'MM'));
|
|
$scope.day = Number($filter('date')(new Date(dateMinLimit), 'dd'));
|
|
$scope.year = Number($filter('date')(new Date(dateMinLimit), 'yyyy'));
|
|
};
|
|
|
|
$scope.resetToMaxDate = function manageResetToMaxDate() {
|
|
|
|
$scope.month = $filter('date')(new Date(dateMaxLimit), 'MMMM');
|
|
$scope.monthNumber = Number($filter('date')(new Date(dateMaxLimit), 'MM'));
|
|
$scope.day = Number($filter('date')(new Date(dateMaxLimit), 'dd'));
|
|
$scope.year = Number($filter('date')(new Date(dateMaxLimit), 'yyyy'));
|
|
};
|
|
|
|
$scope.nextMonth = function manageNextMonth() {
|
|
|
|
if ($scope.monthNumber === 12) {
|
|
|
|
$scope.monthNumber = 1;
|
|
//its happy new year
|
|
$scope.nextYear();
|
|
} else {
|
|
|
|
$scope.monthNumber += 1;
|
|
}
|
|
//set next month
|
|
$scope.month = $filter('date')(new Date($scope.year, $scope.monthNumber - 1), 'MMMM');
|
|
//reinit days
|
|
$scope.setDaysInMonth($scope.monthNumber, $scope.year);
|
|
|
|
//check if max date is ok
|
|
if (dateMaxLimit) {
|
|
if (!$scope.isSelectableMaxDate($scope.year + '/' + $scope.monthNumber + '/' + $scope.day)) {
|
|
|
|
$scope.resetToMaxDate();
|
|
}
|
|
}
|
|
//deactivate selected day
|
|
$scope.day = undefined;
|
|
};
|
|
|
|
$scope.selectedMonthHandle = function manageSelectedMonthHandle (selectedMonth) {
|
|
|
|
$scope.monthNumber = Number($filter('date')(new Date('01 ' + selectedMonth + ' 2000'), 'MM'));
|
|
$scope.setDaysInMonth($scope.monthNumber, $scope.year);
|
|
$scope.setInputValue();
|
|
};
|
|
|
|
$scope.prevMonth = function managePrevMonth() {
|
|
|
|
if ($scope.monthNumber === 1) {
|
|
|
|
$scope.monthNumber = 12;
|
|
//its happy new year
|
|
$scope.prevYear();
|
|
} else {
|
|
|
|
$scope.monthNumber -= 1;
|
|
}
|
|
//set next month
|
|
$scope.month = $filter('date')(new Date($scope.year, $scope.monthNumber - 1), 'MMMM');
|
|
//reinit days
|
|
$scope.setDaysInMonth($scope.monthNumber, $scope.year);
|
|
//check if min date is ok
|
|
if (dateMinLimit) {
|
|
|
|
if (!$scope.isSelectableMinDate($scope.year + '/' + $scope.monthNumber + '/' + $scope.day)) {
|
|
|
|
$scope.resetToMinDate();
|
|
}
|
|
}
|
|
//deactivate selected day
|
|
$scope.day = undefined;
|
|
};
|
|
|
|
$scope.setNewYear = function setNewYear (year) {
|
|
|
|
//deactivate selected day
|
|
$scope.day = undefined;
|
|
|
|
if (dateMaxLimit && $scope.year < Number(year)) {
|
|
|
|
if (!$scope.isSelectableMaxYear(year)) {
|
|
|
|
return;
|
|
}
|
|
} else if (dateMinLimit && $scope.year > Number(year)) {
|
|
|
|
if (!$scope.isSelectableMinYear(year)) {
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
$scope.year = Number(year);
|
|
$scope.setDaysInMonth($scope.monthNumber, $scope.year);
|
|
$scope.paginateYears(year);
|
|
};
|
|
|
|
$scope.nextYear = function manageNextYear() {
|
|
|
|
$scope.year = Number($scope.year) + 1;
|
|
};
|
|
|
|
$scope.prevYear = function managePrevYear() {
|
|
|
|
$scope.year = Number($scope.year) - 1;
|
|
};
|
|
|
|
$scope.setInputValue = function manageInputValue() {
|
|
|
|
if ($scope.isSelectableMinDate($scope.year + '/' + $scope.monthNumber + '/' + $scope.day)
|
|
&& $scope.isSelectableMaxDate($scope.year + '/' + $scope.monthNumber + '/' + $scope.day)) {
|
|
|
|
var modelDate = new Date($scope.year + '/' + $scope.monthNumber + '/' + $scope.day);
|
|
|
|
if (attr.dateFormat) {
|
|
|
|
thisInput.val($filter('date')(modelDate, dateFormat));
|
|
} else {
|
|
|
|
thisInput.val(modelDate);
|
|
}
|
|
|
|
thisInput.triggerHandler('input');
|
|
thisInput.triggerHandler('change');//just to be sure;
|
|
} else {
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
$scope.classHelper = {
|
|
'add': function add(ele, klass){
|
|
if (ele.className.indexOf(klass) > -1){
|
|
return;
|
|
}
|
|
var classes = ele.className.split(' ');
|
|
classes.push(klass);
|
|
ele.className = classes.join(' ');
|
|
},
|
|
'remove': function remove(ele, klass){
|
|
var i, classes;
|
|
if (ele.className.indexOf(klass) === -1){
|
|
return;
|
|
}
|
|
classes = ele.className.split(' ');
|
|
for (i = 0; i < classes.length; i += 1){
|
|
if (classes[i] === klass){
|
|
classes = classes.slice(0, i).concat(classes.slice(i + 1));
|
|
break;
|
|
}
|
|
}
|
|
ele.className = classes.join(' ');
|
|
}
|
|
};
|
|
|
|
$scope.showCalendar = function manageShowCalendar() {
|
|
//lets hide all the latest instances of datepicker
|
|
pageDatepickers = $window.document.getElementsByClassName('_720kb-datepicker-calendar');
|
|
|
|
angular.forEach(pageDatepickers, function forEachDatepickerPages(value, key) {
|
|
if (pageDatepickers[key].classList) {
|
|
pageDatepickers[key].classList.remove('_720kb-datepicker-open');
|
|
} else {
|
|
$scope.classHelper.remove(pageDatepickers[key], '_720kb-datepicker-open');
|
|
}
|
|
});
|
|
|
|
if (theCalendar.classList) {
|
|
theCalendar.classList.add('_720kb-datepicker-open');
|
|
} else {
|
|
$scope.classHelper.add(theCalendar, '_720kb-datepicker-open');
|
|
}
|
|
};
|
|
|
|
$scope.hideCalendar = function manageHideCalendar() {
|
|
if (theCalendar.classList){
|
|
theCalendar.classList.remove('_720kb-datepicker-open');
|
|
} else {
|
|
$scope.classHelper.remove(theCalendar, '_720kb-datepicker-open');
|
|
}
|
|
};
|
|
|
|
|
|
$scope.setDaysInMonth = function setDaysInMonth(month, year) {
|
|
|
|
var i
|
|
, limitDate = new Date(year, month, 0).getDate()
|
|
, firstDayMonthNumber = new Date(year + '/' + month + '/' + 1).getDay()
|
|
, lastDayMonthNumber = new Date(year + '/' + month + '/' + limitDate).getDay()
|
|
, prevMonthDays = []
|
|
, nextMonthDays = []
|
|
, howManyNextDays
|
|
, howManyPreviousDays
|
|
, monthAlias;
|
|
|
|
$scope.days = [];
|
|
|
|
for (i = 1; i <= limitDate; i += 1) {
|
|
|
|
$scope.days.push(i);
|
|
}
|
|
//get previous month days is first day in month is not Sunday
|
|
if (firstDayMonthNumber !== 0) {
|
|
|
|
howManyPreviousDays = firstDayMonthNumber;
|
|
|
|
//get previous month
|
|
if (Number(month) === 1) {
|
|
|
|
monthAlias = 12;
|
|
} else {
|
|
|
|
monthAlias = month - 1;
|
|
}
|
|
//return previous month days
|
|
for (i = 1; i <= new Date(year, monthAlias, 0).getDate(); i += 1) {
|
|
|
|
prevMonthDays.push(i);
|
|
}
|
|
//attach previous month days
|
|
$scope.prevMonthDays = prevMonthDays.slice(-howManyPreviousDays);
|
|
} else {
|
|
//no need for it
|
|
$scope.prevMonthDays = [];
|
|
}
|
|
|
|
//get next month days is first day in month is not Sunday
|
|
if (lastDayMonthNumber < 6) {
|
|
|
|
howManyNextDays = 6 - lastDayMonthNumber;
|
|
//get previous month
|
|
|
|
//return next month days
|
|
for (i = 1; i <= howManyNextDays; i += 1) {
|
|
|
|
nextMonthDays.push(i);
|
|
}
|
|
//attach previous month days
|
|
$scope.nextMonthDays = nextMonthDays;
|
|
} else {
|
|
//no need for it
|
|
$scope.nextMonthDays = [];
|
|
}
|
|
};
|
|
|
|
$scope.setDatepickerDay = function setDatepickeDay(day) {
|
|
|
|
$scope.day = Number(day);
|
|
$scope.setInputValue();
|
|
$scope.hideCalendar();
|
|
};
|
|
|
|
$scope.paginateYears = function paginateYears (startingYear) {
|
|
|
|
$scope.paginationYears = [];
|
|
|
|
var i
|
|
, theNewYears = []
|
|
, daysToPrepend = 10, daysToAppend = 10;
|
|
|
|
if ($scope.isMobile()) {
|
|
daysToPrepend = 50;
|
|
daysToAppend = 50;
|
|
if ( $scope.dateMinLimit && $scope.dateMaxLimit) {
|
|
startingYear = new Date($scope.dateMaxLimit).getFullYear();
|
|
daysToPrepend = startingYear - new Date($scope.dateMinLimit).getFullYear();
|
|
daysToAppend = 1;
|
|
}
|
|
}
|
|
|
|
for (i = daysToPrepend; i > 0; i -= 1) {
|
|
|
|
theNewYears.push(Number(startingYear) - i);
|
|
}
|
|
|
|
for (i = 0; i < daysToAppend; i += 1) {
|
|
|
|
theNewYears.push(Number(startingYear) + i);
|
|
}
|
|
|
|
//check range dates
|
|
if (dateMaxLimit && theNewYears && theNewYears.length && !$scope.isSelectableMaxYear(Number(theNewYears[theNewYears.length - 1]) + 1)) {
|
|
|
|
$scope.paginationYearsNextDisabled = true;
|
|
} else {
|
|
|
|
$scope.paginationYearsNextDisabled = false;
|
|
}
|
|
|
|
if (dateMinLimit && theNewYears && theNewYears.length && !$scope.isSelectableMinYear(Number(theNewYears[0]) - 1)) {
|
|
|
|
$scope.paginationYearsPrevDisabled = true;
|
|
} else {
|
|
|
|
$scope.paginationYearsPrevDisabled = false;
|
|
}
|
|
|
|
$scope.paginationYears = theNewYears;
|
|
};
|
|
|
|
$scope.isSelectableMinDate = function isSelectableMinDate (aDate) {
|
|
//if current date
|
|
if (!!dateMinLimit &&
|
|
!!new Date(dateMinLimit) &&
|
|
new Date(aDate).getTime() < new Date(dateMinLimit).getTime()) {
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
$scope.isSelectableMaxDate = function isSelectableMaxDate (aDate) {
|
|
|
|
//if current date
|
|
if (!!dateMaxLimit &&
|
|
!!new Date(dateMaxLimit) &&
|
|
new Date(aDate).getTime() > new Date(dateMaxLimit).getTime()) {
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
$scope.isSelectableMaxYear = function isSelectableMaxYear (year) {
|
|
|
|
if (!!dateMaxLimit &&
|
|
year > new Date(dateMaxLimit).getFullYear()) {
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
$scope.isSelectableMinYear = function isSelectableMinYear (year) {
|
|
|
|
if (!!dateMinLimit &&
|
|
year < new Date(dateMinLimit).getFullYear()) {
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
//check always if given range of dates is ok
|
|
if (dateMinLimit && !$scope.isSelectableMinYear($scope.year)) {
|
|
|
|
$scope.resetToMinDate();
|
|
}
|
|
|
|
if (dateMaxLimit && !$scope.isSelectableMaxYear($scope.year)) {
|
|
|
|
$scope.resetToMaxDate();
|
|
}
|
|
|
|
$scope.paginateYears($scope.year);
|
|
$scope.setDaysInMonth($scope.monthNumber, $scope.year);
|
|
}
|
|
};
|
|
}]);
|
|
}(angular));
|