ionic2-calendar
Version:
Ionic calendar component
1 lines • 216 kB
Source Map (JSON)
{"version":3,"file":"ionic2-calendar.mjs","sources":["../../src/calendar.service.ts","../../src/monthview.ts","../../src/monthview.html","../../src/init-position-scroll.ts","../../src/weekview.ts","../../src/weekview.html","../../src/dayview.ts","../../src/dayview.html","../../src/calendar.interface.ts","../../src/calendar.ts","../../src/calendar.html","../../src/calendar.module.ts","../../src/ionic2-calendar.ts"],"sourcesContent":["import {Injectable} from '@angular/core';\r\nimport {Observable, Subject} from 'rxjs';\r\n\r\nimport {ICalendarComponent, IView, CalendarMode, QueryMode} from './calendar.interface';\r\n\r\n@Injectable()\r\nexport class CalendarService {\r\n queryMode!: QueryMode;\r\n currentDateChangedFromParent$: Observable<Date>;\r\n currentDateChangedFromChildren$: Observable<Date>;\r\n eventSourceChanged$: Observable<void>;\r\n slideChanged$: Observable<number>;\r\n slideUpdated$: Observable<void>;\r\n\r\n private _currentDate: Date = new Date();\r\n private currentDateChangedFromParent = new Subject<Date>();\r\n private currentDateChangedFromChildren = new Subject<Date>();\r\n private eventSourceChanged = new Subject<void>();\r\n private slideChanged = new Subject<number>();\r\n private slideUpdated = new Subject<void>();\r\n\r\n constructor() {\r\n this.currentDateChangedFromParent$ = this.currentDateChangedFromParent.asObservable();\r\n this.currentDateChangedFromChildren$ = this.currentDateChangedFromChildren.asObservable();\r\n this.eventSourceChanged$ = this.eventSourceChanged.asObservable();\r\n this.slideChanged$ = this.slideChanged.asObservable();\r\n this.slideUpdated$ = this.slideUpdated.asObservable();\r\n }\r\n\r\n setCurrentDate(val: Date, fromParent: boolean = false) {\r\n this._currentDate = new Date(val);\r\n if (fromParent) {\r\n this.currentDateChangedFromParent.next(val);\r\n } else {\r\n this.currentDateChangedFromChildren.next(val);\r\n }\r\n }\r\n\r\n get currentDate(): Date {\r\n return this._currentDate;\r\n }\r\n\r\n rangeChanged(component: ICalendarComponent) {\r\n if (this.queryMode === 'local') {\r\n if (component.eventSource && component.onDataLoaded) {\r\n component.onDataLoaded();\r\n }\r\n } else if (this.queryMode === 'remote') {\r\n let rangeStart = new Date(component.range.startTime.getTime()),\r\n rangeEnd = new Date(component.range.endTime.getTime());\r\n\r\n rangeStart.setHours(0);\r\n if (rangeStart.getHours() === 23) {\r\n rangeStart.setTime(rangeStart.getTime() + 3600000);\r\n }\r\n\r\n rangeEnd.setHours(0);\r\n if (rangeEnd.getHours() === 23) {\r\n rangeEnd.setTime(rangeEnd.getTime() + 3600000);\r\n }\r\n component.onRangeChanged.emit({\r\n startTime: rangeStart,\r\n endTime: rangeEnd\r\n });\r\n }\r\n }\r\n\r\n private getStep(mode: CalendarMode): { years: number; months: number; days: number; } {\r\n switch (mode) {\r\n case 'month':\r\n return {\r\n years: 0,\r\n months: 1,\r\n days: 0\r\n };\r\n case 'week':\r\n return {\r\n years: 0,\r\n months: 0,\r\n days: 7\r\n };\r\n case 'day':\r\n return {\r\n years: 0,\r\n months: 0,\r\n days: 1\r\n };\r\n }\r\n }\r\n\r\n getAdjacentCalendarDate(mode: CalendarMode, direction: number): Date {\r\n let calculateCalendarDate = this.currentDate;\r\n const step = this.getStep(mode),\r\n year = calculateCalendarDate.getFullYear() + direction * step.years,\r\n month = calculateCalendarDate.getMonth() + direction * step.months,\r\n date = calculateCalendarDate.getDate() + direction * step.days;\r\n\r\n calculateCalendarDate = new Date(year, month, date, 12, 0, 0);\r\n\r\n if (mode === 'month') {\r\n const firstDayInNextMonth = new Date(year, month + 1, 1, 12, 0, 0);\r\n if (firstDayInNextMonth.getTime() <= calculateCalendarDate.getTime()) {\r\n calculateCalendarDate = new Date(firstDayInNextMonth.getTime() - 24 * 60 * 60 * 1000);\r\n }\r\n }\r\n return calculateCalendarDate;\r\n }\r\n\r\n getAdjacentViewStartTime(component: ICalendarComponent, direction: number): Date {\r\n let adjacentCalendarDate = this.getAdjacentCalendarDate(component.mode, direction);\r\n return component.getRange(adjacentCalendarDate).startTime;\r\n }\r\n\r\n populateAdjacentViews(component: ICalendarComponent) {\r\n let currentViewStartDate: Date,\r\n currentViewData: IView[],\r\n toUpdateViewIndex: number,\r\n currentViewIndex = component.currentViewIndex;\r\n\r\n if (component.direction === 1) {\r\n currentViewStartDate = this.getAdjacentViewStartTime(component, 1);\r\n toUpdateViewIndex = (currentViewIndex + 1) % 3;\r\n component.views[toUpdateViewIndex] = component.getViewData(currentViewStartDate);\r\n } else if (component.direction === -1) {\r\n currentViewStartDate = this.getAdjacentViewStartTime(component, -1);\r\n toUpdateViewIndex = (currentViewIndex + 2) % 3;\r\n component.views[toUpdateViewIndex] = component.getViewData(currentViewStartDate);\r\n } else {\r\n if (!component.views) {\r\n currentViewData = [];\r\n currentViewStartDate = component.range.startTime;\r\n currentViewData.push(component.getViewData(currentViewStartDate));\r\n currentViewStartDate = this.getAdjacentViewStartTime(component, 1);\r\n currentViewData.push(component.getViewData(currentViewStartDate));\r\n currentViewStartDate = this.getAdjacentViewStartTime(component, -1);\r\n currentViewData.push(component.getViewData(currentViewStartDate));\r\n component.views = currentViewData;\r\n } else {\r\n currentViewStartDate = component.range.startTime;\r\n component.views[currentViewIndex] = component.getViewData(currentViewStartDate);\r\n currentViewStartDate = this.getAdjacentViewStartTime(component, -1);\r\n toUpdateViewIndex = (currentViewIndex + 2) % 3;\r\n component.views[toUpdateViewIndex] = component.getViewData(currentViewStartDate);\r\n currentViewStartDate = this.getAdjacentViewStartTime(component, 1);\r\n toUpdateViewIndex = (currentViewIndex + 1) % 3;\r\n component.views[toUpdateViewIndex] = component.getViewData(currentViewStartDate);\r\n }\r\n }\r\n }\r\n\r\n loadEvents() {\r\n this.eventSourceChanged.next();\r\n }\r\n\r\n slide(direction: number) {\r\n this.slideChanged.next(direction);\r\n }\r\n\r\n update() {\r\n this.slideUpdated.next();\r\n }\r\n}\r\n","import {\r\n Component,\r\n OnInit,\r\n OnChanges,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n SimpleChanges,\r\n TemplateRef,\r\n OnDestroy,\r\n AfterViewInit,\r\n NgZone,\r\n ViewChild,\r\n ElementRef\r\n} from '@angular/core';\r\nimport {Subscription} from 'rxjs';\r\nimport {DatePipe} from '@angular/common';\r\nimport {Swiper} from 'swiper';\r\nimport {SwiperOptions} from 'swiper/types';\r\n\r\nimport {ICalendarComponent, IEvent, IMonthView, IMonthViewRow, ITimeSelected, IRange, CalendarMode, IDateFormatter, IMonthViewDisplayEventTemplateContext} from './calendar.interface';\r\nimport {CalendarService} from './calendar.service';\r\n\r\n@Component({\r\n selector: 'monthview',\r\n templateUrl: './monthview.html',\r\n styleUrls: ['./monthview.css'],\r\n standalone: false,\r\n})\r\nexport class MonthViewComponent implements ICalendarComponent, OnInit, OnDestroy, OnChanges, AfterViewInit {\r\n\r\n constructor(private calendarService: CalendarService, private zone:NgZone) {\r\n } \r\n\r\n private slider!: Swiper;\r\n @ViewChild('monthViewSwiper') swiperElement?: ElementRef;\r\n\r\n @Input() monthviewDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>;\r\n @Input() monthviewInactiveDisplayEventTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>;\r\n @Input() monthviewEventDetailTemplate!: TemplateRef<IMonthViewDisplayEventTemplateContext>;\r\n\r\n @Input() formatDay?: string;\r\n @Input() formatDayHeader?: string;\r\n @Input() formatMonthTitle?: string;\r\n @Input() eventSource!: IEvent[];\r\n @Input() startingDayMonth!: number;\r\n @Input() showEventDetail?: boolean;\r\n @Input() noEventsLabel?: string;\r\n @Input() autoSelect = true;\r\n @Input() markDisabled?: (date: Date) => boolean;\r\n @Input() locale!: string;\r\n @Input() dateFormatter?: IDateFormatter;\r\n @Input() dir = '';\r\n @Input() lockSwipeToPrev?: boolean = false;\r\n @Input() lockSwipeToNext?: boolean = false;\r\n @Input() lockSwipes?: boolean = false;\r\n @Input() sliderOptions?: SwiperOptions;\r\n\r\n @Output() onRangeChanged = new EventEmitter<IRange>();\r\n @Output() onEventSelected = new EventEmitter<IEvent>();\r\n @Output() onTimeSelected = new EventEmitter<ITimeSelected>();\r\n @Output() onTitleChanged = new EventEmitter<string>();\r\n\r\n public views: IMonthView[] = [];\r\n public currentViewIndex = 0;\r\n public selectedDate?: IMonthViewRow;\r\n public range!: IRange;\r\n public mode: CalendarMode = 'month';\r\n public direction = 0;\r\n\r\n private moveOnSelected = false;\r\n private inited = false;\r\n\r\n private currentDateChangedFromParentSubscription?: Subscription;\r\n private eventSourceChangedSubscription?: Subscription;\r\n private slideChangedSubscription?: Subscription;\r\n private slideUpdatedSubscription?: Subscription;\r\n\r\n private formatDayLabel!: (date: Date) => string;\r\n private formatDayHeaderLabel!: (date: Date) => string;\r\n private formatTitle!: (date: Date) => string;\r\n\r\n static getDates(startDate: Date, n: number): Date[] {\r\n const dates = new Array(n),\r\n current = new Date(startDate.getTime());\r\n let i = 0;\r\n while (i < n) {\r\n dates[i++] = new Date(current.getTime());\r\n current.setDate(current.getDate() + 1);\r\n }\r\n return dates;\r\n }\r\n\r\n ngOnInit() {\r\n if (!this.sliderOptions) {\r\n this.sliderOptions = {};\r\n }\r\n this.sliderOptions.loop = true;\r\n this.sliderOptions.allowSlidePrev = !this.lockSwipeToPrev;\r\n this.sliderOptions.allowSlideNext = !this.lockSwipeToNext;\r\n this.sliderOptions.allowTouchMove = !this.lockSwipes;\r\n\r\n if (this.dateFormatter && this.dateFormatter.formatMonthViewDay) {\r\n this.formatDayLabel = this.dateFormatter.formatMonthViewDay;\r\n } else {\r\n const dayLabelDatePipe = new DatePipe('en-US');\r\n this.formatDayLabel = function(date: Date) {\r\n return dayLabelDatePipe.transform(date, this.formatDay)||'';\r\n };\r\n }\r\n\r\n if (this.dateFormatter && this.dateFormatter.formatMonthViewDayHeader) {\r\n this.formatDayHeaderLabel = this.dateFormatter.formatMonthViewDayHeader;\r\n } else {\r\n const datePipe = new DatePipe(this.locale);\r\n this.formatDayHeaderLabel = function(date: Date) {\r\n return datePipe.transform(date, this.formatDayHeader)||'';\r\n };\r\n }\r\n\r\n if (this.dateFormatter && this.dateFormatter.formatMonthViewTitle) {\r\n this.formatTitle = this.dateFormatter.formatMonthViewTitle;\r\n } else {\r\n const datePipe = new DatePipe(this.locale);\r\n this.formatTitle = function(date: Date) {\r\n return datePipe.transform(date, this.formatMonthTitle)||'';\r\n };\r\n }\r\n\r\n this.refreshView();\r\n this.inited = true;\r\n\r\n this.currentDateChangedFromParentSubscription = this.calendarService.currentDateChangedFromParent$.subscribe(currentDate => {\r\n this.refreshView();\r\n });\r\n\r\n this.eventSourceChangedSubscription = this.calendarService.eventSourceChanged$.subscribe(() => {\r\n this.onDataLoaded();\r\n });\r\n\r\n this.slideChangedSubscription = this.calendarService.slideChanged$.subscribe(direction => {\r\n if (direction === 1) {\r\n this.slider.slideNext();\r\n } else if (direction === -1) {\r\n this.slider.slidePrev();\r\n }\r\n });\r\n\r\n this.slideUpdatedSubscription = this.calendarService.slideUpdated$.subscribe(() => {\r\n this.slider.update();\r\n });\r\n }\r\n\r\n ngOnDestroy() {\r\n if (this.currentDateChangedFromParentSubscription) {\r\n this.currentDateChangedFromParentSubscription.unsubscribe();\r\n this.currentDateChangedFromParentSubscription = undefined;\r\n }\r\n\r\n if (this.eventSourceChangedSubscription) {\r\n this.eventSourceChangedSubscription.unsubscribe();\r\n this.eventSourceChangedSubscription = undefined;\r\n }\r\n\r\n if (this.slideChangedSubscription) {\r\n this.slideChangedSubscription.unsubscribe();\r\n this.slideChangedSubscription = undefined;\r\n }\r\n\r\n if (this.slideUpdatedSubscription) {\r\n this.slideUpdatedSubscription.unsubscribe();\r\n this.slideUpdatedSubscription = undefined;\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges) {\r\n if (!this.inited) {\r\n return;\r\n }\r\n\r\n const eventSourceChange = changes['eventSource'];\r\n if (eventSourceChange && eventSourceChange.currentValue) {\r\n this.onDataLoaded();\r\n }\r\n\r\n const lockSwipeToPrev = changes['lockSwipeToPrev'];\r\n if (lockSwipeToPrev) {\r\n this.slider.allowSlidePrev = !lockSwipeToPrev.currentValue;\r\n }\r\n\r\n const lockSwipeToNext = changes['lockSwipeToNext'];\r\n if (lockSwipeToNext) {\r\n this.slider.allowSlideNext = !lockSwipeToNext.currentValue;\r\n }\r\n\r\n const lockSwipes = changes['lockSwipes'];\r\n if (lockSwipes) {\r\n this.slider.allowTouchMove = !lockSwipes.currentValue;\r\n }\r\n }\r\n\r\n ngAfterViewInit() {\r\n this.slider = new Swiper(this.swiperElement?.nativeElement, this.sliderOptions);\r\n let me = this;\r\n this.slider.on('slideNextTransitionEnd', function() {\r\n me.onSlideChanged(1);\r\n });\r\n\r\n this.slider.on('slidePrevTransitionEnd', function() {\r\n me.onSlideChanged(-1);\r\n });\r\n\r\n if(this.dir == 'rtl') {\r\n this.slider.changeLanguageDirection('rtl');\r\n }\r\n\r\n const title = this.getTitle();\r\n this.onTitleChanged.emit(title);\r\n }\r\n\r\n setSwiperInstance(swiper: any) {\r\n this.slider = swiper;\r\n }\r\n\r\n onSlideChanged(direction: number) {\r\n this.currentViewIndex = (this.currentViewIndex + direction + 3) % 3;\r\n this.move(direction);\r\n }\r\n\r\n move(direction: number) {\r\n if (direction === 0) {\r\n return;\r\n }\r\n\r\n this.direction = direction;\r\n if (!this.moveOnSelected) {\r\n const adjacentDate = this.calendarService.getAdjacentCalendarDate(this.mode, direction);\r\n this.calendarService.setCurrentDate(adjacentDate);\r\n }\r\n this.refreshView();\r\n this.direction = 0;\r\n this.moveOnSelected = false;\r\n }\r\n\r\n createDateObject(date: Date): IMonthViewRow {\r\n let disabled = false;\r\n if (this.markDisabled) {\r\n disabled = this.markDisabled(date);\r\n }\r\n\r\n return {\r\n date,\r\n events: [],\r\n label: this.formatDayLabel(date),\r\n secondary: false,\r\n disabled\r\n };\r\n }\r\n\r\n getViewData(startTime: Date): IMonthView {\r\n const startDate = startTime,\r\n date = startDate.getDate(),\r\n month = (startDate.getMonth() + (date !== 1 ? 1 : 0)) % 12;\r\n\r\n const dates = MonthViewComponent.getDates(startDate, 42);\r\n const days: IMonthViewRow[] = [];\r\n for (let i = 0; i < 42; i++) {\r\n const dateObject = this.createDateObject(dates[i]);\r\n dateObject.secondary = dates[i].getMonth() !== month;\r\n days[i] = dateObject;\r\n }\r\n\r\n const dayHeaders: string[] = [];\r\n for (let i = 0; i < 7; i++) {\r\n dayHeaders.push(this.formatDayHeaderLabel(days[i].date));\r\n }\r\n return {\r\n dates: days,\r\n dayHeaders\r\n };\r\n }\r\n\r\n getHighlightClass(date: IMonthViewRow): string {\r\n let className = '';\r\n\r\n if (date.hasEvent) {\r\n if (date.secondary) {\r\n className = 'monthview-secondary-with-event';\r\n } else {\r\n className = 'monthview-primary-with-event';\r\n }\r\n }\r\n\r\n if (date.selected) {\r\n if (className) {\r\n className += ' ';\r\n }\r\n className += 'monthview-selected';\r\n }\r\n\r\n if (date.current) {\r\n if (className) {\r\n className += ' ';\r\n }\r\n className += 'monthview-current';\r\n }\r\n\r\n if (date.secondary) {\r\n if (className) {\r\n className += ' ';\r\n }\r\n className += 'text-muted';\r\n }\r\n\r\n if (date.disabled) {\r\n if (className) {\r\n className += ' ';\r\n }\r\n className += 'monthview-disabled';\r\n }\r\n return className;\r\n }\r\n\r\n getRange(currentDate: Date): IRange {\r\n const year = currentDate.getFullYear(),\r\n month = currentDate.getMonth(),\r\n firstDayOfMonth = new Date(year, month, 1, 12, 0, 0), // set hour to 12 to avoid DST problem\r\n\r\n difference = this.startingDayMonth - firstDayOfMonth.getDay(),\r\n numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,\r\n startDate = new Date(firstDayOfMonth.getTime());\r\n\r\n if (numDisplayedFromPreviousMonth > 0) {\r\n startDate.setDate(-numDisplayedFromPreviousMonth + 1);\r\n }\r\n\r\n const endDate = new Date(startDate.getTime());\r\n endDate.setDate(endDate.getDate() + 42);\r\n\r\n return {\r\n startTime: startDate,\r\n endTime: endDate\r\n };\r\n }\r\n\r\n onDataLoaded() {\r\n const range = this.range,\r\n eventSource = this.eventSource,\r\n len = eventSource ? eventSource.length : 0,\r\n startTime = range.startTime,\r\n endTime = range.endTime,\r\n utcStartTime = Date.UTC(startTime.getFullYear(), startTime.getMonth(), startTime.getDate()),\r\n utcEndTime = Date.UTC(endTime.getFullYear(), endTime.getMonth(), endTime.getDate()),\r\n currentViewIndex = this.currentViewIndex,\r\n dates = this.views[currentViewIndex].dates,\r\n oneDay = 86400000,\r\n eps = 0.0006;\r\n\r\n for (let r = 0; r < 42; r += 1) {\r\n if (dates[r].hasEvent) {\r\n dates[r].hasEvent = false;\r\n dates[r].events = [];\r\n }\r\n }\r\n\r\n for (let i = 0; i < len; i += 1) {\r\n const event = eventSource[i],\r\n eventStartTime = event.startTime,\r\n eventEndTime = event.endTime;\r\n\r\n let eventUTCStartTime: number,\r\n eventUTCEndTime: number;\r\n if (event.allDay) {\r\n eventUTCStartTime = eventStartTime.getTime();\r\n eventUTCEndTime = eventEndTime.getTime();\r\n } else {\r\n eventUTCStartTime = Date.UTC(eventStartTime.getFullYear(), eventStartTime.getMonth(), eventStartTime.getDate());\r\n eventUTCEndTime = Date.UTC(eventEndTime.getFullYear(), eventEndTime.getMonth(), eventEndTime.getDate() + 1);\r\n }\r\n\r\n if (eventUTCEndTime <= utcStartTime || eventUTCStartTime >= utcEndTime) {\r\n continue;\r\n }\r\n\r\n let timeDifferenceStart: number,\r\n timeDifferenceEnd: number;\r\n\r\n if (eventUTCStartTime < utcStartTime) {\r\n timeDifferenceStart = 0;\r\n } else {\r\n timeDifferenceStart = (eventUTCStartTime - utcStartTime) / oneDay;\r\n }\r\n\r\n if (eventUTCEndTime > utcEndTime) {\r\n timeDifferenceEnd = (utcEndTime - utcStartTime) / oneDay;\r\n } else {\r\n timeDifferenceEnd = (eventUTCEndTime - utcStartTime) / oneDay;\r\n }\r\n\r\n let index = Math.floor(timeDifferenceStart);\r\n const endIndex = Math.ceil(timeDifferenceEnd - eps);\r\n while (index < endIndex) {\r\n dates[index].hasEvent = true;\r\n let eventSet = dates[index].events;\r\n if (eventSet) {\r\n eventSet.push(event);\r\n } else {\r\n eventSet = [];\r\n eventSet.push(event);\r\n dates[index].events = eventSet;\r\n }\r\n index += 1;\r\n }\r\n }\r\n\r\n for (let r = 0; r < 42; r += 1) {\r\n if (dates[r].hasEvent) {\r\n dates[r].events.sort(this.compareEvent);\r\n }\r\n }\r\n\r\n if (this.autoSelect) {\r\n let findSelected = false;\r\n for (let r = 0; r < 42; r += 1) {\r\n if (dates[r].selected) {\r\n this.selectedDate = dates[r];\r\n findSelected = true;\r\n break;\r\n }\r\n }\r\n\r\n if (findSelected && this.selectedDate) {\r\n this.onTimeSelected.emit({\r\n selectedTime: this.selectedDate.date,\r\n events: this.selectedDate.events,\r\n disabled: this.selectedDate.disabled\r\n });\r\n }\r\n }\r\n }\r\n\r\n refreshView() {\r\n this.range = this.getRange(this.calendarService.currentDate);\r\n\r\n if (this.inited) {\r\n const title = this.getTitle();\r\n this.onTitleChanged.emit(title);\r\n }\r\n this.calendarService.populateAdjacentViews(this);\r\n this.updateCurrentView(this.range.startTime, this.views[this.currentViewIndex]);\r\n this.calendarService.rangeChanged(this);\r\n }\r\n\r\n getTitle(): string {\r\n const currentViewStartDate = this.range.startTime,\r\n date = currentViewStartDate.getDate(),\r\n month = (currentViewStartDate.getMonth() + (date !== 1 ? 1 : 0)) % 12,\r\n year = currentViewStartDate.getFullYear() + (date !== 1 && month === 0 ? 1 : 0),\r\n headerDate = new Date(year, month, 1, 12, 0, 0, 0);\r\n return this.formatTitle(headerDate);\r\n }\r\n\r\n private compareEvent(event1: IEvent, event2: IEvent): number {\r\n if (event1.allDay) {\r\n return 1;\r\n } else if (event2.allDay) {\r\n return -1;\r\n } else {\r\n return (event1.startTime.getTime() - event2.startTime.getTime());\r\n }\r\n }\r\n\r\n select(viewDate: IMonthViewRow) {\r\n if (!this.views) {\r\n return;\r\n }\r\n\r\n const selectedDate = viewDate.date,\r\n events = viewDate.events;\r\n\r\n if (!viewDate.disabled) {\r\n const dates = this.views[this.currentViewIndex].dates,\r\n currentCalendarDate = this.calendarService.currentDate,\r\n currentMonth = currentCalendarDate.getMonth(),\r\n currentYear = currentCalendarDate.getFullYear(),\r\n selectedMonth = selectedDate.getMonth(),\r\n selectedYear = selectedDate.getFullYear();\r\n let direction = 0;\r\n\r\n if (currentYear === selectedYear) {\r\n if (currentMonth !== selectedMonth) {\r\n direction = currentMonth < selectedMonth ? 1 : -1;\r\n }\r\n } else {\r\n direction = currentYear < selectedYear ? 1 : -1;\r\n }\r\n\r\n this.calendarService.setCurrentDate(selectedDate);\r\n if (direction === 0) {\r\n const currentViewStartDate = this.range.startTime,\r\n oneDay = 86400000,\r\n selectedDayDifference = Math.round((Date.UTC(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate()) - Date.UTC(currentViewStartDate.getFullYear(), currentViewStartDate.getMonth(), currentViewStartDate.getDate())) / oneDay);\r\n\r\n for (let r = 0; r < 42; r += 1) {\r\n dates[r].selected = false;\r\n }\r\n\r\n if (selectedDayDifference >= 0 && selectedDayDifference < 42) {\r\n dates[selectedDayDifference].selected = true;\r\n this.selectedDate = dates[selectedDayDifference];\r\n }\r\n } else {\r\n this.moveOnSelected = true;\r\n this.slideView(direction);\r\n }\r\n }\r\n\r\n this.onTimeSelected.emit({selectedTime: selectedDate, events, disabled: viewDate.disabled});\r\n }\r\n\r\n slideView(direction: number) {\r\n if (direction === 1) {\r\n this.slider.slideNext();\r\n } else if (direction === -1) {\r\n this.slider.slidePrev();\r\n }\r\n }\r\n\r\n updateCurrentView(currentViewStartDate: Date, view: IMonthView) {\r\n const currentCalendarDate = this.calendarService.currentDate,\r\n today = new Date(),\r\n oneDay = 86400000,\r\n selectedDayDifference = Math.round((Date.UTC(currentCalendarDate.getFullYear(), currentCalendarDate.getMonth(), currentCalendarDate.getDate()) - Date.UTC(currentViewStartDate.getFullYear(), currentViewStartDate.getMonth(), currentViewStartDate.getDate())) / oneDay),\r\n currentDayDifference = Math.round((Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()) - Date.UTC(currentViewStartDate.getFullYear(), currentViewStartDate.getMonth(), currentViewStartDate.getDate())) / oneDay);\r\n\r\n for (let r = 0; r < 42; r += 1) {\r\n view.dates[r].selected = false;\r\n }\r\n\r\n if (selectedDayDifference >= 0 && selectedDayDifference < 42 && !view.dates[selectedDayDifference].disabled && (this.autoSelect || this.moveOnSelected)) {\r\n view.dates[selectedDayDifference].selected = true;\r\n this.selectedDate = view.dates[selectedDayDifference];\r\n } else {\r\n this.selectedDate = undefined;\r\n }\r\n\r\n if (currentDayDifference >= 0 && currentDayDifference < 42) {\r\n view.dates[currentDayDifference].current = true;\r\n }\r\n }\r\n\r\n eventSelected(event: IEvent) {\r\n this.onEventSelected.emit(event);\r\n }\r\n}\r\n","<div class=\"swiper monthview-swiper\" #monthViewSwiper>\r\n <div class=\"swiper-wrapper\">\r\n <div class=\"swiper-slide\">\r\n <table *ngIf=\"0===currentViewIndex\" class=\"table table-bordered table-fixed monthview-datetable\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let dayHeader of views[0].dayHeaders\">\r\n <small>{{dayHeader}}</small>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of [0,1,2,3,4,5]\">\r\n <td *ngFor=\"let col of [0,1,2,3,4,5,6]\" tappable (click)=\"select(views[0].dates[row*7+col])\"\r\n [ngClass]=\"getHighlightClass(views[0].dates[row*7+col])\">\r\n <ng-template [ngTemplateOutlet]=\"monthviewDisplayEventTemplate\"\r\n [ngTemplateOutletContext]=\"{view: views[0], row: row, col: col}\">\r\n </ng-template>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n <table *ngIf=\"0!==currentViewIndex\" class=\"table table-bordered table-fixed monthview-datetable\">\r\n <thead>\r\n <tr class=\"text-center\">\r\n <th *ngFor=\"let dayHeader of views[0].dayHeaders\">\r\n <small>{{dayHeader}}</small>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of [0,1,2,3,4,5]\">\r\n <td *ngFor=\"let col of [0,1,2,3,4,5,6]\">\r\n <ng-template [ngTemplateOutlet]=\"monthviewInactiveDisplayEventTemplate\"\r\n [ngTemplateOutletContext]=\"{view: views[0], row: row, col: col}\">\r\n </ng-template>\r\n </td>\r\n <tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n <div class=\"swiper-slide\">\r\n <table *ngIf=\"1===currentViewIndex\" class=\"table table-bordered table-fixed monthview-datetable\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let dayHeader of views[1].dayHeaders\">\r\n <small>{{dayHeader}}</small>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of [0,1,2,3,4,5]\">\r\n <td *ngFor=\"let col of [0,1,2,3,4,5,6]\" tappable (click)=\"select(views[1].dates[row*7+col])\"\r\n [ngClass]=\"getHighlightClass(views[1].dates[row*7+col])\">\r\n <ng-template [ngTemplateOutlet]=\"monthviewDisplayEventTemplate\"\r\n [ngTemplateOutletContext]=\"{view: views[1], row: row, col: col}\">\r\n </ng-template>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n <table *ngIf=\"1!==currentViewIndex\" class=\"table table-bordered table-fixed monthview-datetable\">\r\n <thead>\r\n <tr class=\"text-center\">\r\n <th *ngFor=\"let dayHeader of views[1].dayHeaders\">\r\n <small>{{dayHeader}}</small>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of [0,1,2,3,4,5]\">\r\n <td *ngFor=\"let col of [0,1,2,3,4,5,6]\">\r\n <ng-template [ngTemplateOutlet]=\"monthviewInactiveDisplayEventTemplate\"\r\n [ngTemplateOutletContext]=\"{view: views[1], row: row, col: col}\">\r\n </ng-template>\r\n </td>\r\n <tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n <div class=\"swiper-slide\">\r\n <table *ngIf=\"2===currentViewIndex\" class=\"table table-bordered table-fixed monthview-datetable\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let dayHeader of views[2].dayHeaders\">\r\n <small>{{dayHeader}}</small>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of [0,1,2,3,4,5]\">\r\n <td *ngFor=\"let col of [0,1,2,3,4,5,6]\" tappable (click)=\"select(views[2].dates[row*7+col])\"\r\n [ngClass]=\"getHighlightClass(views[2].dates[row*7+col])\">\r\n <ng-template [ngTemplateOutlet]=\"monthviewDisplayEventTemplate\"\r\n [ngTemplateOutletContext]=\"{view: views[2], row: row, col: col}\">\r\n </ng-template>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n <table *ngIf=\"2!==currentViewIndex\" class=\"table table-bordered table-fixed monthview-datetable\">\r\n <thead>\r\n <tr class=\"text-center\">\r\n <th *ngFor=\"let dayHeader of views[2].dayHeaders\">\r\n <small>{{dayHeader}}</small>\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of [0,1,2,3,4,5]\">\r\n <td *ngFor=\"let col of [0,1,2,3,4,5,6]\">\r\n <ng-template [ngTemplateOutlet]=\"monthviewInactiveDisplayEventTemplate\"\r\n [ngTemplateOutletContext]=\"{view: views[2], row: row, col: col}\">\r\n </ng-template>\r\n </td>\r\n <tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n </div>\r\n <ng-template [ngTemplateOutlet]=\"monthviewEventDetailTemplate\"\r\n [ngTemplateOutletContext]=\"{showEventDetail:showEventDetail, selectedDate: selectedDate, noEventsLabel: noEventsLabel}\">\r\n </ng-template>\r\n</div>","import {\r\n Component,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n ElementRef,\r\n SimpleChanges,\r\n OnChanges,\r\n AfterViewInit,\r\n OnDestroy,\r\n ViewEncapsulation,\r\n NgZone\r\n} from '@angular/core';\r\n\r\n@Component({\r\n selector: 'init-position-scroll',\r\n template: `\r\n <div class=\"scroll-content\" style=\"height:100%\">\r\n <ng-content></ng-content>\r\n </div>\r\n `,\r\n styles: [`\r\n .scroll-content {\r\n overflow-y: auto;\r\n overflow-x: hidden;\r\n } \r\n `],\r\n encapsulation: ViewEncapsulation.None,\r\n standalone: false\r\n})\r\nexport class initPositionScrollComponent implements OnChanges, AfterViewInit, OnDestroy {\r\n @Input() initPosition!:number;\r\n @Input() emitEvent?:boolean;\r\n @Output() onScroll = new EventEmitter<number>();\r\n\r\n private element:ElementRef;\r\n private scrollContent!:HTMLElement;\r\n private handler!:()=>void;\r\n private listenerAttached:boolean = false;\r\n\r\n constructor(el:ElementRef, private ngZone: NgZone) {\r\n this.element = el;\r\n }\r\n\r\n ngOnChanges(changes:SimpleChanges) {\r\n let initPosition = changes['initPosition'];\r\n if (initPosition && initPosition.currentValue !== undefined && this.scrollContent && initPosition.currentValue != this.scrollContent.scrollTop) {\r\n const me =this;\r\n this.ngZone.run(() => {\r\n me.scrollContent.scrollTop = initPosition.currentValue;\r\n });\r\n }\r\n }\r\n\r\n ngAfterViewInit() {\r\n const scrollContent = this.scrollContent = this.element.nativeElement.querySelector('.scroll-content');\r\n if (this.initPosition !== undefined) {\r\n scrollContent.scrollTop = this.initPosition;\r\n }\r\n\r\n if (this.emitEvent && !this.listenerAttached) {\r\n let onScroll = this.onScroll;\r\n let me = this;\r\n this.handler = function () {\r\n if(me.initPosition != scrollContent.scrollTop) {\r\n onScroll.emit(scrollContent.scrollTop);\r\n }\r\n };\r\n this.listenerAttached = true;\r\n scrollContent.addEventListener('scroll', this.handler);\r\n }\r\n }\r\n\r\n ngOnDestroy() {\r\n if (this.listenerAttached) {\r\n this.scrollContent.removeEventListener('scroll', this.handler);\r\n this.listenerAttached = false;\r\n }\r\n }\r\n}\r\n","import {DatePipe} from '@angular/common';\r\nimport {\r\n Component,\r\n OnInit,\r\n OnChanges,\r\n HostBinding,\r\n Input,\r\n Output,\r\n EventEmitter,\r\n SimpleChanges,\r\n ViewEncapsulation,\r\n TemplateRef,\r\n ElementRef,\r\n OnDestroy, \r\n AfterViewInit,\r\n NgZone,\r\n ViewChild\r\n} from '@angular/core';\r\nimport {Subscription} from 'rxjs';\r\nimport {Swiper} from 'swiper';\r\nimport {SwiperOptions} from 'swiper/types';\r\n\r\nimport type {\r\n ICalendarComponent,\r\n IDisplayEvent,\r\n IEvent,\r\n ITimeSelected,\r\n IRange,\r\n IWeekView,\r\n IWeekViewRow,\r\n IWeekViewDateRow,\r\n CalendarMode,\r\n IDateFormatter,\r\n IDisplayWeekViewHeader,\r\n IDisplayAllDayEvent,\r\n IWeekViewAllDayEventSectionTemplateContext,\r\n IWeekViewNormalEventSectionTemplateContext\r\n} from './calendar.interface';\r\nimport {CalendarService} from './calendar.service';\r\n\r\n@Component({\r\n selector: 'weekview',\r\n templateUrl: './weekview.html',\r\n styleUrls: ['./weekview.css'],\r\n encapsulation: ViewEncapsulation.None,\r\n standalone: false\r\n})\r\nexport class WeekViewComponent implements ICalendarComponent, OnInit, OnChanges, OnDestroy, AfterViewInit {\r\n\r\n constructor(private calendarService: CalendarService, private elm: ElementRef, private zone: NgZone) {\r\n }\r\n\r\n private slider!: Swiper;\r\n @ViewChild('weekViewSwiper') swiperElement?: ElementRef;\r\n\r\n @HostBinding('class.weekview') class = true;\r\n\r\n @Input() weekviewHeaderTemplate!: TemplateRef<IDisplayWeekViewHeader>;\r\n @Input() weekviewAllDayEventTemplate!: TemplateRef<IDisplayAllDayEvent>;\r\n @Input() weekviewNormalEventTemplate!: TemplateRef<IDisplayEvent>;\r\n @Input() weekviewAllDayEventSectionTemplate!: TemplateRef<IWeekViewAllDayEventSectionTemplateContext>;\r\n @Input() weekviewNormalEventSectionTemplate!: TemplateRef<IWeekViewNormalEventSectionTemplateContext>;\r\n @Input() weekviewInactiveAllDayEventSectionTemplate!: TemplateRef<IWeekViewAllDayEventSectionTemplateContext>;\r\n @Input() weekviewInactiveNormalEventSectionTemplate!: TemplateRef<IWeekViewNormalEventSectionTemplateContext>;\r\n\r\n @Input() formatWeekTitle?: string;\r\n @Input() formatWeekViewDayHeader?: string;\r\n @Input() formatHourColumn?: string;\r\n @Input() startingDayWeek!: number;\r\n @Input() allDayLabel?: string;\r\n @Input() hourParts!: number;\r\n @Input() eventSource!: IEvent[];\r\n @Input() autoSelect = true;\r\n @Input() markDisabled?: (date: Date) => boolean;\r\n @Input() locale!: string;\r\n @Input() dateFormatter?: IDateFormatter;\r\n @Input() dir = '';\r\n @Input() scrollToHour = 0;\r\n @Input() preserveScrollPosition?: boolean;\r\n @Input() lockSwipeToPrev?: boolean;\r\n @Input() lockSwipeToNext?: boolean;\r\n @Input() lockSwipes?: boolean;\r\n @Input() startHour!: number;\r\n @Input() endHour!: number;\r\n @Input() sliderOptions?: SwiperOptions;\r\n @Input() hourSegments!: number;\r\n\r\n @Output() onRangeChanged = new EventEmitter<IRange>();\r\n @Output() onEventSelected = new EventEmitter<IEvent>();\r\n @Output() onTimeSelected = new EventEmitter<ITimeSelected>();\r\n @Output() onDayHeaderSelected = new EventEmitter<ITimeSelected>();\r\n @Output() onTitleChanged = new EventEmitter<string>();\r\n\r\n public readonly sliderIndexList = [0, 1, 2];\r\n public views: IWeekView[] = [];\r\n public currentViewIndex = 0;\r\n public range!: IRange;\r\n public direction = 0;\r\n public mode: CalendarMode = 'week';\r\n\r\n private inited = false;\r\n private currentDateChangedFromParentSubscription?: Subscription;\r\n private eventSourceChangedSubscription?: Subscription;\r\n private slideChangedSubscription?: Subscription;\r\n private slideUpdatedSubscription?: Subscription;\r\n\r\n public hourColumnLabels!: string[];\r\n public initScrollPosition!: number;\r\n private formatDayHeader!: (date: Date) => string;\r\n private formatTitle!: (date: Date) => string;\r\n private formatHourColumnLabel!: (date: Date) => string;\r\n private hourRange!: number;\r\n\r\n static createDateObjects(startTime: Date, startHour: number, endHour: number, timeInterval: number): IWeekViewRow[][] {\r\n const times: IWeekViewRow[][] = [],\r\n currentHour = 0,\r\n currentDate = startTime.getDate();\r\n let hourStep,\r\n minStep;\r\n\r\n if (timeInterval < 1) {\r\n hourStep = Math.floor(1 / timeInterval);\r\n minStep = 60;\r\n } else {\r\n hourStep = 1;\r\n minStep = Math.floor(60 / timeInterval);\r\n }\r\n\r\n for (let hour = startHour; hour < endHour; hour += hourStep) {\r\n for (let interval = 0; interval < 60; interval += minStep) {\r\n const row: IWeekViewRow[] = [];\r\n for (let day = 0; day < 7; day += 1) {\r\n const time = new Date(startTime.getTime());\r\n time.setHours(currentHour + hour, interval);\r\n time.setDate(currentDate + day);\r\n row.push({\r\n events: [],\r\n time\r\n });\r\n }\r\n times.push(row);\r\n }\r\n }\r\n return times;\r\n }\r\n\r\n static getDates(startTime: Date, n: number): IWeekViewDateRow[] {\r\n const dates = new Array(n),\r\n current = new Date(startTime.getTime());\r\n let i = 0;\r\n while (i < n) {\r\n dates[i++] = {\r\n date: new Date(current.getTime()),\r\n events: [],\r\n dayHeader: ''\r\n };\r\n current.setDate(current.getDate() + 1);\r\n }\r\n return dates;\r\n }\r\n\r\n private static compareEventByStartOffset(eventA: IDisplayEvent, eventB: IDisplayEvent): number {\r\n return eventA.startOffset - eventB.startOffset;\r\n }\r\n\r\n private static calculateWidth(orderedEvents: IDisplayEvent[], size: number, hourParts: number) {\r\n const totalSize = size * hourParts,\r\n cells = new Array(totalSize);\r\n\r\n // sort by position in descending order, the right most columns should be calculated first\r\n orderedEvents.sort((eventA, eventB) => {\r\n return eventB.position - eventA.position;\r\n });\r\n for (let i = 0; i < totalSize; i += 1) {\r\n cells[i] = {\r\n calculated: false,\r\n events: []\r\n };\r\n }\r\n const len = orderedEvents.length;\r\n for (let i = 0; i < len; i += 1) {\r\n const event = orderedEvents[i];\r\n let index = event.startIndex * hourParts + event.startOffset;\r\n while (index < event.endIndex * hourParts - event.endOffset) {\r\n cells[index].events.push(event);\r\n index += 1;\r\n }\r\n }\r\n\r\n let i = 0;\r\n while (i < len) {\r\n let event:IDisplayEvent|undefined = orderedEvents[i];\r\n if (!event.overlapNumber) {\r\n const overlapNumber = event.position + 1;\r\n event.overlapNumber = overlapNumber;\r\n const eventQueue = [event];\r\n while (event = eventQueue.shift()) { \r\n let index = event.startIndex * hourParts + event.startOffset;\r\n while (index < event.endIndex * hourParts - event.endOffset) {\r\n if (!cells[index].calculated) {\r\n cells[index].calculated = true;\r\n if (cells[index].events) {\r\n const eventCountInCell = cells[index].events.length;\r\n for (let j = 0; j < eventCountInCell; j += 1) {\r\n const currentEventInCell = cells[index].events[j];\r\n if (!currentEventInCell.overlapNumber) {\r\n currentEventInCell.overlapNumber = overlapNumber;\r\n eventQueue.push(currentEventInCell);\r\n }\r\n }\r\n }\r\n }\r\n index += 1;\r\n }\r\n }\r\n }\r\n i += 1;\r\n }\r\n }\r\n\r\n ngOnInit() {\r\n if (!this.sliderOptions) {\r\n this.sliderOptions = {};\r\n }\r\n this.sliderOptions.loop = true;\r\n this.sliderOptions.allowSlidePrev = !this.lockSwipeToPrev;\r\n this.sliderOptions.allowSlideNext = !this.lockSwipeToNext;\r\n this.sliderOptions.allowTouchMove = !this.lockSwipes;\r\n\r\n this.hourRange = (this.endHour - this.startHour) * this.hourSegments;\r\n if (this.dateFormatter && this.dateFormatter.formatWeekViewDayHeader) {\r\n this.formatDayHeader = this.dateFormatter.formatWeekViewDayHeader;\r\n } else {\r\n const datePipe = new DatePipe(this.locale);\r\n this.formatDayHeader = function (date: Date) {\r\n return datePipe.transform(date, this.formatWeekViewDayHeader)||'';\r\n };\r\n }\r\n\r\n if (this.dateFormatter && this.dateFormatter.formatWeekViewTitle) {\r\n this.formatTitle = this.dateFormatter.formatWeekViewTitle;\r\n } else {\r\n const datePipe = new DatePipe(this.locale);\r\n this.formatTitle = function (date: Date) {\r\n return datePipe.transform(date, this.formatWeekTitle)||'';\r\n };\r\n }\r\n\r\n if (this.dateFormatter && this.dateFormatter.formatWeekViewHourColumn) {\r\n this.formatHourColumnLabel = this.dateFormatter.formatWeekViewHourColumn;\r\n } else {\r\n const datePipe = new DatePipe(this.locale);\r\n this.formatHourColumnLabel = function (date: Date) {\r\n return datePipe.transform(date, this.formatHourColumn)||'';\r\n };\r\n }\r\n\r\n this.refreshView();\r\n this.hourColumnLabels = this.getHourColumnLabels();\r\n this.inited = true;\r\n\r\n this.currentDateChangedFromParentSubscription = this.calendarService.currentDateChangedFromParent$.subscribe(currentDate => {\r\n this.refreshView();\r\n });\r\n\r\n this.eventSourceChangedSubscription = this.calendarService.eventSourceChanged$.subscribe(() => {\r\n this.onDataLoaded();\r\n });\r\n\r\n this.slideChangedSubscription = this.calendarService.slideChanged$.subscribe(direction => {\r\n if (direction === 1) {\r\n this.slider.slideNext();\r\n } else if (direction === -1) {\r\n this.slider.slidePrev();\r\n }\r\n });\r\n\r\n this.slideUpdatedSubscription = this.calendarService.slideUpdated$.subscribe(() => {\r\n this.slider.update();\r\n });\r\n }\r\n\r\n ngAfterViewInit() {\r\n this.slider = new Swiper(this.swiperElement?.nativeElement, this.sliderOptions);\r\n let me = this;\r\n this.slider.on('slideNextTransitionEnd', function() {\r\n me.onSlideChanged(1);\r\n });\r\n\r\n this.slider.on('slidePrevTransitionEnd', function() {\r\n me.onSlideChanged(-1);\r\n });\r\n\r\n if(this.dir === 'rtl') {\r\n this.slider.changeLanguageDirection('rtl');\r\n }\r\n\r\n const title = this.getTitle();\r\n this.onTitleChanged.emit(title);\r\n\r\n if (this.scrollToHour > 0) {\r\n const hourColumns = this.elm.nativeElement.querySelector('.weekview-normal-event-container').querySelectorAll('.calendar-hour-column');\r\n const me = this;\r\n setTimeout(() => {\r\n me.initSc