UNPKG

moh-common-lib

Version:

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

568 lines 52.4 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import * as tslib_1 from "tslib"; 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 {?} */ var MAX_YEAR_RANGE = 150; /** @type {?} */ var distantFuture = addYears(startOfToday(), MAX_YEAR_RANGE); /** @type {?} */ var distantPast = subYears(startOfToday(), MAX_YEAR_RANGE); var DateComponent = /** @class */ (function (_super) { tslib_1.__extends(DateComponent, _super); function DateComponent(controlDir, injectedValidators) { var _this = _super.call(this) || this; _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; } return _this; } Object.defineProperty(DateComponent.prototype, "dateRangeStart", { /** * The earliest valid date that can be used. * Do NOT combine with restrictDates, as they set the same underlying values. */ set: /** * The earliest valid date that can be used. * Do NOT combine with restrictDates, as they set the same underlying values. * @param {?} dt * @return {?} */ function (dt) { // Set time on date to 00:00:00 for comparing later this._dateRangeStart = dt ? startOfDay(dt) : null; }, enumerable: true, configurable: true }); Object.defineProperty(DateComponent.prototype, "dateRangeEnd", { /** * The latest valid date that can be used. * Do NOT combine with restrictDates, as they set the same underlying values. */ set: /** * The latest valid date that can be used. * Do NOT combine with restrictDates, as they set the same underlying values. * @param {?} dt * @return {?} */ function (dt) { // Set time on date to 00:00:00 for comparing later this._dateRangeEnd = dt ? startOfDay(dt) : null; }, enumerable: true, configurable: true }); /** * @param {?} changes * @return {?} */ DateComponent.prototype.ngOnChanges = /** * @param {?} changes * @return {?} */ function (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 {?} */ DateComponent.prototype.ngOnInit = /** * @return {?} */ function () { var _this = this; this.setErrorMsg(); // Set to midnight, so we don't accidentally compare against hours/minutes/seconds if (this.restrictDate !== 'any' && (this._dateRangeEnd || this._dateRangeStart)) { /** @type {?} */ var msg = "<common-date> - Invalid @Input() option configuration.\nYou cannot use \"[restrictDate]\" in combination with either \"[dateRangeEnd]\" or \"[dateRangeStart]\".\nYou must use either [restrictDate] or the [dateRange*] inputs.\n\n <common-date name='effectiveDate'\n label=\"Effective Date\"\n [restrictDate]=\"'past'\" <<< problem, choose one\n [dateRangeEnd]='today' <<< problem, choose one\n formControlName=\"effectiveDate\"\n ></common-date>\n\n "; 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 {?} */ function (_) { 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 {?} */ function (x) { return x.required; })) .length >= 1; } })); }; Object.defineProperty(DateComponent.prototype, "month", { get: /** * @return {?} */ function () { if (this.date) { return this.date.getMonth(); } }, enumerable: true, configurable: true }); Object.defineProperty(DateComponent.prototype, "day", { get: /** * @return {?} */ function () { if (this.date) { return this.date.getDate(); } }, enumerable: true, configurable: true }); Object.defineProperty(DateComponent.prototype, "year", { get: /** * @return {?} */ function () { if (this.date) { return this.date.getFullYear(); } }, enumerable: true, configurable: true }); /** * Handles creating / destroying date and emitting changes based on user behaviour. */ /** * Handles creating / destroying date and emitting changes based on user behaviour. * @private * @return {?} */ DateComponent.prototype.processDate = /** * Handles creating / destroying date and emitting changes based on user behaviour. * @private * @return {?} */ function () { if (this.canCreateDate()) { /** @type {?} */ var year = this.getNumericValue(this._year); /** @type {?} */ var month = this.getNumericValue(this._month); /** @type {?} */ var 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. */ /** * Returns true if and only if the day/month/year fields are all filled out. * @private * @return {?} */ DateComponent.prototype.canCreateDate = /** * Returns true if and only if the day/month/year fields are all filled out. * @private * @return {?} */ function () { // special because "0" is valid (Jan) /** @type {?} */ var 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 {?} */ DateComponent.prototype._triggerOnChange = /** * @private * @param {?} dt * @return {?} */ function (dt) { this._onChange(dt); this.dateChange.emit(dt); }; /** Convert string to numeric value or null if not */ /** * Convert string to numeric value or null if not * @private * @param {?} value * @return {?} */ DateComponent.prototype.getNumericValue = /** * Convert string to numeric value or null if not * @private * @param {?} value * @return {?} */ function (value) { /** @type {?} */ var parsed = parseInt(value, 10); return (isNaN(parsed) ? null : parsed); }; /** * @private * @return {?} */ DateComponent.prototype.setDisplayVariables = /** * @private * @return {?} */ function () { this._day = this.date.getDate().toString(); this._month = this.date.getMonth().toString(); this._year = this.date.getFullYear().toString(); }; /** * @param {?} value * @return {?} */ DateComponent.prototype.writeValue = /** * @param {?} value * @return {?} */ function (value) { if (value) { this.date = value; this.setDisplayVariables(); } }; /** * @param {?} value * @return {?} */ DateComponent.prototype.onBlurDay = /** * @param {?} value * @return {?} */ function (value) { this._day = value; this.processDate(); }; /** * @param {?} value * @return {?} */ DateComponent.prototype.onBlurYear = /** * @param {?} value * @return {?} */ function (value) { this._year = value; this.processDate(); }; /** * @param {?} value * @return {?} */ DateComponent.prototype.onBlurMonth = /** * @param {?} value * @return {?} */ function (value) { this._month = value; this.processDate(); }; /** * Validates the DateComponent instance itself, using internal private variables. * */ /** * Validates the DateComponent instance itself, using internal private variables. * * @private * @return {?} */ DateComponent.prototype.validateSelf = /** * Validates the DateComponent instance itself, using internal private variables. * * @private * @return {?} */ function () { /** @type {?} */ var year = parseInt(this._year, 10); /** @type {?} */ var month = parseInt(this._month, 10); /** @type {?} */ var 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 {?} */ var daysInMonth = getDaysInMonth(new Date(year, month, 1)); if (day > daysInMonth || day < 1) { return { dayOutOfRange: true }; } /** @type {?} */ var dateRangeResult = this.validateRange(); if (dateRangeResult) { return dateRangeResult; } /** @type {?} */ var 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 // If you set restrictDate, it will return noFutureDatesAllowed / noPastDatesAllowed // If you just set dateRangeStart / dateRangeEnd, you get invalidRange /** * @private * @return {?} */ DateComponent.prototype.validateRange = // If you set restrictDate, it will return noFutureDatesAllowed / noPastDatesAllowed // If you just set dateRangeStart / dateRangeEnd, you get invalidRange /** * @private * @return {?} */ function () { /** @type {?} */ var _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 {?} */ DateComponent.prototype.validateDistantDates = /** * @private * @return {?} */ function () { // 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 = function () { return [ { 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 }] }; return DateComponent; }(AbstractFormControl)); export { DateComponent }; 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9tb2gtY29tbW9uLWxpYi8iLCJzb3VyY2VzIjpbImxpYi9jb21wb25lbnRzL2RhdGUvZGF0ZS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUVULEtBQUssRUFDTCxNQUFNLEVBQ04sWUFBWSxFQUNaLFFBQVEsRUFBRSxJQUFJLEVBQTRCLE1BQU0sRUFDakQsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUVMLFNBQVMsRUFFVCxhQUFhLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUN2QyxPQUFPLEVBQWdCLG1CQUFtQixFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDekYsT0FBTyxjQUFjLE1BQU0seUJBQXlCLENBQUM7QUFDckQsT0FBTyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDdkMsT0FBTyxRQUFRLE1BQU0sbUJBQW1CLENBQUM7QUFDekMsT0FBTyxZQUFZLE1BQU0sdUJBQXVCLENBQUM7QUFDakQsT0FBTyxRQUFRLE1BQU0sbUJBQW1CLENBQUM7QUFDekMsT0FBTyxRQUFRLE1BQU0sbUJBQW1CLENBQUM7QUFDekMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDdkUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDekUsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLE1BQU0sVUFBVSxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7SUFnQ3JELGNBQWMsR0FBRyxHQUFHOztJQUNwQixhQUFhLEdBQUcsUUFBUSxDQUFDLFlBQVksRUFBRSxFQUFFLGNBQWMsQ0FBQzs7SUFDeEQsV0FBVyxHQUFHLFFBQVEsQ0FBQyxZQUFZLEVBQUUsRUFBRSxjQUFjLENBQUM7QUFHNUQ7SUFLbUMseUNBQW1CO0lBb0VwRCx1QkFBd0MsVUFBcUIs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YUFBYTs7O0lBSXhCLDZCQUFvQjs7SUFDcEIsbUNBQW9FOztJQUVwRSw4QkFBZ0M7Ozs7O0lBRWhDLHFDQUF5RDs7SUFLekQsOEJBQWM7O0lBQ2QsK0JBQXdCOztJQUN4Qiw2QkFBYTs7SUFJYix3Q0FBNkI7O0lBQzdCLHNDQUEyQjs7SUFzQjNCLGtDQUdFOztJQUVGLHdDQUFtRDs7SUFDbkQsc0NBQStDOztJQUMvQyx1Q0FBaUQ7O0lBR2pELHVDQVNFOzs7OztJQUVGLDhCQUErQjs7Ozs7SUFDL0IsaUNBQTRDOztJQUM1QyxtQ0FBMkI7O0lBRWQsbUNBQWdEOzs7OztJQUNoRCwyQ0FBNEUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xyXG4gIENvbXBvbmVudCxcclxuICBPbkluaXQsXHJcbiAgSW5wdXQsXHJcbiAgT3V0cHV0LFxyXG4gIEV2ZW50RW1pdHRlcixcclxuICBPcHRpb25hbCwgU2VsZiwgU2ltcGxlQ2hhbmdlcywgT25DaGFuZ2VzLCBJbmplY3RcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHtcclxuICBDb250cm9sVmFsdWVBY2Nlc3NvcixcclxuICBOZ0NvbnRyb2wsXHJcbiAgVmFsaWRhdGlvbkVycm9ycyxcclxuICBOR19WQUxJREFUT1JTfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XHJcbmltcG9ydCB7IEVycm9yTWVzc2FnZSwgTGFiZWxSZXBsYWNlbWVudFRhZyB9IGZyb20gJy4uLy4uL21vZGVscy9lcnJvci1tZXNzYWdlLmludGVyZmFjZSc7XHJcbmltcG9ydCBnZXREYXlzSW5Nb250aCBmcm9tICdkYXRlLWZucy9nZXREYXlzSW5Nb250aCc7XHJcbmltcG9ydCBpc0FmdGVyIGZyb20gJ2RhdGUtZm5zL2lzQWZ0ZXInO1xyXG5pbXBvcnQgaXNCZWZvcmUgZnJvbSAnZGF0ZS1mbnMvaXNCZWZvcmUnO1xyXG5pbXBvcnQgc3RhcnRPZlRvZGF5IGZyb20gJ2RhdGUtZm5zL3N0YXJ0T2ZUb2RheSc7XHJcbmltcG9ydCBhZGRZZWFycyBmcm9tICdkYXRlLWZucy9hZGRZZWFycyc7XHJcbmltcG9ydCBzdWJZZWFycyBmcm9tICdkYXRlLWZucy9zdWJZZWFycyc7XHJcbmltcG9ydCB7IE1vSENvbW1vbkxpYnJhcnlFcnJvciB9IGZyb20gJy4uLy4uLy4uL2hlbHBlcnMvbGlicmFyeS1lcnJvcic7XHJcbmltcG9ydCB7IEFic3RyYWN0Rm9ybUNvbnRyb2wgfSBmcm9tICcuLi8uLi9tb2RlbHMvYWJzdHJhY3QtZm9ybS1jb250cm9sJztcclxuaW1wb3J0IHsgY29tcGFyZUFzYywgc3RhcnRPZkRheSwgYWRkRGF5cyB9IGZyb20gJ2RhdGUtZm5zJztcclxuXHJcbi8qKlxyXG4gKiBEYXRlQ29tcG9uZW50XHJcbiAqIFlvdSBjYW5ub3QgdXNlIFwiW3Jlc3RyaWN0RGF0ZV1cIiBpbiBjb21iaW5hdGlvbiB3aXRoIGVpdGhlciAgXCJbZGF0ZVJhbmdlRW5kXVwiIG9yIFwiW2RhdGVSYW5nZVN0YXJ0XVwiLlxyXG4gKiBZb3UgbXVzdCB1c2UgZWl0aGVyIFtyZXN0cmljdERhdGVdIG9yIHRoZSBbZGF0ZVJhbmdlKl0gaW5wdXRzLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKiAgVG8gdHJpZ2dlciAnbm8gZnV0dXJlIGRhdGVzIGFsbG93ZWQnIHVzaW5nIGRhdGUgcmFuZ2VzIHNldCB0aGUgcmFuZ2UgZW5kIGRhdGUgdG8geWVzdGVyZGF5J3MgZGF0ZS5cclxuICogICAgPGNvbW1vbi1kYXRlIG5hbWU9J2VmZmVjdGl2ZURhdGUnXHJcbiAqICAgICAgIGxhYmVsPVwiRWZmZWN0aXZlIERhdGVcIlxyXG4gKiAgICAgICBbZGF0ZVJhbmdlRW5kXT0neWVzdGVyZGF5J1xyXG4gKiAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJlZmZlY3RpdmVEYXRlXCI+PC9jb21tb24tZGF0ZT5cclxuIConXHJcbiAgKiAgVG8gdHJpZ2dlciAnbm8gcGFzdCBkYXRlcyBhbGxvd2VkJyB1c2luZyBkYXRlIHJhbmdlcyBzZXQgdGhlIHJhbmdlIHN0YXJ0IGRhdGUgdG8gdG9kYXkncyBkYXRlLlxyXG4gKiAgICA8Y29tbW9uLWRhdGUgbmFtZT0nZWZmZWN0aXZlRGF0ZSdcclxuICogICAgICAgbGFiZWw9XCJFZmZlY3RpdmUgRGF0ZVwiXHJcbiAqICAgICAgIFtkYXRlUmFuZ2VTdGFydF09XCJ0b2RheVwiXHJcbiAqICAgICAgIGZvcm1Db250cm9sTmFtZT1cImVmZmVjdGl2ZURhdGVcIj48L2NvbW1vbi1kYXRlPlxyXG4gKlxyXG4gKiAgVG8gYWxsb3cgaW5zdHJ1Y3Rpb25zIHVuZGVyIGxhYmVsLlxyXG4gKiAgICA8Y29tbW9uLWRhdGUgbmFtZT0nZWZmZWN0aXZlRGF0ZSdcclxuICogICAgICAgbGFiZWw9XCJFZmZlY3RpdmUgRGF0ZVwiXHJcbiAqICAgICAgIFtkYXRlUmFuZ2VFbmRdPSd5ZXN0ZXJkYXknXHJcbiAqICAgICAgIGZvcm1Db250cm9sTmFtZT1cImVmZmVjdGl2ZURhdGVcIj5cclxuICogICAgICA8cD5UaGlzIGlzIGEgdGVzdC48L3A+XHJcbiAqICAgIDwvY29tbW9uLWRhdGU+XHJcbiAqIEBleHBvcnRcclxuICpcclxuICovXHJcblxyXG5cclxuY29uc3QgTUFYX1lFQVJfUkFOR0UgPSAxNTA7XHJcbmNvbnN0IGRpc3RhbnRGdXR1cmUgPSBhZGRZZWFycyhzdGFydE9mVG9kYXkoKSwgTUFYX1lFQVJfUkFOR0UpO1xyXG5jb25zdCBkaXN0YW50UGFzdCA9IHN1YlllYXJzKHN0YXJ0T2ZUb2RheSgpLCBNQVhfWUVBUl9SQU5HRSk7XHJcblxyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdjb21tb24tZGF0ZScsXHJcbiAgdGVtcGxhdGVVcmw6ICcuL2RhdGUuY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlVXJsczogWycuL2RhdGUuY29tcG9uZW50LnNjc3MnXSxcclxufSlcclxuZXhwb3J0IGNsYXNzIERhdGVDb21wb25lbnQgZXh0ZW5kcyBBYnN0cmFjdEZvcm1Db250cm9sXHJcbiAgaW1wbGVtZW50cyBPbkluaXQsIENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBPbkNoYW5nZXMge1xyXG5cclxuICAvLyBJbnB1dHMgZm9yIGRpc2FibGVkICYgZXJyb3JNZXNzYWdlIGFyZSBmb3VuZCBpbiB0aGUgQWJzdHJhY3RGb3JtQ29udHJvbCBjbGFzc1xyXG4gIEBJbnB1dCgpIGRhdGU6IERhdGU7XHJcbiAgQE91dHB1dCgpIGRhdGVDaGFuZ2U6IEV2ZW50RW1pdHRlcjxEYXRlPiA9IG5ldyBFdmVudEVtaXR0ZXI8RGF0ZT4oKTtcclxuXHJcbiAgQElucHV0KCkgbGFiZWw6IHN0cmluZyA9ICdEYXRlJztcclxuICAvKiogQ2FuIGJlIG9uZSBvZjogXCJmdXR1cmVcIiwgXCJwYXN0XCIuIFwiZnV0dXJlXCIgaW5jbHVkZXMgdG9kYXksIFwicGFzdFwiIGRvZXMgbm90LiAqL1xyXG4gIEBJbnB1dCgpIHJlc3RyaWN0RGF0ZTogJ2Z1dHVyZScgfCAncGFzdCcgfCAnYW55JyA9ICdhbnknO1xyXG5cclxuICAvLyBUaGUgYWN0dWFsIHZhbHVlcyBkaXNwbGF5ZWQgdG8gdGhlIHVzZXIuICBNYXkgbm90IHByZWNpc2VseSBtYXRjaCBEYXRlXHJcbiAgLy8gb2JqZWN0LCBiZWNhdXNlIHRoZXNlIGZpZWxkcyBjYW4gYmUgYmxhbmsgd2hlcmVhcyBhIERhdGUgY2FuIG5ldmVyIGhhdmUgYVxyXG4gIC8vIFwiYmxhbmtcIiB5ZWFyIGZvciBleGFtcGxlLiBBbGwgYXJlIG51bGxhYmxlIHN0aW5ncyBvZiBudW1iZXJzIFwiMFwiIG9yIFwiMlwiLlxyXG4gIF95ZWFyOiBzdHJpbmc7XHJcbiAgX21vbnRoOiBzdHJpbmcgPSAnbnVsbCc7IC8vIHRoaXMgbWFrZXMgaXQgc28gdGhlIGJsYW5rIG9wdGlvbiBpcyBzZWxlY3RlZCBpbiB0aGUgaW5wdXRcclxuICBfZGF5OiBzdHJpbmc7XHJcblxyXG5cclxuICAvLyB2YXJpYWJsZXMgZm9yIGRhdGUgcmFuZ2VzXHJcbiAgX2RhdGVSYW5nZVN0YXJ0OiBEYXRlID0gbnVsbDtcclxuICBfZGF0ZVJhbmdlRW5kOiBEYXRlID0gbnVsbDtcclxuXHJcbiAgLyoqXHJcbiAgICogVGhlIGVhcmxpZXN0IHZhbGlkIGRhdGUgdGhhdCBjYW4gYmUgdXNlZC5cclxuICAgKiBEbyBOT1QgY29tYmluZSB3aXRoIHJlc3RyaWN0RGF0ZXMsIGFzIHRoZXkgc2V0IHRoZSBzYW1lIHVuZGVybHlpbmcgdmFsdWVzLlxyXG4gICAqL1xyXG4gIEBJbnB1dCgpXHJcbiAgc2V0IGRhdGVSYW5nZVN0YXJ0KGR0OiBEYXRlKSB7XHJcbiAgICAvLyBTZXQgdGltZSBvbiBkYXRlIHRvIDAwOjAwOjAwIGZvciBjb21wYXJpbmcgbGF0ZXJcclxuICAgIHRoaXMuX2RhdGVSYW5nZVN0YXJ0ID0gZHQgPyBzdGFydE9mRGF5KGR0KSA6IG51bGw7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGUgbGF0ZXN0IHZhbGlkIGRhdGUgdGhhdCBjYW4gYmUgdXNlZC5cclxuICAgKiBEbyBOT1QgY29tYmluZSB3aXRoIHJlc3RyaWN0RGF0ZXMsIGFzIHRoZXkgc2V0IHRoZSBzYW1lIHVuZGVybHlpbmcgdmFsdWVzLlxyXG4gICAqL1xyXG4gIEBJbnB1dCgpXHJcbiAgc2V0IGRhdGVSYW5nZUVuZChkdDogRGF0ZSkge1xyXG4gICAgLy8gU2V0IHRpbWUgb24gZGF0ZSB0byAwMDowMDowMCBmb3IgY29tcGFyaW5nIGxhdGVyXHJcbiAgICB0aGlzLl9kYXRlUmFuZ2VFbmQgPSBkdCA/IHN0YXJ0T2ZEYXkoZHQpIDogbnVsbDtcclxuICB9XHJcblxyXG4gIHB1YmxpYyBtb250aExpc3Q6IHN0cmluZ1tdID0gW1xyXG4gICAgJ0phbnVhcnknLCAnRmVicnVhcnknLCAnTWFyY2gnLCAnQXByaWwnLCAnTWF5JywgJ0p1bmUnLFxyXG4gICAgJ0p1bHknLCAnQXVndXN0JywgJ1NlcHRlbWJlcicsICdPY3RvYmVyJywgJ05vdmVtYmVyJywgJ0RlY2VtYmVyJ1xyXG4gIF07XHJcblxyXG4gIG1vbnRoTGFiZWxmb3JJZDogc3RyaW5nID0gJ21vbnRoXycgKyB0aGlzLm9iamVjdElkO1xyXG4gIGRheUxhYmVsZm9ySWQ6IHN0cmluZyA9ICdkYXlfJyArIHRoaXMub2JqZWN0SWQ7XHJcbiAgeWVhckxhYmVsZm9ySWQ6IHN0cmluZyA9ICd5ZWFyXycgKyB0aGlzLm9iamVjdElkO1xyXG5cclxuICAvLyBBYnN0YWN0IHZhcmlhYmxlIGRlZmluZWRcclxuICBfZGVmYXVsdEVyck1zZzogRXJyb3JNZXNzYWdlID0ge1xyXG4gICAgcmVxdWlyZWQ6IGAke0xhYmVsUmVwbGFjZW1lbnRUYWd9IGlzIHJlcXVpcmVkLmAsXHJcbiAgICBkYXlPdXRPZlJhbmdlOiBgSW52YWxpZCAke0xhYmVsUmVwbGFjZW1lbnRUYWd9LmAsXHJcbiAgICB5ZWFyRGlzdGFudFBhc3Q6IGBJbnZhbGlkICR7TGFiZWxSZXBsYWNlbWVudFRhZ30uYCxcclxuICAgIHllYXJEaXN0YW50RnV0dXJlOiBgSW52YWxpZCAke0xhYmVsUmVwbGFjZW1lbnRUYWd9LmAsXHJcbiAgICBub1Bhc3REYXRlc0FsbG93ZWQ6IGBJbnZhbGlkICR7TGFiZWxSZXBsYWNlbWVudFRhZ30uYCxcclxuICAgIG5vRnV0dXJlRGF0ZXNBbGxvd2VkOiBgSW52YWxpZCAke0xhYmVsUmVwbGFjZW1lbnRUYWd9LmAsXHJcbiAgICBpbnZhbGlkVmFsdWU6IGBJbnZhbGlkICR7TGFiZWxSZXBsYWNlbWVudFRhZ30uYCxcclxuICAgIGludmFsaWRSYW5nZTogYEludmFsaWQgJHtMYWJlbFJlcGxhY2VtZW50VGFnfS5gXHJcbiAgfTtcclxuXHJcbiAgcHJpdmF0ZSB0b2RheSA9IHN0YXJ0T2ZUb2RheSgpO1xyXG4gIHByaXZhdGUgdG9tb3Jyb3cgPSBhZGREYXlzKCB0aGlzLnRvZGF5LCAxICk7XHJcbiAgcHVibGljIGlzUmVxdWlyZWQ6IGJvb2xlYW47IC8vIFRPRE86IHJlbW92ZSBpZiBub3QgcmVxdWlyZWQgLSB2YWx1ZSBkb2VzIG5vdCBnZXQgc2V0IHdoZW4gdXNpbmcgUmVhY3RpdmUgZm9ybXNcclxuXHJcbiAgY29uc3RydWN0b3IoIEBPcHRpb25hbCgpIEBTZWxmKCkgcHVibGljIGNvbnRyb2xEaXI6IE5nQ29udHJvbCxcclxuICAgICAgICAgICAgICAgQE9wdGlvbmFsKCkgQFNlbGYoKSBASW5qZWN0KE5HX1ZBTElEQVRPUlMpIHByaXZhdGUgaW5qZWN0ZWRWYWxpZGF0b3JzOiBhbnlbXSApIHtcclxuICAgIHN1cGVyKCk7XHJcbiAgICBpZiAoY29udHJvbERpcikge1xyXG4gICAgICBjb250cm9sRGlyLnZhbHVlQWNjZXNzb3IgPSB0aGlzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcykge1xyXG5cclxuICAgIC8qXHJcbiAgICAgKiBXb3JrcywgY3JlYXRlcyBuZXcgb2JqZWN0IGxpdGVyYWxsXHJcbiAgICAgKiBvYmogPSB7XHJcbiAgICAgKiAgIGVycm9yTWVzc2FnZTogJ25ldydtZXNzYWdlJztcclxuICAgICAqIH1cclxuICAgICAqXHJcbiAgICAgKiBEb2Vzbid0IHdvcmssIG1vZGlmaWVzIGV4aXN0aW5nIG9iamVjdC4gIFdvdWxkIGhhdmUgdG8gbWFudWFsbHkgY2FsbCBjZC5kZXRlY3RDaGFuZ2VzKCkgYWZ0ZXJ3YXJkcy5cclxuICAgICAqIG9iai5lcnJvck1lc3NhZ2UgPSAnbmV3TWVzc2FnZSc7XHJcbiAgICAgKi9cclxuICAgIGlmIChjaGFuZ2VzWydlcnJvck1lc3NhZ2UnXSkge1xyXG4gICAgICB0aGlzLnNldEVycm9yTXNnKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBuZ09uSW5pdCgpIHtcclxuICAgIHRoaXMuc2V0RXJyb3JNc2coKTtcclxuXHJcbiAgICAvLyBTZXQgdG8gbWlkbmlnaHQsIHNvIHdlIGRvbid0IGFjY2lkZW50YWxseSBjb21wYXJlIGFnYWluc3QgaG91cnMvbWludXRlcy9zZWNvbmRzXHJcblxyXG4gICAgaWYgKHRoaXMucmVzdHJpY3REYXRlICE9PSAnYW55JyAmJiAodGhpcy5fZGF0ZVJhbmdlRW5kIHx8IHRoaXMuX2RhdGVSYW5nZVN0YXJ0KSkge1xyXG4gICAgICBjb25zdCBtc2cgPSBgPGNvbW1vbi1kYXRlPiAtIEludmFsaWQgQElucHV0KCkgb3B0aW9uIGNvbmZpZ3VyYXRpb24uXHJcbllvdSBjYW5ub3QgdXNlIFwiW3Jlc3RyaWN0RGF0ZV1cIiBpbiBjb21iaW5hdGlvbiB3aXRoIGVpdGhlciAgXCJbZGF0ZVJhbmdlRW5kXVwiIG9yIFwiW2RhdGVSYW5nZVN0YXJ0XVwiLlxyXG5Zb3UgbXVzdCB1c2UgZWl0aGVyIFtyZXN0cmljdERhdGVdIG9yIHRoZSBbZGF0ZVJhbmdlKl0gaW5wdXRzLlxyXG5cclxuICAgIDxjb21tb24tZGF0ZSBuYW1lPSdlZmZlY3RpdmVEYXRlJ1xyXG4gICAgICAgIGxhYmVsPVwiRWZmZWN0aXZlIERhdGVcIlxyXG4gICAgICAgIFtyZXN0cmljdERhdGVdPVwiJ3Bhc3QnXCIgICA8PDwgcHJvYmxlbSwgY2hvb3NlIG9uZVxyXG4gICAgICAgIFtkYXRlUmFuZ2VFbmRdPSd0b2RheScgICAgPDw8IHByb2JsZW0sIGNob29zZSBvbmVcclxuICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJlZmZlY3RpdmVEYXRlXCJcclxuICAgID48L2NvbW1vbi1kYXRlPlxyXG5cclxuICBgO1xyXG4gICAgICB0aHJvdyBuZXcgTW9IQ29tbW9uTGlicmFyeUVycm9yKG1zZyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW5pdGlhbGl6ZSBkYXRlIHJhbmdlIGxvZ2ljXHJcbiAgICBpZiAodGhpcy5yZXN0cmljdERhdGUgPT09ICdwYXN0Jykge1xyXG4gICAgICAvLyBwYXN0IGRvZXMgYWxsb3cgZm9yIHRvZGF5XHJcbiAgICAgIHRoaXMuX2RhdGVSYW5nZUVuZCA9IHRoaXMudG9kYXk7XHJcbiAgICAgIHRoaXMuX2RhdGVSYW5nZVN0YXJ0ID0gbnVsbDtcclxuICAgIH0gZWxzZSBpZiAodGhpcy5yZXN0cmljdERhdGUgPT09ICdmdXR1cmUnKSB7XHJcbiAgICAgIC8vIGZ1dHVyZSBkb2VzIE5PVCBhbGxvdyBmb3IgdG9kYXlcclxuICAgICAgdGhpcy5fZGF0ZVJhbmdlRW5kID0gbnVsbDtcclxuICAgICAgdGhpcy5fZGF0ZVJhbmdlU3RhcnQgPSB0aGlzLnRvbW9ycm93O1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMucmVnaXN0ZXJWYWxpZGF0aW9uKCB0aGlzLmNvbnRyb2xEaXIsIHRoaXMudmFsaWRhdGVTZWxmIClcclxuICAgICAgLnRoZW4oXyA9PiB7XHJcbiAgICAgICAgaWYgKHRoaXMuaW5qZWN0ZWRWYWxpZGF0b3JzICYmIHRoaXMuaW5qZWN0ZWRWYWxpZGF0b3JzLmxlbmd0aCkge1xyXG4gICAgICAgICAgLy8gVE9ETzogUG90ZW50aWFsbHkgbW92ZSB0byBBYnN0cmFjdEZvcm1Db250cm9sXHJcbiAgICAgICAgICAvLyBJbnNwZWN0IHRoZSB2YWxpZGF0b3IgZnVuY3Rpb25zIGZvciBvbmUgdGhhdCBoYXMgYSB7cmVxdWlyZWQ6IHRydWV9XHJcbiAgICAgICAgICAvLyBwcm9wZXJ0eS4gSW1wb3J0YW50bHksIHdlIGFyZSBpbnNwZWN0aW5nIHRoZSB2YWxpZGF0b3IgZnVuY3Rpb25cclxuICAgICAgICAgIC8vIGl0c2VsZiBhbmQgTk9UIHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBOZ0NvbnRyb2wuIC0tIERvZXMgbm90IHdvcmsgZm9yIHJlYWN0aXZlIGZvcm1zXHJcbiAgICAgICAgICB0aGlzLmlzUmVxdWlyZWQgPSB0aGlzLmluamVjdGVkVmFsaWRhdG9yc1xyXG4gICAgICAgICAgICAuZmlsdGVyKHggPT4geC5yZXF1aXJlZClcclxuICAgICAgICAgICAgLmxlbmd0aCA+PSAxO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgfVxyXG5cclxuICBnZXQgbW9udGgoKTogbnVtYmVyIHtcclxuICAgIGlmICh0aGlzLmRhdGUpIHtcclxuICAgICAgcmV0dXJuIHRoaXMuZGF0ZS5nZXRNb250aCgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgZ2V0IGRheSgpOiBudW1iZXIge1xyXG4gICAgaWYgKHRoaXMuZGF0ZSkge1xyXG4gICAgICByZXR1cm4gdGhpcy5kYXRlLmdldERhdGUoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGdldCB5ZWFyKCk6IG51bWJlciB7XHJcbiAgICBpZiAodGhpcy5kYXRlKSB7XHJcbiAgICAgIHJldHVybiB0aGlzLmRhdGUuZ2V0RnVsbFllYXIoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEhhbmRsZXMgY3JlYXRpbmcgLyBkZXN0cm95aW5nIGRhdGUgYW5kIGVtaXR0aW5nIGNoYW5nZXMgYmFzZWQgb24gdXNlciBiZWhhdmlvdXIuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBwcm9jZXNzRGF0ZSgpIHtcclxuXHJcbiAgICBpZiAodGhpcy5jYW5DcmVhdGVEYXRlKCkpIHtcclxuICAgICAgY29uc3QgeWVhciA9IHRoaXMuZ2V0TnVtZXJpY1ZhbHVlKHRoaXMuX3llYXIpO1xyXG4gICAgICBjb25zdCBtb250aCA9IHRoaXMuZ2V0TnVtZXJpY1ZhbHVlKHRoaXMuX21vbnRoKTtcclxuICAgICAgY29uc3QgZGF5ID0gdGhpcy5nZXROdW1lcmljVmFsdWUodGhpcy5fZGF5KTtcclxuXHJcbiAgICAgIC8vIERhdGUgZnVuY3Rpb24gYXBwZWFycyB0byB1c2Ugc2V0WWVhcigpIHNvIGFueSB5ZWFyIDAtOTkgcmVzdWx0cyBpbiB5ZWFyIDE5MDAgdG8gMTk5OVxyXG4gICAgICAvLyBTZXQgZWFjaCBmaWVsZCBpbmRpdmlkdWFsbHksIHVzZSBzZXRGdWxsWWVhcigpIGluc3RlYWQgb2Ygc2V0WWVhcigpXHJcbiAgICAgIC8vIFNldCB0aW1lIG9uIGRhdGUgdG8gMDA6MDA6MDAgZm9yIGNvbXBhcmluZyBsYXRlclxyXG4gICAgICB0aGlzLmRhdGUgPSBzdGFydE9mRGF5KG5ldyBEYXRlKHllYXIsIG1vbnRoLCBkYXkpKTtcclxuICAgICAgdGhpcy5kYXRlLnNldEZ1bGxZZWFyKHllYXIpO1xyXG5cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIFRyaWdnZXIgdmFsaWRhdG9yIGZvciBlbXB0eWluZyBmaWVsZHMgdXNlIGNhc2UuIFRoaXMgaXMgdG8gcmVtb3ZlIHRoZSAnSW52YWxpZCBkYXRlJyBlcnJvci5cclxuICAgICAgaWYgKHRoaXMuZGF0ZSB8fFxyXG4gICAgICAgICghdGhpcy5feWVhciAmJiAhdGhpcy5fZGF5ICYmIHRoaXMuX21vbnRoID09PSAnbnVsbCcpKSB7XHJcblxyXG4gICAgICAgIC8vIERlc3Ryb3lzIHRoZSBpbnRlcm5hbCBEYXRlIG9iamVjdC5cclxuICAgICAgICB0aGlzLmRhdGUgPSBudWxsO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgdGhpcy5fb25DaGFuZ2UodGhpcy5kYXRlKTtcclxuICAgIHRoaXMuX29uVG91Y2hlZCh0aGlzLmRhdGUpO1xyXG4gICAgdGhpcy5kYXRlQ2hhbmdlLmVtaXQodGhpcy5kYXRlKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHVybnMgdHJ1ZSBpZiBhbmQgb25seSBpZiB0aGUgZGF5L21vbnRoL3llYXIgZmllbGRzIGFyZSBhbGwgZmlsbGVkIG91dC5cclxuICAgKi9cclxuICBwcml2YXRlIGNhbkNyZWF0ZURhdGUoKTogYm9vbGVhbiB7XHJcblxyXG4gICAgLy8gc3BlY2lhbCBiZWNhdXNlIFwiMFwiIGlzIHZhbGlkIChKYW4pXHJcbiAgICBjb25zdCBtb250aENoZWNrID0gKHR5cGVvZiB0aGlzLl9tb250aCA9PT0gJ3N0cmluZycgJiYgdGhpcy5fbW9udGggIT09ICdudWxsJylcclxuICAgICAgfHwgdHlwZW9mIHRoaXMuX21vbnRoID09PSAnbnVtYmVyJztcclxuXHJcbiAgICBpZiAoISF0aGlzLl95ZWFyICYmICEhdGhpcy5fZGF5ICYmIG1vbnRoQ2hlY2spIHtcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIF90cmlnZ2VyT25DaGFuZ2UoIGR0OiBEYXRlICkge1xyXG4gICAgdGhpcy5fb25DaGFuZ2UoZHQpO1xyXG4gICAgdGhpcy5kYXRlQ2hhbmdlLmVtaXQoZHQpO1xyXG4gIH1cclxuXHJcbiAgLyoqIENvbnZlcnQgc3RyaW5nIHRvIG51bWVyaWMgdmFsdWUgb3IgbnVsbCBpZiBub3QgKi9cclxuICBwcml2YXRlIGdldE51bWVyaWNWYWx1ZSh2YWx1ZTogc3RyaW5nKTogbnVtYmVyIHwgbnVsbCB7XHJcbiAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUludCh2YWx1ZSwgMTApO1xyXG4gICAgcmV0dXJuIChpc05hTihwYXJzZWQpID8gbnVsbCA6IHBhcnNlZCk7XHJcbiAgfVxyXG5cclxuXHJcbiAgcHJpdmF0ZSBzZXREaXNwbGF5VmFyaWFibGVzKCkge1xyXG4gICAgdGhpcy5fZGF5ID0gdGhpcy5kYXRlLmdldERhdGUoKS50b1N0cmluZygpO1xyXG4gICAgdGhpcy5fbW9udGggPSB0aGlzLmRhdGUuZ2V0TW9udGgoKS50b1N0cmluZygpO1xyXG4gICAgdGhpcy5feWVhciA9IHRoaXMuZGF0ZS5nZXRGdWxsWWVhcigpLnRvU3RyaW5nKCk7XHJcbiAgfVxyXG5cclxuICB3cml0ZVZhbHVlKHZhbHVlOiBEYXRlKTogdm9pZCB7XHJcbiAgICBpZiAodmFsdWUpIHtcclxuICAgICAgdGhpcy5kYXRlID0gdmFsdWU7XHJcbiAgICAgIHRoaXMuc2V0RGlzcGxheVZhcmlhYmxlcygpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgb25CbHVyRGF5KHZhbHVlOiBzdHJpbmcpIHtcclxuICAgIHRoaXMuX2RheSA9IHZhbHVlO1xyXG4gICAgdGhpcy5wcm9jZXNzRGF0ZSgpO1xyXG4gIH1cclxuICBvbkJsdXJZZWFyKHZhbHVlOiBzdHJpbmcpIHtcclxuICAgIHRoaXMuX3llYXIgPSB2YWx1ZTtcclxuICAgIHRoaXMucHJvY2Vzc0RhdGUoKTtcclxuICB9XHJcbiAgb25CbHVyTW9udGgodmFsdWU6IHN0cmluZykge1xyXG4gICAgdGhpcy5fbW9udGggPSB2YWx1ZTtcclxuICAgIHRoaXMucHJvY2Vzc0RhdGUoKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFZhbGlkYXRlcyB0aGUgRGF0ZUNvbXBvbmVudCBpbnN0YW5jZSBpdHNlbGYsIHVzaW5nIGludGVybmFsIHByaXZhdGUgdmFyaWFibGVzLlxyXG4gICAqXHJcbiAgICovXHJcbiAgcHJpdmF0ZSB2YWxpZGF0ZVNlbGYoKSB7XHJcbiAgICBjb25zdCB5ZWFyID0gcGFyc2VJbnQodGhpcy5feWVhciwgMTApO1xyXG4gICAgY29uc3QgbW9udGggPSBwYXJzZUludCh0aGlzLl9tb250aCwgMTApO1xyXG4gICAgY29uc3QgZGF5ID0gcGFyc2VJbnQodGhpcy5fZGF5LCAxMCk7XHJcblxyXG4gICAgLy8gTm90aGluZyBlbXB0eSBmaWVsZHMgLSBub3RoaW5nIHRvIHZhbGlkYXRlIE9SIGhhdmUgcmVxdWlyZWQgZXJyb3JcclxuICAgIGlmICggaXNOYU4oeWVhcikgJiYgaXNOYU4obW9udGgpICYmIGlzTmFOKGRheSkgKSB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFBhcnRpYWxseSBmaWxsZWQgb3V0IGlzIGFsd2F5cyBpbnZhbGlkLCBpZiB5ZWFyIGlzIHByZXNlbnQgaXQgbXVzdCBiZSBncmVhdGVyIHRoYW4gemVyb1xyXG4gICAgaWYgKCBpc05hTih5ZWFyKSB8fCBpc05hTihtb250aCkgfHwgaXNOYU4oZGF5KSB8fCAoIWlzTmFOKCB5ZWFyKSAmJiB5ZWFyIDw9IDApICkge1xyXG4gICAgICByZXR1cm4ge2ludmFsaWRWYWx1ZTogdHJ1ZX07XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2UgY2FuIGhhcmRjb2RlIHRoZSBkYXksIHNpbmNlIHdlJ3JlIG9ubHkgaW50ZXJlc3RlZCBpbiB0b3RhbCBkYXlzIGZvciB0aGF0IG1vbnRoLlxyXG4gICAgY29uc3QgZGF5c0luTW9udGggPSB