ng-material-date-range-picker
Version:
This library provides the date range selection with two views.
238 lines • 35.5 kB
JavaScript
/**
* @(#)calendar.component.scss Sept 07, 2023
*
* Custom Calendar Component that manages two side-by-side
* month views with support for date range selection, hover
* highlighting, and navigation controls.
*
* @author Aakash Kumar
*/
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, inject, Input, Renderer2, signal, ViewChild, } from '@angular/core';
import { DateRange } from '@angular/material/datepicker';
import { getDateOfNextMonth, getFirstDateOfNextMonth, overrideActiveDateSetter, } from '../utils/date-picker-utilities';
import { ACTIVE_DATE_DEBOUNCE } from '../constant/date-filter-const';
import * as i0 from "@angular/core";
import * as i1 from "@angular/material/datepicker";
export class CalendarComponent {
constructor() {
this.firstViewStartDate = signal(new Date());
this.secondViewStartDate = signal(getDateOfNextMonth(this.firstViewStartDate()));
this.secondViewMinDate = signal(getFirstDateOfNextMonth(this.firstViewStartDate()));
this.isAllowHoverEvent = false;
this.cdref = inject(ChangeDetectorRef);
this.el = inject(ElementRef);
this.renderer = inject(Renderer2);
}
/**
* Updates the selected date range and synchronizes both calendar views.
*/
set selectedDates(selectedDates) {
this._selectedDates = selectedDates;
if (!selectedDates || !(selectedDates.start && selectedDates.end))
return;
const startDate = selectedDates.start ?? new Date();
const endDate = selectedDates.end;
this.firstViewStartDate.set(startDate);
this.secondViewMinDate.set(getFirstDateOfNextMonth(startDate));
const computedEndDate = startDate.getMonth() === endDate.getMonth()
? getDateOfNextMonth(endDate)
: endDate;
this.secondViewStartDate.set(computedEndDate);
}
get selectedDates() {
return this._selectedDates;
}
/**
* Lifecycle hook that is called after Angular has fully initialized
* the component's view (and child views).
*
* Used here to attach hover events and register active date change
* listeners once the calendar views are available in the DOM.
*/
ngAfterViewInit() {
this.attachHoverEvent('firstCalendarView');
this.attachHoverEvent('secondCalendarView');
this.registerActiveDateChangeEvents();
}
/**
* Handles month selection in the first view.
*
* @param event - Selected month date
*/
monthSelected(viewName) {
if (viewName === 'secondCalendarView') {
this.removeDefaultFocus(this);
}
this.attachHoverEvent(viewName);
}
/**
* Updates the selected date range when a date is clicked.
*
* @param date - Date clicked by the user
*/
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();
}
/**
* Registers event handlers for active date changes on both calendar views.
*
* This method overrides the default `activeDate` property setter of each
* calendar view to ensure custom handlers are executed whenever the
* active date changes.
*/
registerActiveDateChangeEvents() {
overrideActiveDateSetter(this.firstCalendarView, this.cdref, this.onFirstViewActiveDateChange.bind(this));
overrideActiveDateSetter(this.secondCalendarView, this.cdref, this.onSecondViewActiveDateChange.bind(this));
}
/**
* Handles the event when the active date of the first calendar view changes.
*
* @param activeDate - Object containing `previous` and `current` date values.
*/
onFirstViewActiveDateChange(activeDate) {
const handler = this.isPrevious(activeDate)
? () => this.handleFirstViewPrevEvent(activeDate)
: () => this.handleFirstViewNextEvent(activeDate.current);
// Delay execution because active date event fires before view update
setTimeout(handler, ACTIVE_DATE_DEBOUNCE);
}
/**
* Handles the event when the active date of the second calendar view changes.
*
* @param activeDate - Object containing `previous` and `current` date values.
*/
onSecondViewActiveDateChange(activeDate) {
this.attachHoverEvent('secondCalendarView');
}
/**
* Handles the "next" navigation event for the first calendar view.
*
* @param currDate - The currently active date in the first calendar view.
* @param force - Optional flag that can be used to enforce updates (not used in current logic).
*/
handleFirstViewNextEvent(currDate, force) {
if (this.firstCalendarView.currentView.toLocaleLowerCase() !== 'month') {
return;
}
this.attachHoverEvent('firstCalendarView');
const nextMonthDate = getFirstDateOfNextMonth(currDate);
let secondViewActiveDate = this.secondCalendarView.activeDate;
if (nextMonthDate < secondViewActiveDate) {
this.secondViewMinDate.set(nextMonthDate);
this.attachHoverEvent('secondCalendarView');
return;
}
secondViewActiveDate = getDateOfNextMonth(currDate);
this.secondViewMinDate.set(nextMonthDate);
this.secondCalendarView.activeDate = secondViewActiveDate;
this.cdref.detectChanges();
}
/**
* Handles the "previous" navigation event for the first calendar view.
*
* @param activeDate - Object containing `previous` and `current` date values.
*/
handleFirstViewPrevEvent(activeDate) {
if (this.firstCalendarView.currentView.toLocaleLowerCase() !== 'month') {
return;
}
this.secondViewMinDate.set(getFirstDateOfNextMonth(activeDate.current));
this.attachHoverEvent('firstCalendarView');
this.attachHoverEvent('secondCalendarView');
}
/**
* Checks whether the previous date is greater than the current date.
*
* @param activeDate - Object containing `previous` and `current` date values.
* @returns `true` if the previous date is later than the current date, otherwise `false`.
*/
isPrevious(activeDate) {
return activeDate.previous > activeDate.current;
}
/**
* Attaches hover events to all date cells in the first view.
*/
attachHoverEvent(viewId) {
const nodes = this.el.nativeElement.querySelectorAll(`#${viewId} .mat-calendar-body-cell`);
setTimeout(() => this.addHoverEvents(nodes), ACTIVE_DATE_DEBOUNCE);
}
/**
* Removes active focus from the second view.
*
* @param classRef - Reference to this component
*/
removeDefaultFocus(classRef) {
setTimeout(() => {
const btn = classRef.el.nativeElement.querySelectorAll('#secondCalendarView button.mat-calendar-body-active');
if (btn?.length) {
btn[0].blur();
}
}, 1);
}
/**
* Updates the selection range dynamically on hover.
*
* @param date - Hovered 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;
}
}
/**
* Attaches hover events to given nodes to update range selection.
*
* @param nodes - Date cell nodes
*/
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.firstCalendarView['_changeDetectorRef'].markForCheck();
this.secondCalendarView['_changeDetectorRef'].markForCheck();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CalendarComponent, selector: "lib-calendar", inputs: { minDate: "minDate", maxDate: "maxDate", selectedDates: "selectedDates" }, 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]=\"firstViewStartDate()\" [selected]=\"selectedDates\"\r\n (selectedChange)=\"updateDateRangeSelection($event)\" (monthSelected)=\"monthSelected('firstCalendarView')\" [minDate]=\"minDate\"\r\n [maxDate]=\"maxDate\"></mat-calendar>\r\n </div>\r\n <div class=\"second-view\">\r\n <mat-calendar id=\"secondCalendarView\" #secondCalendarView [startAt]=\"secondViewStartDate()\" [minDate]=\"secondViewMinDate()\"\r\n [maxDate]=\"maxDate\" [selected]=\"selectedDates\" (selectedChange)=\"updateDateRangeSelection($event)\"\r\n (monthSelected)=\"monthSelected('secondCalendarView')\"></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: "18.2.14", 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]=\"firstViewStartDate()\" [selected]=\"selectedDates\"\r\n (selectedChange)=\"updateDateRangeSelection($event)\" (monthSelected)=\"monthSelected('firstCalendarView')\" [minDate]=\"minDate\"\r\n [maxDate]=\"maxDate\"></mat-calendar>\r\n </div>\r\n <div class=\"second-view\">\r\n <mat-calendar id=\"secondCalendarView\" #secondCalendarView [startAt]=\"secondViewStartDate()\" [minDate]=\"secondViewMinDate()\"\r\n [maxDate]=\"maxDate\" [selected]=\"selectedDates\" (selectedChange)=\"updateDateRangeSelection($event)\"\r\n (monthSelected)=\"monthSelected('secondCalendarView')\"></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"] }]
}], propDecorators: { minDate: [{
type: Input
}], maxDate: [{
type: Input
}], firstCalendarView: [{
type: ViewChild,
args: ['firstCalendarView']
}], secondCalendarView: [{
type: ViewChild,
args: ['secondCalendarView']
}], selectedDates: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsZW5kYXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmctZGF0ZS1waWNrZXIvc3JjL2xpYi9jYWxlbmRhci9jYWxlbmRhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZy1kYXRlLXBpY2tlci9zcmMvbGliL2NhbGVuZGFyL2NhbGVuZGFyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7OztHQVFHO0FBQ0gsT0FBTyxFQUVMLHVCQUF1QixFQUN2QixpQkFBaUIsRUFDakIsU0FBUyxFQUNULFVBQVUsRUFDVixNQUFNLEVBQ04sS0FBSyxFQUNMLFNBQVMsRUFDVCxNQUFNLEVBQ04sU0FBUyxHQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxTQUFTLEVBQWUsTUFBTSw4QkFBOEIsQ0FBQztBQUV0RSxPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLHVCQUF1QixFQUN2Qix3QkFBd0IsR0FDekIsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN4QyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQzs7O0FBUXJFLE1BQU0sT0FBTyxpQkFBaUI7SUFOOUI7UUFPRSx1QkFBa0IsR0FBRyxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ3hDLHdCQUFtQixHQUFHLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDNUUsc0JBQWlCLEdBQUcsTUFBTSxDQUN4Qix1QkFBdUIsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUNuRCxDQUFDO1FBU00sc0JBQWlCLEdBQVksS0FBSyxDQUFDO1FBQ25DLFVBQUssR0FBRyxNQUFNLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNsQyxPQUFFLEdBQUcsTUFBTSxDQUFDLFVBQVUs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SSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztvQkFDM0IsTUFBTSxJQUFJLEdBQUcsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDO29CQUNqRCxJQUFJLENBQUMsMkJBQTJCLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3pDLENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLGlCQUFpQixDQUFDLG9CQUFvQixDQUFDLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDNUQsSUFBSSxDQUFDLGtCQUFrQixDQUFDLG9CQUFvQixDQUFDLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDL0QsQ0FBQzsrR0FyUFUsaUJBQWlCO21HQUFqQixpQkFBaUIsMldDcEM5QixxM0JBaUJBOzs0RkRtQmEsaUJBQWlCO2tCQU43QixTQUFTOytCQUNFLGNBQWMsbUJBR1AsdUJBQXVCLENBQUMsTUFBTTs4QkFTdEMsT0FBTztzQkFBZixLQUFLO2dCQUNHLE9BQU87c0JBQWYsS0FBSztnQkFFMEIsaUJBQWlCO3NCQUFoRCxTQUFTO3VCQUFDLG1CQUFtQjtnQkFDRyxrQkFBa0I7c0JBQWxELFNBQVM7dUJBQUMsb0JBQW9CO2dCQVkzQixhQUFhO3NCQURoQixLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIEAoIyljYWxlbmRhci5jb21wb25lbnQuc2NzcyBTZXB0IDA3LCAyMDIzXHJcbiAqXHJcbiAqIEN1c3RvbSBDYWxlbmRhciBDb21wb25lbnQgdGhhdCBtYW5hZ2VzIHR3byBzaWRlLWJ5LXNpZGVcclxuICogbW9udGggdmlld3Mgd2l0aCBzdXBwb3J0IGZvciBkYXRlIHJhbmdlIHNlbGVjdGlvbiwgaG92ZXJcclxuICogaGlnaGxpZ2h0aW5nLCBhbmQgbmF2aWdhdGlvbiBjb250cm9scy5cclxuICpcclxuICogQGF1dGhvciBBYWthc2ggS3VtYXJcclxuICovXHJcbmltcG9ydCB7XHJcbiAgQWZ0ZXJWaWV3SW5pdCxcclxuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcclxuICBDaGFuZ2VEZXRlY3RvclJlZixcclxuICBDb21wb25lbnQsXHJcbiAgRWxlbWVudFJlZixcclxuICBpbmplY3QsXHJcbiAgSW5wdXQsXHJcbiAgUmVuZGVyZXIyLFxyXG4gIHNpZ25hbCxcclxuICBWaWV3Q2hpbGQsXHJcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IERhdGVSYW5nZSwgTWF0Q2FsZW5kYXIgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9kYXRlcGlja2VyJztcclxuaW1wb3J0IHsgQWN0aXZlRGF0ZSB9IGZyb20gJy4uL21vZGVsL2FjdGl2ZS1kYXRlLm1vZGVsJztcclxuaW1wb3J0IHtcclxuICBnZXREYXRlT2ZOZXh0TW9udGgsXHJcbiAgZ2V0Rmlyc3REYXRlT2ZOZXh0TW9udGgsXHJcbiAgb3ZlcnJpZGVBY3RpdmVEYXRlU2V0dGVyLFxyXG59IGZyb20gJy4uL3V0aWxzL2RhdGUtcGlja2VyLXV0aWxpdGllcyc7XHJcbmltcG9ydCB7IEFDVElWRV9EQVRFX0RFQk9VTkNFIH0gZnJvbSAnLi4vY29uc3RhbnQvZGF0ZS1maWx0ZXItY29uc3QnO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdsaWItY2FsZW5kYXInLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9jYWxlbmRhci5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmxzOiBbJy4vY2FsZW5kYXIuY29tcG9uZW50LmNzcyddLFxyXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgQ2FsZW5kYXJDb21wb25lbnQgaW1wbGVtZW50cyBBZnRlclZpZXdJbml0IHtcclxuICBmaXJzdFZpZXdTdGFydERhdGUgPSBzaWduYWwobmV3IERhdGUoKSk7XHJcbiAgc2Vjb25kVmlld1N0YXJ0RGF0ZSA9IHNpZ25hbChnZXREYXRlT2ZOZXh0TW9udGgodGhpcy5maXJzdFZpZXdTdGFydERhdGUoKSkpO1xyXG4gIHNlY29uZFZpZXdNaW5EYXRlID0gc2lnbmFsKFxyXG4gICAgZ2V0Rmlyc3REYXRlT2ZOZXh0TW9udGgodGhpcy5maXJzdFZpZXdTdGFydERhdGUoKSlcclxuICApO1xyXG5cclxuICBASW5wdXQoKSBtaW5EYXRlITogRGF0ZTtcclxuICBASW5wdXQoKSBtYXhEYXRlITogRGF0ZTtcclxuXHJcbiAgQFZpZXdDaGlsZCgnZmlyc3RDYWxlbmRhclZpZXcnKSBmaXJzdENhbGVuZGFyVmlldyE6IE1hdENhbGVuZGFyPERhdGU+O1xyXG4gIEBWaWV3Q2hpbGQoJ3NlY29uZENhbGVuZGFyVmlldycpIHNlY29uZENhbGVuZGFyVmlldyE6IE1hdENhbGVuZGFyPERhdGU+O1xyXG5cclxuICBwcml2YXRlIF9zZWxlY3RlZERhdGVzITogRGF0ZVJhbmdlPERhdGU+IHwgbnVsbDtcclxuICBwcml2YXRlIGlzQWxsb3dIb3ZlckV2ZW50OiBib29sZWFuID0gZmFsc2U7XHJcbiAgcHJpdmF0ZSBjZHJlZiA9IGluamVjdChDaGFuZ2VEZXRlY3RvclJlZik7XHJcbiAgcHJpdmF0ZSBlbCA9IGluamVjdChFbGVtZW50UmVmKTtcclxuICBwcml2YXRlIHJlbmRlcmVyID0gaW5qZWN0KFJlbmRlcmVyMik7XHJcblxyXG4gIC8qKlxyXG4gICAqIFVwZGF0ZXMgdGhlIHNlbGVjdGVkIGRhdGUgcmFuZ2UgYW5kIHN5bmNocm9uaXplcyBib3RoIGNhbGVuZGFyIHZpZXdzLlxyXG4gICAqL1xyXG4gIEBJbnB1dCgpXHJcbiAgc2V0IHNlbGVjdGVkRGF0ZXMoc2VsZWN0ZWREYXRlczogRGF0ZVJhbmdlPERhdGU+IHwgbnVsbCkge1xyXG4gICAgdGhpcy5fc2VsZWN0ZWREYXRlcyA9IHNlbGVjdGVkRGF0ZXM7XHJcbiAgICBpZiAoIXNlbGVjdGVkRGF0ZXMgfHwgIShzZWxlY3RlZERhdGVzLnN0YXJ0ICYmIHNlbGVjdGVkRGF0ZXMuZW5kKSkgcmV0dXJuO1xyXG5cclxuICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IHNlbGVjdGVkRGF0ZXMuc3RhcnQgPz8gbmV3IERhdGUoKTtcclxuICAgIGNvbnN0IGVuZERhdGUgPSBzZWxlY3RlZERhdGVzLmVuZDtcclxuICAgIHRoaXMuZmlyc3RWaWV3U3RhcnREYXRlLnNldChzdGFydERhdGUpO1xyXG4gICAgdGhpcy5zZWNvbmRWaWV3TWluRGF0ZS5zZXQoZ2V0Rmlyc3REYXRlT2ZOZXh0TW9udGgoc3RhcnREYXRlKSk7XHJcbiAgICBjb25zdCBjb21wdXRlZEVuZERhdGUgPVxyXG4gICAgICBzdGFydERhdGUuZ2V0TW9udGgoKSA9PT0gZW5kRGF0ZS5nZXRNb250aCgpXHJcbiAgICAgICAgPyBnZXREYXRlT2ZOZXh0TW9udGgoZW5kRGF0ZSlcclxuICAgICAgICA6IGVuZERhdGU7XHJcbiAgICB0aGlzLnNlY29uZFZpZXdTdGFydERhdGUuc2V0KGNvbXB1dGVkRW5kRGF0ZSk7XHJcbiAgfVxyXG5cclxuICBnZXQgc2VsZWN0ZWREYXRlcygpIHtcclxuICAgIHJldHVybiB0aGlzLl9zZWxlY3RlZERhdGVzO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogTGlmZWN5Y2xlIGhvb2sgdGhhdCBpcyBjYWxsZWQgYWZ0ZXIgQW5ndWxhciBoYXMgZnVsbHkgaW5pdGlhbGl6ZWRcclxuICAgKiB0aGUgY29tcG9uZW50J3MgdmlldyAoYW5kIGNoaWxkIHZpZXdzKS5cclxuICAgKlxyXG4gICAqIFVzZWQgaGVyZSB0byBhdHRhY2ggaG92ZXIgZXZlbnRzIGFuZCByZWdpc3RlciBhY3RpdmUgZGF0ZSBjaGFuZ2VcclxuICAgKiBsaXN0ZW5lcnMgb25jZSB0aGUgY2FsZW5kYXIgdmlld3MgYXJlIGF2YWlsYWJsZSBpbiB0aGUgRE9NLlxyXG4gICAqL1xyXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcclxuICAgIHRoaXMuYXR0YWNoSG92ZXJFdmVudCgnZmlyc3RDYWxlbmRhclZpZXcnKTtcclxuICAgIHRoaXMuYXR0YWNoSG92ZXJFdmVudCgnc2Vjb25kQ2FsZW5kYXJWaWV3Jyk7XHJcbiAgICB0aGlzLnJlZ2lzdGVyQWN0aXZlRGF0ZUNoYW5nZUV2ZW50cygpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogSGFuZGxlcyBtb250aCBzZWxlY3Rpb24gaW4gdGhlIGZpcnN0IHZpZXcuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZXZlbnQgLSBTZWxlY3RlZCBtb250aCBkYXRlXHJcbiAgICovXHJcbiAgbW9udGhTZWxlY3RlZCh2aWV3TmFtZTogc3RyaW5nKSB7XHJcbiAgICBpZiAodmlld05hbWUgPT09ICdzZWNvbmRDYWxlbmRhclZpZXcnKSB7XHJcbiAgICAgIHRoaXMucmVtb3ZlRGVmYXVsdEZvY3VzKHRoaXMpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5hdHRhY2hIb3ZlckV2ZW50KHZpZXdOYW1lKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFVwZGF0ZXMgdGhlIHNlbGVjdGVkIGRhdGUgcmFuZ2Ugd2hlbiBhIGRhdGUgaXMgY2xpY2tlZC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBkYXRlIC0gRGF0ZSBjbGlja2VkIGJ5IHRoZSB1c2VyXHJcbiAgICovXHJcbiAgdXBkYXRlRGF0ZVJhbmdlU2VsZWN0aW9uKGRhdGU6IERhdGUgfCBudWxsKTogdm9pZCB7XHJcbiAgICBjb25zdCBzZWxlY3RlZERhdGVzID0gdGhpcy5zZWxlY3RlZERhdGVzO1xyXG4gICAgaWYgKFxyXG4gICAgICAhc2VsZWN0ZWREYXRlcyB8fFxyXG4gICAgICAoc2VsZWN0ZWREYXRlcy5zdGFydCAmJiBzZWxlY3RlZERhdGVzLmVuZCkgfHxcclxuICAgICAgKHNlbGVjdGVkRGF0ZXMuc3RhcnQgJiYgZGF0ZSAmJiBzZWxlY3RlZERhdGVzLnN0YXJ0ID4gZGF0ZSlcclxuICAgICkge1xyXG4gICAgICB0aGlzLl9zZWxlY3RlZERhdGVzID0gbmV3IERhdGVSYW5nZTxEYXRlPihkYXRlLCBudWxsKTtcclxuICAgICAgdGhpcy5pc0FsbG93SG92ZXJFdmVudCA9IHRydWU7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aGlzLmlzQWxsb3dIb3ZlckV2ZW50ID0gZmFsc2U7XHJcbiAgICAgIHRoaXMuX3NlbGVjdGVkRGF0ZXMgPSBuZXcgRGF0ZVJhbmdlPERhdGU+KHNlbGVjdGVkRGF0ZXMuc3RhcnQsIGRhdGUpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5jZHJlZi5tYXJrRm9yQ2hlY2soKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJlZ2lzdGVycyBldmVudCBoYW5kbGVycyBmb3IgYWN0aXZlIGRhdGUgY2hhbmdlcyBvbiBib3RoIGNhbGVuZGFyIHZpZXdzLlxyXG4gICAqXHJcbiAgICogVGhpcyBtZXRob2Qgb3ZlcnJpZGVzIHRoZSBkZWZhdWx0IGBhY3RpdmVEYXRlYCBwcm9wZXJ0eSBzZXR0ZXIgb2YgZWFjaFxyXG4gICAqIGNhbGVuZGFyIHZpZXcgdG8gZW5zdXJlIGN1c3RvbSBoYW5kbGVycyBhcmUgZXhlY3V0ZWQgd2hlbmV2ZXIgdGhlXHJcbiAgICogYWN0aXZlIGRhdGUgY2hhbmdlcy5cclxuICAgKi9cclxuICBwcml2YXRlIHJlZ2lzdGVyQWN0aXZlRGF0ZUNoYW5nZUV2ZW50cygpOiB2b2lkIHtcclxuICAgIG92ZXJyaWRlQWN0aXZlRGF0ZVNldHRlcihcclxuICAgICAgdGhpcy5maXJzdENhbGVuZGFyVmlldyxcclxuICAgICAgdGhpcy5jZHJlZixcclxuICAgICAgdGhpcy5vbkZpcnN0Vmlld0FjdGl2ZURhdGVDaGFuZ2UuYmluZCh0aGlzKVxyXG4gICAgKTtcclxuICAgIG92ZXJyaWRlQWN0aXZlRGF0ZVNldHRlcihcclxuICAgICAgdGhpcy5zZWNvbmRDYWxlbmRhclZpZXcsXHJcbiAgICAgIHRoaXMuY2RyZWYsXHJcbiAgICAgIHRoaXMub25TZWNvbmRWaWV3QWN0aXZlRGF0ZUNoYW5nZS5iaW5kKHRoaXMpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogSGFuZGxlcyB0aGUgZXZlbnQgd2hlbiB0aGUgYWN0aXZlIGRhdGUgb2YgdGhlIGZpcnN0IGNhbGVuZGFyIHZpZXcgY2hhbmdlcy5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBhY3RpdmVEYXRlIC0gT2JqZWN0IGNvbnRhaW5pbmcgYHByZXZpb3VzYCBhbmQgYGN1cnJlbnRgIGRhdGUgdmFsdWVzLlxyXG4gICAqL1xyXG4gIHByaXZhdGUgb25GaXJzdFZpZXdBY3RpdmVEYXRlQ2hhbmdlKGFjdGl2ZURhdGU6IEFjdGl2ZURhdGUpOiB2b2lkIHtcclxuICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLmlzUHJldmlvdXMoYWN0aXZlRGF0ZSlcclxuICAgICAgPyAoKSA9PiB0aGlzLmhhbmRsZUZpcnN0Vmlld1ByZXZFdmVudChhY3RpdmVEYXRlKVxyXG4gICAgICA6ICgpID0+IHRoaXMuaGFuZGxlRmlyc3RWaWV3TmV4dEV2ZW50KGFjdGl2ZURhdGUuY3VycmVudCk7XHJcblxyXG4gICAgLy8gRGVsYXkgZXhlY3V0aW9uIGJlY2F1c2UgYWN0aXZlIGRhdGUgZXZlbnQgZmlyZXMgYmVmb3JlIHZpZXcgdXBkYXRlXHJcbiAgICBzZXRUaW1lb3V0KGhhbmRsZXIsIEFDVElWRV9EQVRFX0RFQk9VTkNFKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEhhbmRsZXMgdGhlIGV2ZW50IHdoZW4gdGhlIGFjdGl2ZSBkYXRlIG9mIHRoZSBzZWNvbmQgY2FsZW5kYXIgdmlldyBjaGFuZ2VzLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGFjdGl2ZURhdGUgLSBPYmplY3QgY29udGFpbmluZyBgcHJldmlvdXNgIGFuZCBgY3VycmVudGAgZGF0ZSB2YWx1ZXMuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBvblNlY29uZFZpZXdBY3RpdmVEYXRlQ2hhbmdlKGFjdGl2ZURhdGU6IEFjdGl2ZURhdGUpOiB2b2lkIHtcclxuICAgIHRoaXMuYXR0YWNoSG92ZXJFdmVudCgnc2Vjb25kQ2FsZW5kYXJWaWV3Jyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBIYW5kbGVzIHRoZSBcIm5leHRcIiBuYXZpZ2F0aW9uIGV2ZW50IGZvciB0aGUgZmlyc3QgY2FsZW5kYXIgdmlldy5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBjdXJyRGF0ZSAtIFRoZSBjdXJyZW50bHkgYWN0aXZlIGRhdGUgaW4gdGhlIGZpcnN0IGNhbGVuZGFyIHZpZXcuXHJcbiAgICogQHBhcmFtIGZvcmNlIC0gT3B0aW9uYWwgZmxhZyB0aGF0IGNhbiBiZSB1c2VkIHRvIGVuZm9yY2UgdXBkYXRlcyAobm90IHVzZWQgaW4gY3VycmVudCBsb2dpYykuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBoYW5kbGVGaXJzdFZpZXdOZXh0RXZlbnQoY3VyckRhdGU6IERhdGUsIGZvcmNlPzogYm9vbGVhbik6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMuZmlyc3RDYWxlbmRhclZpZXcuY3VycmVudFZpZXcudG9Mb2NhbGVMb3dlckNhc2UoKSAhPT0gJ21vbnRoJykge1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICB0aGlzLmF0dGFjaEhvdmVyRXZlbnQoJ2ZpcnN0Q2FsZW5kYXJWaWV3Jyk7XHJcbiAgICBjb25zdCBuZXh0TW9udGhEYXRlID0gZ2V0Rmlyc3REYXRlT2ZOZXh0TW9udGgoY3VyckRhdGUpO1xyXG4gICAgbGV0IHNlY29uZFZpZXdBY3RpdmVEYXRlID0gdGhpcy5zZWNvbmRDYWxlbmRhclZpZXcuYWN0aXZlRGF0ZTtcclxuICAgIGlmIChuZXh0TW9udGhEYXRlIDwgc2Vjb25kVmlld0FjdGl2ZURhdGUpIHtcclxuICAgICAgdGhpcy5zZWNvbmRWaWV3TWluRGF0ZS5zZXQobmV4dE1vbnRoRGF0ZSk7XHJcbiAgICAgIHRoaXMuYXR0YWNoSG92ZXJFdmVudCgnc2Vjb25kQ2FsZW5kYXJWaWV3Jyk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIHNlY29uZFZpZXdBY3RpdmVEYXRlID0gZ2V0RGF0ZU9mTmV4dE1vbnRoKGN1cnJEYXRlKTtcclxuICAgIHRoaXMuc2Vjb25kVmlld01pbkRhdGUuc2V0KG5leHRNb250aERhdGUpO1xyXG4gICAgdGhpcy5zZWNvbmRDYWxlbmRhclZpZXcuYWN0aXZlRGF0ZSA9IHNlY29uZFZpZXdBY3RpdmVEYXRlO1xyXG4gICAgdGhpcy5jZHJlZi5kZXRlY3RDaGFuZ2VzKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBIYW5kbGVzIHRoZSBcInByZXZpb3VzXCIgbmF2aWdhdGlvbiBldmVudCBmb3IgdGhlIGZpcnN0IGNhbGVuZGFyIHZpZXcuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gYWN0aXZlRGF0ZSAtIE9iamVjdCBjb250YWluaW5nIGBwcmV2aW91c2AgYW5kIGBjdXJyZW50YCBkYXRlIHZhbHVlcy5cclxuICAgKi9cclxuICBwcml2YXRlIGhhbmRsZUZpcnN0Vmlld1ByZXZFdmVudChhY3RpdmVEYXRlOiBBY3RpdmVEYXRlKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy5maXJzdENhbGVuZGFyVmlldy5jdXJyZW50Vmlldy50b0xvY2FsZUxvd2VyQ2FzZSgpICE9PSAnbW9udGgnKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIHRoaXMuc2Vjb25kVmlld01pbkRhdGUuc2V0KGdldEZpcnN0RGF0ZU9mTmV4dE1vbnRoKGFjdGl2ZURhdGUuY3VycmVudCkpO1xyXG4gICAgdGhpcy5hdHRhY2hIb3ZlckV2ZW50KCdmaXJzdENhbGVuZGFyVmlldycpO1xyXG4gICAgdGhpcy5hdHRhY2hIb3ZlckV2ZW50KCdzZWNvbmRDYWxlbmRhclZpZXcnKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENoZWNrcyB3aGV0aGVyIHRoZSBwcmV2aW91cyBkYXRlIGlzIGdyZWF0ZXIgdGhhbiB0aGUgY3VycmVudCBkYXRlLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGFjdGl2ZURhdGUgLSBPYmplY3QgY29udGFpbmluZyBgcHJldmlvdXNgIGFuZCBgY3VycmVudGAgZGF0ZSB2YWx1ZXMuXHJcbiAgICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBwcmV2aW91cyBkYXRlIGlzIGxhdGVyIHRoYW4gdGhlIGN1cnJlbnQgZGF0ZSwgb3RoZXJ3aXNlIGBmYWxzZWAuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBpc1ByZXZpb3VzKGFjdGl2ZURhdGU6IEFjdGl2ZURhdGUpOiBib29sZWFuIHtcclxuICAgIHJldHVybiBhY3RpdmVEYXRlLnByZXZpb3VzID4gYWN0aXZlRGF0ZS5jdXJyZW50O1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogQXR0YWNoZXMgaG92ZXIgZXZlbnRzIHRvIGFsbCBkYXRlIGNlbGxzIGluIHRoZSBmaXJzdCB2aWV3LlxyXG4gICAqL1xyXG4gIHByaXZhdGUgYXR0YWNoSG92ZXJFdmVudCh2aWV3SWQ6IHN0cmluZykge1xyXG4gICAgY29uc3Qgbm9kZXMgPSB0aGlzLmVsLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbChcclxuICAgICAgYCMke3ZpZXdJZH0gLm1hdC1jYWxlbmRhci1ib2R5LWNlbGxgXHJcbiAgICApO1xyXG4gICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmFkZEhvdmVyRXZlbnRzKG5vZGVzKSwgQUNUSVZFX0RBVEVfREVCT1VOQ0UpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmVtb3ZlcyBhY3RpdmUgZm9jdXMgZnJvbSB0aGUgc2Vjb25kIHZpZXcuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gY2xhc3NSZWYgLSBSZWZlcmVuY2UgdG8gdGhpcyBjb21wb25lbnRcclxuICAgKi9cclxuICBwcml2YXRlIHJlbW92ZURlZmF1bHRGb2N1cyhjbGFzc1JlZjogQ2FsZW5kYXJDb21wb25lbnQpOiB2b2lkIHtcclxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xyXG4gICAgICBjb25zdCBidG46IEhUTUxCdXR0b25FbGVtZW50W10gPVxyXG4gICAgICAgIGNsYXNzUmVmLmVsLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbChcclxuICAgICAgICAgICcjc2Vjb25kQ2FsZW5kYXJWaWV3IGJ1dHRvbi5tYXQtY2FsZW5kYXItYm9keS1hY3RpdmUnXHJcbiAgICAgICAgKTtcclxuICAgICAgaWYgKGJ0bj8ubGVuZ3RoKSB7XHJcbiAgICAgICAgYnRuWzBdLmJsdXIoKTtcclxuICAgICAgfVxyXG4gICAgfSwgMSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBVcGRhdGVzIHRoZSBzZWxlY3Rpb24gcmFuZ2UgZHluYW1pY2FsbHkgb24gaG92ZXIuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZGF0ZSAtIEhvdmVyZWQgZGF0ZVxyXG4gICAqL1xyXG4gIHByaXZhdGUgdXBkYXRlU2VsZWN0aW9uT25Nb3VzZUhvdmVyKGRhdGU6IERhdGUpOiB2b2lkIHtcclxuICAgIGNvbnN0IHNlbGVjdGVkRGF0ZXMgPSB0aGlzLnNlbGVjdGVkRGF0ZXM7XHJcbiAgICBpZiAoc2VsZWN0ZWREYXRlcz8uc3RhcnQgJiYgZGF0ZSAmJiBzZWxlY3RlZERhdGVzLnN0YXJ0IDwgZGF0ZSkge1xyXG4gICAgICBjb25zdCBkYXRlUmFuZ2U6IERhdGVSYW5nZTxEYXRlPiA9IG5ldyBEYXRlUmFuZ2U8RGF0ZT4oXHJcbiAgICAgICAgc2VsZWN0ZWREYXRlcy5zdGFydCxcclxuICAgICAgICBkYXRlXHJcbiAgICAgICk7XHJcbiAgICAgIHRoaXMuZmlyc3RDYWxlbmRhclZpZXcuc2VsZWN0ZWQgPSBkYXRlUmFuZ2U7XHJcbiAgICAgIHRoaXMuc2Vjb25kQ2FsZW5kYXJWaWV3LnNlbGVjdGVkID0gZGF0ZVJhbmdlO1xyXG4gICAgICB0aGlzLmZpcnN0Q2FsZW5kYXJWaWV3WydfY2hhbmdlRGV0ZWN0b3JSZWYnXS5tYXJrRm9yQ2hlY2soKTtcclxuICAgICAgdGhpcy5zZWNvbmRDYWxlbmRhclZpZXdbJ19jaGFuZ2VEZXRlY3RvclJlZiddLm1hcmtGb3JDaGVjaygpO1xyXG4gICAgICB0aGlzLmlzQWxsb3dIb3ZlckV2ZW50ID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEF0dGFjaGVzIGhvdmVyIGV2ZW50cyB0byBnaXZlbiBub2RlcyB0byB1cGRhdGUgcmFuZ2Ugc2VsZWN0aW9uLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIG5vZGVzIC0gRGF0ZSBjZWxsIG5vZGVzXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBhZGRIb3ZlckV2ZW50cyhub2RlczogYW55KTogdm9pZCB7XHJcbiAgICBpZiAoIW5vZGVzKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIEFycmF5LmZyb20obm9kZXMpLmZvckVhY2goKGJ1dHRvbikgPT4ge1xyXG4gICAgICB0aGlzLnJlbmRlcmVyLmxpc3RlbihidXR0b24sICdtb3VzZW92ZXInLCAoZXZlbnQpID0+IHtcclxuICAgICAgICBpZiAodGhpcy5pc0FsbG93SG92ZXJFdmVudCkge1xyXG4gICAgICAgICAgY29uc3QgZGF0ZSA9IG5ldyBEYXRlKGV2ZW50LnRhcmdldFsnYXJpYUxhYmVsJ10pO1xyXG4gICAgICAgICAgdGhpcy51cGRhdGVTZWxlY3Rpb25Pbk1vdXNlSG92ZXIoZGF0ZSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH0pO1xyXG4gICAgdGhpcy5maXJzdENhbGVuZGFyVmlld1snX2NoYW5nZURldGVjdG9yUmVmJ10ubWFya0ZvckNoZWNrKCk7XHJcbiAgICB0aGlzLnNlY29uZENhbGVuZGFyVmlld1snX2NoYW5nZURldGVjdG9yUmVmJ10ubWFya0ZvckNoZWNrKCk7XHJcbiAgfVxyXG59XHJcbiIsIjwhLS0qKlxyXG4gKiBAKCMpY2FsZW5kYXIuY29tcG9uZW50Lmh0bWwgU2VwdCAwNywgMjAyM1xyXG5cclxuICogQGF1dGhvciBBYWthc2ggS3VtYXJcclxuICotLT5cclxuPGRpdiBjbGFzcz1cImNhbGVuZGFyLWNvbnRhaW5lclwiPlxyXG4gIDxkaXYgY2xhc3M9XCJmaXJzdC12aWV3XCI+XHJcbiAgICA8bWF0LWNhbGVuZGFyIGlkPVwiZmlyc3RDYWxlbmRhclZpZXdcIiAjZmlyc3RDYWxlbmRhclZpZXcgW3N0YXJ0QXRdPVwiZmlyc3RWaWV3U3RhcnREYXRlKClcIiBbc2VsZWN0ZWRdPVwic2VsZWN0ZWREYXRlc1wiXHJcbiAgICAgIChzZWxlY3RlZENoYW5nZSk9XCJ1cGRhdGVEYXRlUmFuZ2VTZWxlY3Rpb24oJGV2ZW50KVwiIChtb250aFNlbGVjdGVkKT1cIm1vbnRoU2VsZWN0ZWQoJ2ZpcnN0Q2FsZW5kYXJWaWV3JylcIiBbbWluRGF0ZV09XCJtaW5EYXRlXCJcclxuICAgICAgW21heERhdGVdPVwibWF4RGF0ZVwiPjwvbWF0LWNhbGVuZGFyPlxyXG4gIDwvZGl2PlxyXG4gIDxkaXYgY2xhc3M9XCJzZWNvbmQtdmlld1wiPlxyXG4gICAgPG1hdC1jYWxlbmRhciBpZD1cInNlY29uZENhbGVuZGFyVmlld1wiICNzZWNvbmRDYWxlbmRhclZpZXcgW3N0YXJ0QXRdPVwic2Vjb25kVmlld1N0YXJ0RGF0ZSgpXCIgW21pbkRhdGVdPVwic2Vjb25kVmlld01pbkRhdGUoKVwiXHJcbiAgICAgIFttYXhEYXRlXT1cIm1heERhdGVcIiBbc2VsZWN0ZWRdPVwic2VsZWN0ZWREYXRlc1wiIChzZWxlY3RlZENoYW5nZSk9XCJ1cGRhdGVEYXRlUmFuZ2VTZWxlY3Rpb24oJGV2ZW50KVwiXHJcbiAgICAgIChtb250aFNlbGVjdGVkKT1cIm1vbnRoU2VsZWN0ZWQoJ3NlY29uZENhbGVuZGFyVmlldycpXCI+PC9tYXQtY2FsZW5kYXI+XHJcbiAgPC9kaXY+XHJcbjwvZGl2PlxyXG4iXX0=