import {NgModule,Component,ElementRef,AfterViewInit,AfterViewChecked,OnDestroy,OnInit,Input,Output,SimpleChange,EventEmitter,forwardRef,Renderer2,
ViewChild,ChangeDetectorRef,TemplateRef,ContentChildren,QueryList} from '@angular/core';
import {trigger,state,style,transition,animate} from '@angular/animations';
import {CommonModule} from '@angular/common';
import {ButtonModule} from '../button/button';
import {DomHandler} from '../dom/domhandler';
import {SharedModule,PrimeTemplate} from '../common/shared';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
export const CALENDAR_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Calendar),
multi: true
};
export interface LocaleSettings {
firstDayOfWeek?: number;
dayNames: string[];
dayNamesShort: string[];
dayNamesMin: string[];
monthNames: string[];
monthNamesShort: string[];
today: string,
clear: string
}
@Component({
selector: 'p-calendar',
template: `
`,
animations: [
trigger('overlayState', [
state('hidden', style({
opacity: 0
})),
state('visible', style({
opacity: 1
})),
transition('visible => hidden', animate('400ms ease-in')),
transition('hidden => visible', animate('400ms ease-out'))
])
],
host: {
'[class.ui-inputwrapper-filled]': 'filled',
'[class.ui-inputwrapper-focus]': 'focus'
},
providers: [DomHandler,CALENDAR_VALUE_ACCESSOR]
})
export class Calendar implements AfterViewInit,AfterViewChecked,OnInit,OnDestroy,ControlValueAccessor {
@Input() defaultDate: Date;
@Input() style: string;
@Input() styleClass: string;
@Input() inputStyle: string;
@Input() inputId: string;
@Input() name: string;
@Input() inputStyleClass: string;
@Input() placeholder: string;
@Input() disabled: any;
@Input() dateFormat: string = 'mm/dd/yy';
@Input() inline: boolean = false;
@Input() showOtherMonths: boolean = true;
@Input() selectOtherMonths: boolean;
@Input() showIcon: boolean;
@Input() icon: string = 'fa-calendar';
@Input() appendTo: any;
@Input() readonlyInput: boolean;
@Input() shortYearCutoff: any = '+10';
@Input() monthNavigator: boolean;
@Input() yearNavigator: boolean;
@Input() yearRange: string;
@Input() hourFormat: string = '24';
@Input() timeOnly: boolean;
@Input() stepHour: number = 1;
@Input() stepMinute: number = 1;
@Input() stepSecond: number = 1;
@Input() showSeconds: boolean = false;
@Input() required: boolean;
@Input() showOnFocus: boolean = true;
@Input() dataType: string = 'date';
@Input() utc: boolean;
@Input() selectionMode: string = 'single';
@Input() maxDateCount: number;
@Input() showButtonBar: boolean;
@Input() todayButtonStyleClass: string = 'ui-button-secondary';
@Input() clearButtonStyleClass: string = 'ui-button-secondary';
@Input() autoZIndex: boolean = true;
@Input() baseZIndex: number = 0;
@Input() panelStyleClass: string;
@Input() keepInvalid: boolean = false;
@Input() hideOnDateTimeSelect: boolean = false;
@Output() onFocus: EventEmitter = new EventEmitter();
@Output() onBlur: EventEmitter = new EventEmitter();
@Output() onClose: EventEmitter = new EventEmitter();
@Output() onSelect: EventEmitter = new EventEmitter();
@Output() onInput: EventEmitter = new EventEmitter();
@Output() onTodayClick: EventEmitter = new EventEmitter();
@Output() onClearClick: EventEmitter = new EventEmitter();
@Output() onMonthChange: EventEmitter = new EventEmitter();
@Output() onYearChange: EventEmitter = new EventEmitter();
@ContentChildren(PrimeTemplate) templates: QueryList;
_locale: LocaleSettings = {
firstDayOfWeek: 0,
dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"],
monthNames: [ "January","February","March","April","May","June","July","August","September","October","November","December" ],
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
today: 'Today',
clear: 'Clear'
};
@Input() tabindex: number;
@ViewChild('datepicker') overlayViewChild: ElementRef;
@ViewChild('inputfield') inputfieldViewChild: ElementRef;
value: any;
dates: any[];
weekDays: string[];
currentMonthText: string;
currentMonth: number;
currentYear: number;
currentHour: number;
currentMinute: number;
currentSecond: number;
pm: boolean;
overlay: HTMLDivElement;
overlayVisible: boolean;
overlayShown: boolean;
datepickerClick: boolean;
onModelChange: Function = () => {};
onModelTouched: Function = () => {};
calendarElement: any;
documentClickListener: any;
ticksTo1970: number;
yearOptions: number[];
focus: boolean;
isKeydown: boolean;
filled: boolean;
inputFieldValue: string = null;
_minDate: Date;
_maxDate: Date;
_showTime: boolean;
preventDocumentListener: boolean;
dateTemplate: TemplateRef;
_disabledDates: Array;
_disabledDays: Array;
@Input() get minDate(): Date {
return this._minDate;
}
set minDate(date: Date) {
this._minDate = date;
if(this.currentMonth != undefined && this.currentMonth != null && this.currentYear) {
this.createMonth(this.currentMonth, this.currentYear);
}
}
@Input() get maxDate(): Date {
return this._maxDate;
}
set maxDate(date: Date) {
this._maxDate = date;
if(this.currentMonth != undefined && this.currentMonth != null && this.currentYear) {
this.createMonth(this.currentMonth, this.currentYear);
}
}
@Input() get disabledDates(): Date[] {
return this._disabledDates;
}
set disabledDates(disabledDates: Date[]) {
this._disabledDates = disabledDates;
if(this.currentMonth != undefined && this.currentMonth != null && this.currentYear) {
this.createMonth(this.currentMonth, this.currentYear);
}
}
@Input() get disabledDays(): number[] {
return this._disabledDays;
}
set disabledDays(disabledDays: number[]) {
this._disabledDays = disabledDays;
if(this.currentMonth != undefined && this.currentMonth != null && this.currentYear) {
this.createMonth(this.currentMonth, this.currentYear);
}
}
@Input() get showTime(): boolean {
return this._showTime;
}
set showTime(showTime: boolean) {
this._showTime = showTime;
if(this.currentHour === undefined) {
this.initTime(this.value||new Date());
}
this.updateInputfield();
}
get locale() {
return this._locale;
}
@Input()
set locale(newLocale: LocaleSettings) {
this._locale = newLocale;
this.createWeekDays();
this.createMonth(this.currentMonth, this.currentYear);
}
constructor(public el: ElementRef, public domHandler: DomHandler, public renderer: Renderer2, public cd: ChangeDetectorRef) {}
ngOnInit() {
let date = this.defaultDate||new Date();
this.createWeekDays();
this.currentMonth = date.getMonth();
this.currentYear = date.getFullYear();
this.initTime(date);
this.createMonth(this.currentMonth, this.currentYear);
this.ticksTo1970 = (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000);
if(this.yearNavigator && this.yearRange) {
this.yearOptions = [];
let years = this.yearRange.split(':'),
yearStart = parseInt(years[0]),
yearEnd = parseInt(years[1]);
for(let i = yearStart; i <= yearEnd; i++) {
this.yearOptions.push(i);
}
}
}
ngAfterViewInit() {
if(!this.inline && this.appendTo) {
if(this.appendTo === 'body')
document.body.appendChild(this.overlayViewChild.nativeElement);
else
this.domHandler.appendChild(this.overlayViewChild.nativeElement, this.appendTo);
}
}
ngAfterViewChecked() {
if(this.overlayShown) {
this.alignOverlay();
this.overlayShown = false;
}
}
ngAfterContentInit() {
this.templates.forEach((item) => {
switch(item.getType()) {
case 'date':
this.dateTemplate = item.template;
break;
default:
this.dateTemplate = item.template;
break;
}
});
}
createWeekDays() {
this.weekDays = [];
let dayIndex = this.locale.firstDayOfWeek;
for(let i = 0; i < 7; i++) {
this.weekDays.push(this.locale.dayNamesMin[dayIndex]);
dayIndex = (dayIndex == 6) ? 0 : ++dayIndex;
}
}
createMonth(month: number, year: number) {
this.dates = [];
this.currentMonth = month;
this.currentYear = year;
this.currentMonthText = this.locale.monthNames[month];
let firstDay = this.getFirstDayOfMonthIndex(month, year);
let daysLength = this.getDaysCountInMonth(month, year);
let prevMonthDaysLength = this.getDaysCountInPrevMonth(month, year);
let sundayIndex = this.getSundayIndex();
let dayNo = 1;
let today = new Date();
for(let i = 0; i < 6; i++) {
let week = [];
if(i == 0) {
for(let j = (prevMonthDaysLength - firstDay + 1); j <= prevMonthDaysLength; j++) {
let prev = this.getPreviousMonthAndYear(month, year);
week.push({day: j, month: prev.month, year: prev.year, otherMonth: true,
today: this.isToday(today, j, prev.month, prev.year), selectable: this.isSelectable(j, prev.month, prev.year)});
}
let remainingDaysLength = 7 - week.length;
for(let j = 0; j < remainingDaysLength; j++) {
week.push({day: dayNo, month: month, year: year, today: this.isToday(today, dayNo, month, year),
selectable: this.isSelectable(dayNo, month, year)});
dayNo++;
}
}
else {
for (let j = 0; j < 7; j++) {
if(dayNo > daysLength) {
let next = this.getNextMonthAndYear(month, year);
week.push({day: dayNo - daysLength, month: next.month, year: next.year, otherMonth:true,
today: this.isToday(today, dayNo - daysLength, next.month, next.year),
selectable: this.isSelectable((dayNo - daysLength), next.month, next.year)});
}
else {
week.push({day: dayNo, month: month, year: year, today: this.isToday(today, dayNo, month, year),
selectable: this.isSelectable(dayNo, month, year)});
}
dayNo++;
}
}
this.dates.push(week);
}
}
initTime(date: Date) {
this.pm = (!this.utc) ? (date.getHours() > 11) : (date.getUTCHours() > 11);
if (this.showTime) {
if (this.utc) {
this.currentMinute = date.getUTCMinutes();
this.currentSecond = date.getUTCSeconds();
if(this.hourFormat == '12')
this.currentHour = date.getUTCHours() == 0 ? 12 : date.getUTCHours() % 12;
else
this.currentHour = date.getUTCHours();
}
else {
this.currentMinute = date.getMinutes();
this.currentSecond = date.getSeconds();
if(this.hourFormat == '12')
this.currentHour = date.getHours() == 0 ? 12 : date.getHours() % 12;
else
this.currentHour = date.getHours();
}
}
else if(this.timeOnly) {
this.currentMinute = 0;
this.currentHour = 0;
this.currentSecond = 0;
}
}
prevMonth(event) {
if(this.disabled) {
event.preventDefault();
return;
}
if(this.currentMonth === 0) {
this.currentMonth = 11;
this.currentYear--;
if(this.yearNavigator && this.currentYear < this.yearOptions[0]) {
this.currentYear = this.yearOptions[this.yearOptions.length - 1];
}
}
else {
this.currentMonth--;
}
this.onMonthChange.emit({ month: this.currentMonth + 1, year: this.currentYear });
this.createMonth(this.currentMonth, this.currentYear);
event.preventDefault();
}
nextMonth(event) {
if(this.disabled) {
event.preventDefault();
return;
}
if(this.currentMonth === 11) {
this.currentMonth = 0;
this.currentYear++;
if(this.yearNavigator && this.currentYear > this.yearOptions[this.yearOptions.length - 1]) {
this.currentYear = this.yearOptions[0];
}
}
else {
this.currentMonth++;
}
this.onMonthChange.emit({ month: this.currentMonth + 1, year: this.currentYear });
this.createMonth(this.currentMonth, this.currentYear);
event.preventDefault();
}
onDateSelect(event, dateMeta) {
if(this.disabled || !dateMeta.selectable) {
event.preventDefault();
return;
}
if(this.isMultipleSelection() && this.isSelected(dateMeta)) {
this.value = this.value.filter((date, i) => {
return !this.isDateEquals(date, dateMeta);
});
this.updateModel(this.value);
}
else {
if(this.shouldSelectDate(dateMeta)) {
if(dateMeta.otherMonth) {
if(this.selectOtherMonths) {
this.currentMonth = dateMeta.month;
this.currentYear = dateMeta.year;
this.createMonth(this.currentMonth, this.currentYear);
this.selectDate(dateMeta);
}
}
else {
this.selectDate(dateMeta);
}
}
}
if(this.isSingleSelection() && (!this.showTime || this.hideOnDateTimeSelect)) {
this.overlayVisible = false;
}
this.updateInputfield();
event.preventDefault();
}
shouldSelectDate(dateMeta) {
if(this.isMultipleSelection())
return !this.maxDateCount || !this.value || this.maxDateCount > this.value.length;
else
return true;
}
updateInputfield() {
let formattedValue = '';
if(this.value) {
if(this.isSingleSelection()) {
formattedValue = this.formatDateTime(this.value);
}
else if(this.isMultipleSelection()) {
for(let i = 0; i < this.value.length; i++) {
let dateAsString = this.formatDateTime(this.value[i]);
formattedValue += dateAsString;
if(i !== (this.value.length - 1)) {
formattedValue += ', ';
}
}
}
else if(this.isRangeSelection()) {
if(this.value && this.value.length) {
let startDate = this.value[0];
let endDate = this.value[1];
formattedValue = this.formatDateTime(startDate);
if(endDate) {
formattedValue += ' - ' + this.formatDateTime(endDate);
}
}
}
}
this.inputFieldValue = formattedValue;
this.updateFilledState();
if(this.inputfieldViewChild && this.inputfieldViewChild.nativeElement) {
this.inputfieldViewChild.nativeElement.value = this.inputFieldValue;
}
}
formatDateTime(date) {
let formattedValue = null;
if(date) {
if(this.timeOnly) {
formattedValue = this.formatTime(date);
}
else {
formattedValue = this.formatDate(date, this.dateFormat);
if(this.showTime) {
formattedValue += ' ' + this.formatTime(date);
}
}
}
return formattedValue;
}
selectDate(dateMeta) {
let date;
if(this.utc)
date = new Date(Date.UTC(dateMeta.year, dateMeta.month, dateMeta.day));
else
date = new Date(dateMeta.year, dateMeta.month, dateMeta.day);
if(this.showTime) {
if(this.utc) {
if(this.hourFormat === '12' && this.pm && this.currentHour != 12)
date.setUTCHours(this.currentHour + 12);
else
date.setUTCHours(this.currentHour);
date.setUTCMinutes(this.currentMinute);
date.setUTCSeconds(this.currentSecond);
}
else {
if(this.hourFormat === '12' && this.pm && this.currentHour != 12)
date.setHours(this.currentHour + 12);
else
date.setHours(this.currentHour);
date.setMinutes(this.currentMinute);
date.setSeconds(this.currentSecond);
}
}
if(this.minDate && this.minDate > date) {
date = this.minDate;
this.currentHour = date.getHours();
this.currentMinute = date.getMinutes();
this.currentSecond = date.getSeconds();
}
if(this.maxDate && this.maxDate < date) {
date = this.maxDate;
this.currentHour = date.getHours();
this.currentMinute = date.getMinutes();
this.currentSecond = date.getSeconds();
}
if(this.isSingleSelection()) {
this.updateModel(date);
}
else if(this.isMultipleSelection()) {
this.updateModel(this.value ? [...this.value, date] : [date]);
}
else if(this.isRangeSelection()) {
if(this.value && this.value.length) {
let startDate = this.value[0];
let endDate = this.value[1];
if(!endDate && date.getTime() >= startDate.getTime()) {
endDate = date;
}
else {
startDate = date;
endDate = null;
}
this.updateModel([startDate, endDate]);
}
else {
this.updateModel([date, null]);
}
}
this.onSelect.emit(date);
}
updateModel(value) {
this.value = value;
if(this.dataType == 'date') {
this.onModelChange(this.value);
}
else if(this.dataType == 'string') {
if(this.isSingleSelection()) {
this.onModelChange(this.formatDateTime(this.value));
}
else {
let stringArrValue = null;
if(this.value) {
stringArrValue = this.value.map(date => this.formatDateTime(date));
}
this.onModelChange(stringArrValue);
}
}
}
getFirstDayOfMonthIndex(month: number, year: number) {
let day = new Date();
day.setDate(1);
day.setMonth(month);
day.setFullYear(year);
let dayIndex = day.getDay() + this.getSundayIndex();
return dayIndex >= 7 ? dayIndex - 7 : dayIndex;
}
getDaysCountInMonth(month: number, year: number) {
return 32 - this.daylightSavingAdjust(new Date(year, month, 32)).getDate();
}
getDaysCountInPrevMonth(month: number, year: number) {
let prev = this.getPreviousMonthAndYear(month, year);
return this.getDaysCountInMonth(prev.month, prev.year);
}
getPreviousMonthAndYear(month: number, year: number) {
let m, y;
if(month === 0) {
m = 11;
y = year - 1;
}
else {
m = month - 1;
y = year;
}
return {'month':m,'year':y};
}
getNextMonthAndYear(month: number, year: number) {
let m, y;
if(month === 11) {
m = 0;
y = year + 1;
}
else {
m = month + 1;
y = year;
}
return {'month':m,'year':y};
}
getSundayIndex() {
return this.locale.firstDayOfWeek > 0 ? 7 - this.locale.firstDayOfWeek : 0;
}
isSelected(dateMeta): boolean {
if(this.value) {
if(this.isSingleSelection()) {
return this.isDateEquals(this.value, dateMeta);
}
else if(this.isMultipleSelection()) {
let selected = false;
for(let date of this.value) {
selected = this.isDateEquals(date, dateMeta);
if(selected) {
break;
}
}
return selected;
}
else if(this.isRangeSelection()) {
if(this.value[1])
return this.isDateEquals(this.value[0], dateMeta) || this.isDateEquals(this.value[1], dateMeta) || this.isDateBetween(this.value[0], this.value[1], dateMeta);
else
return this.isDateEquals(this.value[0], dateMeta)
}
}
else
return false;
}
isDateEquals(value, dateMeta) {
if(value)
return value.getDate() === dateMeta.day && value.getMonth() === dateMeta.month && value.getFullYear() === dateMeta.year;
else
return false;
}
isDateBetween(start, end, dateMeta) {
let between : boolean = false;
if(start && end) {
let date: Date = new Date(dateMeta.year, dateMeta.month, dateMeta.day);
return start.getTime() <= date.getTime() && end.getTime() >= date.getTime();
}
return between;
}
isSingleSelection(): boolean {
return this.selectionMode === 'single';
}
isRangeSelection(): boolean {
return this.selectionMode === 'range';
}
isMultipleSelection(): boolean {
return this.selectionMode === 'multiple';
}
isToday(today, day, month, year): boolean {
return today.getDate() === day && today.getMonth() === month && today.getFullYear() === year;
}
isSelectable(day, month, year): boolean {
let validMin = true;
let validMax = true;
let validDate = true;
let validDay = true;
if(this.minDate) {
if(this.minDate.getFullYear() > year) {
validMin = false;
}
else if(this.minDate.getFullYear() === year) {
if(this.minDate.getMonth() > month) {
validMin = false;
}
else if(this.minDate.getMonth() === month) {
if(this.minDate.getDate() > day) {
validMin = false;
}
}
}
}
if(this.maxDate) {
if(this.maxDate.getFullYear() < year) {
validMax = false;
}
else if(this.maxDate.getFullYear() === year) {
if(this.maxDate.getMonth() < month) {
validMax = false;
}
else if(this.maxDate.getMonth() === month) {
if(this.maxDate.getDate() < day) {
validMax = false;
}
}
}
}
if(this.disabledDates) {
validDate = !this.isDateDisabled(day,month,year);
}
if(this.disabledDays) {
validDay = !this.isDayDisabled(day,month,year)
}
return validMin && validMax && validDate && validDay;
}
isDateDisabled(day:number, month:number, year:number):boolean {
if(this.disabledDates) {
for(let disabledDate of this.disabledDates) {
if(disabledDate.getFullYear() === year && disabledDate.getMonth() === month && disabledDate.getDate() === day) {
return true;
}
}
}
return false;
}
isDayDisabled(day:number, month:number, year:number):boolean {
if(this.disabledDays) {
let weekday = new Date(year, month, day);
let weekdayNumber = weekday.getDay();
return this.disabledDays.indexOf(weekdayNumber) !== -1;
}
return false;
}
onInputFocus(event: Event) {
this.focus = true;
if(this.showOnFocus) {
this.showOverlay();
}
this.onFocus.emit(event);
}
onInputClick(event: Event) {
this.datepickerClick=true;
if(this.autoZIndex) {
this.overlayViewChild.nativeElement.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
}
}
onInputBlur(event: Event) {
this.focus = false;
this.onBlur.emit(event);
if(!this.keepInvalid) {
this.updateInputfield();
}
this.onModelTouched();
}
onButtonClick(event,inputfield) {
if(!this.overlayViewChild.nativeElement.offsetParent || this.overlayViewChild.nativeElement.style.display === 'none') {
inputfield.focus();
this.showOverlay();
}
else
this.overlayVisible = false;
this.datepickerClick = true;
}
onInputKeydown(event) {
this.isKeydown = true;
if(event.keyCode === 9) {
this.overlayVisible = false;
}
}
onMonthDropdownChange(m: string) {
this.currentMonth = parseInt(m);
this.onMonthChange.emit({ month: this.currentMonth + 1, year: this.currentYear });
this.createMonth(this.currentMonth, this.currentYear);
}
onYearDropdownChange(y: string) {
this.currentYear = parseInt(y);
this.onYearChange.emit({ month: this.currentMonth + 1, year: this.currentYear });
this.createMonth(this.currentMonth, this.currentYear);
}
incrementHour(event) {
const prevHour = this.currentHour;
const newHour = this.currentHour + this.stepHour;
if(this.validateHour(newHour)) {
if(this.hourFormat == '24')
this.currentHour = (newHour >= 24) ? (newHour - 24) : newHour;
else if(this.hourFormat == '12') {
// Before the AM/PM break, now after
if (prevHour < 12 && newHour > 11) {
this.pm = !this.pm;
}
this.currentHour = (newHour >= 13) ? (newHour - 12) : newHour;
}
this.updateTime();
}
event.preventDefault();
}
decrementHour(event) {
const newHour = this.currentHour - this.stepHour;
if(this.validateHour(newHour)) {
if(this.hourFormat == '24')
this.currentHour = (newHour < 0) ? (24 + newHour) : newHour;
else if(this.hourFormat == '12') {
// If we were at noon/midnight, then switch
if (this.currentHour === 12) {
this.pm = !this.pm;
}
this.currentHour = (newHour <= 0) ? (12 + newHour) : newHour;
}
this.updateTime();
}
event.preventDefault();
}
validateHour(hour): boolean {
let valid: boolean = true;
let value = this.value;
if(this.isRangeSelection()) {
value = this.value[1] || this.value[0];
}
if(this.isMultipleSelection()) {
value = this.value[this.value.length - 1];
}
let valueDateString = value ? value.toDateString() : null;
if(this.minDate && valueDateString && this.minDate.toDateString() === valueDateString) {
if(this.minDate.getHours() > hour) {
valid = false;
}
}
if(this.maxDate && valueDateString && this.maxDate.toDateString() === valueDateString) {
if(this.maxDate.getHours() < hour) {
valid = false;
}
}
return valid;
}
incrementMinute(event) {
let newMinute = this.currentMinute + this.stepMinute;
if(this.validateMinute(newMinute)) {
this.currentMinute = (newMinute > 59) ? newMinute - 60 : newMinute;
this.updateTime();
}
event.preventDefault();
}
decrementMinute(event) {
let newMinute = this.currentMinute - this.stepMinute;
if(this.validateMinute(newMinute)) {
this.currentMinute = (newMinute < 0) ? 60 + newMinute : newMinute;
this.updateTime();
}
event.preventDefault();
}
validateMinute(minute): boolean {
let valid: boolean = true;
let value = this.value;
if(this.isRangeSelection()) {
value = this.value[1] || this.value[0];
}
if(this.isMultipleSelection()) {
value = this.value[this.value.length - 1];
}
let valueDateString = value ? value.toDateString() : null;
if(this.minDate && valueDateString && this.minDate.toDateString() === valueDateString) {
if(this.minDate.getMinutes() > minute) {
valid = false;
}
}
if(this.maxDate && valueDateString && this.maxDate.toDateString() === valueDateString) {
if(this.maxDate.getMinutes() < minute) {
valid = false;
}
}
return valid;
}
incrementSecond(event) {
let newSecond = this.currentSecond + this.stepSecond;
if(this.validateSecond(newSecond)) {
this.currentSecond = (newSecond > 59) ? newSecond - 60 : newSecond;
this.updateTime();
}
event.preventDefault();
}
decrementSecond(event) {
let newSecond = this.currentSecond - this.stepSecond;
if(this.validateSecond(newSecond)) {
this.currentSecond = (newSecond < 0) ? 60 + newSecond : newSecond;
this.updateTime();
}
event.preventDefault();
}
validateSecond(second): boolean {
let valid: boolean = true;
let value = this.value;
if(this.isRangeSelection()) {
value = this.value[1] || this.value[0];
}
if(this.isMultipleSelection()) {
value = this.value[this.value.length - 1];
}
let valueDateString = value ? value.toDateString() : null;
if(this.minDate && valueDateString && this.minDate.toDateString() === valueDateString) {
if(this.minDate.getSeconds() > second) {
valid = false;
}
}
if(this.maxDate && valueDateString && this.maxDate.toDateString() === valueDateString) {
if(this.maxDate.getSeconds() < second) {
valid = false;
}
}
return valid;
}
updateTime() {
let value = this.value;
if(this.isRangeSelection()) {
value = this.value[1] || this.value[0];
}
if(this.isMultipleSelection()) {
value = this.value[this.value.length - 1];
}
value = value ? new Date(value.getTime()) : new Date();
if (this.utc) {
if (this.hourFormat == '12') {
if (this.currentHour === 12)
value.setUTCHours(this.pm ? 12 : 0);
else
value.setUTCHours(this.pm ? this.currentHour + 12 : this.currentHour);
}
else {
value.setUTCHours(this.currentHour);
}
}
else {
if (this.hourFormat == '12') {
if (this.currentHour === 12)
value.setHours(this.pm ? 12 : 0);
else
value.setHours(this.pm ? this.currentHour + 12 : this.currentHour);
}
else {
value.setHours(this.currentHour);
}
}
value.setMinutes(this.currentMinute);
value.setSeconds(this.currentSecond);
if(this.isRangeSelection()) {
if(this.value[1]) {
value = [this.value[0], value];
} else {
value = [value, null];
}
}
if(this.isMultipleSelection()){
value = [...this.value.slice(0, -1), value];
}
this.updateModel(value);
this.onSelect.emit(value);
this.updateInputfield();
}
toggleAMPM(event) {
this.pm = !this.pm;
this.updateTime();
event.preventDefault();
}
onUserInput(event) {
// IE 11 Workaround for input placeholder : https://github.com/primefaces/primeng/issues/2026
if(!this.isKeydown) {
return;
}
this.isKeydown = false;
let val = event.target.value;
try {
let value = this.parseValueFromString(val);
this.updateModel(value);
this.updateUI();
}
catch(err) {
//invalid date
this.updateModel(null);
}
this.filled = val != null && val.length;
this.onInput.emit(event);
}
parseValueFromString(text: string): Date {
if(!text || text.trim().length === 0) {
return null;
}
let value: any;
if(this.isSingleSelection()) {
value = this.parseDateTime(text);
}
else if(this.isMultipleSelection()) {
let tokens = text.split(',');
value = [];
for(let token of tokens) {
value.push(this.parseDateTime(token.trim()));
}
}
else if(this.isRangeSelection()) {
let tokens = text.split(' - ');
value = [];
for(let i = 0; i < tokens.length; i++) {
value[i] = this.parseDateTime(tokens[i].trim());
}
}
return value;
}
parseDateTime(text): Date {
let date: Date;
let parts: string[] = text.split(' ');
if(this.timeOnly) {
date = new Date();
this.populateTime(date, parts[0], parts[1]);
}
else {
if(this.showTime) {
date = this.parseDate(parts[0], this.dateFormat);
this.populateTime(date, parts[1], parts[2]);
}
else {
date = this.parseDate(text, this.dateFormat);
}
}
return date;
}
populateTime(value, timeString, ampm) {
if(this.hourFormat == '12' && !ampm) {
throw 'Invalid Time';
}
this.pm = (ampm === 'PM' || ampm === 'pm');
let time = this.parseTime(timeString);
if (!this.utc)
value.setHours(time.hour);
else
value.setUTCHours(time.hour);
value.setMinutes(time.minute);
value.setSeconds(time.second);
}
updateUI() {
let val = this.value||this.defaultDate||new Date();
if (Array.isArray(val)){
val = val[0];
}
this.createMonth(val.getMonth(), val.getFullYear());
if(this.showTime||this.timeOnly) {
let hours = (this.utc) ? val.getUTCHours() : val.getHours();
if(this.hourFormat == '12') {
this.pm = hours > 11;
if(hours >= 12) {
this.currentHour = (hours == 12) ? 12 : hours - 12;
}
else {
this.currentHour = (hours == 0) ? 12 : hours;
}
}
else {
this.currentHour = (this.utc) ? val.getUTCHours() : val.getHours();
}
this.currentMinute = val.getMinutes();
this.currentSecond = val.getSeconds();
}
}
onDatePickerClick(event) {
this.datepickerClick = true;
}
showOverlay() {
this.overlayVisible = true;
this.overlayShown = true;
if(this.autoZIndex) {
this.overlayViewChild.nativeElement.style.zIndex = String(this.baseZIndex + (++DomHandler.zindex));
}
this.bindDocumentClickListener();
}
alignOverlay() {
if(this.appendTo)
this.domHandler.absolutePosition(this.overlayViewChild.nativeElement, this.inputfieldViewChild.nativeElement);
else
this.domHandler.relativePosition(this.overlayViewChild.nativeElement, this.inputfieldViewChild.nativeElement);
}
writeValue(value: any) : void {
this.value = value;
if(this.value && typeof this.value === 'string') {
this.value = this.parseValueFromString(this.value);
}
this.updateInputfield();
this.updateUI();
}
registerOnChange(fn: Function): void {
this.onModelChange = fn;
}
registerOnTouched(fn: Function): void {
this.onModelTouched = fn;
}
setDisabledState(val: boolean): void {
this.disabled = val;
}
// Ported from jquery-ui datepicker formatDate
formatDate(date, format) {
if (!date) {
return '';
}
let iFormat;
const lookAhead = (match) => {
const matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
if (matches) {
iFormat++;
}
return matches;
},
formatNumber = (match, value, len) => {
let num = '' + value;
if (lookAhead(match)) {
while (num.length < len) {
num = '0' + num;
}
}
return num;
},
formatName = (match, value, shortNames, longNames) => {
return (lookAhead(match) ? longNames[value] : shortNames[value]);
};
let output = '';
let literal = false;
if (date) {
for (iFormat = 0; iFormat < format.length; iFormat++) {
if (literal) {
if (format.charAt(iFormat) === '\'' && !lookAhead('\'')) {
literal = false;
} else {
output += format.charAt(iFormat);
}
} else {
switch (format.charAt(iFormat)) {
case 'd':
output += formatNumber('d', this.utc ? date.getUTCDate() : date.getDate(), 2);
break;
case 'D':
output += formatName('D', this.utc ? date.getUTCDay() : date.getDay(), this.locale.dayNamesShort, this.locale.dayNames);
break;
case 'o':
if (this.utc) {
output += formatNumber('o',
Math.round((
new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()).getTime() -
new Date(date.getUTCFullYear(), 0, 0).getTime()) / 86400000), 3);
} else {
output += formatNumber('o',
Math.round((
new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() -
new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
}
break;
case 'm':
output += formatNumber('m', (this.utc ? date.getUTCMonth() : date.getMonth()) + 1, 2);
break;
case 'M':
output += formatName('M', this.utc ? date.getUTCMonth() : date.getMonth(), this.locale.monthNamesShort, this.locale.monthNames);
break;
case 'y':
output += (lookAhead('y') ? (this.utc ? date.getUTCFullYear() : date.getFullYear()) :
((this.utc ? date.getUTCFullYear() : date.getFullYear()) % 100 < 10 ? '0' : '') +
(this.utc ? date.getUTCFullYear() : date.getFullYear()) % 100);
break;
case '@':
output += date.getTime();
break;
case '!':
output += date.getTime() * 10000 + this.ticksTo1970;
break;
case '\'':
if (lookAhead('\'')) {
output += '\'';
} else {
literal = true;
}
break;
default:
output += format.charAt(iFormat);
}
}
}
}
return output;
}
formatTime(date) {
if(!date) {
return '';
}
let output = '';
let hours = (this.utc) ? date.getUTCHours() : date.getHours();
let minutes = date.getMinutes();
let seconds = date.getSeconds();
if(this.hourFormat == '12' && hours > 11 && hours != 12) {
hours-=12;
}
output += hours === 0 ? 12 : (hours < 10) ? '0' + hours : hours;
output += ':';
output += (minutes < 10) ? '0' + minutes : minutes;
if(this.showSeconds) {
output += ':';
output += (seconds < 10) ? '0' + seconds : seconds;
}
if(this.hourFormat == '12') {
output += date.getHours() > 11 ? ' PM' : ' AM';
}
return output;
}
parseTime(value) {
let tokens: string[] = value.split(':');
let validTokenLength = this.showSeconds ? 3 : 2;
if(tokens.length !== validTokenLength) {
throw "Invalid time";
}
let h = parseInt(tokens[0]);
let m = parseInt(tokens[1]);
let s = this.showSeconds ? parseInt(tokens[2]) : null;
if(isNaN(h) || isNaN(m) || h > 23 || m > 59 || (this.hourFormat == '12' && h > 12) || (this.showSeconds && (isNaN(s) || s > 59))) {
throw "Invalid time";
}
else {
if(this.hourFormat == '12' && h !== 12 && this.pm) {
h+= 12;
}
return {hour: h, minute: m, second: s};
}
}
// Ported from jquery-ui datepicker parseDate
parseDate(value, format) {
if(format == null || value == null) {
throw "Invalid arguments";
}
value = (typeof value === "object" ? value.toString() : value + "");
if(value === "") {
return null;
}
let iFormat, dim, extra,
iValue = 0,
shortYearCutoff = (typeof this.shortYearCutoff !== "string" ? this.shortYearCutoff : new Date().getFullYear() % 100 + parseInt(this.shortYearCutoff, 10)),
year = -1,
month = -1,
day = -1,
doy = -1,
literal = false,
date,
lookAhead = (match) => {
let matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
if(matches) {
iFormat++;
}
return matches;
},
getNumber = (match) => {
let isDoubled = lookAhead(match),
size = (match === "@" ? 14 : (match === "!" ? 20 :
(match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
minSize = (match === "y" ? size : 1),
digits = new RegExp("^\\d{" + minSize + "," + size + "}"),
num = value.substring(iValue).match(digits);
if(!num) {
throw "Missing number at position " + iValue;
}
iValue += num[ 0 ].length;
return parseInt(num[ 0 ], 10);
},
getName = (match, shortNames, longNames) => {
let index = -1;
let arr = lookAhead(match) ? longNames : shortNames;
let names = [];
for(let i = 0; i < arr.length; i++) {
names.push([i,arr[i]]);
}
names.sort((a,b) => {
return -(a[ 1 ].length - b[ 1 ].length);
});
for(let i = 0; i < names.length; i++) {
let name = names[i][1];
if(value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
index = names[i][0];
iValue += name.length;
break;
}
}
if(index !== -1) {
return index + 1;
} else {
throw "Unknown name at position " + iValue;
}
},
checkLiteral = () => {
if(value.charAt(iValue) !== format.charAt(iFormat)) {
throw "Unexpected literal at position " + iValue;
}
iValue++;
};
for (iFormat = 0; iFormat < format.length; iFormat++) {
if(literal) {
if(format.charAt(iFormat) === "'" && !lookAhead("'")) {
literal = false;
} else {
checkLiteral();
}
} else {
switch (format.charAt(iFormat)) {
case "d":
day = getNumber("d");
break;
case "D":
getName("D", this.locale.dayNamesShort, this.locale.dayNames);
break;
case "o":
doy = getNumber("o");
break;
case "m":
month = getNumber("m");
break;
case "M":
month = getName("M", this.locale.monthNamesShort, this.locale.monthNames);
break;
case "y":
year = getNumber("y");
break;
case "@":
date = new Date(getNumber("@"));
year = date.getFullYear();
month = date.getMonth() + 1;
day = date.getDate();
break;
case "!":
date = new Date((getNumber("!") - this.ticksTo1970) / 10000);
year = date.getFullYear();
month = date.getMonth() + 1;
day = date.getDate();
break;
case "'":
if(lookAhead("'")) {
checkLiteral();
} else {
literal = true;
}
break;
default:
checkLiteral();
}
}
}
if(iValue < value.length) {
extra = value.substr(iValue);
if(!/^\s+/.test(extra)) {
throw "Extra/unparsed characters found in date: " + extra;
}
}
if(year === -1) {
year = new Date().getFullYear();
} else if(year < 100) {
year += new Date().getFullYear() - new Date().getFullYear() % 100 +
(year <= shortYearCutoff ? 0 : -100);
}
if(doy > -1) {
month = 1;
day = doy;
do {
dim = this.getDaysCountInMonth(year, month - 1);
if(day <= dim) {
break;
}
month++;
day -= dim;
} while (true);
}
if (this.utc) {
date = new Date(Date.UTC(year, month - 1, day));
if (date.getUTCFullYear() !== year || date.getUTCMonth() + 1 !== month || date.getUTCDate() !== day) {
throw "Invalid date"; // E.g. 31/02/00
}
} else {
date = this.daylightSavingAdjust(new Date(year, month - 1, day));
if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
throw "Invalid date"; // E.g. 31/02/00
}
}
return date;
}
daylightSavingAdjust(date) {
if(!date) {
return null;
}
if(!this.utc) {
date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
}
return date;
}
updateFilledState() {
this.filled = this.inputFieldValue && this.inputFieldValue != '';
}
onTodayButtonClick(event) {
let date: Date = new Date();
let dateMeta = {day: date.getDate(), month: date.getMonth(), year: date.getFullYear(), today: true, selectable: true};
this.createMonth(dateMeta.month, dateMeta.year);
this.onDateSelect(event, dateMeta);
this.onTodayClick.emit(event);
}
onClearButtonClick(event) {
this.updateModel(null);
this.updateInputfield();
this.overlayVisible = false;
this.onClearClick.emit(event);
}
bindDocumentClickListener() {
if(!this.documentClickListener) {
this.documentClickListener = this.renderer.listen('document', 'click', (event) => {
if(!this.datepickerClick&&this.overlayVisible) {
this.overlayVisible = false;
this.onClose.emit(event);
}
this.datepickerClick = false;
this.cd.detectChanges();
});
}
}
unbindDocumentClickListener() {
if(this.documentClickListener) {
this.documentClickListener();
this.documentClickListener = null;
}
}
ngOnDestroy() {
this.unbindDocumentClickListener();
if(!this.inline && this.appendTo) {
this.el.nativeElement.appendChild(this.overlayViewChild.nativeElement);
}
}
}
@NgModule({
imports: [CommonModule,ButtonModule,SharedModule],
exports: [Calendar,ButtonModule,SharedModule],
declarations: [Calendar]
})
export class CalendarModule { }