ng-material-date-range-picker
Version:
This library provides the date range selection with two views.
261 lines (260 loc) • 36.2 kB
JavaScript
/**
* @(#)calendar.component.scss Sept 07, 2023
* @author Aakash Kumar
*/
import { ChangeDetectionStrategy, Component, Input, ViewChild, } from '@angular/core';
import { DateRange } from '@angular/material/datepicker';
import { CalendarViewData } from './../model/calendar-view-data';
import * as i0 from "@angular/core";
import * as i1 from "@angular/material/datepicker";
export class CalendarComponent {
constructor(cdref, el, renderer) {
this.cdref = cdref;
this.el = el;
this.renderer = renderer;
this.isAllowHoverEvent = false;
}
ngOnInit() {
this.initFirstCalendar();
this.initSecondCalendar();
}
ngAfterViewInit() {
this.addFirstCalendarButtonEvents();
this.attachHoverEventOnFirstViewDates();
this.attachHoverEventOnSecondViewDates();
this.addSecondCalendarButtonEvents();
}
/**
* This method gets all eligible cells on second view for hover event.
*/
attachHoverEventOnSecondViewDates() {
const nodes = this.el.nativeElement.querySelectorAll('#secondCalendarView .mat-calendar-body-cell');
setTimeout(() => this.addHoverEvents(nodes), 200);
}
/**
* This method handles second calendar view month selection.
*
* @param event Date
*/
secondViewMonthSelected(event) {
this.removeDefaultFocus(this);
setTimeout(() => {
this.attachHoverEventOnSecondViewDates();
}, 300);
}
/**
* This method handles first calendar view month selection.
*
* @param event Date
*/
monthSelected(event) {
this.secondCalendarView._goToDateInView(event, 'year');
setTimeout(() => this.handleFirstCalendarNextEvent(this, true), 1);
}
/**
* This method updates the date selection range.
*
* @param date Date
*/
updateDateRangeSelection(date) {
const selectedDates = this.selectedDates;
if (!selectedDates ||
(selectedDates.start && selectedDates.end) ||
(selectedDates.start && date && selectedDates.start > date)) {
this.selectedDates = new DateRange(date, null);
this.isAllowHoverEvent = true;
}
else {
this.isAllowHoverEvent = false;
this.selectedDates = new DateRange(selectedDates.start, date);
}
this.cdref.markForCheck();
}
/**
* This method handles First calendar prev button event.
* @param classRef CalendarComponent
*/
handleFirstCalDatePrevEvent(classRef) {
const leftDateCalender = classRef.firstCalendarView;
if (leftDateCalender.currentView.toLocaleLowerCase() === 'month') {
const date = new Date(leftDateCalender['_clampedActiveDate']);
classRef.secondCalendarView.minDate =
classRef.getFirstDateOfNextMonth(date);
classRef.cdref.markForCheck();
}
classRef.attachHoverEventOnFirstViewDates();
}
/**
* This method gets all eligible cells on first view for hover event.
*/
attachHoverEventOnFirstViewDates() {
const nodes = this.el.nativeElement.querySelectorAll('#firstCalendarView .mat-calendar-body-cell');
setTimeout(() => this.addHoverEvents(nodes), 200);
}
/**
* This method handle the next button event.
*
* @param classRef CalendarComponent
* @param isForced boolean
*/
handleFirstCalendarNextEvent(classRef, isForced = false) {
const firstCalendar = classRef.firstCalendarView;
if (firstCalendar.currentView.toLocaleLowerCase() === 'month' || isForced) {
const date = new Date(firstCalendar['_clampedActiveDate']);
const nextMonthDate = classRef.getFirstDateOfNextMonth(date);
classRef.secondCalendarView.minDate = nextMonthDate;
classRef.secondCalendarView._goToDateInView(nextMonthDate, 'month');
classRef.removeDefaultFocus(classRef);
classRef.cdref.markForCheck();
}
setTimeout(() => {
classRef.attachHoverEventOnFirstViewDates();
classRef.attachHoverEventOnSecondViewDates();
}, 300);
}
/**
* This method remove active focus on second view.
*
* @param classRef CalendarComponent
*/
removeDefaultFocus(classRef) {
setTimeout(() => {
const btn = classRef.el.nativeElement.querySelectorAll('#secondCalendarView button.mat-calendar-body-active');
if (btn?.length) {
btn[0].blur();
}
}, 1);
}
/**
* This method attaches next and prev events on buttons.
*
*/
addFirstCalendarButtonEvents() {
const monthPrevBtn = this.el.nativeElement.querySelectorAll('#firstCalendarView .mat-calendar-previous-button');
const monthNextBtn = this.el.nativeElement.querySelectorAll('#firstCalendarView .mat-calendar-next-button');
this.attachClickEvent(monthPrevBtn, this.handleFirstCalDatePrevEvent);
this.attachClickEvent(monthNextBtn, this.handleFirstCalendarNextEvent);
}
/**
* This method attaches next and prev events on buttons.
*
*/
addSecondCalendarButtonEvents() {
const monthPrevBtn = this.el.nativeElement.querySelectorAll('#secondCalendarView .mat-calendar-previous-button');
const monthNextBtn = this.el.nativeElement.querySelectorAll('#secondCalendarView .mat-calendar-next-button');
if (!monthPrevBtn || !monthNextBtn) {
return;
}
this.attachSecondViewClickEvent(monthPrevBtn);
this.attachSecondViewClickEvent(monthNextBtn);
}
/**
* This method attach click event of next and prev button on second view.
*
*/
attachSecondViewClickEvent(nodes) {
Array.from(nodes).forEach((button) => {
this.renderer.listen(button, 'click', () => {
this.attachHoverEventOnSecondViewDates();
});
});
}
/**
* This method will update the range selection on mouse hover event.
*
* @param date Date
*/
updateSelectionOnMouseHover(date) {
const selectedDates = this.selectedDates;
if (selectedDates?.start && date && selectedDates.start < date) {
const dateRange = new DateRange(selectedDates.start, date);
this.firstCalendarView.selected = dateRange;
this.secondCalendarView.selected = dateRange;
this.firstCalendarView['_changeDetectorRef'].markForCheck();
this.secondCalendarView['_changeDetectorRef'].markForCheck();
this.isAllowHoverEvent = true;
}
}
/**
* This method attach hover event on specified nodes.
*
* @param nodes any
*/
addHoverEvents(nodes) {
if (!nodes) {
return;
}
Array.from(nodes).forEach((button) => {
this.renderer.listen(button, 'mouseover', (event) => {
if (this.isAllowHoverEvent) {
const date = new Date(event.target['ariaLabel']);
this.updateSelectionOnMouseHover(date);
}
});
});
}
/**
* This method attach the next and prev events on specified nodes.
*
* @param nodes any
* @param handler Function
*/
attachClickEvent(nodes, handler) {
if (!nodes) {
return;
}
Array.from(nodes).forEach((button) => {
this.renderer.listen(button, 'click', () => {
handler(this);
});
});
}
/**
* This method initialize data for first calendar view.
*/
initFirstCalendar() {
this.firstCalendarViewData = new CalendarViewData();
this.firstCalendarViewData.startDate = new Date();
}
/**
* This method initialize data for second calendar view.
*/
initSecondCalendar() {
const currDate = new Date();
this.secondCalendarViewData = new CalendarViewData();
this.secondCalendarViewData.minDate =
this.getFirstDateOfNextMonth(currDate);
currDate.setMonth(currDate.getMonth() + 1);
this.secondCalendarViewData.startDate = this.selectedDates?.end
? this.selectedDates.end
: currDate;
}
/**
* This method returns the next months first date.
*
* @param currDate Date
* @returns Date
*/
getFirstDateOfNextMonth(currDate) {
return new Date(currDate.getFullYear(), currDate.getMonth() + 1, 1);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: CalendarComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.6", type: CalendarComponent, selector: "lib-calendar", inputs: { selectedDates: "selectedDates", minDate: "minDate", maxDate: "maxDate" }, viewQueries: [{ propertyName: "firstCalendarView", first: true, predicate: ["firstCalendarView"], descendants: true }, { propertyName: "secondCalendarView", first: true, predicate: ["secondCalendarView"], descendants: true }], ngImport: i0, template: "<!--**\r\n * @(#)calendar.component.html Sept 07, 2023\r\n\r\n * @author Aakash Kumar\r\n *-->\r\n<div class=\"calendar-container\">\r\n <div class=\"first-view\">\r\n <mat-calendar id=\"firstCalendarView\" #firstCalendarView [startAt]=\"firstCalendarViewData.startDate\"\r\n [selected]=\"selectedDates\" (selectedChange)=\"updateDateRangeSelection($event)\"\r\n (monthSelected)=\"monthSelected($event)\" [minDate]=\"minDate\" [maxDate]=\"maxDate\"></mat-calendar>\r\n </div>\r\n <div class=\"second-view\">\r\n <mat-calendar id=\"secondCalendarView\" #secondCalendarView [startAt]=\"secondCalendarViewData.startDate\"\r\n [minDate]=\"secondCalendarViewData.minDate\" [maxDate]=\"maxDate\" [selected]=\"selectedDates\"\r\n (selectedChange)=\"updateDateRangeSelection($event)\"\r\n (monthSelected)=\" secondViewMonthSelected($event)\"></mat-calendar>\r\n </div>\r\n</div>\r\n", styles: [".mat-calendar{min-width:250px}.calendar-container{width:100%;display:block;float:left}.first-view,.second-view{width:50%;display:block;float:left}.first-view,.second-view{margin-top:.5rem}@media (max-width: 490px){.first-view,.second-view{width:100%}}\n"], dependencies: [{ kind: "component", type: i1.MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.6", ngImport: i0, type: CalendarComponent, decorators: [{
type: Component,
args: [{ selector: 'lib-calendar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--**\r\n * @(#)calendar.component.html Sept 07, 2023\r\n\r\n * @author Aakash Kumar\r\n *-->\r\n<div class=\"calendar-container\">\r\n <div class=\"first-view\">\r\n <mat-calendar id=\"firstCalendarView\" #firstCalendarView [startAt]=\"firstCalendarViewData.startDate\"\r\n [selected]=\"selectedDates\" (selectedChange)=\"updateDateRangeSelection($event)\"\r\n (monthSelected)=\"monthSelected($event)\" [minDate]=\"minDate\" [maxDate]=\"maxDate\"></mat-calendar>\r\n </div>\r\n <div class=\"second-view\">\r\n <mat-calendar id=\"secondCalendarView\" #secondCalendarView [startAt]=\"secondCalendarViewData.startDate\"\r\n [minDate]=\"secondCalendarViewData.minDate\" [maxDate]=\"maxDate\" [selected]=\"selectedDates\"\r\n (selectedChange)=\"updateDateRangeSelection($event)\"\r\n (monthSelected)=\" secondViewMonthSelected($event)\"></mat-calendar>\r\n </div>\r\n</div>\r\n", styles: [".mat-calendar{min-width:250px}.calendar-container{width:100%;display:block;float:left}.first-view,.second-view{width:50%;display:block;float:left}.first-view,.second-view{margin-top:.5rem}@media (max-width: 490px){.first-view,.second-view{width:100%}}\n"] }]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { selectedDates: [{
type: Input
}], minDate: [{
type: Input
}], maxDate: [{
type: Input
}], firstCalendarView: [{
type: ViewChild,
args: ['firstCalendarView']
}], secondCalendarView: [{
type: ViewChild,
args: ['secondCalendarView']
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsZW5kYXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmctZGF0ZS1waWNrZXIvc3JjL2xpYi9jYWxlbmRhci9jYWxlbmRhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZy1kYXRlLXBpY2tlci9zcmMvbGliL2NhbGVuZGFyL2NhbGVuZGFyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFFSCxPQUFPLEVBRUwsdUJBQXVCLEVBRXZCLFNBQVMsRUFFVCxLQUFLLEVBR0wsU0FBUyxHQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxTQUFTLEVBQWUsTUFBTSw4QkFBOEIsQ0FBQztBQUN0RSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQzs7O0FBUWpFLE1BQU0sT0FBTyxpQkFBaUI7SUFXNUIsWUFDVSxLQUF3QixFQUN4QixFQUFjLEVBQ2QsUUFBbUI7UUFGbkIsVUFBSyxHQUFMLEtBQUssQ0FBbUI7UUFDeEIsT0FBRSxHQUFGLEVBQUUsQ0FBWTtRQUNkLGFBQVEsR0FBUixRQUFRLENBQVc7UUFSckIsc0JBQWlCLEdBQVksS0FBSyxDQUFDO0lBU3hDLENBQUM7SUFFSixRQUFRO1FBQ04sSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7SUFDNUIsQ0FBQztJQUVELGVBQWU7UUFDYixJQUFJLENBQUMsNEJBQTRCLEVBQUUsQ0FBQztRQUNwQyxJQUFJLENBQUMsZ0NBQWdDLEVBQUUsQ0FBQztRQUN4QyxJQUFJLENBQUMsaUNBQWlDLEVBQUUsQ0FBQztRQUN6QyxJQUFJLENBQUMsNkJBQTZCLEVBQUUsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxpQ0FBaUM7UUFDL0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxD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QUFTOytCQUNFLGNBQWMsbUJBR1AsdUJBQXVCLENBQUMsTUFBTTt1SUFLdEMsYUFBYTtzQkFBckIsS0FBSztnQkFDRyxPQUFPO3NCQUFmLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUcwQixpQkFBaUI7c0JBQWhELFNBQVM7dUJBQUMsbUJBQW1CO2dCQUNHLGtCQUFrQjtzQkFBbEQsU0FBUzt1QkFBQyxvQkFBb0IiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcclxuICogQCgjKWNhbGVuZGFyLmNvbXBvbmVudC5zY3NzIFNlcHQgMDcsIDIwMjNcclxuXHJcbiAqIEBhdXRob3IgQWFrYXNoIEt1bWFyXHJcbiAqL1xyXG5cclxuaW1wb3J0IHtcclxuICBBZnRlclZpZXdJbml0LFxyXG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxyXG4gIENoYW5nZURldGVjdG9yUmVmLFxyXG4gIENvbXBvbmVudCxcclxuICBFbGVtZW50UmVmLFxyXG4gIElucHV0LFxyXG4gIE9uSW5pdCxcclxuICBSZW5kZXJlcjIsXHJcbiAgVmlld0NoaWxkLFxyXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBEYXRlUmFuZ2UsIE1hdENhbGVuZGFyIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvZGF0ZXBpY2tlcic7XHJcbmltcG9ydCB7IENhbGVuZGFyVmlld0RhdGEgfSBmcm9tICcuLy4uL21vZGVsL2NhbGVuZGFyLXZpZXctZGF0YSc7XHJcblxyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ2xpYi1jYWxlbmRhcicsXHJcbiAgdGVtcGxhdGVVcmw6ICcuL2NhbGVuZGFyLmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybHM6IFsnLi9jYWxlbmRhci5jb21wb25lbnQuY3NzJ10sXHJcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBDYWxlbmRhckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCB7XHJcbiAgZmlyc3RDYWxlbmRhclZpZXdEYXRhITogQ2FsZW5kYXJWaWV3RGF0YTtcclxuICBzZWNvbmRDYWxlbmRhclZpZXdEYXRhITogQ2FsZW5kYXJWaWV3RGF0YTtcclxuICBASW5wdXQoKSBzZWxlY3RlZERhdGVzITogRGF0ZVJhbmdlPERhdGU+IHwgbnVsbDtcclxuICBASW5wdXQoKSBtaW5EYXRlITogRGF0ZTtcclxuICBASW5wdXQoKSBtYXhEYXRlITogRGF0ZTtcclxuICBwcml2YXRlIGlzQWxsb3dIb3ZlckV2ZW50OiBib29sZWFuID0gZmFsc2U7XHJcblxyXG4gIEBWaWV3Q2hpbGQoJ2ZpcnN0Q2FsZW5kYXJWaWV3JykgZmlyc3RDYWxlbmRhclZpZXchOiBNYXRDYWxlbmRhcjxEYXRlPjtcclxuICBAVmlld0NoaWxkKCdzZWNvbmRDYWxlbmRhclZpZXcnKSBzZWNvbmRDYWxlbmRhclZpZXchOiBNYXRDYWxlbmRhcjxEYXRlPjtcclxuXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIGNkcmVmOiBDaGFuZ2VEZXRlY3RvclJlZixcclxuICAgIHByaXZhdGUgZWw6IEVsZW1lbnRSZWYsXHJcbiAgICBwcml2YXRlIHJlbmRlcmVyOiBSZW5kZXJlcjJcclxuICApIHt9XHJcblxyXG4gIG5nT25Jbml0KCk6IHZvaWQge1xyXG4gICAgdGhpcy5pbml0Rmlyc3RDYWxlbmRhcigpO1xyXG4gICAgdGhpcy5pbml0U2Vjb25kQ2FsZW5kYXIoKTtcclxuICB9XHJcblxyXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcclxuICAgIHRoaXMuYWRkRmlyc3RDYWxlbmRhckJ1dHRvbkV2ZW50cygpO1xyXG4gICAgdGhpcy5hdHRhY2hIb3ZlckV2ZW50T25GaXJzdFZpZXdEYXRlcygpO1xyXG4gICAgdGhpcy5hdHRhY2hIb3ZlckV2ZW50T25TZWNvbmRWaWV3RGF0ZXMoKTtcclxuICAgIHRoaXMuYWRkU2Vjb25kQ2FsZW5kYXJCdXR0b25FdmVudHMoKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIGdldHMgYWxsIGVsaWdpYmxlIGNlbGxzIG9uIHNlY29uZCB2aWV3IGZvciBob3ZlciBldmVudC5cclxuICAgKi9cclxuICBhdHRhY2hIb3ZlckV2ZW50T25TZWNvbmRWaWV3RGF0ZXMoKSB7XHJcbiAgICBjb25zdCBub2RlcyA9IHRoaXMuZWwubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKFxyXG4gICAgICAnI3NlY29uZENhbGVuZGFyVmlldyAubWF0LWNhbGVuZGFyLWJvZHktY2VsbCdcclxuICAgICk7XHJcbiAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuYWRkSG92ZXJFdmVudHMobm9kZXMpLCAyMDApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgaGFuZGxlcyBzZWNvbmQgY2FsZW5kYXIgdmlldyBtb250aCBzZWxlY3Rpb24uXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZXZlbnQgRGF0ZVxyXG4gICAqL1xyXG4gIHNlY29uZFZpZXdNb250aFNlbGVjdGVkKGV2ZW50OiBEYXRlKSB7XHJcbiAgICB0aGlzLnJlbW92ZURlZmF1bHRGb2N1cyh0aGlzKTtcclxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICB0aGlzLmF0dGFjaEhvdmVyRXZlbnRPblNlY29uZFZpZXdEYXRlcygpO1xyXG4gICAgfSwgMzAwKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIGhhbmRsZXMgZmlyc3QgY2FsZW5kYXIgdmlldyBtb250aCBzZWxlY3Rpb24uXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZXZlbnQgRGF0ZVxyXG4gICAqL1xyXG4gIG1vbnRoU2VsZWN0ZWQoZXZlbnQ6IERhdGUpIHtcclxuICAgIHRoaXMuc2Vjb25kQ2FsZW5kYXJWaWV3Ll9nb1RvRGF0ZUluVmlldyhldmVudCwgJ3llYXInKTtcclxuICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy5oYW5kbGVGaXJzdENhbGVuZGFyTmV4dEV2ZW50KHRoaXMsIHRydWUpLCAxKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIHVwZGF0ZXMgdGhlIGRhdGUgc2VsZWN0aW9uIHJhbmdlLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGRhdGUgRGF0ZVxyXG4gICAqL1xyXG4gIHVwZGF0ZURhdGVSYW5nZVNlbGVjdGlvbihkYXRlOiBEYXRlIHwgbnVsbCk6IHZvaWQge1xyXG4gICAgY29uc3Qgc2VsZWN0ZWREYXRlcyA9IHRoaXMuc2VsZWN0ZWREYXRlcztcclxuICAgIGlmIChcclxuICAgICAgIXNlbGVjdGVkRGF0ZXMgfHxcclxuICAgICAgKHNlbGVjdGVkRGF0ZXMuc3RhcnQgJiYgc2VsZWN0ZWREYXRlcy5lbmQpIHx8XHJcbiAgICAgIChzZWxlY3RlZERhdGVzLnN0YXJ0ICYmIGRhdGUgJiYgc2VsZWN0ZWREYXRlcy5zdGFydCA+IGRhdGUpXHJcbiAgICApIHtcclxuICAgICAgdGhpcy5zZWxlY3RlZERhdGVzID0gbmV3IERhdGVSYW5nZTxEYXRlPihkYXRlLCBudWxsKTtcclxuICAgICAgdGhpcy5pc0FsbG93SG92ZXJFdmVudCA9IHRydWU7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLmlzQWxsb3dIb3ZlckV2ZW50ID0gZmFsc2U7XHJcbiAgICAgIHRoaXMuc2VsZWN0ZWREYXRlcyA9IG5ldyBEYXRlUmFuZ2U8RGF0ZT4oc2VsZWN0ZWREYXRlcy5zdGFydCwgZGF0ZSk7XHJcbiAgICB9XHJcbiAgICB0aGlzLmNkcmVmLm1hcmtGb3JDaGVjaygpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgaGFuZGxlcyBGaXJzdCBjYWxlbmRhciBwcmV2IGJ1dHRvbiBldmVudC5cclxuICAgKiBAcGFyYW0gY2xhc3NSZWYgQ2FsZW5kYXJDb21wb25lbnRcclxuICAgKi9cclxuICBwcml2YXRlIGhhbmRsZUZpcnN0Q2FsRGF0ZVByZXZFdmVudChjbGFzc1JlZjogQ2FsZW5kYXJDb21wb25lbnQpOiB2b2lkIHtcclxuICAgIGNvbnN0IGxlZnREYXRlQ2FsZW5kZXIgPSBjbGFzc1JlZi5maXJzdENhbGVuZGFyVmlldztcclxuICAgIGlmIChsZWZ0RGF0ZUNhbGVuZGVyLmN1cnJlbnRWaWV3LnRvTG9jYWxlTG93ZXJDYXNlKCkgPT09ICdtb250aCcpIHtcclxuICAgICAgY29uc3QgZGF0ZTogRGF0ZSA9IG5ldyBEYXRlKGxlZnREYXRlQ2FsZW5kZXJbJ19jbGFtcGVkQWN0aXZlRGF0ZSddKTtcclxuICAgICAgY2xhc3NSZWYuc2Vjb25kQ2FsZW5kYXJWaWV3Lm1pbkRhdGUgPVxyXG4gICAgICAgIGNsYXNzUmVmLmdldEZpcnN0RGF0ZU9mTmV4dE1vbnRoKGRhdGUpO1xyXG4gICAgICBjbGFzc1JlZi5jZHJlZi5tYXJrRm9yQ2hlY2soKTtcclxuICAgIH1cclxuICAgIGNsYXNzUmVmLmF0dGFjaEhvdmVyRXZlbnRPbkZpcnN0Vmlld0RhdGVzKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIG1ldGhvZCBnZXRzIGFsbCBlbGlnaWJsZSBjZWxscyBvbiBmaXJzdCB2aWV3IGZvciBob3ZlciBldmVudC5cclxuICAgKi9cclxuICBwcml2YXRlIGF0dGFjaEhvdmVyRXZlbnRPbkZpcnN0Vmlld0RhdGVzKCkge1xyXG4gICAgY29uc3Qgbm9kZXMgPSB0aGlzLmVsLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbChcclxuICAgICAgJyNmaXJzdENhbGVuZGFyVmlldyAubWF0LWNhbGVuZGFyLWJvZHktY2VsbCdcclxuICAgICk7XHJcbiAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuYWRkSG92ZXJFdmVudHMobm9kZXMpLCAyMDApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgaGFuZGxlIHRoZSBuZXh0IGJ1dHRvbiBldmVudC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjbGFzc1JlZiBDYWxlbmRhckNvbXBvbmVudFxyXG4gICAqIEBwYXJhbSBpc0ZvcmNlZCBib29sZWFuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBoYW5kbGVGaXJzdENhbGVuZGFyTmV4dEV2ZW50KFxyXG4gICAgY2xhc3NSZWY6IENhbGVuZGFyQ29tcG9uZW50LFxyXG4gICAgaXNGb3JjZWQgPSBmYWxzZVxyXG4gICk6IHZvaWQge1xyXG4gICAgY29uc3QgZmlyc3RDYWxlbmRhciA9IGNsYXNzUmVmLmZpcnN0Q2FsZW5kYXJWaWV3O1xyXG4gICAgaWYgKGZpcnN0Q2FsZW5kYXIuY3VycmVudFZpZXcudG9Mb2NhbGVMb3dlckNhc2UoKSA9PT0gJ21vbnRoJyB8fCBpc0ZvcmNlZCkge1xyXG4gICAgICBjb25zdCBkYXRlOiBEYXRlID0gbmV3IERhdGUoZmlyc3RDYWxlbmRhclsnX2NsYW1wZWRBY3RpdmVEYXRlJ10pO1xyXG4gICAgICBjb25zdCBuZXh0TW9udGhEYXRlID0gY2xhc3NSZWYuZ2V0Rmlyc3REYXRlT2ZOZXh0TW9udGgoZGF0ZSk7XHJcbiAgICAgIGNsYXNzUmVmLnNlY29uZENhbGVuZGFyVmlldy5taW5EYXRlID0gbmV4dE1vbnRoRGF0ZTtcclxuICAgICAgY2xhc3NSZWYuc2Vjb25kQ2FsZW5kYXJWaWV3Ll9nb1RvRGF0ZUluVmlldyhuZXh0TW9udGhEYXRlLCAnbW9udGgnKTtcclxuICAgICAgY2xhc3NSZWYucmVtb3ZlRGVmYXVsdEZvY3VzKGNsYXNzUmVmKTtcclxuICAgICAgY2xhc3NSZWYuY2RyZWYubWFya0ZvckNoZWNrKCk7XHJcbiAgICB9XHJcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgY2xhc3NSZWYuYXR0YWNoSG92ZXJFdmVudE9uRmlyc3RWaWV3RGF0ZXMoKTtcclxuICAgICAgY2xhc3NSZWYuYXR0YWNoSG92ZXJFdmVudE9uU2Vjb25kVmlld0RhdGVzKCk7XHJcbiAgICB9LCAzMDApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgcmVtb3ZlIGFjdGl2ZSBmb2N1cyBvbiBzZWNvbmQgdmlldy5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjbGFzc1JlZiBDYWxlbmRhckNvbXBvbmVudFxyXG4gICAqL1xyXG4gIHJlbW92ZURlZmF1bHRGb2N1cyhjbGFzc1JlZjogQ2FsZW5kYXJDb21wb25lbnQpOiB2b2lkIHtcclxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICBjb25zdCBidG46IEhUTUxCdXR0b25FbGVtZW50W10gPVxyXG4gICAgICAgIGNsYXNzUmVmLmVsLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbChcclxuICAgICAgICAgICcjc2Vjb25kQ2FsZW5kYXJWaWV3IGJ1dHRvbi5tYXQtY2FsZW5kYXItYm9keS1hY3RpdmUnXHJcbiAgICAgICAgKTtcclxuICAgICAgaWYgKGJ0bj8ubGVuZ3RoKSB7XHJcbiAgICAgICAgYnRuWzBdLmJsdXIoKTtcclxuICAgICAgfVxyXG4gICAgfSwgMSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIG1ldGhvZCBhdHRhY2hlcyBuZXh0IGFuZCBwcmV2IGV2ZW50cyBvbiBidXR0b25zLlxyXG4gICAqXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBhZGRGaXJzdENhbGVuZGFyQnV0dG9uRXZlbnRzKCk6IHZvaWQge1xyXG4gICAgY29uc3QgbW9udGhQcmV2QnRuID0gdGhpcy5lbC5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXHJcbiAgICAgICcjZmlyc3RDYWxlbmRhclZpZXcgLm1hdC1jYWxlbmRhci1wcmV2aW91cy1idXR0b24nXHJcbiAgICApO1xyXG4gICAgY29uc3QgbW9udGhOZXh0QnRuID0gdGhpcy5lbC5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXHJcbiAgICAgICcjZmlyc3RDYWxlbmRhclZpZXcgLm1hdC1jYWxlbmRhci1uZXh0LWJ1dHRvbidcclxuICAgICk7XHJcbiAgICB0aGlzLmF0dGFjaENsaWNrRXZlbnQobW9udGhQcmV2QnRuLCB0aGlzLmhhbmRsZUZpcnN0Q2FsRGF0ZVByZXZFdmVudCk7XHJcbiAgICB0aGlzLmF0dGFjaENsaWNrRXZlbnQobW9udGhOZXh0QnRuLCB0aGlzLmhhbmRsZUZpcnN0Q2FsZW5kYXJOZXh0RXZlbnQpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgYXR0YWNoZXMgbmV4dCBhbmQgcHJldiBldmVudHMgb24gYnV0dG9ucy5cclxuICAgKlxyXG4gICAqL1xyXG4gIHByaXZhdGUgYWRkU2Vjb25kQ2FsZW5kYXJCdXR0b25FdmVudHMoKTogdm9pZCB7XHJcbiAgICBjb25zdCBtb250aFByZXZCdG46IGFueVtdID0gdGhpcy5lbC5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXHJcbiAgICAgICcjc2Vjb25kQ2FsZW5kYXJWaWV3IC5tYXQtY2FsZW5kYXItcHJldmlvdXMtYnV0dG9uJ1xyXG4gICAgKTtcclxuICAgIGNvbnN0IG1vbnRoTmV4dEJ0bjogYW55W10gPSB0aGlzLmVsLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbChcclxuICAgICAgJyNzZWNvbmRDYWxlbmRhclZpZXcgLm1hdC1jYWxlbmRhci1uZXh0LWJ1dHRvbidcclxuICAgICk7XHJcbiAgICBpZiAoIW1vbnRoUHJldkJ0biB8fCAhbW9udGhOZXh0QnRuKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIHRoaXMuYXR0YWNoU2Vjb25kVmlld0NsaWNrRXZlbnQobW9udGhQcmV2QnRuKTtcclxuICAgIHRoaXMuYXR0YWNoU2Vjb25kVmlld0NsaWNrRXZlbnQobW9udGhOZXh0QnRuKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIGF0dGFjaCBjbGljayBldmVudCBvZiBuZXh0IGFuZCBwcmV2IGJ1dHRvbiBvbiBzZWNvbmQgdmlldy5cclxuICAgKlxyXG4gICAqL1xyXG4gIHByaXZhdGUgYXR0YWNoU2Vjb25kVmlld0NsaWNrRXZlbnQobm9kZXM6IGFueSk6IHZvaWQge1xyXG4gICAgQXJyYXkuZnJvbShub2RlcykuZm9yRWFjaCgoYnV0dG9uKSA9PiB7XHJcbiAgICAgIHRoaXMucmVuZGVyZXIubGlzdGVuKGJ1dHRvbiwgJ2NsaWNrJywgKCkgPT4ge1xyXG4gICAgICAgIHRoaXMuYXR0YWNoSG92ZXJFdmVudE9uU2Vjb25kVmlld0RhdGVzKCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIG1ldGhvZCB3aWxsIHVwZGF0ZSB0aGUgcmFuZ2Ugc2VsZWN0aW9uIG9uIG1vdXNlIGhvdmVyIGV2ZW50LlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGRhdGUgRGF0ZVxyXG4gICAqL1xyXG4gIHByaXZhdGUgdXBkYXRlU2VsZWN0aW9uT25Nb3VzZUhvdmVyKGRhdGU6IERhdGUpOiB2b2lkIHtcclxuICAgIGNvbnN0IHNlbGVjdGVkRGF0ZXMgPSB0aGlzLnNlbGVjdGVkRGF0ZXM7XHJcbiAgICBpZiAoc2VsZWN0ZWREYXRlcz8uc3RhcnQgJiYgZGF0ZSAmJiBzZWxlY3RlZERhdGVzLnN0YXJ0IDwgZGF0ZSkge1xyXG4gICAgICBjb25zdCBkYXRlUmFuZ2U6IERhdGVSYW5nZTxEYXRlPiA9IG5ldyBEYXRlUmFuZ2U8RGF0ZT4oXHJcbiAgICAgICAgc2VsZWN0ZWREYXRlcy5zdGFydCxcclxuICAgICAgICBkYXRlXHJcbiAgICAgICk7XHJcbiAgICAgIHRoaXMuZmlyc3RDYWxlbmRhclZpZXcuc2VsZWN0ZWQgPSBkYXRlUmFuZ2U7XHJcbiAgICAgIHRoaXMuc2Vjb25kQ2FsZW5kYXJWaWV3LnNlbGVjdGVkID0gZGF0ZVJhbmdlO1xyXG4gICAgICB0aGlzLmZpcnN0Q2FsZW5kYXJWaWV3WydfY2hhbmdlRGV0ZWN0b3JSZWYnXS5tYXJrRm9yQ2hlY2soKTtcclxuICAgICAgdGhpcy5zZWNvbmRDYWxlbmRhclZpZXdbJ19jaGFuZ2VEZXRlY3RvclJlZiddLm1hcmtGb3JDaGVjaygpO1xyXG4gICAgICB0aGlzLmlzQWxsb3dIb3ZlckV2ZW50ID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIGF0dGFjaCBob3ZlciBldmVudCBvbiBzcGVjaWZpZWQgbm9kZXMuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gbm9kZXMgYW55XHJcbiAgICovXHJcbiAgcHJpdmF0ZSBhZGRIb3ZlckV2ZW50cyhub2RlczogYW55KTogdm9pZCB7XHJcbiAgICBpZiAoIW5vZGVzKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIEFycmF5LmZyb20obm9kZXMpLmZvckVhY2goKGJ1dHRvbikgPT4ge1xyXG4gICAgICB0aGlzLnJlbmRlcmVyLmxpc3RlbihidXR0b24sICdtb3VzZW92ZXInLCAoZXZlbnQpID0+IHtcclxuICAgICAgICBpZiAodGhpcy5pc0FsbG93SG92ZXJFdmVudCkge1xyXG4gICAgICAgICAgY29uc3QgZGF0ZSA9IG5ldyBEYXRlKGV2ZW50LnRhcmdldFsnYXJpYUxhYmVsJ10pO1xyXG4gICAgICAgICAgdGhpcy51cGRhdGVTZWxlY3Rpb25Pbk1vdXNlSG92ZXIoZGF0ZSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgYXR0YWNoIHRoZSBuZXh0IGFuZCBwcmV2IGV2ZW50cyBvbiBzcGVjaWZpZWQgbm9kZXMuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gbm9kZXMgYW55XHJcbiAgICogQHBhcmFtIGhhbmRsZXIgRnVuY3Rpb25cclxuICAgKi9cclxuICBwcml2YXRlIGF0dGFjaENsaWNrRXZlbnQobm9kZXM6IGFueSwgaGFuZGxlcjogRnVuY3Rpb24pOiB2b2lkIHtcclxuICAgIGlmICghbm9kZXMpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gICAgQXJyYXkuZnJvbShub2RlcykuZm9yRWFjaCgoYnV0dG9uKSA9PiB7XHJcbiAgICAgIHRoaXMucmVuZGVyZXIubGlzdGVuKGJ1dHRvbiwgJ2NsaWNrJywgKCkgPT4ge1xyXG4gICAgICAgIGhhbmRsZXIodGhpcyk7XHJcbiAgICAgIH0pO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIG1ldGhvZCBpbml0aWFsaXplIGRhdGEgZm9yIGZpcnN0IGNhbGVuZGFyIHZpZXcuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBpbml0Rmlyc3RDYWxlbmRhcigpOiB2b2lkIHtcclxuICAgIHRoaXMuZmlyc3RDYWxlbmRhclZpZXdEYXRhID0gbmV3IENhbGVuZGFyVmlld0RhdGEoKTtcclxuICAgIHRoaXMuZmlyc3RDYWxlbmRhclZpZXdEYXRhLnN0YXJ0RGF0ZSA9IG5ldyBEYXRlKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIG1ldGhvZCBpbml0aWFsaXplIGRhdGEgZm9yIHNlY29uZCBjYWxlbmRhciB2aWV3LlxyXG4gICAqL1xyXG4gIHByaXZhdGUgaW5pdFNlY29uZENhbGVuZGFyKCk6IHZvaWQge1xyXG4gICAgY29uc3QgY3VyckRhdGUgPSBuZXcgRGF0ZSgpO1xyXG4gICAgdGhpcy5zZWNvbmRDYWxlbmRhclZpZXdEYXRhID0gbmV3IENhbGVuZGFyVmlld0RhdGEoKTtcclxuICAgIHRoaXMuc2Vjb25kQ2FsZW5kYXJWaWV3RGF0YS5taW5EYXRlID1cclxuICAgICAgdGhpcy5nZXRGaXJzdERhdGVPZk5leHRNb250aChjdXJyRGF0ZSk7XHJcbiAgICBjdXJyRGF0ZS5zZXRNb250aChjdXJyRGF0ZS5nZXRNb250aCgpICsgMSk7XHJcbiAgICB0aGlzLnNlY29uZENhbGVuZGFyVmlld0RhdGEuc3RhcnREYXRlID0gdGhpcy5zZWxlY3RlZERhdGVzPy5lbmRcclxuICAgICAgPyB0aGlzLnNlbGVjdGVkRGF0ZXMuZW5kXHJcbiAgICAgIDogY3VyckRhdGU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIG1ldGhvZCByZXR1cm5zIHRoZSBuZXh0IG1vbnRocyBmaXJzdCBkYXRlLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGN1cnJEYXRlIERhdGVcclxuICAgKiBAcmV0dXJucyBEYXRlXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBnZXRGaXJzdERhdGVPZk5leHRNb250aChjdXJyRGF0ZTogRGF0ZSk6IERhdGUge1xyXG4gICAgcmV0dXJuIG5ldyBEYXRlKGN1cnJEYXRlLmdldEZ1bGxZZWFyKCksIGN1cnJEYXRlLmdldE1vbnRoKCkgKyAxLCAxKTtcclxuICB9XHJcbn1cclxuIiwiPCEtLSoqXHJcbiAqIEAoIyljYWxlbmRhci5jb21wb25lbnQuaHRtbCBTZXB0IDA3LCAyMDIzXHJcblxyXG4gKiBAYXV0aG9yIEFha2FzaCBLdW1hclxyXG4gKi0tPlxyXG48ZGl2IGNsYXNzPVwiY2FsZW5kYXItY29udGFpbmVyXCI+XHJcbiAgPGRpdiBjbGFzcz1cImZpcnN0LXZpZXdcIj5cclxuICAgIDxtYXQtY2FsZW5kYXIgaWQ9XCJmaXJzdENhbGVuZGFyVmlld1wiICNmaXJzdENhbGVuZGFyVmlldyBbc3RhcnRBdF09XCJmaXJzdENhbGVuZGFyVmlld0RhdGEuc3RhcnREYXRlXCJcclxuICAgICAgW3NlbGVjdGVkXT1cInNlbGVjdGVkRGF0ZXNcIiAoc2VsZWN0ZWRDaGFuZ2UpPVwidXBkYXRlRGF0ZVJhbmdlU2VsZWN0aW9uKCRldmVudClcIlxyXG4gICAgICAobW9udGhTZWxlY3RlZCk9XCJtb250aFNlbGVjdGVkKCRldmVudClcIiBbbWluRGF0ZV09XCJtaW5EYXRlXCIgW21heERhdGVdPVwibWF4RGF0ZVwiPjwvbWF0LWNhbGVuZGFyPlxyXG4gIDwvZGl2PlxyXG4gIDxkaXYgY2xhc3M9XCJzZWNvbmQtdmlld1wiPlxyXG4gICAgPG1hdC1jYWxlbmRhciBpZD1cInNlY29uZENhbGVuZGFyVmlld1wiICNzZWNvbmRDYWxlbmRhclZpZXcgW3N0YXJ0QXRdPVwic2Vjb25kQ2FsZW5kYXJWaWV3RGF0YS5zdGFydERhdGVcIlxyXG4gICAgICBbbWluRGF0ZV09XCJzZWNvbmRDYWxlbmRhclZpZXdEYXRhLm1pbkRhdGVcIiBbbWF4RGF0ZV09XCJtYXhEYXRlXCIgW3NlbGVjdGVkXT1cInNlbGVjdGVkRGF0ZXNcIlxyXG4gICAgICAoc2VsZWN0ZWRDaGFuZ2UpPVwidXBkYXRlRGF0ZVJhbmdlU2VsZWN0aW9uKCRldmVudClcIlxyXG4gICAgICAobW9udGhTZWxlY3RlZCk9XCIgc2Vjb25kVmlld01vbnRoU2VsZWN0ZWQoJGV2ZW50KVwiPjwvbWF0LWNhbGVuZGFyPlxyXG4gIDwvZGl2PlxyXG48L2Rpdj5cclxuIl19