ng-jalaali-flat-datepicker
Version:
Angular.js Datepicker built with Moment.js for Jalaali Calendar
294 lines (249 loc) • 15.3 kB
JavaScript
(function() {
'use strict';
/**
* @desc Datepicker directive
* @example <ng-datepicker></ng-datepicker>
*/
ngJalaaliFlatDatepickerDirective.$inject = ["$templateCache", "$compile", "$document", "datesCalculator"];
angular
.module('ngJalaaliFlatDatepicker', [])
.directive('ngJalaaliFlatDatepicker', ngJalaaliFlatDatepickerDirective);
function ngJalaaliFlatDatepickerDirective($templateCache, $compile, $document, datesCalculator) {
var parseConfig = function(config) {
var temp = angular.fromJson(config);
if (typeof(temp.minDate) == 'undefined') {
temp.minDate = moment.utc(temp.minDate);
}
if (typeof(temp.maxDate) == 'undefined') {
temp.maxDate = moment.utc(temp.maxDate);
}
return temp;
};
return {
restrict: 'A',
require: 'ngModel',
scope: {
config: '=?datepickerConfig',
gPickedDate: '=?gregorianPickedDate',
gFormattedPickedDate: '=?gregorianFormattedPickedDate'
},
link: function(scope, element, attrs, ngModel) {
moment.loadPersian();
var template = angular.element($templateCache.get('datepicker.html'));
var dateSelected = '';
var today = moment.utc();
// Default options
var defaultConfig = {
allowFuture: true,
dateFormat: null,
gregorianDateFormat: null,
dropDownYears: 10,
minDate: null,
maxDate: null
};
/*
* returns start year based on configuration
*/
var getStartYear = function() {
if (typeof(scope.config.minDate) == 'undefined') {
return scope.config.minDate.format('YYYY');
}
if (scope.config.allowFuture) {
return moment.utc().subtract('5', 'years').format('YYYY');
}
return moment.utc().subtract('10', 'years').format('YYYY');
};
// Apply and init options
scope.config = angular.extend(defaultConfig, scope.config);
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');
if (!angular.isDefined(scope.config.gregorianDateFormat)) scope.config.gregorianDateFormat = scope.config.dateFormat.replace(/j/g, "");
// Data
scope.calendarCursor = today;
scope.currentWeeks = [];
scope.daysNameList = datesCalculator.getDaysNames();
scope.monthsList = moment.localeData()._jMonths;
scope.yearsList = datesCalculator.getYearsList(getStartYear(), scope.config.dropDownYears);
// 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.$apply(function() {
scope.currentWeeks = getWeeks(val);
//});
});
/**
* ClickOutside, handle all clicks outside the DatePicker when visible
*/
element.bind('click', function(e) {
scope.$apply(function(){
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() {
scope.calendarCursor = moment(scope.calendarCursor).subtract(1, 'jMonths');
};
/**
* Display the next month in the datepicker
* @return {}
*/
scope.nextMonth = function nextMonth() {
scope.calendarCursor = moment(scope.calendarCursor).add(1, 'jMonths');
};
/**
* 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;
scope.calendarCursor = moment(scope.calendarCursor).jMonth(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;
scope.calendarCursor = moment(scope.calendarCursor).jYear(parseInt(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(moment.utc(day.date).format(scope.config.dateFormat));
ngModel.$render();
scope.gPickedDate = moment.utc(day.date);
scope.gFormattedPickedDate = moment.utc(day.date).format(scope.config.gregorianDateFormat);
scope.pickerDisplayed = false;
}
};
/**
* Init the directive
* @return {}
*/
function init() {
element.wrap('<div class="ng-jalaali-flat-datepicker-wrapper"></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 = [];
date = moment.utc(date);
var firstDayOfMonth = moment.utc(date).jDate(1);
var lastDayOfMonth = moment.utc(date).jDate(moment.jDaysInMonth(moment(date).jYear, moment(date).jMonth()));
var startDay = moment.utc(firstDayOfMonth);
var endDay = moment.utc(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.utc(startDay.toDate()); start.isBefore(moment.utc(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 beforeFuture = scope.config.allowFuture || !isFuture;
var tempStart = moment.utc(start.toDate()); //there's a bug in isSame method below
var tempStart2 = moment.utc(start.toDate()); //there's a bug in isSame method below
var day = {
date: moment(start).toDate(),
jDate: moment(start).format('jDD'),
isToday: start.isSame(today, 'day'),
isInMonth: tempStart.isSame(firstDayOfMonth, 'jmonth'),
isSelected: tempStart2.isSame(dateSelected, 'day'),
isSelectable: afterMinDate && beforeMaxDate && beforeFuture
};
currentWeek.push(day);
if (start.weekday() === 6 || start === endDay) {
weeks.push(currentWeek);
currentWeek = [];
}
}
return weeks;
}
/**
* Reset all selected days
*/
function resetSelectedDays () {
scope.currentWeeks.forEach(function(week, wIndex){
week.forEach(function(day, dIndex){
scope.currentWeeks[wIndex][dIndex].isSelected = false;
});
});
}
}
};
}
})();
(function(){
'use strict';
/**
* @desc Dates calculator factory
*/
angular
.module('ngJalaaliFlatDatepicker')
.factory('datesCalculator', datesCalculator);
function datesCalculator () {
/**
* List all years for the select
* @param {Integer} start year eg. 2005
* @param {Integer} total number of years to be appear in the drop down
* @return {Array<integer>} years list
*/
function getYearsList(startYear, dropDownYears) {
var yearsList = [];
for (var i = startYear; i <= parseInt(startYear) + parseInt(dropDownYears); i++) {
yearsList.push(moment.utc(i.toString(), 'YYYY').format('jYYYY'));
}
return yearsList;
}
/**
* List all days name in the current locale
* @return {[type]} [description]
*/
function getDaysNames () {
var daysNameList = [];
for (var i = 0; i < 7 ; i++) {
daysNameList.push(moment().weekday(i).format('dd'));
}
return daysNameList;
}
return {
getYearsList: getYearsList,
getDaysNames: getDaysNames
};
}
})();
angular.module("ngJalaaliFlatDatepicker").run(["$templateCache", function($templateCache) {$templateCache.put("datepicker.html","<div class=\"ng-jalaali-flat-datepicker\" ng-show=\"pickerDisplayed\">\n <div class=\"ng-jalaali-flat-datepicker-table-header-bckgrnd\"></div>\n <table>\n <caption>\n <div class=\"ng-jalaali-flat-datepicker-header-wrapper\">\n <span class=\"ng-jalaali-flat-datepicker-arrow ng-jalaali-flat-datepicker-arrow-left\" ng-click=\"nextMonth()\">\n <svg version=\"1.0\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"50\" y=\"50\" viewBox=\"0 0 100 100\" xml:space=\"preserve\">\n <polygon points=\"64.8,36.2 35.2,6.5 22.3,19.4 51.9,49.1 22.3,78.8 35.2,91.7 77.7,49.1\" />\n </svg>\n </span>\n <div class=\"ng-jalaali-flat-datepicker-header-year\">\n <div class=\"ng-jalaali-flat-datepicker-custom-select-box\" outside-click=\"showMonthsList = false\">\n <span class=\"ng-jalaali-flat-datepicker-custom-select-title ng-jalaali-flat-datepicker-month-name\" ng-click=\"showMonthsList = !showMonthsList; showYearsList = false\" ng-class=\"{selected: showMonthsList }\">{{ calendarCursor.isValid() ? calendarCursor.format(\'jMMMM\') : \"\" }}</span>\n <div class=\"ng-jalaali-flat-datepicker-custom-select\" ng-show=\"showMonthsList\">\n <span ng-repeat=\"monthName in monthsList\" ng-click=\"selectMonth(monthName); showMonthsList = false\">{{ monthName }}</span>\n </div>\n </div>\n <div class=\"ng-jalaali-flat-datepicker-custom-select-box\" outside-click=\"showYearsList = false\">\n <span class=\"ng-jalaali-flat-datepicker-custom-select-title\" ng-click=\"showYearsList = !showYearsList; showMonthsList = false\" ng-class=\"{selected: showYearsList }\">{{ calendarCursor.isValid() ? calendarCursor.format(\'jYYYY\') : \"\" }}</span>\n <div class=\"ng-jalaali-flat-datepicker-custom-select\" ng-show=\"showYearsList\">\n <span ng-repeat=\"yearNumber in yearsList\" ng-click=\"selectYear(yearNumber)\">{{ yearNumber }}</span>\n </div>\n </div>\n </div>\n <span class=\"ng-jalaali-flat-datepicker-arrow ng-jalaali-flat-datepicker-arrow-right\" ng-click=\"prevMonth()\">\n <svg version=\"1.0\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"50\" y=\"50\" viewBox=\"0 0 100 100\" xml:space=\"preserve\">\n <polygon points=\"64.8,36.2 35.2,6.5 22.3,19.4 51.9,49.1 22.3,78.8 35.2,91.7 77.7,49.1\" />\n </svg>\n </span>\n </div>\n </caption>\n <tbody>\n <tr class=\"days-head\">\n <td class=\"day-head\" ng-repeat=\"dayName in daysNameList\">{{ dayName }}</td>\n </tr>\n <tr class=\"days\" ng-repeat=\"week in currentWeeks\">\n <td ng-repeat=\"day in week\" ng-click=\"selectDay(day)\" ng-class=\"[\'day-item\', { \'isToday\': day.isToday, \'isInMonth\': day.isInMonth, \'isDisabled\': !day.isSelectable, \'isSelected\': day.isSelected }]\">{{ day.jDate }}</td>\n </tr>\n </tbody>\n </table>\n</div>\n");}]);