UNPKG

@hxui/angular

Version:

An Angular library based on the [HXUI design system](https://hxui.io).

194 lines 43.8 kB
import { Component, EventEmitter, HostBinding, Input, Output } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { Visibility } from '../enums'; import { DatepickerViewModeEnum } from './datepicker.model'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; export class DatepickerComponent { constructor() { this.update = new EventEmitter(); this.viewMode$ = new BehaviorSubject(DatepickerViewModeEnum.Days); this.DatepickerViewModeEnum = DatepickerViewModeEnum; this.visibilityEnum = Visibility; this.visibility = Visibility.Hidden; this.days = new Array(); this.week = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]; this.months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; this.years = new Array(); this.cellCount = 41; this.yearCellCount = 20; } get classes() { return 'hxui-reset hx-card hxa-datepicker-calendar'; } ngOnInit() { const date = this.selectedDate ? this.selectedDate : new Date(); this.presentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); this.viewDate = this.viewDate || new Date(date.getFullYear(), date.getMonth()); this.renderCalendar(); } ngOnChanges(changes) { // update view date if (changes.selectedDate && changes.selectedDate.currentValue) { this.viewDate = new Date(this.selectedDate.getFullYear(), this.selectedDate.getMonth()); } } /** update and emit selected date */ setSelectedDate(date) { if (!this.isInvalidDay(date)) { this.selectedDate = date; this.update.emit(date); } } // Populates the days array with the current month, and completes the view with partial dates from sibling months renderCalendar() { for (let i = 0; i <= this.cellCount; i++) { // date will be set to the first day of the month set in this.viewDate const date = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth()); // Shifts the week to start from Monday, rather than Sunday, this causes the index to start at 1 const dayOffset = date.getDay() === 0 ? 7 : date.getDay(); this.days[i] = new Date(date.setDate(2 - dayOffset + i)); } } next() { if (this.viewMode$.value === DatepickerViewModeEnum.Days) { this.nextMonth(); } else if (this.viewMode$.value === DatepickerViewModeEnum.Years) { this.nextYear(); } } previous() { if (this.viewMode$.value === DatepickerViewModeEnum.Days) { this.previousMonth(); } else if (this.viewMode$.value === DatepickerViewModeEnum.Years) { this.previousYear(); } } previousMonth() { this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() - 1); this.renderCalendar(); } nextMonth() { this.viewDate = new Date(this.viewDate.getFullYear(), this.viewDate.getMonth() + 1); this.renderCalendar(); } isCurrentMonth(inputDate) { return inputDate.getMonth() === this.viewDate.getMonth(); } isCurrentDay(inputDate) { return inputDate.getTime() === this.presentDate.getTime(); } isSelectedDay(inputDate) { if (this.selectedDate) { return inputDate.getTime() === this.selectedDate.getTime(); } return false; } isInvalidDay(inputDate) { return this.validators .map(fn => fn(inputDate)) .reduce((prev, next) => prev || next, false); } isCurrentYear(year) { return year === this.presentDate.getFullYear(); } isSelectedYear(year) { return year === this.viewDate.getFullYear(); } isInvalidYear(year) { const newDate = new Date(new Date(this.viewDate.getTime()).setFullYear(year)); return this.validators .map(fn => fn(newDate)) .reduce((prev, next) => prev || next, false); } isCurrentMonthByIndex(month) { return month === this.presentDate.getMonth(); } isSelectedMonthByIndex(month) { return month === this.viewDate.getMonth(); } isInvalidMonthByIndex(month) { const newDate = new Date(new Date(this.viewDate.getTime()).setMonth(month)); return this.validators .map(fn => fn(newDate)) .reduce((prev, next) => prev || next, false); } previousYear() { this.getYearCollection(this.years[0] - this.yearCellCount); } nextYear() { this.getYearCollection(this.years[0] + this.yearCellCount); } setYear(year) { if (!this.isInvalidYear(year)) { this.viewDate.setFullYear(year); this.renderCalendar(); this.toggleYear(); } } setMonth(month) { if (!this.isInvalidMonthByIndex(month)) { this.viewDate.setMonth(month); this.renderCalendar(); this.viewMode$.next(DatepickerViewModeEnum.Days); } } toggleYear() { this.viewMode$.next(this.viewMode$.value === DatepickerViewModeEnum.Years ? DatepickerViewModeEnum.Months : DatepickerViewModeEnum.Years); if (this.viewMode$.value === DatepickerViewModeEnum.Years) { this.getYearCollection(); } } getYearCollection(startFrom = null) { const yearsBeforeActive = 7; const activeYear = startFrom ? startFrom : this.viewDate.getFullYear() - yearsBeforeActive; this.years = []; for (let i = 0; i < this.yearCellCount; i++) { this.years.push(activeYear + i); } } } DatepickerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: DatepickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); DatepickerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: DatepickerComponent, selector: "hxa-datepicker", inputs: { selectedDate: "selectedDate", validators: "validators" }, outputs: { update: "update" }, host: { properties: { "class": "this.classes" } }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"{ viewMode$ : viewMode$ | async } as observables\">\r\n <div class=\"hx-card-header hxa-datepicker-calendar__header\">\r\n <div class=\"hxa-datepicker-calendar__month\">\r\n <button\r\n class=\"hxa-datepicker-calendar__icon hx-button is-transparent is-large\"\r\n *ngIf=\"observables.viewMode$ !== DatepickerViewModeEnum.Months\"\r\n title=\"Previous Month\"\r\n (click)=\"previous()\"\r\n >\r\n <span class=\"hx-icon-control\">\r\n <i class=\"hx-icon icon-angle-left is-medium\"></i>\r\n </span>\r\n </button>\r\n <div class=\"hxa-datepicker-calendar__month-title\">\r\n <button\r\n class=\"hx-button is-flat is-info is-large\"\r\n (click)=\"toggleYear()\"\r\n >\r\n {{ (observables.viewMode$ === DatepickerViewModeEnum.Years) ? years[0]\r\n + ' - ' + years[years.length-1] : viewDate?.toLocaleString(\"en-au\", {\r\n month: \"long\", year: \"numeric\"}) }}\r\n </button>\r\n </div>\r\n <button\r\n class=\"hxa-datepicker-calendar__icon hx-button is-transparent is-large\"\r\n *ngIf=\"observables.viewMode$ !== DatepickerViewModeEnum.Months\"\r\n title=\"Next Month\"\r\n (click)=\"next()\"\r\n >\r\n <span class=\"hx-icon-control\">\r\n <i class=\"hx-icon icon-angle-right is-medium\"></i>\r\n </span>\r\n </button>\r\n </div>\r\n <div\r\n class=\"hxa-datepicker-calendar__week\"\r\n *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Days\"\r\n >\r\n <div\r\n class=\"hxa-datepicker-calendar__weekday\"\r\n *ngFor=\"let weekday of week\"\r\n >\r\n {{weekday | slice:0:3}}\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"hxa-datepicker-calendar__contents\">\r\n <ng-container *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Days\">\r\n <button\r\n class=\"hxa-datepicker-calendar__day hx-button is-transparent\"\r\n *ngFor=\"let day of days\"\r\n [ngClass]=\"{\r\n 'hxa-datepicker-calendar__day-siblingmonth': !isCurrentMonth(day),\r\n 'hxa-datepicker-calendar__day-currentday': isCurrentDay(day),\r\n 'hxa-datepicker-calendar__day-selectedday': isSelectedDay(day),\r\n 'hxa-datepicker-calendar__day-invalidday': isInvalidDay(day)\r\n }\"\r\n (click)=\"setSelectedDate(day)\"\r\n >\r\n {{day.getDate()}}\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Months\"\r\n >\r\n <button\r\n class=\"hxa-datepicker-calendar__months hx-button is-transparent\"\r\n *ngFor=\"let month of months; let indexOfMth=index;\"\r\n [ngClass]=\"{\r\n 'hxa-datepicker-calendar__day-currentday': isCurrentMonthByIndex(indexOfMth),\r\n 'hxa-datepicker-calendar__day-selectedday': isSelectedMonthByIndex(indexOfMth),\r\n 'hxa-datepicker-calendar__day-invalidday': isInvalidMonthByIndex(indexOfMth)\r\n }\"\r\n (click)=\"setMonth(indexOfMth)\"\r\n >\r\n {{ month }}\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Years\"\r\n >\r\n <button\r\n *ngFor=\"let year of years\"\r\n class=\"hxa-datepicker-calendar__year hx-button is-transparent\"\r\n [ngClass]=\"{\r\n 'hxa-datepicker-calendar__day-currentday': isCurrentYear(year),\r\n 'hxa-datepicker-calendar__day-selectedday': isSelectedYear(year),\r\n 'hxa-datepicker-calendar__day-invalidday': isInvalidYear(year)\r\n }\"\r\n (click)=\"setYear(year)\"\r\n >\r\n {{ year }}\r\n </button>\r\n </ng-container>\r\n </div>\r\n</ng-container>\r\n", styles: [":host.hx-card{border:unset}:host.hx-card:hover,:host.hx-card.is-hovered{box-shadow:none}.hxa-datepicker-calendar:host{width:19rem;height:21rem;display:flex;flex-direction:column;font-size:1rem;border:unset}.hxa-datepicker-calendar__header{padding:.5rem .5rem 0;flex-direction:column;align-items:stretch}.hxa-datepicker-calendar__icon.hx-button.is-transparent:hover{color:#000}.hxa-datepicker-calendar__icon .hx-icon{color:#0d4d78}.hxa-datepicker-calendar__month{display:flex;align-items:center;justify-content:space-between;text-align:center;margin-bottom:.5rem}.hxa-datepicker-calendar__month-title{font-size:1.25em;font-weight:400;flex:3;display:flex;justify-content:center;align-items:center;color:#0d4d78}.hxa-datepicker-calendar__week{display:flex;width:100%;text-align:center}.hxa-datepicker-calendar__weekday{flex:1;color:#0d4d78;font-size:.85em}.hxa-datepicker-calendar__contents{padding:.5rem;background-color:#f6f6f980;display:flex;flex-flow:row wrap;flex:1;justify-content:space-around;align-content:space-around}.hxa-datepicker-calendar__year{margin:.5rem}.hxa-datepicker-calendar__year.hx-button{padding:.5rem}.hxa-datepicker-calendar__months{margin:.7rem}.hxa-datepicker-calendar__months.hx-button{padding:.5rem;font-weight:500}.hxa-datepicker-calendar__day,.hxa-datepicker-calendar__year,.hxa-datepicker-calendar__months{flex:1 1 14%;height:16.666%;display:flex;justify-content:center;align-items:center}.hxa-datepicker-calendar__day.hx-button.is-transparent,.hxa-datepicker-calendar__year.hx-button.is-transparent,.hxa-datepicker-calendar__months.hx-button.is-transparent{font-weight:400;padding:0;color:#41b987}.hxa-datepicker-calendar__day-siblingmonth.hx-button.is-transparent,.hxa-datepicker-calendar__year-siblingmonth.hx-button.is-transparent,.hxa-datepicker-calendar__months-siblingmonth.hx-button.is-transparent{color:#3b3b3b;font-weight:100}.hxa-datepicker-calendar__day-selectedday.hx-button.is-transparent,.hxa-datepicker-calendar__year-selectedday.hx-button.is-transparent,.hxa-datepicker-calendar__months-selectedday.hx-button.is-transparent{color:#fff;background:#41b987}.hxa-datepicker-calendar__day-invalidday.hx-button.is-transparent,.hxa-datepicker-calendar__year-invalidday.hx-button.is-transparent,.hxa-datepicker-calendar__months-invalidday.hx-button.is-transparent{color:#e0e0e1;pointer-events:none}.hxa-datepicker-calendar__day-currentday.hx-button.is-transparent,.hxa-datepicker-calendar__year-currentday.hx-button.is-transparent,.hxa-datepicker-calendar__months-currentday.hx-button.is-transparent{border:2px solid #41b987}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], pipes: { "async": i1.AsyncPipe, "slice": i1.SlicePipe } }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: DatepickerComponent, decorators: [{ type: Component, args: [{ selector: 'hxa-datepicker', template: "<ng-container *ngIf=\"{ viewMode$ : viewMode$ | async } as observables\">\r\n <div class=\"hx-card-header hxa-datepicker-calendar__header\">\r\n <div class=\"hxa-datepicker-calendar__month\">\r\n <button\r\n class=\"hxa-datepicker-calendar__icon hx-button is-transparent is-large\"\r\n *ngIf=\"observables.viewMode$ !== DatepickerViewModeEnum.Months\"\r\n title=\"Previous Month\"\r\n (click)=\"previous()\"\r\n >\r\n <span class=\"hx-icon-control\">\r\n <i class=\"hx-icon icon-angle-left is-medium\"></i>\r\n </span>\r\n </button>\r\n <div class=\"hxa-datepicker-calendar__month-title\">\r\n <button\r\n class=\"hx-button is-flat is-info is-large\"\r\n (click)=\"toggleYear()\"\r\n >\r\n {{ (observables.viewMode$ === DatepickerViewModeEnum.Years) ? years[0]\r\n + ' - ' + years[years.length-1] : viewDate?.toLocaleString(\"en-au\", {\r\n month: \"long\", year: \"numeric\"}) }}\r\n </button>\r\n </div>\r\n <button\r\n class=\"hxa-datepicker-calendar__icon hx-button is-transparent is-large\"\r\n *ngIf=\"observables.viewMode$ !== DatepickerViewModeEnum.Months\"\r\n title=\"Next Month\"\r\n (click)=\"next()\"\r\n >\r\n <span class=\"hx-icon-control\">\r\n <i class=\"hx-icon icon-angle-right is-medium\"></i>\r\n </span>\r\n </button>\r\n </div>\r\n <div\r\n class=\"hxa-datepicker-calendar__week\"\r\n *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Days\"\r\n >\r\n <div\r\n class=\"hxa-datepicker-calendar__weekday\"\r\n *ngFor=\"let weekday of week\"\r\n >\r\n {{weekday | slice:0:3}}\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"hxa-datepicker-calendar__contents\">\r\n <ng-container *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Days\">\r\n <button\r\n class=\"hxa-datepicker-calendar__day hx-button is-transparent\"\r\n *ngFor=\"let day of days\"\r\n [ngClass]=\"{\r\n 'hxa-datepicker-calendar__day-siblingmonth': !isCurrentMonth(day),\r\n 'hxa-datepicker-calendar__day-currentday': isCurrentDay(day),\r\n 'hxa-datepicker-calendar__day-selectedday': isSelectedDay(day),\r\n 'hxa-datepicker-calendar__day-invalidday': isInvalidDay(day)\r\n }\"\r\n (click)=\"setSelectedDate(day)\"\r\n >\r\n {{day.getDate()}}\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Months\"\r\n >\r\n <button\r\n class=\"hxa-datepicker-calendar__months hx-button is-transparent\"\r\n *ngFor=\"let month of months; let indexOfMth=index;\"\r\n [ngClass]=\"{\r\n 'hxa-datepicker-calendar__day-currentday': isCurrentMonthByIndex(indexOfMth),\r\n 'hxa-datepicker-calendar__day-selectedday': isSelectedMonthByIndex(indexOfMth),\r\n 'hxa-datepicker-calendar__day-invalidday': isInvalidMonthByIndex(indexOfMth)\r\n }\"\r\n (click)=\"setMonth(indexOfMth)\"\r\n >\r\n {{ month }}\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"observables.viewMode$ === DatepickerViewModeEnum.Years\"\r\n >\r\n <button\r\n *ngFor=\"let year of years\"\r\n class=\"hxa-datepicker-calendar__year hx-button is-transparent\"\r\n [ngClass]=\"{\r\n 'hxa-datepicker-calendar__day-currentday': isCurrentYear(year),\r\n 'hxa-datepicker-calendar__day-selectedday': isSelectedYear(year),\r\n 'hxa-datepicker-calendar__day-invalidday': isInvalidYear(year)\r\n }\"\r\n (click)=\"setYear(year)\"\r\n >\r\n {{ year }}\r\n </button>\r\n </ng-container>\r\n </div>\r\n</ng-container>\r\n", styles: [":host.hx-card{border:unset}:host.hx-card:hover,:host.hx-card.is-hovered{box-shadow:none}.hxa-datepicker-calendar:host{width:19rem;height:21rem;display:flex;flex-direction:column;font-size:1rem;border:unset}.hxa-datepicker-calendar__header{padding:.5rem .5rem 0;flex-direction:column;align-items:stretch}.hxa-datepicker-calendar__icon.hx-button.is-transparent:hover{color:#000}.hxa-datepicker-calendar__icon .hx-icon{color:#0d4d78}.hxa-datepicker-calendar__month{display:flex;align-items:center;justify-content:space-between;text-align:center;margin-bottom:.5rem}.hxa-datepicker-calendar__month-title{font-size:1.25em;font-weight:400;flex:3;display:flex;justify-content:center;align-items:center;color:#0d4d78}.hxa-datepicker-calendar__week{display:flex;width:100%;text-align:center}.hxa-datepicker-calendar__weekday{flex:1;color:#0d4d78;font-size:.85em}.hxa-datepicker-calendar__contents{padding:.5rem;background-color:#f6f6f980;display:flex;flex-flow:row wrap;flex:1;justify-content:space-around;align-content:space-around}.hxa-datepicker-calendar__year{margin:.5rem}.hxa-datepicker-calendar__year.hx-button{padding:.5rem}.hxa-datepicker-calendar__months{margin:.7rem}.hxa-datepicker-calendar__months.hx-button{padding:.5rem;font-weight:500}.hxa-datepicker-calendar__day,.hxa-datepicker-calendar__year,.hxa-datepicker-calendar__months{flex:1 1 14%;height:16.666%;display:flex;justify-content:center;align-items:center}.hxa-datepicker-calendar__day.hx-button.is-transparent,.hxa-datepicker-calendar__year.hx-button.is-transparent,.hxa-datepicker-calendar__months.hx-button.is-transparent{font-weight:400;padding:0;color:#41b987}.hxa-datepicker-calendar__day-siblingmonth.hx-button.is-transparent,.hxa-datepicker-calendar__year-siblingmonth.hx-button.is-transparent,.hxa-datepicker-calendar__months-siblingmonth.hx-button.is-transparent{color:#3b3b3b;font-weight:100}.hxa-datepicker-calendar__day-selectedday.hx-button.is-transparent,.hxa-datepicker-calendar__year-selectedday.hx-button.is-transparent,.hxa-datepicker-calendar__months-selectedday.hx-button.is-transparent{color:#fff;background:#41b987}.hxa-datepicker-calendar__day-invalidday.hx-button.is-transparent,.hxa-datepicker-calendar__year-invalidday.hx-button.is-transparent,.hxa-datepicker-calendar__months-invalidday.hx-button.is-transparent{color:#e0e0e1;pointer-events:none}.hxa-datepicker-calendar__day-currentday.hx-button.is-transparent,.hxa-datepicker-calendar__year-currentday.hx-button.is-transparent,.hxa-datepicker-calendar__months-currentday.hx-button.is-transparent{border:2px solid #41b987}\n"] }] }], propDecorators: { classes: [{ type: HostBinding, args: ['class'] }], selectedDate: [{ type: Input }], validators: [{ type: Input }], update: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZXBpY2tlci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9oeC11aS9zcmMvbGliL2RhdGVwaWNrZXIvZGF0ZXBpY2tlci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9oeC11aS9zcmMvbGliL2RhdGVwaWNrZXIvZGF0ZXBpY2tlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULFlBQVksRUFDWixXQUFXLEVBQ1gsS0FBSyxFQUdMLE1BQU0sRUFFUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDdEMsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7OztBQU81RCxNQUFNLE9BQU8sbUJBQW1CO0lBTGhDO1FBa0JFLFdBQU0sR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBRWxDLGNBQVMsR0FBRyxJQUFJLGVBQWUsQ0FDN0Isc0JBQXNCLENBQUMsSUFBSSxDQUM1QixDQUFDO1FBQ0YsMkJBQXNCLEdBQUcsc0JBQXNCLENBQUM7UUFDaEQsbUJBQWMsR0FBRyxVQUFVLENBQUM7UUFDNUIsZUFBVSxHQUFlLFVBQVUsQ0FBQyxNQUFNLENBQUM7UUFFM0MsU0FBSSxHQUFnQixJQUFJLEtBQUssRUFBUSxDQUFDO1FBQ3RDLFNBQUksR0FBa0I7WUFDcEIsUUFBUTtZQUNSLFNBQVM7WUFDVCxXQUFXO1lBQ1gsVUFBVTtZQUNWLFFBQVE7WUFDUixVQUFVO1lBQ1YsUUFBUTtTQUNULENBQUM7UUFDRixXQUFNLEdBQWtCO1lBQ3RCLEtBQUs7WUFDTCxLQUFLO1lBQ0wsS0FBSztZQUNMLEtBQUs7WUFDTCxLQUFLO1lBQ0wsS0FBSztZQUNMLEtBQUs7WUFD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RW1pdHRlcixcclxuICBIb3N0QmluZGluZyxcclxuICBJbnB1dCxcclxuICBPbkNoYW5nZXMsXHJcbiAgT25Jbml0LFxyXG4gIE91dHB1dCxcclxuICBTaW1wbGVDaGFuZ2VzXHJcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCB9IGZyb20gJ3J4anMnO1xyXG5pbXBvcnQgeyBWaXNpYmlsaXR5IH0gZnJvbSAnLi4vZW51bXMnO1xyXG5pbXBvcnQgeyBEYXRlcGlja2VyVmlld01vZGVFbnVtIH0gZnJvbSAnLi9kYXRlcGlja2VyLm1vZGVsJztcclxuXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnaHhhLWRhdGVwaWNrZXInLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9kYXRlcGlja2VyLmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybHM6IFsnLi9kYXRlcGlja2VyLmNvbXBvbmVudC5zY3NzJ11cclxufSlcclxuZXhwb3J0IGNsYXNzIERhdGVwaWNrZXJDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uQ2hhbmdlcyB7XHJcbiAgQEhvc3RCaW5kaW5nKCdjbGFzcycpXHJcbiAgZ2V0IGNsYXNzZXMoKSB7XHJcbiAgICByZXR1cm4gJ2h4dWktcmVzZXQgaHgtY2FyZCBoeGEtZGF0ZXBpY2tlci1jYWxlbmRhcic7XHJcbiAgfVxyXG5cclxuICBASW5wdXQoKVxyXG4gIHNlbGVjdGVkRGF0ZTogRGF0ZTtcclxuXHJcbiAgQElucHV0KClcclxuICB2YWxpZGF0b3JzOiBBcnJheTwoZGF0ZTogRGF0ZSkgPT4gYm9vbGVhbj47XHJcblxyXG4gIEBPdXRwdXQoKVxyXG4gIHVwZGF0ZSA9IG5ldyBFdmVudEVtaXR0ZXI8RGF0ZT4oKTtcclxuXHJcbiAgdmlld01vZGUkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxEYXRlcGlja2VyVmlld01vZGVFbnVtPihcclxuICAgIERhdGVwaWNrZXJWaWV3TW9kZUVudW0uRGF5c1xyXG4gICk7XHJcbiAgRGF0ZXBpY2tlclZpZXdNb2RlRW51bSA9IERhdGVwaWNrZXJWaWV3TW9kZUVudW07XHJcbiAgdmlzaWJpbGl0eUVudW0gPSBWaXNpYmlsaXR5O1xyXG4gIHZpc2liaWxpdHk6IFZpc2liaWxpdHkgPSBWaXNpYmlsaXR5LkhpZGRlbjtcclxuICB2aWV3RGF0ZTogRGF0ZTtcclxuICBkYXlzOiBBcnJheTxEYXRlPiA9IG5ldyBBcnJheTxEYXRlPigpO1xyXG4gIHdlZWs6IEFycmF5PHN0cmluZz4gPSBbXHJcbiAgICAnTW9uZGF5JyxcclxuICAgICdUdWVzZGF5JyxcclxuICAgICdXZWRuZXNkYXknLFxyXG4gICAgJ1RodXJzZGF5JyxcclxuICAgICdGcmlkYXknLFxyXG4gICAgJ1NhdHVyZGF5JyxcclxuICAgICdTdW5kYXknXHJcbiAgXTtcclxuICBtb250aHM6IEFycmF5PHN0cmluZz4gPSBbXHJcbiAgICAnSmFuJyxcclxuICAgICdGZWInLFxyXG4gICAgJ01hcicsXHJcbiAgICAnQXByJyxcclxuICAgICdNYXknLFxyXG4gICAgJ0p1bicsXHJcbiAgICAnSnVsJyxcclxuICAgICdBdWcnLFxyXG4gICAgJ1NlcCcsXHJcbiAgICAnT2N0JyxcclxuICAgICdOb3YnLFxyXG4gICAgJ0RlYydcclxuICBdO1xyXG4gIHllYXJzOiBBcnJheTxudW1iZXI+ID0gbmV3IEFycmF5PG51bWJlcj4oKTtcclxuICBwcml2YXRlIHByZXNlbnREYXRlOiBEYXRlO1xyXG4gIHByaXZhdGUgY2VsbENvdW50ID0gNDE7XHJcbiAgcHJpdmF0ZSB5ZWFyQ2VsbENvdW50ID0gMjA7XHJcblxyXG4gIG5nT25Jbml0KCk6IHZvaWQge1xyXG4gICAgY29uc3QgZGF0ZTogRGF0ZSA9IHRoaXMuc2VsZWN0ZWREYXRlID8gdGhpcy5zZWxlY3RlZERhdGUgOiBuZXcgRGF0ZSgpO1xyXG4gICAgdGhpcy5wcmVzZW50RGF0ZSA9IG5ldyBEYXRlKFxyXG4gICAgICBkYXRlLmdldEZ1bGxZZWFyKCksXHJcbiAgICAgIGRhdGUuZ2V0TW9udGgoKSxcclxuICAgICAgZGF0ZS5nZXREYXRlKClcclxuICAgICk7XHJcbiAgICB0aGlzLnZpZXdEYXRlID1cclxuICAgICAgdGhpcy52aWV3RGF0ZSB8fCBuZXcgRGF0ZShkYXRlLmdldEZ1bGxZZWFyKCksIGRhdGUuZ2V0TW9udGgoKSk7XHJcbiAgICB0aGlzLnJlbmRlckNhbGVuZGFyKCk7XHJcbiAgfVxyXG5cclxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XHJcbiAgICAvLyB1cGRhdGUgdmlldyBkYXRlXHJcbiAgICBpZiAoY2hhbmdlcy5zZWxlY3RlZERhdGUgJiYgY2hhbmdlcy5zZWxlY3RlZERhdGUuY3VycmVudFZhbHVlKSB7XHJcbiAgICAgIHRoaXMudmlld0RhdGUgPSBuZXcgRGF0ZShcclxuICAgICAgICB0aGlzLnNlbGVjdGVkRGF0ZS5nZXRGdWxsWWVhcigpLFxyXG4gICAgICAgIHRoaXMuc2VsZWN0ZWREYXRlLmdldE1vbnRoKClcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKiB1cGRhdGUgYW5kIGVtaXQgc2VsZWN0ZWQgZGF0ZSAgKi9cclxuICBwdWJsaWMgc2V0U2VsZWN0ZWREYXRlKGRhdGU6IERhdGUpOiB2b2lkIHtcclxuICAgIGlmICghdGhpcy5pc0ludmFsaWREYXkoZGF0ZSkpIHtcclxuICAgICAgdGhpcy5zZWxlY3RlZERhdGUgPSBkYXRlO1xyXG4gICAgICB0aGlzLnVwZGF0ZS5lbWl0KGRhdGUpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUG9wdWxhdGVzIHRoZSBkYXlzIGFycmF5IHdpdGggdGhlIGN1cnJlbnQgbW9udGgsIGFuZCBjb21wbGV0ZXMgdGhlIHZpZXcgd2l0aCBwYXJ0aWFsIGRhdGVzIGZyb20gc2libGluZyBtb250aHNcclxuICBwdWJsaWMgcmVuZGVyQ2FsZW5kYXIoKTogdm9pZCB7XHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8PSB0aGlzLmNlbGxDb3VudDsgaSsrKSB7XHJcbiAgICAgIC8vIGRhdGUgd2lsbCBiZSBzZXQgdG8gdGhlIGZpcnN0IGRheSBvZiB0aGUgbW9udGggc2V0IGluIHRoaXMudmlld0RhdGVcclxuICAgICAgY29uc3QgZGF0ZTogRGF0ZSA9IG5ldyBEYXRlKFxyXG4gICAgICAgIHRoaXMudmlld0RhdGUuZ2V0RnVsbFllYXIoKSxcclxuICAgICAgICB0aGlzLnZpZXdEYXRlLmdldE1vbnRoKClcclxuICAgICAgKTtcclxuICAgICAgLy8gU2hpZnRzIHRoZSB3ZWVrIHRvIHN0YXJ0IGZyb20gTW9uZGF5LCByYXRoZXIgdGhhbiBTdW5kYXksIHRoaXMgY2F1c2VzIHRoZSBpbmRleCB0byBzdGFydCBhdCAxXHJcbiAgICAgIGNvbnN0IGRheU9mZnNldCA9IGRhdGUuZ2V0RGF5KCkgPT09IDAgPyA3IDogZGF0ZS5nZXREYXkoKTtcclxuICAgICAgdGhpcy5kYXlzW2ldID0gbmV3IERhdGUoZGF0ZS5zZXREYXRlKDIgLSBkYXlPZmZzZXQgKyBpKSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgbmV4dCgpIHtcclxuICAgIGlmICh0aGlzLnZpZXdNb2RlJC52YWx1ZSA9PT0gRGF0ZXBpY2tlclZpZXdNb2RlRW51bS5EYXlzKSB7XHJcbiAgICAgIHRoaXMubmV4dE1vbnRoKCk7XHJcbiAgICB9IGVsc2UgaWYgKHRoaXMudmlld01vZGUkLnZhbHVlID09PSBEYXRlcGlja2VyVmlld01vZGVFbnVtLlllYXJzKSB7XHJcbiAgICAgIHRoaXMubmV4dFllYXIoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHB1YmxpYyBwcmV2aW91cygpIHtcclxuICAgIGlmICh0aGlzLnZpZXdNb2RlJC52YWx1ZSA9PT0gRGF0ZXBpY2tlclZpZXdNb2RlRW51bS5EYXlzKSB7XHJcbiAgICAgIHRoaXMucHJldmlvdXNNb250aCgpO1xyXG4gICAgfSBlbHNlIGlmICh0aGlzLnZpZXdNb2RlJC52YWx1ZSA9PT0gRGF0ZXBpY2tlclZpZXdNb2RlRW51bS5ZZWFycykge1xyXG4gICAgICB0aGlzLnByZXZpb3VzWWVhcigpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHVibGljIHByZXZpb3VzTW9udGgoKTogdm9pZCB7XHJcbiAgICB0aGlzLnZpZXdEYXRlID0gbmV3IERhdGUoXHJcbiAgICAgIHRoaXMudmlld0RhdGUuZ2V0RnVsbFllYXIoKSxcclxuICAgICAgdGhpcy52aWV3RGF0ZS5nZXRNb250aCgpIC0gMVxyXG4gICAgKTtcclxuICAgIHRoaXMucmVuZGVyQ2FsZW5kYXIoKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBuZXh0TW9udGgoKTogdm9pZCB7XHJcbiAgICB0aGlzLnZpZXdEYXRlID0gbmV3IERhdGUoXHJcbiAgICAgIHRoaXMudmlld0RhdGUuZ2V0RnVsbFllYXIoKSxcclxuICAgICAgdGhpcy52aWV3RGF0ZS5nZXRNb250aCgpICsgMVxyXG4gICAgKTtcclxuICAgIHRoaXMucmVuZGVyQ2FsZW5kYXIoKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBpc0N1cnJlbnRNb250aChpbnB1dERhdGU6IERhdGUpOiBib29sZWFuIHtcclxuICAgIHJldHVybiBpbnB1dERhdGUuZ2V0TW9udGgoKSA9PT0gdGhpcy52aWV3RGF0ZS5nZXRNb250aCgpO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIGlzQ3VycmVudERheShpbnB1dERhdGU6IERhdGUpOiBib29sZWFuIHtcclxuICAgIHJldHVybiBpbnB1dERhdGUuZ2V0VGltZSgpID09PSB0aGlzLnByZXNlbnREYXRlLmdldFRpbWUoKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBpc1NlbGVjdGVkRGF5KGlucHV0RGF0ZTogRGF0ZSk6IGJvb2xlYW4ge1xyXG4gICAgaWYgKHRoaXMuc2VsZWN0ZWREYXRlKSB7XHJcbiAgICAgIHJldHVybiBpbnB1dERhdGUuZ2V0VGltZSgpID09PSB0aGlzLnNlbGVjdGVkRGF0ZS5nZXRUaW1lKCk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIGlzSW52YWxpZERheShpbnB1dERhdGU6IERhdGUpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLnZhbGlkYXRvcnNcclxuICAgICAgLm1hcChmbiA9PiBmbihpbnB1dERhdGUpKVxyXG4gICAgICAucmVkdWNlKChwcmV2LCBuZXh0KSA9PiBwcmV2IHx8IG5leHQsIGZhbHNlKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBpc0N1cnJlbnRZZWFyKHllYXI6IG51bWJlcik6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHllYXIgPT09IHRoaXMucHJlc2VudERhdGUuZ2V0RnVsbFllYXIoKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBpc1NlbGVjdGVkWWVhcih5ZWFyOiBudW1iZXIpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB5ZWFyID09PSB0aGlzLnZpZXdEYXRlLmdldEZ1bGxZZWFyKCk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgaXNJbnZhbGlkWWVhcih5ZWFyOiBudW1iZXIpOiBib29sZWFuIHtcclxuICAgIGNvbnN0IG5ld0RhdGUgPSBuZXcgRGF0ZShcclxuICAgICAgbmV3IERhdGUodGhpcy52aWV3RGF0ZS5nZXRUaW1lKCkpLnNldEZ1bGxZZWFyKHllYXIpXHJcbiAgICApO1xyXG4gICAgcmV0dXJuIHRoaXMudmFsaWRhdG9yc1xyXG4gICAgICAubWFwKGZuID0+IGZuKG5ld0RhdGUpKVxyXG4gICAgICAucmVkdWNlKChwcmV2LCBuZXh0KSA9PiBwcmV2IHx8IG5leHQsIGZhbHNlKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBpc0N1cnJlbnRNb250aEJ5SW5kZXgobW9udGg6IG51bWJlcik6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIG1vbnRoID09PSB0aGlzLnByZXNlbnREYXRlLmdldE1vbnRoKCk7XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgaXNTZWxlY3RlZE1vbnRoQnlJbmRleChtb250aDogbnVtYmVyKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gbW9udGggPT09IHRoaXMudmlld0RhdGUuZ2V0TW9udGgoKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBpc0ludmFsaWRNb250aEJ5SW5kZXgobW9udGg6IG51bWJlcik6IGJvb2xlYW4ge1xyXG4gICAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKG5ldyBEYXRlKHRoaXMudmlld0RhdGUuZ2V0VGltZSgpKS5zZXRNb250aChtb250aCkpO1xyXG4gICAgcmV0dXJuIHRoaXMudmFsaWRhdG9yc1xyXG4gICAgICAubWFwKGZuID0+IGZuKG5ld0RhdGUpKVxyXG4gICAgICAucmVkdWNlKChwcmV2LCBuZXh0KSA9PiBwcmV2IHx8IG5leHQsIGZhbHNlKTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBwcmV2aW91c1llYXIoKTogdm9pZCB7XHJcbiAgICB0aGlzLmdldFllYXJDb2xsZWN0aW9uKHRoaXMueWVhcnNbMF0gLSB0aGlzLnllYXJDZWxsQ291bnQpO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIG5leHRZZWFyKCk6IHZvaWQge1xyXG4gICAgdGhpcy5nZXRZZWFyQ29sbGVjdGlvbih0aGlzLnllYXJzWzBdICsgdGhpcy55ZWFyQ2VsbENvdW50KTtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBzZXRZZWFyKHllYXIpIHtcclxuICAgIGlmICghdGhpcy5pc0ludmFsaWRZZWFyKHllYXIpKSB7XHJcbiAgICAgIHRoaXMudmlld0RhdGUuc2V0RnVsbFllYXIoeWVhcik7XHJcbiAgICAgIHRoaXMucmVuZGVyQ2FsZW5kYXIoKTtcclxuICAgICAgdGhpcy50b2dnbGVZZWFyKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgc2V0TW9udGgobW9udGgpIHtcclxuICAgIGlmICghdGhpcy5pc0ludmFsaWRNb250aEJ5SW5kZXgobW9udGgpKSB7XHJcbiAgICAgIHRoaXMudmlld0RhdGUuc2V0TW9udGgobW9udGgpO1xyXG4gICAgICB0aGlzLnJlbmRlckNhbGVuZGFyKCk7XHJcbiAgICAgIHRoaXMudmlld01vZGUkLm5leHQoRGF0ZXBpY2tlclZpZXdNb2RlRW51bS5EYXlzKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHB1YmxpYyB0b2dnbGVZZWFyKCkge1xyXG4gICAgdGhpcy52aWV3TW9kZSQubmV4dChcclxuICAgICAgdGhpcy52aWV3TW9kZSQudmFsdWUgPT09IERhdGVwaWNrZXJWaWV3TW9kZUVudW0uWWVhcnNcclxuICAgICAgICA/IERhdGVwaWNrZXJWaWV3TW9kZUVudW0uTW9udGhzXHJcbiAgICAgICAgOiBEYXRlcGlja2VyVmlld01vZGVFbnVtLlllYXJzXHJcbiAgICApO1xyXG4gICAgaWYgKHRoaXMudmlld01vZGUkLnZhbHVlID09PSBEYXRlcGlja2VyVmlld01vZGVFbnVtLlllYXJzKSB7XHJcbiAgICAgIHRoaXMuZ2V0WWVhckNvbGxlY3Rpb24oKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHByaXZhdGUgZ2V0WWVhckNvbGxlY3Rpb24oc3RhcnRGcm9tID0gbnVsbCkge1xyXG4gICAgY29uc3QgeWVhcnNCZWZvcmVBY3RpdmUgPSA3O1xyXG4gICAgY29uc3QgYWN0aXZlWWVhciA9IHN0YXJ0RnJvbVxyXG4gICAgICA/IHN0YXJ0RnJvbVxyXG4gICAgICA6IHRoaXMudmlld0RhdGUuZ2V0RnVsbFllYXIoKSAtIHllYXJzQmVmb3JlQWN0aXZlO1xyXG4gICAgdGhpcy55ZWFycyA9IFtdO1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLnllYXJDZWxsQ291bnQ7IGkrKykge1xyXG4gICAgICB0aGlzLnllYXJzLnB1c2goYWN0aXZlWWVhciArIGkpO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG4iLCI8bmctY29udGFpbmVyICpuZ0lmPVwieyB2aWV3TW9kZSQgOiB2aWV3TW9kZSQgfCBhc3luYyB9IGFzIG9ic2VydmFibGVzXCI+XHJcbiAgPGRpdiBjbGFzcz1cImh4LWNhcmQtaGVhZGVyIGh4YS1kYXRlcGlja2VyLWNhbGVuZGFyX19oZWFkZXJcIj5cclxuICAgIDxkaXYgY2xhc3M9XCJoeGEtZGF0ZXBpY2tlci1jYWxlbmRhcl9fbW9udGhcIj5cclxuICAgICAgPGJ1dHRvblxyXG4gICAgICAgIGNsYXNzPVwiaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX2ljb24gaHgtYnV0dG9uIGlzLXRyYW5zcGFyZW50IGlzLWxhcmdlXCJcclxuICAgICAgICAqbmdJZj1cIm9ic2VydmFibGVzLnZpZXdNb2RlJCAhPT0gRGF0ZXBpY2tlclZpZXdNb2RlRW51bS5Nb250aHNcIlxyXG4gICAgICAgIHRpdGxlPVwiUHJldmlvdXMgTW9udGhcIlxyXG4gICAgICAgIChjbGljayk9XCJwcmV2aW91cygpXCJcclxuICAgICAgPlxyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwiaHgtaWNvbi1jb250cm9sXCI+XHJcbiAgICAgICAgICA8aSBjbGFzcz1cImh4LWljb24gaWNvbi1hbmdsZS1sZWZ0IGlzLW1lZGl1bVwiPjwvaT5cclxuICAgICAgICA8L3NwYW4+XHJcbiAgICAgIDwvYnV0dG9uPlxyXG4gICAgICA8ZGl2IGNsYXNzPVwiaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX21vbnRoLXRpdGxlXCI+XHJcbiAgICAgICAgPGJ1dHRvblxyXG4gICAgICAgICAgY2xhc3M9XCJoeC1idXR0b24gaXMtZmxhdCBpcy1pbmZvIGlzLWxhcmdlXCJcclxuICAgICAgICAgIChjbGljayk9XCJ0b2dnbGVZZWFyKClcIlxyXG4gICAgICAgID5cclxuICAgICAgICAgIHt7IChvYnNlcnZhYmxlcy52aWV3TW9kZSQgPT09IERhdGVwaWNrZXJWaWV3TW9kZUVudW0uWWVhcnMpID8geWVhcnNbMF1cclxuICAgICAgICAgICsgJyAtICcgKyB5ZWFyc1t5ZWFycy5sZW5ndGgtMV0gOiB2aWV3RGF0ZT8udG9Mb2NhbGVTdHJpbmcoXCJlbi1hdVwiLCB7XHJcbiAgICAgICAgICBtb250aDogXCJsb25nXCIsIHllYXI6IFwibnVtZXJpY1wifSkgfX1cclxuICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgPC9kaXY+XHJcbiAgICAgIDxidXR0b25cclxuICAgICAgICBjbGFzcz1cImh4YS1kYXRlcGlja2VyLWNhbGVuZGFyX19pY29uIGh4LWJ1dHRvbiBpcy10cmFuc3BhcmVudCBpcy1sYXJnZVwiXHJcbiAgICAgICAgKm5nSWY9XCJvYnNlcnZhYmxlcy52aWV3TW9kZSQgIT09IERhdGVwaWNrZXJWaWV3TW9kZUVudW0uTW9udGhzXCJcclxuICAgICAgICB0aXRsZT1cIk5leHQgTW9udGhcIlxyXG4gICAgICAgIChjbGljayk9XCJuZXh0KClcIlxyXG4gICAgICA+XHJcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJoeC1pY29uLWNvbnRyb2xcIj5cclxuICAgICAgICAgIDxpIGNsYXNzPVwiaHgtaWNvbiBpY29uLWFuZ2xlLXJpZ2h0IGlzLW1lZGl1bVwiPjwvaT5cclxuICAgICAgICA8L3NwYW4+XHJcbiAgICAgIDwvYnV0dG9uPlxyXG4gICAgPC9kaXY+XHJcbiAgICA8ZGl2XHJcbiAgICAgIGNsYXNzPVwiaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX3dlZWtcIlxyXG4gICAgICAqbmdJZj1cIm9ic2VydmFibGVzLnZpZXdNb2RlJCA9PT0gRGF0ZXBpY2tlclZpZXdNb2RlRW51bS5EYXlzXCJcclxuICAgID5cclxuICAgICAgPGRpdlxyXG4gICAgICAgIGNsYXNzPVwiaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX3dlZWtkYXlcIlxyXG4gICAgICAgICpuZ0Zvcj1cImxldCB3ZWVrZGF5IG9mIHdlZWtcIlxyXG4gICAgICA+XHJcbiAgICAgICAge3t3ZWVrZGF5IHwgc2xpY2U6MDozfX1cclxuICAgICAgPC9kaXY+XHJcbiAgICA8L2Rpdj5cclxuICA8L2Rpdj5cclxuICA8ZGl2IGNsYXNzPVwiaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX2NvbnRlbnRzXCI+XHJcbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwib2JzZXJ2YWJsZXMudmlld01vZGUkID09PSBEYXRlcGlja2VyVmlld01vZGVFbnVtLkRheXNcIj5cclxuICAgICAgPGJ1dHRvblxyXG4gICAgICAgIGNsYXNzPVwiaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX2RheSBoeC1idXR0b24gaXMtdHJhbnNwYXJlbnRcIlxyXG4gICAgICAgICpuZ0Zvcj1cImxldCBkYXkgb2YgZGF5c1wiXHJcbiAgICAgICAgW25nQ2xhc3NdPVwie1xyXG4gICAgICAgICAgJ2h4YS1kYXRlcGlja2VyLWNhbGVuZGFyX19kYXktc2libGluZ21vbnRoJzogIWlzQ3VycmVudE1vbnRoKGRheSksXHJcbiAgICAgICAgICAnaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX2RheS1jdXJyZW50ZGF5JzogaXNDdXJyZW50RGF5KGRheSksXHJcbiAgICAgICAgICAnaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX2RheS1zZWxlY3RlZGRheSc6IGlzU2VsZWN0ZWREYXkoZGF5KSxcclxuICAgICAgICAgICdoeGEtZGF0ZXBpY2tlci1jYWxlbmRhcl9fZGF5LWludmFsaWRkYXknOiBpc0ludmFsaWREYXkoZGF5KVxyXG4gICAgICAgIH1cIlxyXG4gICAgICAgIChjbGljayk9XCJzZXRTZWxlY3RlZERhdGUoZGF5KVwiXHJcbiAgICAgID5cclxuICAgICAgICB7e2RheS5nZXREYXRlKCl9fVxyXG4gICAgICA8L2J1dHRvbj5cclxuICAgIDwvbmctY29udGFpbmVyPlxyXG5cclxuICAgIDxuZy1jb250YWluZXJcclxuICAgICAgKm5nSWY9XCJvYnNlcnZhYmxlcy52aWV3TW9kZSQgPT09IERhdGVwaWNrZXJWaWV3TW9kZUVudW0uTW9udGhzXCJcclxuICAgID5cclxuICAgICAgPGJ1dHRvblxyXG4gICAgICAgIGNsYXNzPVwiaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX21vbnRocyBoeC1idXR0b24gaXMtdHJhbnNwYXJlbnRcIlxyXG4gICAgICAgICpuZ0Zvcj1cImxldCBtb250aCBvZiBtb250aHM7IGxldCBpbmRleE9mTXRoPWluZGV4O1wiXHJcbiAgICAgICAgW25nQ2xhc3NdPVwie1xyXG4gICAgICAgICAgJ2h4YS1kYXRlcGlja2VyLWNhbGVuZGFyX19kYXktY3VycmVudGRheSc6IGlzQ3VycmVudE1vbnRoQnlJbmRleChpbmRleE9mTXRoKSxcclxuICAgICAgICAgICdoeGEtZGF0ZXBpY2tlci1jYWxlbmRhcl9fZGF5LXNlbGVjdGVkZGF5JzogaXNTZWxlY3RlZE1vbnRoQnlJbmRleChpbmRleE9mTXRoKSxcclxuICAgICAgICAgICdoeGEtZGF0ZXBpY2tlci1jYWxlbmRhcl9fZGF5LWludmFsaWRkYXknOiBpc0ludmFsaWRNb250aEJ5SW5kZXgoaW5kZXhPZk10aClcclxuICAgICAgICB9XCJcclxuICAgICAgICAoY2xpY2spPVwic2V0TW9udGgoaW5kZXhPZk10aClcIlxyXG4gICAgICA+XHJcbiAgICAgICAge3sgbW9udGggfX1cclxuICAgICAgPC9idXR0b24+XHJcbiAgICA8L25nLWNvbnRhaW5lcj5cclxuXHJcbiAgICA8bmctY29udGFpbmVyXHJcbiAgICAgICpuZ0lmPVwib2JzZXJ2YWJsZXMudmlld01vZGUkID09PSBEYXRlcGlja2VyVmlld01vZGVFbnVtLlllYXJzXCJcclxuICAgID5cclxuICAgICAgPGJ1dHRvblxyXG4gICAgICAgICpuZ0Zvcj1cImxldCB5ZWFyIG9mIHllYXJzXCJcclxuICAgICAgICBjbGFzcz1cImh4YS1kYXRlcGlja2VyLWNhbGVuZGFyX195ZWFyIGh4LWJ1dHRvbiBpcy10cmFuc3BhcmVudFwiXHJcbiAgICAgICAgW25nQ2xhc3NdPVwie1xyXG4gICAgICAgICAgJ2h4YS1kYXRlcGlja2VyLWNhbGVuZGFyX19kYXktY3VycmVudGRheSc6IGlzQ3VycmVudFllYXIoeWVhciksXHJcbiAgICAgICAgICAnaHhhLWRhdGVwaWNrZXItY2FsZW5kYXJfX2RheS1zZWxlY3RlZGRheSc6IGlzU2VsZWN0ZWRZZWFyKHllYXIpLFxyXG4gICAgICAgICAgJ2h4YS1kYXRlcGlja2VyLWNhbGVuZGFyX19kYXktaW52YWxpZGRheSc6IGlzSW52YWxpZFllYXIoeWVhcilcclxuICAgICAgICB9XCJcclxuICAgICAgICAoY2xpY2spPVwic2V0WWVhcih5ZWFyKVwiXHJcbiAgICAgID5cclxuICAgICAgICB7eyB5ZWFyIH19XHJcbiAgICAgIDwvYnV0dG9uPlxyXG4gICAgPC9uZy1jb250YWluZXI+XHJcbiAgPC9kaXY+XHJcbjwvbmctY29udGFpbmVyPlxyXG4iXX0=