ng-hijri-gregorian-datepicker
Version:
Lightweight Angular.js hijri and gregorian datepicker directive built with Moment.js.
314 lines (271 loc) • 14.6 kB
JavaScript
(function() {
'use strict';
/**
* @desc Datepicker directive
* @example <ng-hijri-gregorian-datepicker></ng-hijri-gregorian-datepicker>
*/
angular
.module('ngHijriGregorianDatepicker', [])
.directive('ngHijriGregorianDatepicker', ngHijriGregorianDatepickerDirective);
function ngHijriGregorianDatepickerDirective($templateCache, $compile, $document, datesCalculator) {
return {
restrict: 'A',
require: 'ngModel',
scope: {
config: '=datepickerConfig',
selectedDate: '=selectedDate'
},
link: function(scope, element, attrs, ngModel) {
var template = angular.element($templateCache.get('datepicker.html'));
var dateSelected = '';
var today = moment.utc();
scope.currentLocale = moment.locale();
// Default options
var defaultConfig = {
allowFuture: true,
allowPast: true,
dateFormat: 'DD/MM/YYYY',
gregorianDateFormat: 'DD/MM/YYYY',
hijriDateFormat: 'iDD/iMM/iYYYY',
minDate: moment().subtract(5, 'years'),//for hijri subtract(5, 'iYear');
maxDate: moment().add(5, 'years'),//for hijri add(5, 'iYear');
defaultDisplay: 'gregorian',//hijri,
locale: null
};
var locale = {
ar:{
gregorian: "ميلادي",
hijri: "هجري",
},
en:{
gregorian: "Gregorian",
hijri: "Hijri",
}
};
// Apply and init options
scope.config = angular.extend(defaultConfig, scope.config);
if (angular.isDefined(scope.config.locale)) moment.locale(scope.config.locale);
if (angular.isDefined(scope.config.minDate)) moment.utc(scope.config.minDate).subtract(1, 'day');
if (angular.isDefined(scope.config.maxDate)) moment.utc(scope.config.maxDate).add(1, 'day');
scope.config.isRTL = moment.locale().startsWith('ar');
setSwitchButtonVal();
// Data
scope.calendarCursor = today;
scope.currentWeeks = [];
scope.daysNameList = datesCalculator.getDaysNames();
scope.monthsList = moment.months();
scope.monthsInHijriList = moment.localeData()._iMonths;
scope.yearsList = datesCalculator.getYearsList(scope.config);
scope.yearsInHijriList = datesCalculator.getYearsInHijriList(scope.config);
// Display
scope.pickerDisplayed = false;
scope.$watch(function(){ return ngModel.$modelValue; }, function(value){
if (value) {
dateSelected = scope.calendarCursor = moment.utc(value, scope.config.dateFormat);
}
});
scope.$watch('calendarCursor', function(val){
scope.currentWeeks = getWeeks(val);
});
scope.$watch('currentLocale', function(){
scope.daysNameList = datesCalculator.getDaysNames();
scope.monthsList = moment.months();
scope.monthsInHijriList = moment.localeData()._iMonths;
scope.calendarCursor.locale(moment.locale());
scope.config.isRTL = moment.locale().startsWith('ar');
setSwitchButtonVal();
});
/**
* ClickOutside, handle all clicks outside the DatePicker when visible
*/
element.bind('click', function(e) {
scope.$apply(function(){
scope.currentLocale = moment.locale();
scope.pickerDisplayed = true;
$document.on('click', onDocumentClick);
});
});
function onDocumentClick(e) {
if (template !== e.target && !template[0].contains(e.target) && e.target !== element[0]) {
$document.off('click', onDocumentClick);
scope.$apply(function () {
scope.calendarCursor = dateSelected ? dateSelected : today;
scope.pickerDisplayed = scope.showMonthsList = scope.showYearsList = false;
});
}
}
init();
/**
* Display the previous month in the datepicker
* @return {}
*/
scope.prevMonth = function() {
if(scope.config.defaultDisplay === 'gregorian'){
scope.calendarCursor = moment(scope.calendarCursor).subtract(1, 'months');
} else {
scope.calendarCursor = moment(scope.calendarCursor).subtract(1, 'iMonth');
}
};
/**
* Display the next month in the datepicker
* @return {}
*/
scope.nextMonth = function nextMonth() {
if(scope.config.defaultDisplay === 'gregorian'){
scope.calendarCursor = moment(scope.calendarCursor).add(1, 'months');
} else {
scope.calendarCursor = moment(scope.calendarCursor).add(1, 'iMonth');
}
};
/**
* Select a month and display it in the datepicker
* @param {string} month The month selected in the select element
* @return {}
*/
scope.selectMonth = function selectMonth(month) {
scope.showMonthsList = false;
if(scope.config.defaultDisplay === 'gregorian'){
scope.calendarCursor = moment(scope.calendarCursor).month(month);
} else {
scope.calendarCursor = moment(scope.calendarCursor).iMonth(month);
}
};
/**
* Select a year and display it in the datepicker depending on the current month
* @param {string} year The year selected in the select element
* @return {}
*/
scope.selectYear = function selectYear(year) {
scope.showYearsList = false;
if(scope.config.defaultDisplay === 'gregorian'){
scope.calendarCursor = moment(scope.calendarCursor).year(year);
} else {
scope.calendarCursor = moment(scope.calendarCursor).iYear(year);
}
};
/**
* Select a day
* @param {[type]} day [description]
* @return {[type]} [description]
*/
scope.selectDay = function(day) {
if (day.isSelectable && !day.isFuture || (scope.config.allowFuture && day.isFuture)) {
resetSelectedDays();
day.isSelected = true;
ngModel.$setViewValue(parseNumberToString(moment.utc(day.date).format(scope.config.dateFormat)));
scope.selectedDate = {
'hijri': parseNumberToString( moment.utc(day.date).format(scope.config.hijriDateFormat) ),
'gregorian': parseNumberToString( moment.utc(day.date).format(scope.config.gregorianDateFormat) )
};
ngModel.$render();
scope.pickerDisplayed = false;
}
};
/**
* Init the directive
* @return {}
*/
function init() {
element.parent().addClass('ng-hg-datepicker-wrapper');
//.wrap('<div class=""></div>');
$compile(template)(scope);
element.after(template);
if (angular.isDefined(ngModel.$modelValue) && moment.isDate(ngModel.$modelValue)) {
scope.calendarCursor = ngModel.$modelValue;
}
}
/**
* Get all weeks needed to display a month on the Datepicker
* @return {array} list of weeks objects
*/
function getWeeks (date) {
var weeks = [];
var date = moment.utc(date);
var firstDayOfMonth, lastDayOfMonth = null;
if(scope.config.defaultDisplay === 'gregorian'){
firstDayOfMonth = moment(date).date(1);
lastDayOfMonth = moment(date).date(date.daysInMonth());
} else {
firstDayOfMonth = moment(date).iDate(1);
lastDayOfMonth = moment(date).iDate(parseArabic(""+date.endOf('iMonth').format('iDD')));
}
var startDay = moment(firstDayOfMonth);
var endDay = moment(lastDayOfMonth);
// NB: We use weekday() to get a locale aware weekday
startDay = firstDayOfMonth.weekday() === 0 ? startDay : startDay.weekday(0);
endDay = lastDayOfMonth.weekday() === 6 ? endDay : endDay.weekday(6);
var currentWeek = [];
for (var start = moment(startDay); start.isBefore(moment(endDay).add(1, 'days')); start.add(1, 'days')) {
var afterMinDate = !scope.config.minDate || start.isAfter(scope.config.minDate, 'day');
var beforeMaxDate = !scope.config.maxDate || start.isBefore(scope.config.maxDate, 'day');
var isFuture = start.isAfter(today);
var isPast = start.isBefore(today);
var beforeFuture = scope.config.allowFuture || !isFuture;
var beforePast = scope.config.allowPast || !isPast;
var day = {
date: moment(start).toDate(),
isToday: start.isSame(today, 'day'),
isInMonth: start.isSame(firstDayOfMonth, 'month'),
isInHijriMonth: start.iMonth() === firstDayOfMonth.iMonth(),
isSelected: start.isSame(dateSelected, 'day'),
dayInHijri: parseArabic(""+start.format('iDD')),
day: parseArabic(""+start.format('DD')),
isSelectable: afterMinDate && beforeMaxDate && beforeFuture && beforePast
};
currentWeek.push(day);
if (start.weekday() === 6 || start === endDay) {
weeks.push(currentWeek);
currentWeek = [];
}
}
return weeks;
}
function parseArabic(str) {
return Number( str.replace(/[٠١٢٣٤٥٦٧٨٩]/g, function(d) {
return d.charCodeAt(0) - 1632; // Convert Arabic numbers
}).replace(/[۰۱۲۳۴۵۶۷۸۹]/g, function(d) {
return d.charCodeAt(0) - 1776; // Convert Persian numbers
}) );
}
function parseNumberToString(str) {
return str.replace(/[٠١٢٣٤٥٦٧٨٩]/g, function(d) {
return d.charCodeAt(0) - 1632; // Convert Arabic numbers
}).replace(/[۰۱۲۳۴۵۶۷۸۹]/g, function(d) {
return d.charCodeAt(0) - 1776; // Convert Persian numbers
});
}
scope.switchDate = function() {
if(scope.config.defaultDisplay === 'gregorian'){
scope.config.defaultDisplay = 'hijri';
} else {
scope.config.defaultDisplay = 'gregorian';
}
setSwitchButtonVal();
scope.currentWeeks = getWeeks(scope.calendarCursor);
scope.showMonthsList = scope.showYearsList = false;
}
function setSwitchButtonVal(){
if(scope.config.defaultDisplay === 'gregorian' && moment.locale().startsWith("ar")) {
scope.switchButtonLabel = locale.ar.hijri;
} else if(scope.config.defaultDisplay === 'gregorian' && moment.locale().startsWith("en")){
scope.switchButtonLabel = locale.en.hijri;
} else if(scope.config.defaultDisplay === 'hijri' && moment.locale().startsWith("ar")) {
scope.switchButtonLabel = locale.ar.gregorian;
} else if(scope.config.defaultDisplay === 'hijri' && moment.locale().startsWith("en")){
scope.switchButtonLabel = locale.en.gregorian;
}
}
/**
* Reset all selected days
*/
function resetSelectedDays () {
scope.currentWeeks.forEach(function(week, wIndex){
week.forEach(function(day, dIndex){
scope.currentWeeks[wIndex][dIndex].isSelected = false;
});
});
}
}
};
}
})();