UNPKG

ng-material-date-range-picker

Version:
238 lines 35.5 kB
/** * @(#)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=