import { Component , Input , Output , EventEmitter , OnChanges , SimpleChanges , ElementRef , ViewEncapsulation , ChangeDetectorRef , ViewChild , forwardRef , OnDestroy } from "@angular/core" ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from "@angular/forms" ; import { IMyDate , IMyDateRange , IMyMonth , IMyCalendarDay , IMyCalendarMonth , IMyCalendarYear , IMyWeek , IMyDayLabels , IMyMonthLabels , IMyOptions , IMyDateModel , IMyInputFieldChanged , IMyCalendarViewChanged , IMyInputFocusBlur , IMyMarkedDates , IMyMarkedDate , IMyDateFormat } from "./interfaces/index" ; import { LocaleService } from "./services/my-date-picker.locale.service" ; import { UtilService } from "./services/my-date-picker.util.service" ; declare var require ; const myDpStyles = require ( "./my-date-picker.component.css" ) ; const myDpTpl = require ( "./my-date-picker.component.html" ) ; export const MYDP_VALUE_ACCESSOR = { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => MyDatePicker ) , multi : true } ; enum CalToggle { Open = 1 , CloseByDateSel = 2 , CloseByCalBtn = 3 , CloseByOutClick = 4 , CloseByEsc = 5 , CloseByApi = 6 } enum Year { min = 1000 , max = 9999 } enum InputFocusBlur { focus = 1 , blur = 2 } enum KeyCode { enter = 13 , esc = 27 , space = 32 } enum MonthId { prev = 1 , curr = 2 , next = 3 } const MMM = "mmm" ; @ Component ( { selector : "my-date-picker" , exportAs : "mydatepicker" , styles : [ myDpStyles ] , template : myDpTpl , providers : [ LocaleService , UtilService , MYDP_VALUE_ACCESSOR ] , encapsulation : ViewEncapsulation . None } ) export class MyDatePicker implements OnChanges , ControlValueAccessor , OnDestroy { @ Input ( ) options : IMyOptions ; @ Input ( ) locale : string ; @ Input ( ) defaultMonth : string ; @ Input ( ) selDate : string ; @ Input ( ) placeholder : string ; @ Input ( ) selector : number ; @ Input ( ) disabled : boolean ; @ Output ( ) dateChanged : EventEmitter < IMyDateModel > = new EventEmitter < IMyDateModel > ( ) ; @ Output ( ) inputFieldChanged : EventEmitter < IMyInputFieldChanged > = new EventEmitter < IMyInputFieldChanged > ( ) ; @ Output ( ) calendarViewChanged : EventEmitter < IMyCalendarViewChanged > = new EventEmitter < IMyCalendarViewChanged > ( ) ; @ Output ( ) calendarToggle : EventEmitter < number > = new EventEmitter < number > ( ) ; @ Output ( ) inputFocusBlur : EventEmitter < IMyInputFocusBlur > = new EventEmitter < IMyInputFocusBlur > ( ) ; @ ViewChild ( "selectorEl" ) selectorEl : ElementRef ; @ ViewChild ( "inputBoxEl" ) inputBoxEl : ElementRef ; onChangeCb : ( _ : any ) => void = ( ) => { } ; onTouchedCb : ( ) => void = ( ) => { } ; showSelector : boolean = false ; visibleMonth : IMyMonth = { monthTxt : "" , monthNbr : 0 , year : 0 } ; selectedMonth : IMyMonth = { monthTxt : "" , monthNbr : 0 , year : 0 } ; selectedDate : IMyDate = { year : 0 , month : 0 , day : 0 } ; weekDays : Array < string > = [ ] ; dates : Array < IMyWeek > = [ ] ; months : Array < Array < IMyCalendarMonth > > = [ ] ; years : Array < Array < IMyCalendarYear > > = [ ] ; selectionDayTxt : string = "" ; invalidDate : boolean = false ; disableTodayBtn : boolean = false ; dayIdx : number = 0 ; selectMonth : boolean = false ; selectYear : boolean = false ; prevMonthDisabled : boolean = false ; nextMonthDisabled : boolean = false ; prevYearDisabled : boolean = false ; nextYearDisabled : boolean = false ; prevYearsDisabled : boolean = false ; nextYearsDisabled : boolean = false ; prevMonthId : number = MonthId . prev ; currMonthId : number = MonthId . curr ; nextMonthId : number = MonthId . next ; opts : IMyOptions = { dayLabels : < IMyDayLabels > { } , monthLabels : < IMyMonthLabels > { } , dateFormat : < string > "" , showTodayBtn : < boolean > true , todayBtnTxt : < string > "" , firstDayOfWeek : < string > "" , satHighlight : < boolean > false , sunHighlight : < boolean > true , highlightDates : < Array < IMyDate > > [ ] , markCurrentDay : < boolean > true , markCurrentMonth : < boolean > true , markCurrentYear : < boolean > true , disableUntil : < IMyDate > { year : 0 , month : 0 , day : 0 } , disableSince : < IMyDate > { year : 0 , month : 0 , day : 0 } , disableDays : < Array < IMyDate > > [ ] , enableDays : < Array < IMyDate > > [ ] , markDates : < Array < IMyMarkedDates > > [ ] , markWeekends : < IMyMarkedDate > { } , disableDateRanges : < Array < IMyDateRange > > [ ] , disableWeekends : < boolean > false , disableWeekdays : < Array < string > > [ ] , showWeekNumbers : < boolean > false , height : < string > "34px" , width : < string > "100%" , selectionTxtFontSize : < string > "14px" , selectorHeight : < string > "232px" , selectorWidth : < string > "252px" , allowDeselectDate : < boolean > false , inline : < boolean > false , showClearDateBtn : < boolean > true , showDecreaseDateBtn : < boolean > false , showIncreaseDateBtn : < boolean > false , alignSelectorRight : < boolean > false , openSelectorTopOfInput : < boolean > false , indicateInvalidDate : < boolean > true , editableDateField : < boolean > true , monthSelector : < boolean > true , yearSelector : < boolean > true , disableHeaderButtons : < boolean > true , minYear : < number > Year . min , maxYear : < number > Year . max , componentDisabled : < boolean > false , showSelectorArrow : < boolean > true , showInputField : < boolean > true , openSelectorOnInputClick : < boolean > false , allowSelectionOnlyInCurrentMonth : < boolean > true , ariaLabelInputField : < string > "Date input field" , ariaLabelClearDate : < string > "Clear Date" , ariaLabelDecreaseDate : < string > "Decrease Date" , ariaLabelIncreaseDate : < string > "Increase Date" , ariaLabelOpenCalendar : < string > "Open Calendar" , ariaLabelPrevMonth : < string > "Previous Month" , ariaLabelNextMonth : < string > "Next Month" , ariaLabelPrevYear : < string > "Previous Year" , ariaLabelNextYear : < string > "Next Year" , ariaLabelDay : < string > "Select day" } ; constructor ( public elem , private cdr , private localeService , private utilService ) { this . setLocaleOptions ( ) ; } setLocaleOptions ( ) { let opts = this . localeService . getLocaleOptions ( this . locale ) ; Object . keys ( opts ) . forEach ( ( k ) => { ( < IMyOptions > this . opts ) [ k ] = opts [ k ] ; } ) ; } setOptions ( ) { if ( this . options !== undefined ) { Object . keys ( this . options ) . forEach ( ( k ) => { ( < IMyOptions > this . opts ) [ k ] = this . options [ k ] ; } ) ; } if ( this . opts . minYear < Year . min ) { this . opts . minYear = Year . min ; } if ( this . opts . maxYear > Year . max ) { this . opts . maxYear = Year . max ; } if ( this . disabled !== undefined ) { this . opts . componentDisabled = this . disabled ; } } getSelectorTopPosition ( ) { if ( this . opts . openSelectorTopOfInput ) { return this . elem . nativeElement . children [ 0 ] . offsetHeight + "px" ; } } resetMonthYearSelect ( ) { this . selectMonth = false ; this . selectYear = false ; } onSelectMonthClicked ( event ) { event . stopPropagation ( ) ; this . selectMonth = ! this . selectMonth ; this . selectYear = false ; this . cdr . detectChanges ( ) ; if ( this . selectMonth ) { let today = this . getToday ( ) ; this . months . length = 0 ; for ( let i = 1 ; i <= 12 ; i += 3 ) { let row = [ ] ; for ( let j = i ; j < i + 3 ; j ++ ) { let disabled = this . utilService . isMonthDisabledByDisableUntil ( { year : this . visibleMonth . year , month : j , day : this . daysInMonth ( j , this . visibleMonth . year ) } , this . opts . disableUntil ) || this . utilService . isMonthDisabledByDisableSince ( { year : this . visibleMonth . year , month : j , day : 1 } , this . opts . disableSince ) ; row . push ( { nbr : j , name : this . opts . monthLabels [ j ] , currMonth : j === today . month && this . visibleMonth . year === today . year , selected : j === this . visibleMonth . monthNbr , disabled : disabled } ) ; } this . months . push ( row ) ; } } } onMonthCellClicked ( cell ) { let mc = cell . nbr !== this . visibleMonth . monthNbr ; this . visibleMonth = { monthTxt : this . monthText ( cell . nbr ) , monthNbr : cell . nbr , year : this . visibleMonth . year } ; this . generateCalendar ( cell . nbr , this . visibleMonth . year , mc ) ; this . selectMonth = false ; this . selectorEl . nativeElement . focus ( ) ; } onMonthCellKeyDown ( event , cell ) { if ( ( event . keyCode === KeyCode . enter || event . keyCode === KeyCode . space ) && ! cell . disabled ) { event . preventDefault ( ) ; this . onMonthCellClicked ( cell ) ; } } onSelectYearClicked ( event ) { event . stopPropagation ( ) ; this . selectYear = ! this . selectYear ; this . selectMonth = false ; this . cdr . detectChanges ( ) ; if ( this . selectYear ) { this . generateYears ( Number ( this . visibleMonth . year ) ) ; } } onYearCellClicked ( cell ) { let yc = cell . year !== this . visibleMonth . year ; this . visibleMonth = { monthTxt : this . visibleMonth . monthTxt , monthNbr : this . visibleMonth . monthNbr , year : cell . year } ; this . generateCalendar ( this . visibleMonth . monthNbr , cell . year , yc ) ; this . selectYear = false ; this . selectorEl . nativeElement . focus ( ) ; } onYearCellKeyDown ( event , cell ) { if ( ( event . keyCode === KeyCode . enter || event . keyCode === KeyCode . space ) && ! cell . disabled ) { event . preventDefault ( ) ; this . onYearCellClicked ( cell ) ; } } onPrevYears ( event , year ) { event . stopPropagation ( ) ; this . generateYears ( Number ( year ) - 25 ) ; } onNextYears ( event , year ) { event . stopPropagation ( ) ; this . generateYears ( Number ( year ) + 25 ) ; } generateYears ( year ) { this . years . length = 0 ; let today = this . getToday ( ) ; for ( let i = year ; i <= 20 + year ; i += 5 ) { let row = [ ] ; for ( let j = i ; j < i + 5 ; j ++ ) { let disabled = this . utilService . isMonthDisabledByDisableUntil ( { year : j , month : this . visibleMonth . monthNbr , day : this . daysInMonth ( this . visibleMonth . monthNbr , j ) } , this . opts . disableUntil ) || this . utilService . isMonthDisabledByDisableSince ( { year : j , month : this . visibleMonth . monthNbr , day : 1 } , this . opts . disableSince ) ; let minMax = j < this . opts . minYear || j > this . opts . maxYear ; row . push ( { year : j , currYear : j === today . year , selected : j === this . visibleMonth . year , disabled : disabled || minMax } ) ; } this . years . push ( row ) ; } this . prevYearsDisabled = this . years [ 0 ] [ 0 ] . year <= this . opts . minYear || this . utilService . isMonthDisabledByDisableUntil ( { year : this . years [ 0 ] [ 0 ] . year - 1 , month : this . visibleMonth . monthNbr , day : this . daysInMonth ( this . visibleMonth . monthNbr , this . years [ 0 ] [ 0 ] . year - 1 ) } , this . opts . disableUntil ) ; this . nextYearsDisabled = this . years [ 4 ] [ 4 ] . year >= this . opts . maxYear || this . utilService . isMonthDisabledByDisableSince ( { year : this . years [ 4 ] [ 4 ] . year + 1 , month : this . visibleMonth . monthNbr , day : 1 } , this . opts . disableSince ) ; } onUserDateInput ( value ) { if ( value . length === 0 ) { if ( this . utilService . isInitializedDate ( this . selectedDate ) ) { this . clearDate ( ) ; } else { this . invalidInputFieldChanged ( value ) ; } } else { let date = this . utilService . isDateValid ( value , this . opts . dateFormat , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableWeekdays , this . opts . disableDays , this . opts . disableDateRanges , this . opts . monthLabels , this . opts . enableDays ) ; if ( this . utilService . isInitializedDate ( date ) ) { if ( ! this . utilService . isSameDate ( date , this . selectedDate ) ) { this . selectDate ( date , CalToggle . CloseByDateSel ) ; } else { this . updateDateValue ( date ) ; } } else { this . invalidInputFieldChanged ( value ) ; } } } onFocusInput ( event ) { this . inputFocusBlur . emit ( { reason : InputFocusBlur . focus , value : event . target . value } ) ; } onBlurInput ( event ) { this . selectionDayTxt = event . target . value ; this . onTouchedCb ( ) ; this . inputFocusBlur . emit ( { reason : InputFocusBlur . blur , value : event . target . value } ) ; } onCloseSelector ( event ) { if ( event . keyCode === KeyCode . esc && this . showSelector && ! this . opts . inline ) { this . removeGlobalListener ( ) ; this . calendarToggle . emit ( CalToggle . CloseByEsc ) ; this . showSelector = false ; } } invalidInputFieldChanged ( value ) { this . invalidDate = value . length > 0 ; this . inputFieldChanged . emit ( { value : value , dateFormat : this . opts . dateFormat , valid : false } ) ; this . onChangeCb ( null ) ; this . onTouchedCb ( ) ; } isTodayDisabled ( ) { this . disableTodayBtn = this . utilService . isDisabledDay ( this . getToday ( ) , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableWeekdays , this . opts . disableDays , this . opts . disableDateRanges , this . opts . enableDays ) ; } parseOptions ( ) { if ( this . locale ) { this . setLocaleOptions ( ) ; } this . setOptions ( ) ; let weekDays = this . utilService . getWeekDays ( ) ; this . isTodayDisabled ( ) ; this . dayIdx = weekDays . indexOf ( this . opts . firstDayOfWeek ) ; if ( this . dayIdx !== - 1 ) { let idx = this . dayIdx ; for ( let i = 0 ; i < weekDays . length ; i ++ ) { this . weekDays . push ( this . opts . dayLabels [ weekDays [ idx ] ] ) ; idx = weekDays [ idx ] === "sa" ? 0 : idx + 1 ; } } } writeValue ( value ) { if ( value && ( value [ "date" ] || value [ "jsdate" ] || value [ "formatted" ] ) ) { this . selectedDate = value [ "date" ] ? this . parseSelectedDate ( value [ "date" ] ) : value [ "jsdate" ] ? this . parseSelectedDate ( this . jsDateToMyDate ( value [ "jsdate" ] ) ) : this . parseSelectedDate ( value [ "formatted" ] ) ; let cvc = this . visibleMonth . year !== this . selectedDate . year || this . visibleMonth . monthNbr !== this . selectedDate . month ; if ( cvc ) { this . visibleMonth = { monthTxt : this . opts . monthLabels [ this . selectedDate . month ] , monthNbr : this . selectedDate . month , year : this . selectedDate . year } ; this . generateCalendar ( this . selectedDate . month , this . selectedDate . year , cvc ) ; } this . selectionDayTxt = this . utilService . formatDate ( this . selectedDate , this . opts . dateFormat , this . opts . monthLabels ) ; } else if ( value === null || value === "" ) { this . selectedDate = { year : 0 , month : 0 , day : 0 } ; this . selectionDayTxt = "" ; } this . inputFieldChanged . emit ( { value : this . selectionDayTxt , dateFormat : this . opts . dateFormat , valid : this . selectionDayTxt . length > 0 } ) ; this . invalidDate = false ; } setDisabledState ( disabled ) { this . opts . componentDisabled = disabled ; this . cdr . detectChanges ( ) ; } registerOnChange ( fn ) { this . onChangeCb = fn ; } registerOnTouched ( fn ) { this . onTouchedCb = fn ; } ngOnDestroy ( ) { this . removeGlobalListener ( ) ; } ngOnChanges ( changes ) { if ( changes . hasOwnProperty ( "selector" ) ) { let s = changes [ "selector" ] . currentValue ; if ( typeof s === "object" ) { if ( s . open ) { this . showSelector = true ; this . openSelector ( CalToggle . Open ) ; } else { this . showSelector = false ; this . closeSelector ( CalToggle . CloseByApi ) ; } } else if ( s > 0 ) { this . openBtnClicked ( ) ; } } if ( changes . hasOwnProperty ( "placeholder" ) ) { this . placeholder = changes [ "placeholder" ] . currentValue ; } if ( changes . hasOwnProperty ( "locale" ) ) { this . locale = changes [ "locale" ] . currentValue ; } if ( changes . hasOwnProperty ( "disabled" ) ) { this . disabled = changes [ "disabled" ] . currentValue ; } if ( changes . hasOwnProperty ( "options" ) ) { this . options = changes [ "options" ] . currentValue ; } this . weekDays . length = 0 ; this . parseOptions ( ) ; let dmChange = false ; if ( changes . hasOwnProperty ( "defaultMonth" ) ) { let dm = changes [ "defaultMonth" ] . currentValue ; if ( typeof dm === "object" ) { dm = dm . defMonth ; } if ( dm !== null && dm !== undefined && dm !== "" ) { this . selectedMonth = this . parseSelectedMonth ( dm ) ; } else { this . selectedMonth = { monthTxt : "" , monthNbr : 0 , year : 0 } ; } dmChange = true ; } if ( changes . hasOwnProperty ( "selDate" ) ) { let sd = changes [ "selDate" ] ; if ( sd . currentValue !== null && sd . currentValue !== undefined && sd . currentValue !== "" && Object . keys ( sd . currentValue ) . length !== 0 ) { this . selectedDate = this . parseSelectedDate ( sd . currentValue ) ; setTimeout ( ( ) => { this . onChangeCb ( this . getDateModel ( this . selectedDate ) ) ; } ) ; } else { if ( ! sd . isFirstChange ( ) ) { this . clearDate ( ) ; } } } if ( this . visibleMonth . year === 0 && this . visibleMonth . monthNbr === 0 || dmChange ) { this . setVisibleMonth ( ) ; } else { this . visibleMonth . monthTxt = this . opts . monthLabels [ this . visibleMonth . monthNbr ] ; this . generateCalendar ( this . visibleMonth . monthNbr , this . visibleMonth . year , false ) ; } } removeBtnClicked ( ) { this . clearDate ( ) ; if ( this . showSelector ) { this . calendarToggle . emit ( CalToggle . CloseByCalBtn ) ; this . removeGlobalListener ( ) ; } this . showSelector = false ; } onDecreaseBtnClicked ( ) { this . decreaseIncreaseDate ( true ) ; } onIncreaseBtnClicked ( ) { this . decreaseIncreaseDate ( false ) ; } openBtnClicked ( ) { this . showSelector = ! this . showSelector ; this . cdr . detectChanges ( ) ; if ( this . showSelector ) { this . openSelector ( CalToggle . Open ) ; } else { this . closeSelector ( CalToggle . CloseByCalBtn ) ; this . removeGlobalListener ( ) ; } } onClickListener = ( evt ) => this . onClickDocument ( evt ) ; addGlobalListener ( ) { document . addEventListener ( "click" , this . onClickListener ) ; } removeGlobalListener ( ) { document . removeEventListener ( "click" , this . onClickListener ) ; } onClickDocument ( event ) { if ( this . showSelector && event . target && this . elem . nativeElement !== event . target && ! this . elem . nativeElement . contains ( event . target ) ) { this . showSelector = false ; this . cdr . detectChanges ( ) ; this . calendarToggle . emit ( CalToggle . CloseByOutClick ) ; this . removeGlobalListener ( ) ; } if ( this . opts . monthSelector || this . opts . yearSelector ) { this . resetMonthYearSelect ( ) ; } } openSelector ( reason ) { this . addGlobalListener ( ) ; this . setVisibleMonth ( ) ; this . calendarToggle . emit ( reason ) ; } closeSelector ( reason ) { this . calendarToggle . emit ( reason ) ; } setVisibleMonth ( ) { let y = 0 , m = 0 ; if ( ! this . utilService . isInitializedDate ( this . selectedDate ) ) { if ( this . selectedMonth . year === 0 && this . selectedMonth . monthNbr === 0 ) { let today = this . getToday ( ) ; y = today . year ; m = today . month ; } else { y = this . selectedMonth . year ; m = this . selectedMonth . monthNbr ; } } else { y = this . selectedDate . year ; m = this . selectedDate . month ; } this . visibleMonth = { monthTxt : this . opts . monthLabels [ m ] , monthNbr : m , year : y } ; this . generateCalendar ( m , y , true ) ; } onPrevMonth ( ) { let d = this . getDate ( this . visibleMonth . year , this . visibleMonth . monthNbr , 1 ) ; d . setMonth ( d . getMonth ( ) - 1 ) ; let y = d . getFullYear ( ) ; let m = d . getMonth ( ) + 1 ; this . visibleMonth = { monthTxt : this . monthText ( m ) , monthNbr : m , year : y } ; this . generateCalendar ( m , y , true ) ; } onNextMonth ( ) { let d = this . getDate ( this . visibleMonth . year , this . visibleMonth . monthNbr , 1 ) ; d . setMonth ( d . getMonth ( ) + 1 ) ; let y = d . getFullYear ( ) ; let m = d . getMonth ( ) + 1 ; this . visibleMonth = { monthTxt : this . monthText ( m ) , monthNbr : m , year : y } ; this . generateCalendar ( m , y , true ) ; } onPrevYear ( ) { this . visibleMonth . year -- ; this . generateCalendar ( this . visibleMonth . monthNbr , this . visibleMonth . year , true ) ; } onNextYear ( ) { this . visibleMonth . year ++ ; this . generateCalendar ( this . visibleMonth . monthNbr , this . visibleMonth . year , true ) ; } onTodayClicked ( ) { let today = this . getToday ( ) ; this . selectDate ( today , CalToggle . CloseByDateSel ) ; if ( this . opts . inline && today . year !== this . visibleMonth . year || today . month !== this . visibleMonth . monthNbr ) { this . visibleMonth = { monthTxt : this . opts . monthLabels [ today . month ] , monthNbr : today . month , year : today . year } ; this . generateCalendar ( today . month , today . year , true ) ; } } onCellClicked ( cell ) { if ( cell . cmo === this . prevMonthId ) { this . onPrevMonth ( ) ; if ( ! this . opts . allowSelectionOnlyInCurrentMonth ) { this . selectDate ( cell . dateObj , CalToggle . CloseByDateSel ) ; } } else if ( cell . cmo === this . currMonthId ) { if ( this . opts . allowDeselectDate && this . utilService . isSameDate ( cell . dateObj , this . selectedDate ) ) { this . clearDate ( ) ; } else { this . selectDate ( cell . dateObj , CalToggle . CloseByDateSel ) ; } } else if ( cell . cmo === this . nextMonthId ) { this . onNextMonth ( ) ; if ( ! this . opts . allowSelectionOnlyInCurrentMonth ) { this . selectDate ( cell . dateObj , CalToggle . CloseByDateSel ) ; } } this . resetMonthYearSelect ( ) ; } onCellKeyDown ( event , cell ) { if ( ( event . keyCode === KeyCode . enter || event . keyCode === KeyCode . space ) && ! cell . disabled ) { event . preventDefault ( ) ; this . onCellClicked ( cell ) ; } } clearDate ( ) { this . updateDateValue ( { year : 0 , month : 0 , day : 0 } ) ; this . setFocusToInputBox ( ) ; } decreaseIncreaseDate ( decrease ) { let date = this . selectedDate ; if ( this . utilService . isInitializedDate ( date ) ) { let d = this . getDate ( date . year , date . month , date . day ) ; d . setDate ( decrease ? d . getDate ( ) - 1 : d . getDate ( ) + 1 ) ; date = { year : d . getFullYear ( ) , month : d . getMonth ( ) + 1 , day : d . getDate ( ) } ; } else { date = this . getToday ( ) ; } this . selectDate ( date , CalToggle . CloseByCalBtn ) ; } selectDate ( date , closeReason ) { this . updateDateValue ( date ) ; if ( this . showSelector ) { this . calendarToggle . emit ( closeReason ) ; } this . removeGlobalListener ( ) ; this . showSelector = false ; this . setFocusToInputBox ( ) ; } setFocusToInputBox ( ) { if ( ! this . opts . inline && this . opts . showInputField ) { setTimeout ( ( ) => { this . inputBoxEl . nativeElement . focus ( ) ; } , 100 ) ; } } updateDateValue ( date ) { let clear = ! this . utilService . isInitializedDate ( date ) ; this . selectedDate = date ; this . emitDateChanged ( date ) ; if ( ! this . opts . inline ) { this . selectionDayTxt = clear ? "" : this . utilService . formatDate ( date , this . opts . dateFormat , this . opts . monthLabels ) ; this . inputFieldChanged . emit ( { value : this . selectionDayTxt , dateFormat : this . opts . dateFormat , valid : ! clear } ) ; this . invalidDate = false ; } } emitDateChanged ( date ) { if ( this . utilService . isInitializedDate ( date ) ) { let dateModel = this . getDateModel ( date ) ; this . dateChanged . emit ( dateModel ) ; this . onChangeCb ( dateModel ) ; this . onTouchedCb ( ) ; } else { this . dateChanged . emit ( { date : date , jsdate : null , formatted : "" , epoc : 0 } ) ; this . onChangeCb ( null ) ; this . onTouchedCb ( ) ; } } getDateModel ( date ) { return { date : date , jsdate : this . getDate ( date . year , date . month , date . day ) , formatted : this . utilService . formatDate ( date , this . opts . dateFormat , this . opts . monthLabels ) , epoc : Math . round ( this . getTimeInMilliseconds ( date ) / 1000.0 ) } ; } monthText ( m ) { return this . opts . monthLabels [ m ] ; } monthStartIdx ( y , m ) { let d = new Date ( ) ; d . setDate ( 1 ) ; d . setMonth ( m - 1 ) ; d . setFullYear ( y ) ; let idx = d . getDay ( ) + this . sundayIdx ( ) ; return idx >= 7 ? idx - 7 : idx ; } daysInMonth ( m , y ) { return new Date ( y , m , 0 ) . getDate ( ) ; } daysInPrevMonth ( m , y ) { let d = this . getDate ( y , m , 1 ) ; d . setMonth ( d . getMonth ( ) - 1 ) ; return this . daysInMonth ( d . getMonth ( ) + 1 , d . getFullYear ( ) ) ; } isCurrDay ( d , m , y , cmo , today ) { return d === today . day && m === today . month && y === today . year && cmo === this . currMonthId ; } getToday ( ) { let date = new Date ( ) ; return { year : date . getFullYear ( ) , month : date . getMonth ( ) + 1 , day : date . getDate ( ) } ; } getTimeInMilliseconds ( date ) { return this . getDate ( date . year , date . month , date . day ) . getTime ( ) ; } getWeekday ( date ) { let weekDays = this . utilService . getWeekDays ( ) ; return weekDays [ this . utilService . getDayNumber ( date ) ] ; } getDate ( year , month , day ) { return new Date ( year , month - 1 , day , 0 , 0 , 0 , 0 ) ; } sundayIdx ( ) { return this . dayIdx > 0 ? 7 - this . dayIdx : 0 ; } generateCalendar ( m , y , notifyChange ) { this . dates . length = 0 ; let today = this . getToday ( ) ; let monthStart = this . monthStartIdx ( y , m ) ; let dInThisM = this . daysInMonth ( m , y ) ; let dInPrevM = this . daysInPrevMonth ( m , y ) ; let dayNbr = 1 ; let cmo = this . prevMonthId ; for ( let i = 1 ; i < 7 ; i ++ ) { let week = [ ] ; if ( i === 1 ) { let pm = dInPrevM - monthStart + 1 ; for ( let j = pm ; j <= dInPrevM ; j ++ ) { let date = { year : m === 1 ? y - 1 : y , month : m === 1 ? 12 : m - 1 , day : j } ; week . push ( { dateObj : date , cmo : cmo , currDay : this . isCurrDay ( j , m , y , cmo , today ) , disabled : this . utilService . isDisabledDay ( date , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableWeekdays , this . opts . disableDays , this . opts . disableDateRanges , this . opts . enableDays ) , markedDate : this . utilService . isMarkedDate ( date , this . opts . markDates , this . opts . markWeekends ) , highlight : this . utilService . isHighlightedDate ( date , this . opts . sunHighlight , this . opts . satHighlight , this . opts . highlightDates ) } ) ; } cmo = this . currMonthId ; let daysLeft = 7 - week . length ; for ( let j = 0 ; j < daysLeft ; j ++ ) { let date = { year : y , month : m , day : dayNbr } ; week . push ( { dateObj : date , cmo : cmo , currDay : this . isCurrDay ( dayNbr , m , y , cmo , today ) , disabled : this . utilService . isDisabledDay ( date , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableWeekdays , this . opts . disableDays , this . opts . disableDateRanges , this . opts . enableDays ) , markedDate : this . utilService . isMarkedDate ( date , this . opts . markDates , this . opts . markWeekends ) , highlight : this . utilService . isHighlightedDate ( date , this . opts . sunHighlight , this . opts . satHighlight , this . opts . highlightDates ) } ) ; dayNbr ++ ; } } else { for ( let j = 1 ; j < 8 ; j ++ ) { if ( dayNbr > dInThisM ) { dayNbr = 1 ; cmo = this . nextMonthId ; } let date = { year : cmo === this . nextMonthId && m === 12 ? y + 1 : y , month : cmo === this . currMonthId ? m : cmo === this . nextMonthId && m < 12 ? m + 1 : 1 , day : dayNbr } ; week . push ( { dateObj : date , cmo : cmo , currDay : this . isCurrDay ( dayNbr , m , y , cmo , today ) , disabled : this . utilService . isDisabledDay ( date , this . opts . minYear , this . opts . maxYear , this . opts . disableUntil , this . opts . disableSince , this . opts . disableWeekends , this . opts . disableWeekdays , this . opts . disableDays , this . opts . disableDateRanges , this . opts . enableDays ) , markedDate : this . utilService . isMarkedDate ( date , this . opts . markDates , this . opts . markWeekends ) , highlight : this . utilService . isHighlightedDate ( date , this . opts . sunHighlight , this . opts . satHighlight , this . opts . highlightDates ) } ) ; dayNbr ++ ; } } let weekNbr = this . opts . showWeekNumbers && this . opts . firstDayOfWeek === "mo" ? this . utilService . getWeekNumber ( week [ 0 ] . dateObj ) : 0 ; this . dates . push ( { week : week , weekNbr : weekNbr } ) ; } this . setHeaderBtnDisabledState ( m , y ) ; if ( notifyChange ) { this . calendarViewChanged . emit ( { year : y , month : m , first : { number : 1 , weekday : this . getWeekday ( { year : y , month : m , day : 1 } ) } , last : { number : dInThisM , weekday : this . getWeekday ( { year : y , month : m , day : dInThisM } ) } } ) ; } } parseSelectedDate ( selDate ) { let date = { day : 0 , month : 0 , year : 0 } ; if ( typeof selDate === "string" ) { let sd = < string > selDate ; let df = this . opts . dateFormat ; let delimeters = this . utilService . getDateFormatDelimeters ( df ) ; let dateValue = this . utilService . getDateValue ( sd , df , delimeters ) ; date . year = this . utilService . getNumberByValue ( dateValue [ 0 ] ) ; date . month = df . indexOf ( MMM ) !== - 1 ? this . utilService . getMonthNumberByMonthName ( dateValue [ 1 ] , this . opts . monthLabels ) : this . utilService . getNumberByValue ( dateValue [ 1 ] ) ; date . day = this . utilService . getNumberByValue ( dateValue [ 2 ] ) ; } else if ( typeof selDate === "object" ) { date = selDate ; } this . selectionDayTxt = this . utilService . formatDate ( date , this . opts . dateFormat , this . opts . monthLabels ) ; return date ; } jsDateToMyDate ( date ) { return { year : date . getFullYear ( ) , month : date . getMonth ( ) + 1 , day : date . getDate ( ) } ; } parseSelectedMonth ( ms ) { return this . utilService . parseDefaultMonth ( ms ) ; } setHeaderBtnDisabledState ( m , y ) { let dpm = false ; let dpy = false ; let dnm = false ; let dny = false ; if ( this . opts . disableHeaderButtons ) { dpm = this . utilService . isMonthDisabledByDisableUntil ( { year : m === 1 ? y - 1 : y , month : m === 1 ? 12 : m - 1 , day : this . daysInMonth ( m === 1 ? 12 : m - 1 , m === 1 ? y - 1 : y ) } , this . opts . disableUntil ) ; dpy = this . utilService . isMonthDisabledByDisableUntil ( { year : y - 1 , month : m , day : this . daysInMonth ( m , y - 1 ) } , this . opts . disableUntil ) ; dnm = this . utilService . isMonthDisabledByDisableSince ( { year : m === 12 ? y + 1 : y , month : m === 12 ? 1 : m + 1 , day : 1 } , this . opts . disableSince ) ; dny = this . utilService . isMonthDisabledByDisableSince ( { year : y + 1 , month : m , day : 1 } , this . opts . disableSince ) ; } this . prevMonthDisabled = m === 1 && y === this . opts . minYear || dpm ; this . prevYearDisabled = y - 1 < this . opts . minYear || dpy ; this . nextMonthDisabled = m === 12 && y === this . opts . maxYear || dnm ; this . nextYearDisabled = y + 1 > this . opts . maxYear || dny ; } }