UNPKG

moh-common-lib

Version:

A library of Angular components, services, and styles for B.C. Government Ministry of Health (MoH).

472 lines (470 loc) 48.2 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import { Component, Input, Output, EventEmitter, Optional, Self, Inject } from '@angular/core'; import { NgControl, NG_VALIDATORS } from '@angular/forms'; import { LabelReplacementTag } from '../../models/error-message.interface'; import getDaysInMonth from 'date-fns/getDaysInMonth'; import isAfter from 'date-fns/isAfter'; import isBefore from 'date-fns/isBefore'; import startOfToday from 'date-fns/startOfToday'; import addYears from 'date-fns/addYears'; import subYears from 'date-fns/subYears'; import { MoHCommonLibraryError } from '../../../helpers/library-error'; import { AbstractFormControl } from '../../models/abstract-form-control'; import { compareAsc, startOfDay, addDays } from 'date-fns'; /** * DateComponent * You cannot use "[restrictDate]" in combination with either "[dateRangeEnd]" or "[dateRangeStart]". * You must use either [restrictDate] or the [dateRange*] inputs. * * \@example * To trigger 'no future dates allowed' using date ranges set the range end date to yesterday's date. * <common-date name='effectiveDate' * label="Effective Date" * [dateRangeEnd]='yesterday' * formControlName="effectiveDate"></common-date> * ' * To trigger 'no past dates allowed' using date ranges set the range start date to today's date. * <common-date name='effectiveDate' * label="Effective Date" * [dateRangeStart]="today" * formControlName="effectiveDate"></common-date> * * To allow instructions under label. * <common-date name='effectiveDate' * label="Effective Date" * [dateRangeEnd]='yesterday' * formControlName="effectiveDate"> * <p>This is a test.</p> * </common-date> * @export * * @type {?} */ const MAX_YEAR_RANGE = 150; /** @type {?} */ const distantFuture = addYears(startOfToday(), MAX_YEAR_RANGE); /** @type {?} */ const distantPast = subYears(startOfToday(), MAX_YEAR_RANGE); export class DateComponent extends AbstractFormControl { // TODO: remove if not required - value does not get set when using Reactive forms /** * @param {?} controlDir * @param {?} injectedValidators */ constructor(controlDir, injectedValidators) { super(); this.controlDir = controlDir; this.injectedValidators = injectedValidators; this.dateChange = new EventEmitter(); this.label = 'Date'; /** * Can be one of: "future", "past". "future" includes today, "past" does not. */ this.restrictDate = 'any'; this._month = 'null'; // this makes it so the blank option is selected in the input // variables for date ranges this._dateRangeStart = null; this._dateRangeEnd = null; this.monthList = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; this.monthLabelforId = 'month_' + this.objectId; this.dayLabelforId = 'day_' + this.objectId; this.yearLabelforId = 'year_' + this.objectId; // Abstact variable defined this._defaultErrMsg = { required: `${LabelReplacementTag} is required.`, dayOutOfRange: `Invalid ${LabelReplacementTag}.`, yearDistantPast: `Invalid ${LabelReplacementTag}.`, yearDistantFuture: `Invalid ${LabelReplacementTag}.`, noPastDatesAllowed: `Invalid ${LabelReplacementTag}.`, noFutureDatesAllowed: `Invalid ${LabelReplacementTag}.`, invalidValue: `Invalid ${LabelReplacementTag}.`, invalidRange: `Invalid ${LabelReplacementTag}.` }; this.today = startOfToday(); this.tomorrow = addDays(this.today, 1); if (controlDir) { controlDir.valueAccessor = this; } } /** * The earliest valid date that can be used. * Do NOT combine with restrictDates, as they set the same underlying values. * @param {?} dt * @return {?} */ set dateRangeStart(dt) { // Set time on date to 00:00:00 for comparing later this._dateRangeStart = dt ? startOfDay(dt) : null; } /** * The latest valid date that can be used. * Do NOT combine with restrictDates, as they set the same underlying values. * @param {?} dt * @return {?} */ set dateRangeEnd(dt) { // Set time on date to 00:00:00 for comparing later this._dateRangeEnd = dt ? startOfDay(dt) : null; } /** * @param {?} changes * @return {?} */ ngOnChanges(changes) { /* * Works, creates new object literall * obj = { * errorMessage: 'new'message'; * } * * Doesn't work, modifies existing object. Would have to manually call cd.detectChanges() afterwards. * obj.errorMessage = 'newMessage'; */ if (changes['errorMessage']) { this.setErrorMsg(); } } /** * @return {?} */ ngOnInit() { this.setErrorMsg(); // Set to midnight, so we don't accidentally compare against hours/minutes/seconds if (this.restrictDate !== 'any' && (this._dateRangeEnd || this._dateRangeStart)) { /** @type {?} */ const msg = `<common-date> - Invalid @Input() option configuration. You cannot use "[restrictDate]" in combination with either "[dateRangeEnd]" or "[dateRangeStart]". You must use either [restrictDate] or the [dateRange*] inputs. <common-date name='effectiveDate' label="Effective Date" [restrictDate]="'past'" <<< problem, choose one [dateRangeEnd]='today' <<< problem, choose one formControlName="effectiveDate" ></common-date> `; throw new MoHCommonLibraryError(msg); } // Initialize date range logic if (this.restrictDate === 'past') { // past does allow for today this._dateRangeEnd = this.today; this._dateRangeStart = null; } else if (this.restrictDate === 'future') { // future does NOT allow for today this._dateRangeEnd = null; this._dateRangeStart = this.tomorrow; } this.registerValidation(this.controlDir, this.validateSelf) .then((/** * @param {?} _ * @return {?} */ _ => { if (this.injectedValidators && this.injectedValidators.length) { // TODO: Potentially move to AbstractFormControl // Inspect the validator functions for one that has a {required: true} // property. Importantly, we are inspecting the validator function // itself and NOT the current state of the NgControl. -- Does not work for reactive forms this.isRequired = this.injectedValidators .filter((/** * @param {?} x * @return {?} */ x => x.required)) .length >= 1; } })); } /** * @return {?} */ get month() { if (this.date) { return this.date.getMonth(); } } /** * @return {?} */ get day() { if (this.date) { return this.date.getDate(); } } /** * @return {?} */ get year() { if (this.date) { return this.date.getFullYear(); } } /** * Handles creating / destroying date and emitting changes based on user behaviour. * @private * @return {?} */ processDate() { if (this.canCreateDate()) { /** @type {?} */ const year = this.getNumericValue(this._year); /** @type {?} */ const month = this.getNumericValue(this._month); /** @type {?} */ const day = this.getNumericValue(this._day); // Date function appears to use setYear() so any year 0-99 results in year 1900 to 1999 // Set each field individually, use setFullYear() instead of setYear() // Set time on date to 00:00:00 for comparing later this.date = startOfDay(new Date(year, month, day)); this.date.setFullYear(year); } else { // Trigger validator for emptying fields use case. This is to remove the 'Invalid date' error. if (this.date || (!this._year && !this._day && this._month === 'null')) { // Destroys the internal Date object. this.date = null; } } this._onChange(this.date); this._onTouched(this.date); this.dateChange.emit(this.date); } /** * Returns true if and only if the day/month/year fields are all filled out. * @private * @return {?} */ canCreateDate() { // special because "0" is valid (Jan) /** @type {?} */ const monthCheck = (typeof this._month === 'string' && this._month !== 'null') || typeof this._month === 'number'; if (!!this._year && !!this._day && monthCheck) { return true; } return false; } /** * @private * @param {?} dt * @return {?} */ _triggerOnChange(dt) { this._onChange(dt); this.dateChange.emit(dt); } /** * Convert string to numeric value or null if not * @private * @param {?} value * @return {?} */ getNumericValue(value) { /** @type {?} */ const parsed = parseInt(value, 10); return (isNaN(parsed) ? null : parsed); } /** * @private * @return {?} */ setDisplayVariables() { this._day = this.date.getDate().toString(); this._month = this.date.getMonth().toString(); this._year = this.date.getFullYear().toString(); } /** * @param {?} value * @return {?} */ writeValue(value) { if (value) { this.date = value; this.setDisplayVariables(); } } /** * @param {?} value * @return {?} */ onBlurDay(value) { this._day = value; this.processDate(); } /** * @param {?} value * @return {?} */ onBlurYear(value) { this._year = value; this.processDate(); } /** * @param {?} value * @return {?} */ onBlurMonth(value) { this._month = value; this.processDate(); } /** * Validates the DateComponent instance itself, using internal private variables. * * @private * @return {?} */ validateSelf() { /** @type {?} */ const year = parseInt(this._year, 10); /** @type {?} */ const month = parseInt(this._month, 10); /** @type {?} */ const day = parseInt(this._day, 10); // Nothing empty fields - nothing to validate OR have required error if (isNaN(year) && isNaN(month) && isNaN(day)) { return null; } // Partially filled out is always invalid, if year is present it must be greater than zero if (isNaN(year) || isNaN(month) || isNaN(day) || (!isNaN(year) && year <= 0)) { return { invalidValue: true }; } // We can hardcode the day, since we're only interested in total days for that month. /** @type {?} */ const daysInMonth = getDaysInMonth(new Date(year, month, 1)); if (day > daysInMonth || day < 1) { return { dayOutOfRange: true }; } /** @type {?} */ const dateRangeResult = this.validateRange(); if (dateRangeResult) { return dateRangeResult; } /** @type {?} */ const distantDatesResult = this.validateDistantDates(); if (distantDatesResult) { return distantDatesResult; } return null; } // If you set restrictDate, it will return noFutureDatesAllowed / noPastDatesAllowed // If you just set dateRangeStart / dateRangeEnd, you get invalidRange /** * @private * @return {?} */ validateRange() { /** @type {?} */ const _dt = startOfDay(this.date); if (this._dateRangeEnd && isAfter(_dt, this._dateRangeEnd)) { if (this.restrictDate === 'past' || compareAsc(this._dateRangeEnd, this.today) === 0) { return { noFutureDatesAllowed: true }; } return { invalidRange: true }; } if (this._dateRangeStart && isBefore(_dt, this._dateRangeStart)) { if (this.restrictDate === 'future' || compareAsc(this._dateRangeStart, this.tomorrow) === 0) { return { noPastDatesAllowed: true }; } return { invalidRange: true }; } return null; } /** * @private * @return {?} */ validateDistantDates() { // Null end range only allow 150 years in future if (!this._dateRangeEnd && isAfter(this.date, distantFuture)) { return { yearDistantFuture: true }; } // Null start range only allow 150 years in past if (!this._dateRangeStart && isBefore(this.date, distantPast)) { return { yearDistantPast: true }; } return null; } } DateComponent.decorators = [ { type: Component, args: [{ selector: 'common-date', template: "<fieldset>\r\n <legend class=\"date--legend\">{{label}}</legend>\r\n <ng-container *ngTemplateOutlet=\"instructionText\"></ng-container>\r\n <div class=\"date-row\">\r\n <select class=\"form-control monthSelect\"\r\n id=\"{{monthLabelforId}}\"\r\n name=\"{{monthLabelforId}}\"\r\n [value]=\"_month\"\r\n (blur)=\"onBlurMonth($event.target.value)\"\r\n [disabled]='disabled'\r\n [required]=\"isRequired\"\r\n aria-label=\"Month\">\r\n <!-- We show the blank option so the user can clear out their data.-->\r\n <option value=\"null\" label=\"Month\" selected [disabled]='isRequired'></option>\r\n <option *ngFor=\"let month of monthList; let i = index;\" [value]=\"i\">{{month}}</option>\r\n </select>\r\n\r\n <input type=\"number\"\r\n class=\"form-control dayInput\"\r\n id=\"{{dayLabelforId}}\"\r\n name=\"{{dayLabelforId}}\"\r\n placeholder=\"Day\"\r\n [value]=\"_day\"\r\n (blur)=\"onBlurDay($event.target.value)\"\r\n commonDateFieldFormat\r\n [disabled]='disabled'\r\n [required]=\"isRequired\"\r\n maxlength=\"2\"\r\n step=\"any\"\r\n aria-label=\"Day\"\r\n autocomplete=\"off\"/>\r\n\r\n <input type=\"number\"\r\n class=\"form-control yearInput\"\r\n id=\"{{yearLabelforId}}\"\r\n name=\"{{yearLabelforId}}\"\r\n placeholder=\"Year\"\r\n [value]=\"_year\"\r\n (blur)=\"onBlurYear($event.target.value)\"\r\n commonDateFieldFormat\r\n [disabled]='disabled'\r\n [required]=\"isRequired\"\r\n maxlength=\"4\"\r\n step=\"any\"\r\n aria-label=\"Year\"\r\n autocomplete=\"off\"/>\r\n\r\n </div>\r\n</fieldset>\r\n\r\n<common-error-container\r\n [displayError]=\"!disabled && (controlDir?.touched || controlDir?.dirty) && controlDir.errors\">\r\n <div *ngIf=\"controlDir?.errors?.required; else ComponentErrors\">\r\n {{_defaultErrMsg.required}}\r\n </div>\r\n</common-error-container>\r\n\r\n<ng-template #instructionText>\r\n <ng-content></ng-content>\r\n</ng-template>\r\n\r\n<!--Errors triggered by self-validation within component-->\r\n<ng-template #ComponentErrors>\r\n <div *ngIf=\"controlDir?.errors?.dayOutOfRange\">\r\n {{_defaultErrMsg.dayOutOfRange}}\r\n </div>\r\n <div *ngIf=\"controlDir?.errors?.yearDistantPast\">\r\n {{_defaultErrMsg.yearDistantPast}}\r\n </div>\r\n <div *ngIf=\"controlDir?.errors?.yearDistantFuture\">\r\n {{_defaultErrMsg.yearDistantFuture}}\r\n </div>\r\n <div *ngIf=\"controlDir?.errors?.noPastDatesAllowed\">\r\n {{_defaultErrMsg.noPastDatesAllowed}}\r\n </div>\r\n <div *ngIf=\"controlDir?.errors?.noFutureDatesAllowed\">\r\n {{_defaultErrMsg.noFutureDatesAllowed}}\r\n </div>\r\n <div *ngIf=\"controlDir?.errors?.invalidRange\">\r\n {{_defaultErrMsg.invalidRange}}\r\n </div>\r\n\r\n <!-- Case should not happen until something is not formatted correctly-->\r\n <div *ngIf=\"controlDir?.errors?.invalidValue\">\r\n {{_defaultErrMsg.invalidValue}}\r\n </div>\r\n</ng-template>\r\n", styles: [".date--legend{font-size:inherit;font-weight:700}.date-row{display:flex;flex-wrap:nowrap;justify-content:space-between}.monthSelect{max-width:50%;height:35px;margin-right:1em}.dayInput{max-width:25%;height:35px;margin-right:1em}.yearInput{max-width:25%;height:35px}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0}select option[selected]{color:gray!important}"] }] } ]; /** @nocollapse */ DateComponent.ctorParameters = () => [ { type: NgControl, decorators: [{ type: Optional }, { type: Self }] }, { type: Array, decorators: [{ type: Optional }, { type: Self }, { type: Inject, args: [NG_VALIDATORS,] }] } ]; DateComponent.propDecorators = { date: [{ type: Input }], dateChange: [{ type: Output }], label: [{ type: Input }], restrictDate: [{ type: Input }], dateRangeStart: [{ type: Input }], dateRangeEnd: [{ type: Input }] }; if (false) { /** @type {?} */ DateComponent.prototype.date; /** @type {?} */ DateComponent.prototype.dateChange; /** @type {?} */ DateComponent.prototype.label; /** * Can be one of: "future", "past". "future" includes today, "past" does not. * @type {?} */ DateComponent.prototype.restrictDate; /** @type {?} */ DateComponent.prototype._year; /** @type {?} */ DateComponent.prototype._month; /** @type {?} */ DateComponent.prototype._day; /** @type {?} */ DateComponent.prototype._dateRangeStart; /** @type {?} */ DateComponent.prototype._dateRangeEnd; /** @type {?} */ DateComponent.prototype.monthList; /** @type {?} */ DateComponent.prototype.monthLabelforId; /** @type {?} */ DateComponent.prototype.dayLabelforId; /** @type {?} */ DateComponent.prototype.yearLabelforId; /** @type {?} */ DateComponent.prototype._defaultErrMsg; /** * @type {?} * @private */ DateComponent.prototype.today; /** * @type {?} * @private */ DateComponent.prototype.tomorrow; /** @type {?} */ DateComponent.prototype.isRequired; /** @type {?} */ DateComponent.prototype.controlDir; /** * @type {?} * @private */ DateComponent.prototype.injectedValidators; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9tb2gtY29tbW9uLWxpYi8iLCJzb3VyY2VzIjpbImxpYi9jb21wb25lbnRzL2RhdGUvZGF0ZS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQUFBLE9BQU8sRUFDTCxTQUFTLEVBRVQsS0FBSyxFQUNMLE1BQU0sRUFDTixZQUFZLEVBQ1osUUFBUSxFQUFFLElBQUksRUFBNEIsTUFBTSxFQUNqRCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBRUwsU0FBUyxFQUVULGFBQWEsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBQ3ZDLE9BQU8sRUFBZ0IsbUJBQW1CLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUN6RixPQUFPLGNBQWMsTUFBTSx5QkFBeUIsQ0FBQztBQUNyRCxPQUFPLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUN2QyxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQztBQUN6QyxPQUFPLFlBQVksTUFBTSx1QkFBdUIsQ0FBQztBQUNqRCxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQztBQUN6QyxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQztBQUN6QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN2RSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUN6RSxPQUFPLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsTUFBTSxVQUFVLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztNQWdDckQsY0FBYyxHQUFHLEdBQUc7O01BQ3BCLGFBQWEsR0FBRyxRQUFRLENBQUMsWUFBWSxFQUFFLEVBQUUsY0FBYyxDQUFDOztNQUN4RCxXQUFXLEdBQUcsUUFBUSxDQUFDLFlBQVksRUFBRSxFQUFFLGNBQWMsQ0FBQztBQVE1RCxNQUFNLE9BQU8sYUFBYyxTQUFRLG1CQUFtQjs7Ozs7O0lB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QUFDOzs7OztJQUVPLG9CQUFvQjtRQUUxQixnREFBZ0Q7UUFDaEQsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsYUFBYSxDQUFDLEVBQUU7WUFDNUQsT0FBTyxFQUFDLGlCQUFpQixFQUFFLElBQUksRUFBQyxDQUFDO1NBQ2xDO1FBRUQsZ0RBQWdEO1FBQ2hELElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxJQUFJLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxFQUFFO1lBQzdELE9BQU8sRUFBQyxlQUFlLEVBQUUsSUFBSSxFQUFDLENBQUM7U0FDaEM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7OztZQXRVRixTQUFTLFNBQUM7Z0JBQ1QsUUFBUSxFQUFFLGFBQWE7Z0JBQ3ZCLGlyR0FBb0M7O2FBRXJDOzs7O1lBckRDLFNBQVMsdUJBMEhLLFFBQVEsWUFBSSxJQUFJO3dDQUNoQixRQUFRLFlBQUksSUFBSSxZQUFJLE1BQU0sU0FBQyxhQUFhOzs7bUJBakVyRCxLQUFLO3lCQUNMLE1BQU07b0JBRU4sS0FBSzsyQkFFTCxLQUFLOzZCQWtCTCxLQUFLOzJCQVVMLEtBQUs7Ozs7SUFqQ04sNkJBQW9COztJQUNwQixtQ0FBb0U7O0lBRXBFLDhCQUFnQzs7Ozs7SUFFaEMscUNBQXlEOztJQUt6RCw4QkFBYzs7SUFDZCwrQkFBd0I7O0lBQ3hCLDZCQUFhOztJQUliLHdDQUE2Qjs7SUFDN0Isc0NBQTJCOztJQXNCM0Isa0NBR0U7O0lBRUYsd0NBQW1EOztJQUNuRCxzQ0FBK0M7O0lBQy9DLHVDQUFpRDs7SUFHakQsdUNBU0U7Ozs7O0lBRUYsOEJBQStCOzs7OztJQUMvQixpQ0FBNEM7O0lBQzVDLG1DQUEyQjs7SUFFZCxtQ0FBZ0Q7Ozs7O0lBQ2hELDJDQUE0RSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XHJcbiAgQ29tcG9uZW50LFxyXG4gIE9uSW5pdCxcclxuICBJbnB1dCxcclxuICBPdXRwdXQsXHJcbiAgRXZlbnRFbWl0dGVyLFxyXG4gIE9wdGlvbmFsLCBTZWxmLCBTaW1wbGVDaGFuZ2VzLCBPbkNoYW5nZXMsIEluamVjdFxyXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQge1xyXG4gIENvbnRyb2xWYWx1ZUFjY2Vzc29yLFxyXG4gIE5nQ29udHJvbCxcclxuICBWYWxpZGF0aW9uRXJyb3JzLFxyXG4gIE5HX1ZBTElEQVRPUlN9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHsgRXJyb3JNZXNzYWdlLCBMYWJlbFJlcGxhY2VtZW50VGFnIH0gZnJvbSAnLi4vLi4vbW9kZWxzL2Vycm9yLW1lc3NhZ2UuaW50ZXJmYWNlJztcclxuaW1wb3J0IGdldERheXNJbk1vbnRoIGZyb20gJ2RhdGUtZm5zL2dldERheXNJbk1vbnRoJztcclxuaW1wb3J0IGlzQWZ0ZXIgZnJvbSAnZGF0ZS1mbnMvaXNBZnRlcic7XHJcbmltcG9ydCBpc0JlZm9yZSBmcm9tICdkYXRlLWZucy9pc0JlZm9yZSc7XHJcbmltcG9ydCBzdGFydE9mVG9kYXkgZnJvbSAnZGF0ZS1mbnMvc3RhcnRPZlRvZGF5JztcclxuaW1wb3J0IGFkZFllYXJzIGZyb20gJ2RhdGUtZm5zL2FkZFllYXJzJztcclxuaW1wb3J0IHN1YlllYXJzIGZyb20gJ2RhdGUtZm5zL3N1YlllYXJzJztcclxuaW1wb3J0IHsgTW9IQ29tbW9uTGlicmFyeUVycm9yIH0gZnJvbSAnLi4vLi4vLi4vaGVscGVycy9saWJyYXJ5LWVycm9yJztcclxuaW1wb3J0IHsgQWJzdHJhY3RGb3JtQ29udHJvbCB9IGZyb20gJy4uLy4uL21vZGVscy9hYnN0cmFjdC1mb3JtLWNvbnRyb2wnO1xyXG5pbXBvcnQgeyBjb21wYXJlQXNjLCBzdGFydE9mRGF5LCBhZGREYXlzIH0gZnJvbSAnZGF0ZS1mbnMnO1xyXG5cclxuLyoqXHJcbiAqIERhdGVDb21wb25lbnRcclxuICogWW91IGNhbm5vdCB1c2UgXCJbcmVzdHJpY3REYXRlXVwiIGluIGNvbWJpbmF0aW9uIHdpdGggZWl0aGVyICBcIltkYXRlUmFuZ2VFbmRdXCIgb3IgXCJbZGF0ZVJhbmdlU3RhcnRdXCIuXHJcbiAqIFlvdSBtdXN0IHVzZSBlaXRoZXIgW3Jlc3RyaWN0RGF0ZV0gb3IgdGhlIFtkYXRlUmFuZ2UqXSBpbnB1dHMuXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqICBUbyB0cmlnZ2VyICdubyBmdXR1cmUgZGF0ZXMgYWxsb3dlZCcgdXNpbmcgZGF0ZSByYW5nZXMgc2V0IHRoZSByYW5nZSBlbmQgZGF0ZSB0byB5ZXN0ZXJkYXkncyBkYXRlLlxyXG4gKiAgICA8Y29tbW9uLWRhdGUgbmFtZT0nZWZmZWN0aXZlRGF0ZSdcclxuICogICAgICAgbGFiZWw9XCJFZmZlY3RpdmUgRGF0ZVwiXHJcbiAqICAgICAgIFtkYXRlUmFuZ2VFbmRdPSd5ZXN0ZXJkYXknXHJcbiAqICAgICAgIGZvcm1Db250cm9sTmFtZT1cImVmZmVjdGl2ZURhdGVcIj48L2NvbW1vbi1kYXRlPlxyXG4gKidcclxuICAqICBUbyB0cmlnZ2VyICdubyBwYXN0IGRhdGVzIGFsbG93ZWQnIHVzaW5nIGRhdGUgcmFuZ2VzIHNldCB0aGUgcmFuZ2Ugc3RhcnQgZGF0ZSB0byB0b2RheSdzIGRhdGUuXHJcbiAqICAgIDxjb21tb24tZGF0ZSBuYW1lPSdlZmZlY3RpdmVEYXRlJ1xyXG4gKiAgICAgICBsYWJlbD1cIkVmZmVjdGl2ZSBEYXRlXCJcclxuICogICAgICAgW2RhdGVSYW5nZVN0YXJ0XT1cInRvZGF5XCJcclxuICogICAgICAgZm9ybUNvbnRyb2xOYW1lPVwiZWZmZWN0aXZlRGF0ZVwiPjwvY29tbW9uLWRhdGU+XHJcbiAqXHJcbiAqICBUbyBhbGxvdyBpbnN0cnVjdGlvbnMgdW5kZXIgbGFiZWwuXHJcbiAqICAgIDxjb21tb24tZGF0ZSBuYW1lPSdlZmZlY3RpdmVEYXRlJ1xyXG4gKiAgICAgICBsYWJlbD1cIkVmZmVjdGl2ZSBEYXRlXCJcclxuICogICAgICAgW2RhdGVSYW5nZUVuZF09J3llc3RlcmRheSdcclxuICogICAgICAgZm9ybUNvbnRyb2xOYW1lPVwiZWZmZWN0aXZlRGF0ZVwiPlxyXG4gKiAgICAgIDxwPlRoaXMgaXMgYSB0ZXN0LjwvcD5cclxuICogICAgPC9jb21tb24tZGF0ZT5cclxuICogQGV4cG9ydFxyXG4gKlxyXG4gKi9cclxuXHJcblxyXG5jb25zdCBNQVhfWUVBUl9SQU5HRSA9IDE1MDtcclxuY29uc3QgZGlzdGFudEZ1dHVyZSA9IGFkZFllYXJzKHN0YXJ0T2ZUb2RheSgpLCBNQVhfWUVBUl9SQU5HRSk7XHJcbmNvbnN0IGRpc3RhbnRQYXN0ID0gc3ViWWVhcnMoc3RhcnRPZlRvZGF5KCksIE1BWF9ZRUFSX1JBTkdFKTtcclxuXHJcblxyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ2NvbW1vbi1kYXRlJyxcclxuICB0ZW1wbGF0ZVVybDogJy4vZGF0ZS5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmxzOiBbJy4vZGF0ZS5jb21wb25lbnQuc2NzcyddLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgRGF0ZUNvbXBvbmVudCBleHRlbmRzIEFic3RyYWN0Rm9ybUNvbnRyb2xcclxuICBpbXBsZW1lbnRzIE9uSW5pdCwgQ29udHJvbFZhbHVlQWNjZXNzb3IsIE9uQ2hhbmdlcyB7XHJcblxyXG4gIC8vIElucHV0cyBmb3IgZGlzYWJsZWQgJiBlcnJvck1lc3NhZ2UgYXJlIGZvdW5kIGluIHRoZSBBYnN0cmFjdEZvcm1Db250cm9sIGNsYXNzXHJcbiAgQElucHV0KCkgZGF0ZTogRGF0ZTtcclxuICBAT3V0cHV0KCkgZGF0ZUNoYW5nZTogRXZlbnRFbWl0dGVyPERhdGU+ID0gbmV3IEV2ZW50RW1pdHRlcjxEYXRlPigpO1xyXG5cclxuICBASW5wdXQoKSBsYWJlbDogc3RyaW5nID0gJ0RhdGUnO1xyXG4gIC8qKiBDYW4gYmUgb25lIG9mOiBcImZ1dHVyZVwiLCBcInBhc3RcIi4gXCJmdXR1cmVcIiBpbmNsdWRlcyB0b2RheSwgXCJwYXN0XCIgZG9lcyBub3QuICovXHJcbiAgQElucHV0KCkgcmVzdHJpY3REYXRlOiAnZnV0dXJlJyB8ICdwYXN0JyB8ICdhbnknID0gJ2FueSc7XHJcblxyXG4gIC8vIFRoZSBhY3R1YWwgdmFsdWVzIGRpc3BsYXllZCB0byB0aGUgdXNlci4gIE1heSBub3QgcHJlY2lzZWx5IG1hdGNoIERhdGVcclxuICAvLyBvYmplY3QsIGJlY2F1c2UgdGhlc2UgZmllbGRzIGNhbiBiZSBibGFuayB3aGVyZWFzIGEgRGF0ZSBjYW4gbmV2ZXIgaGF2ZSBhXHJcbiAgLy8gXCJibGFua1wiIHllYXIgZm9yIGV4YW1wbGUuIEFsbCBhcmUgbnVsbGFibGUgc3RpbmdzIG9mIG51bWJlcnMgXCIwXCIgb3IgXCIyXCIuXHJcbiAgX3llYXI6IHN0cmluZztcclxuICBfbW9udGg6IHN0cmluZyA9ICdudWxsJzsgLy8gdGhpcyBtYWtlcyBpdCBzbyB0aGUgYmxhbmsgb3B0aW9uIGlzIHNlbGVjdGVkIGluIHRoZSBpbnB1dFxyXG4gIF9kYXk6IHN0cmluZztcclxuXHJcblxyXG4gIC8vIHZhcmlhYmxlcyBmb3IgZGF0ZSByYW5nZXNcclxuICBfZGF0ZVJhbmdlU3RhcnQ6IERhdGUgPSBudWxsO1xyXG4gIF9kYXRlUmFuZ2VFbmQ6IERhdGUgPSBudWxsO1xyXG5cclxuICAvKipcclxuICAgKiBUaGUgZWFybGllc3QgdmFsaWQgZGF0ZSB0aGF0IGNhbiBiZSB1c2VkLlxyXG4gICAqIERvIE5PVCBjb21iaW5lIHdpdGggcmVzdHJpY3REYXRlcywgYXMgdGhleSBzZXQgdGhlIHNhbWUgdW5kZXJseWluZyB2YWx1ZXMuXHJcbiAgICovXHJcbiAgQElucHV0KClcclxuICBzZXQgZGF0ZVJhbmdlU3RhcnQoZHQ6IERhdGUpIHtcclxuICAgIC8vIFNldCB0aW1lIG9uIGRhdGUgdG8gMDA6MDA6MDAgZm9yIGNvbXBhcmluZyBsYXRlclxyXG4gICAgdGhpcy5fZGF0ZVJhbmdlU3RhcnQgPSBkdCA/IHN0YXJ0T2ZEYXkoZHQpIDogbnVsbDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoZSBsYXRlc3QgdmFsaWQgZGF0ZSB0aGF0IGNhbiBiZSB1c2VkLlxyXG4gICAqIERvIE5PVCBjb21iaW5lIHdpdGggcmVzdHJpY3REYXRlcywgYXMgdGhleSBzZXQgdGhlIHNhbWUgdW5kZXJseWluZyB2YWx1ZXMuXHJcbiAgICovXHJcbiAgQElucHV0KClcclxuICBzZXQgZGF0ZVJhbmdlRW5kKGR0OiBEYXRlKSB7XHJcbiAgICAvLyBTZXQgdGltZSBvbiBkYXRlIHRvIDAwOjAwOjAwIGZvciBjb21wYXJpbmcgbGF0ZXJcclxuICAgIHRoaXMuX2RhdGVSYW5nZUVuZCA9IGR0ID8gc3RhcnRPZkRheShkdCkgOiBudWxsO1xyXG4gIH1cclxuXHJcbiAgcHVibGljIG1vbnRoTGlzdDogc3RyaW5nW10gPSBbXHJcbiAgICAnSmFudWFyeScsICdGZWJydWFyeScsICdNYXJjaCcsICdBcHJpbCcsICdNYXknLCAnSnVuZScsXHJcbiAgICAnSnVseScsICdBdWd1c3QnLCAnU2VwdGVtYmVyJywgJ09jdG9iZXInLCAnTm92ZW1iZXInLCAnRGVjZW1iZXInXHJcbiAgXTtcclxuXHJcbiAgbW9udGhMYWJlbGZvcklkOiBzdHJpbmcgPSAnbW9udGhfJyArIHRoaXMub2JqZWN0SWQ7XHJcbiAgZGF5TGFiZWxmb3JJZDogc3RyaW5nID0gJ2RheV8nICsgdGhpcy5vYmplY3RJZDtcclxuICB5ZWFyTGFiZWxmb3JJZDogc3RyaW5nID0gJ3llYXJfJyArIHRoaXMub2JqZWN0SWQ7XHJcblxyXG4gIC8vIEFic3RhY3QgdmFyaWFibGUgZGVmaW5lZFxyXG4gIF9kZWZhdWx0RXJyTXNnOiBFcnJvck1lc3NhZ2UgPSB7XHJcbiAgICByZXF1aXJlZDogYCR7TGFiZWxSZXBsYWNlbWVudFRhZ30gaXMgcmVxdWlyZWQuYCxcclxuICAgIGRheU91dE9mUmFuZ2U6IGBJbnZhbGlkICR7TGFiZWxSZXBsYWNlbWVudFRhZ30uYCxcclxuICAgIHllYXJEaXN0YW50UGFzdDogYEludmFsaWQgJHtMYWJlbFJlcGxhY2VtZW50VGFnfS5gLFxyXG4gICAgeWVhckRpc3RhbnRGdXR1cmU6IGBJbnZhbGlkICR7TGFiZWxSZXBsYWNlbWVudFRhZ30uYCxcclxuICAgIG5vUGFzdERhdGVzQWxsb3dlZDogYEludmFsaWQgJHtMYWJlbFJlcGxhY2VtZW50VGFnfS5gLFxyXG4gICAgbm9GdXR1cmVEYXRlc0FsbG93ZWQ6IGBJbnZhbGlkICR7TGFiZWxSZXBsYWNlbWVudFRhZ30uYCxcclxuICAgIGludmFsaWRWYWx1ZTogYEludmFsaWQgJHtMYWJlbFJlcGxhY2VtZW50VGFnfS5gLFxyXG4gICAgaW52YWxpZFJhbmdlOiBgSW52YWxpZCAke0xhYmVsUmVwbGFjZW1lbnRUYWd9LmBcclxuICB9O1xyXG5cclxuICBwcml2YXRlIHRvZGF5ID0gc3RhcnRPZlRvZGF5KCk7XHJcbiAgcHJpdmF0ZSB0b21vcnJvdyA9IGFkZERheXMoIHRoaXMudG9kYXksIDEgKTtcclxuICBwdWJsaWMgaXNSZXF1aXJlZDogYm9vbGVhbjsgLy8gVE9ETzogcmVtb3ZlIGlmIG5vdCByZXF1aXJlZCAtIHZhbHVlIGRvZXMgbm90IGdldCBzZXQgd2hlbiB1c2luZyBSZWFjdGl2ZSBmb3Jtc1xyXG5cclxuICBjb25zdHJ1Y3RvciggQE9wdGlvbmFsKCkgQFNlbGYoKSBwdWJsaWMgY29udHJvbERpcjogTmdDb250cm9sLFxyXG4gICAgICAgICAgICAgICBAT3B0aW9uYWwoKSBAU2VsZigpIEBJbmplY3QoTkdfVkFMSURBVE9SUykgcHJpdmF0ZSBpbmplY3RlZFZhbGlkYXRvcnM6IGFueVtdICkge1xyXG4gICAgc3VwZXIoKTtcclxuICAgIGlmIChjb250cm9sRGlyKSB7XHJcbiAgICAgIGNvbnRyb2xEaXIudmFsdWVBY2Nlc3NvciA9IHRoaXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKSB7XHJcblxyXG4gICAgLypcclxuICAgICAqIFdvcmtzLCBjcmVhdGVzIG5ldyBvYmplY3QgbGl0ZXJhbGxcclxuICAgICAqIG9iaiA9IHtcclxuICAgICAqICAgZXJyb3JNZXNzYWdlOiAnbmV3J21lc3NhZ2UnO1xyXG4gICAgICogfVxyXG4gICAgICpcclxuICAgICAqIERvZXNuJ3Qgd29yaywgbW9kaWZpZXMgZXhpc3Rpbmcgb2JqZWN0LiAgV291bGQgaGF2ZSB0byBtYW51YWxseSBjYWxsIGNkLmRldGVjdENoYW5nZXMoKSBhZnRlcndhcmRzLlxyXG4gICAgICogb2JqLmVycm9yTWVzc2FnZSA9ICduZXdNZXNzYWdlJztcclxuICAgICAqL1xyXG4gICAgaWYgKGNoYW5nZXNbJ2Vycm9yTWVzc2FnZSddKSB7XHJcbiAgICAgIHRoaXMuc2V0RXJyb3JNc2coKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIG5nT25Jbml0KCkge1xyXG4gICAgdGhpcy5zZXRFcnJvck1zZygpO1xyXG5cclxuICAgIC8vIFNldCB0byBtaWRuaWdodCwgc28gd2UgZG9uJ3QgYWNjaWRlbnRhbGx5IGNvbXBhcmUgYWdhaW5zdCBob3Vycy9taW51dGVzL3NlY29uZHNcclxuXHJcbiAgICBpZiAodGhpcy5yZXN0cmljdERhdGUgIT09ICdhbnknICYmICh0aGlzLl9kYXRlUmFuZ2VFbmQgfHwgdGhpcy5fZGF0ZVJhbmdlU3RhcnQpKSB7XHJcbiAgICAgIGNvbnN0IG1zZyA9IGA8Y29tbW9uLWRhdGU+IC0gSW52YWxpZCBASW5wdXQoKSBvcHRpb24gY29uZmlndXJhdGlvbi5cclxuWW91IGNhbm5vdCB1c2UgXCJbcmVzdHJpY3REYXRlXVwiIGluIGNvbWJpbmF0aW9uIHdpdGggZWl0aGVyICBcIltkYXRlUmFuZ2VFbmRdXCIgb3IgXCJbZGF0ZVJhbmdlU3RhcnRdXCIuXHJcbllvdSBtdXN0IHVzZSBlaXRoZXIgW3Jlc3RyaWN0RGF0ZV0gb3IgdGhlIFtkYXRlUmFuZ2UqXSBpbnB1dHMuXHJcblxyXG4gICAgPGNvbW1vbi1kYXRlIG5hbWU9J2VmZmVjdGl2ZURhdGUnXHJcbiAgICAgICAgbGFiZWw9XCJFZmZlY3RpdmUgRGF0ZVwiXHJcbiAgICAgICAgW3Jlc3RyaWN0RGF0ZV09XCIncGFzdCdcIiAgIDw8PCBwcm9ibGVtLCBjaG9vc2Ugb25lXHJcbiAgICAgICAgW2RhdGVSYW5nZUVuZF09J3RvZGF5JyAgICA8PDwgcHJvYmxlbSwgY2hvb3NlIG9uZVxyXG4gICAgICAgIGZvcm1Db250cm9sTmFtZT1cImVmZmVjdGl2ZURhdGVcIlxyXG4gICAgPjwvY29tbW9uLWRhdGU+XHJcblxyXG4gIGA7XHJcbiAgICAgIHRocm93IG5ldyBNb0hDb21tb25MaWJyYXJ5RXJyb3IobXNnKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJbml0aWFsaXplIGRhdGUgcmFuZ2UgbG9naWNcclxuICAgIGlmICh0aGlzLnJlc3RyaWN0RGF0ZSA9PT0gJ3Bhc3QnKSB7XHJcbiAgICAgIC8vIHBhc3QgZG9lcyBhbGxvdyBmb3IgdG9kYXlcclxuICAgICAgdGhpcy5fZGF0ZVJhbmdlRW5kID0gdGhpcy50b2RheTtcclxuICAgICAgdGhpcy5fZGF0ZVJhbmdlU3RhcnQgPSBudWxsO1xyXG4gICAgfSBlbHNlIGlmICh0aGlzLnJlc3RyaWN0RGF0ZSA9PT0gJ2Z1dHVyZScpIHtcclxuICAgICAgLy8gZnV0dXJlIGRvZXMgTk9UIGFsbG93IGZvciB0b2RheVxyXG4gICAgICB0aGlzLl9kYXRlUmFuZ2VFbmQgPSBudWxsO1xyXG4gICAgICB0aGlzLl9kYXRlUmFuZ2VTdGFydCA9IHRoaXMudG9tb3Jyb3c7XHJcbiAgICB9XHJcblxyXG4gICAgdGhpcy5yZWdpc3RlclZhbGlkYXRpb24oIHRoaXMuY29udHJvbERpciwgdGhpcy52YWxpZGF0ZVNlbGYgKVxyXG4gICAgICAudGhlbihfID0+IHtcclxuICAgICAgICBpZiAodGhpcy5pbmplY3RlZFZhbGlkYXRvcnMgJiYgdGhpcy5pbmplY3RlZFZhbGlkYXRvcnMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBUT0RPOiBQb3RlbnRpYWxseSBtb3ZlIHRvIEFic3RyYWN0Rm9ybUNvbnRyb2xcclxuICAgICAgICAgIC8vIEluc3BlY3QgdGhlIHZhbGlkYXRvciBmdW5jdGlvbnMgZm9yIG9uZSB0aGF0IGhhcyBhIHtyZXF1aXJlZDogdHJ1ZX1cclxuICAgICAgICAgIC8vIHByb3BlcnR5LiBJbXBvcnRhbnRseSwgd2UgYXJlIGluc3BlY3RpbmcgdGhlIHZhbGlkYXRvciBmdW5jdGlvblxyXG4gICAgICAgICAgLy8gaXRzZWxmIGFuZCBOT1QgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIE5nQ29udHJvbC4gLS0gRG9lcyBub3Qgd29yayBmb3IgcmVhY3RpdmUgZm9ybXNcclxuICAgICAgICAgIHRoaXMuaXNSZXF1aXJlZCA9IHRoaXMuaW5qZWN0ZWRWYWxpZGF0b3JzXHJcbiAgICAgICAgICAgIC5maWx0ZXIoeCA9PiB4LnJlcXVpcmVkKVxyXG4gICAgICAgICAgICAubGVuZ3RoID49IDE7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICB9XHJcblxyXG4gIGdldCBtb250aCgpOiBudW1iZXIge1xyXG4gICAgaWYgKHRoaXMuZGF0ZSkge1xyXG4gICAgICByZXR1cm4gdGhpcy5kYXRlLmdldE1vbnRoKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBnZXQgZGF5KCk6IG51bWJlciB7XHJcbiAgICBpZiAodGhpcy5kYXRlKSB7XHJcbiAgICAgIHJldHVybiB0aGlzLmRhdGUuZ2V0RGF0ZSgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZ2V0IHllYXIoKTogbnVtYmVyIHtcclxuICAgIGlmICh0aGlzLmRhdGUpIHtcclxuICAgICAgcmV0dXJuIHRoaXMuZGF0ZS5nZXRGdWxsWWVhcigpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogSGFuZGxlcyBjcmVhdGluZyAvIGRlc3Ryb3lpbmcgZGF0ZSBhbmQgZW1pdHRpbmcgY2hhbmdlcyBiYXNlZCBvbiB1c2VyIGJlaGF2aW91ci5cclxuICAgKi9cclxuICBwcml2YXRlIHByb2Nlc3NEYXRlKCkge1xyXG5cclxuICAgIGlmICh0aGlzLmNhbkNyZWF0ZURhdGUoKSkge1xyXG4gICAgICBjb25zdCB5ZWFyID0gdGhpcy5nZXROdW1lcmljVmFsdWUodGhpcy5feWVhcik7XHJcbiAgICAgIGNvbnN0IG1vbnRoID0gdGhpcy5nZXROdW1lcmljVmFsdWUodGhpcy5fbW9udGgpO1xyXG4gICAgICBjb25zdCBkYXkgPSB0aGlzLmdldE51bWVyaWNWYWx1ZSh0aGlzLl9kYXkpO1xyXG5cclxuICAgICAgLy8gRGF0ZSBmdW5jdGlvbiBhcHBlYXJzIHRvIHVzZSBzZXRZZWFyKCkgc28gYW55IHllYXIgMC05OSByZXN1bHRzIGluIHllYXIgMTkwMCB0byAxOTk5XHJcbiAgICAgIC8vIFNldCBlYWNoIGZpZWxkIGluZGl2aWR1YWxseSwgdXNlIHNldEZ1bGxZZWFyKCkgaW5zdGVhZCBvZiBzZXRZZWFyKClcclxuICAgICAgLy8gU2V0IHRpbWUgb24gZGF0ZSB0byAwMDowMDowMCBmb3IgY29tcGFyaW5nIGxhdGVyXHJcbiAgICAgIHRoaXMuZGF0ZSA9IHN0YXJ0T2ZEYXkobmV3IERhdGUoeWVhciwgbW9udGgsIGRheSkpO1xyXG4gICAgICB0aGlzLmRhdGUuc2V0RnVsbFllYXIoeWVhcik7XHJcblxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gVHJpZ2dlciB2YWxpZGF0b3IgZm9yIGVtcHR5aW5nIGZpZWxkcyB1c2UgY2FzZS4gVGhpcyBpcyB0byByZW1vdmUgdGhlICdJbnZhbGlkIGRhdGUnIGVycm9yLlxyXG4gICAgICBpZiAodGhpcy5kYXRlIHx8XHJcbiAgICAgICAgKCF0aGlzLl95ZWFyICYmICF0aGlzLl9kYXkgJiYgdGhpcy5fbW9udGggPT09ICdudWxsJykpIHtcclxuXHJcbiAgICAgICAgLy8gRGVzdHJveXMgdGhlIGludGVybmFsIERhdGUgb2JqZWN0LlxyXG4gICAgICAgIHRoaXMuZGF0ZSA9IG51bGw7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICB0aGlzLl9vbkNoYW5nZSh0aGlzLmRhdGUpO1xyXG4gICAgdGhpcy5fb25Ub3VjaGVkKHRoaXMuZGF0ZSk7XHJcbiAgICB0aGlzLmRhdGVDaGFuZ2UuZW1pdCh0aGlzLmRhdGUpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmV0dXJucyB0cnVlIGlmIGFuZCBvbmx5IGlmIHRoZSBkYXkvbW9udGgveWVhciBmaWVsZHMgYXJlIGFsbCBmaWxsZWQgb3V0LlxyXG4gICAqL1xyXG4gIHByaXZhdGUgY2FuQ3JlYXRlRGF0ZSgpOiBib29sZWFuIHtcclxuXHJcbiAgICAvLyBzcGVjaWFsIGJlY2F1c2UgXCIwXCIgaXMgdmFsaWQgKEphbilcclxuICAgIGNvbnN0IG1vbnRoQ2hlY2sgPSAodHlwZW9mIHRoaXMuX21vbnRoID09PSAnc3RyaW5nJyAmJiB0aGlzLl9tb250aCAhPT0gJ251bGwnKVxyXG4gICAgICB8fCB0eXBlb2YgdGhpcy5fbW9udGggPT09ICdudW1iZXInO1xyXG5cclxuICAgIGlmICghIXRoaXMuX3llYXIgJiYgISF0aGlzLl9kYXkgJiYgbW9udGhDaGVjaykge1xyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICAgIHJldHVybiBmYWxzZTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgX3RyaWdnZXJPbkNoYW5nZSggZHQ6IERhdGUgKSB7XHJcbiAgICB0aGlzLl9vbkNoYW5nZShkdCk7XHJcbiAgICB0aGlzLmRhdGVDaGFuZ2UuZW1pdChkdCk7XHJcbiAgfVxyXG5cclxuICAvKiogQ29udmVydCBzdHJpbmcgdG8gbnVtZXJpYyB2YWx1ZSBvciBudWxsIGlmIG5vdCAqL1xyXG4gIHByaXZhdGUgZ2V0TnVtZXJpY1ZhbHVlKHZhbHVlOiBzdHJpbmcpOiBudW1iZXIgfCBudWxsIHtcclxuICAgIGNvbnN0IHBhcnNlZCA9IHBhcnNlSW50KHZhbHVlLCAxMCk7XHJcbiAgICByZXR1cm4gKGlzTmFOKHBhcnNlZCkgPyBudWxsIDogcGFyc2VkKTtcclxuICB9XHJcblxyXG5cclxuICBwcml2YXRlIHNldERpc3BsYXlWYXJpYWJsZXMoKSB7XHJcbiAgICB0aGlzLl9kYXkgPSB0aGlzLmRhdGUuZ2V0RGF0ZSgpLnRvU3RyaW5nKCk7XHJcbiAgICB0aGlzLl9tb250aCA9IHRoaXMuZGF0ZS5nZXRNb250aCgpLnRvU3RyaW5nKCk7XHJcbiAgICB0aGlzLl95ZWFyID0gdGhpcy5kYXRlLmdldEZ1bGxZZWFyKCkudG9TdHJpbmcoKTtcclxuICB9XHJcblxyXG4gIHdyaXRlVmFsdWUodmFsdWU6IERhdGUpOiB2b2lkIHtcclxuICAgIGlmICh2YWx1ZSkge1xyXG4gICAgICB0aGlzLmRhdGUgPSB2YWx1ZTtcclxuICAgICAgdGhpcy5zZXREaXNwbGF5VmFyaWFibGVzKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBvbkJsdXJEYXkodmFsdWU6IHN0cmluZykge1xyXG4gICAgdGhpcy5fZGF5ID0gdmFsdWU7XHJcbiAgICB0aGlzLnByb2Nlc3NEYXRlKCk7XHJcbiAgfVxyXG4gIG9uQmx1clllYXIodmFsdWU6IHN0cmluZykge1xyXG4gICAgdGhpcy5feWVhciA9IHZhbHVlO1xyXG4gICAgdGhpcy5wcm9jZXNzRGF0ZSgpO1xyXG4gIH1cclxuICBvbkJsdXJNb250aCh2YWx1ZTogc3RyaW5nKSB7XHJcbiAgICB0aGlzLl9tb250aCA9IHZhbHVlO1xyXG4gICAgdGhpcy5wcm9jZXNzRGF0ZSgpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVmFsaWRhdGVzIHRoZSBEYXRlQ29tcG9uZW50IGluc3RhbmNlIGl0c2VsZiwgdXNpbmcgaW50ZXJuYWwgcHJpdmF0ZSB2YXJpYWJsZXMuXHJcbiAgICpcclxuICAgKi9cclxuICBwcml2YXRlIHZhbGlkYXRlU2VsZigpIHtcclxuICAgIGNvbnN0IHllYXIgPSBwYXJzZUludCh0aGlzLl95ZWFyLCAxMCk7XHJcbiAgICBjb25zdCBtb250aCA9IHBhcnNlSW50KHRoaXMuX21vbnRoLCAxMCk7XHJcbiAgICBjb25zdCBkYXkgPSBwYXJzZUludCh0aGlzLl9kYXksIDEwKTtcclxuXHJcbiAgICAvLyBOb3RoaW5nIGVtcHR5IGZpZWxkcyAtIG5vdGhpbmcgdG8gdmFsaWRhdGUgT1IgaGF2ZSByZXF1aXJlZCBlcnJvclxyXG4gICAgaWYgKCBpc05hTih5ZWFyKSAmJiBpc05hTihtb250aCkgJiYgaXNOYU4oZGF5KSApIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUGFydGlhbGx5IGZpbGxlZCBvdXQgaXMgYWx3YXlzIGludmFsaWQsIGlmIHllYXIgaXMgcHJlc2VudCBpdCBtdXN0IGJlIGdyZWF0ZXIgdGhhbiB6ZXJvXHJcbiAgICBpZiAoIGlzTmFOKHllYXIpIHx8IGlzTmFOKG1vbnRoKSB8fCBpc05hTihkYXkpIHx8ICghaXNOYU4oIHllYXIpICYmIHllYXIgPD0gMCkgKSB7XHJcbiAgICAgIHJldHVybiB7aW52YWxpZFZhbHVlOiB0cnVlfTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXZSBjYW4gaGFyZGNvZGUgdGhlIGRheSwgc2luY2Ugd2UncmUgb25seSBpbnRlcmVzdGVkIGluIHRvdGFsIGRheXMgZm9yIHRoYXQgbW9udGguXHJcbiAgICBjb25zdCBkYXlzSW5Nb250aCA9IGdldERheXNJbk1vbnRoKG5ldyBEYXRlKHllYXIsIG1vbnRoLCAxKSk7XHJcbiAgICBpZiAoZGF5ID4gZGF5c0luTW9udGggfHwgZGF5IDwgMSkge1xyXG4gICAgICByZXR1cm4geyBkYXlPdXRPZlJhbmdlOiB0cnVlIH07XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgZGF0ZVJhbmdlUmVzdWx0ID0gdGhpcy52YWxpZGF0ZVJhbmdlKCk7XHJcbiAgICBpZiAoZGF0ZVJhbmdlUmVzdWx0KSB7XHJcbiAgICAgIHJldHVybiBkYXRlUmFuZ2VSZXN1bHQ7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgZGlzdGFudERhdGVzUmVzdWx0ID0gdGhpcy52YWxpZGF0ZURpc3RhbnREYXRlcygpO1xyXG4gICAgaWYgKGRpc3RhbnREYXRlc1Jlc3VsdCkge1xyXG4gICAgICByZXR1cm4gZGlzdGFudERhdGVzUmVzdWx0O1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuXHJcblxyXG4gIC8vIElmIHlvdSBzZXQgcmVzdHJpY3REYXRlLCBpdCB3aWxsIHJldHVybiBub0Z1dHVyZURhdGVzQWxsb3dlZCAvIG5vUGFzdERhdGVzQWxsb3dlZFxyXG4gIC8vIElmIHlvdSBqdXN0IHNldCBkYXRlUmFuZ2VTdGFydCAvIGRhdGVSYW5nZUVuZCwgeW91IGdldCBpbnZhbGlkUmFuZ2VcclxuICBwcml2YXRlIHZhbGlkYXRlUmFuZ2UoKTogVmFsaWRhdGlvbkVycm9ycyB8IG51bGwge1xyXG5cclxuICAgIGNvbnN0IF9kdCA9IHN0YXJ0T2ZEYXkoIHRoaXMuZGF0ZSApO1xyXG5cclxuICAgIGlmICh0aGlzLl9kYXRlUmFuZ2VFbmQgJiYgaXNBZnRlcihfZHQsIHRoaXMuX2RhdGVSYW5nZUVuZCkpIHtcclxuXHJcbiAgICAgIGlmICh0aGlzLnJlc3RyaWN0RGF0ZSA9PT0gJ3Bhc3QnIHx8XHJcbiAgICAgICAgICBjb21wYXJlQXNjKHRoaXMuX2RhdGVSYW5nZUVuZCwgdGhpcy50b2RheSkgPT09IDApIHtcclxuICAgICAgICByZXR1cm4ge25vRnV0dXJlRGF0ZXNBbGxvd2VkOiB0cnVlfTtcclxuICAgICAgfVxyXG5cclxuICAgICAgcmV0dXJuIHtpbnZhbGlkUmFuZ2U6IHRydWV9O1xyXG4gICAgfVxyXG5cclxuICAgIGlmICh0aGlzLl9kYXRlUmFuZ2VTdGFydCAmJiBpc0JlZm9yZShfZHQsIHRoaXMuX2RhdGVSYW5nZVN0YXJ0KSkge1xyXG5cclxuICAgICAgaWYgKHRoaXMucmVzdHJpY3REYXRlID09PSAnZnV0dXJlJyAgfHxcclxuICAgICAgICAgIGNvbXBhcmVBc2ModGhpcy5fZGF0ZVJhbmdlU3RhcnQsIHRoaXMudG9tb3Jyb3cpID09PSAwKSB7XHJcbiAgICAgICAgcmV0dXJuIHtub1Bhc3REYXRlc0FsbG93ZWQ6IHRydWV9O1xyXG4gICAgICB9XHJcblxyXG4gICAgICByZXR1cm4ge2ludmFsaWRSYW5nZTogdHJ1ZX07XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIHZhbGlkYXRlRGlzdGFudERhdGVzKCk6IFZhbGlkYXRpb25FcnJvcnMgfCBudWxsIHtcclxuXHJcbiAgICAvLyBOdWxsIGVuZCByYW5nZSBvbmx5IGFsbG93IDE1MCB5ZWFycyBpbiBmdXR1cmVcclxuICAgIGlmICghdGhpcy5fZGF0ZVJhbmdlRW5kICYmIGlzQWZ0ZXIodGhpcy5kYXRlLCBkaXN0YW50RnV0dXJlKSkge1xyXG4gICAgICByZXR1cm4ge3llYXJEaXN0YW50RnV0dXJlOiB0cnVlfTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBOdWxsIHN0YXJ0IHJhbmdlIG9ubHkgYWxsb3cgMTUwIHllYXJzIGluIHBhc3RcclxuICAgIGlmICghdGhpcy5fZGF0ZVJhbmdlU3RhcnQgJiYgaXNCZWZvcmUodGhpcy5kYXRlLCBkaXN0YW50UGFzdCkpIHtcclxuICAgICAgcmV0dXJuIHt5ZWFyRGlzdGFudFBhc3Q6IHRydWV9O1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuXHJcbn1cclxuIl19