UNPKG

@bsachref/ng-form

Version:

A dynamic form component for Angular using PrimeNG or Angular Material

110 lines 28.4 kB
/** * @component * @name MaterialFormsComponent * @description * A standalone Angular component that renders a dynamic form using Angular Material components. * The form configuration is provided via inputs, and the form submission is handled via an output event. * * @selector material-forms * @standalone true * @imports * - CommonModule * - ReactiveFormsModule * - MatInputModule * - MatSelectModule * - MatCheckboxModule * - MatRadioModule * - MatButtonModule * - ValidationMessagesComponent * - MatDatepickerModule * - MatFormFieldModule * @providers provideNativeDateAdapter * @templateUrl ./material-form.component.html * @styleUrl ./material-form.component.css * @changeDetection ChangeDetectionStrategy.OnPush * * @inputs * @property {string} formName - The name of the form. * @property {FormControlConfig[]} controls - The configuration for the form controls. * * @outputs * @property {EventEmitter<Record<string, any>>} formSubmit - Event emitted when the form is submitted. * * @class * @name MaterialFormsComponent * @description * This class defines the MaterialFormsComponent which initializes the form based on the provided controls configuration, * handles form submission, and provides validation for the form controls. * * @constructor * @param {FormBuilder} fb - Angular FormBuilder service to create form groups and controls. * @param {ChangeDetectorRef} cdr - Angular ChangeDetectorRef service to manually trigger change detection. * * @method * @name ngOnInit * @description * Lifecycle hook that is called after the component's view has been initialized. It initializes the form. * * @method * @name initializeForm * @description * Initializes the form controls based on the provided configuration. Supports datepicker range controls. * * @method * @name getValidators * @description * Returns an array of Angular validators based on the provided control configuration. * @param {FormControlConfig} control - The configuration for the form control. * @returns {Validators[]} - An array of Angular validators. * * @method * @name onSubmit * @description * Handles the form submission. Marks all controls as touched and dirty, updates the form's validity, * emits the form values if the form is valid, and resets the form. Triggers change detection. */ import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy, } from '@angular/core'; import { ReactiveFormsModule, } from '@angular/forms'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatRadioModule } from '@angular/material/radio'; import { MatButtonModule } from '@angular/material/button'; import { ValidationMessagesComponent } from '../validation-messages/validation-messages.component'; import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { provideNativeDateAdapter } from '@angular/material/core'; import { BaseFormsComponent } from '../base.component'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@angular/forms"; import * as i3 from "@angular/material/input"; import * as i4 from "@angular/material/form-field"; import * as i5 from "@angular/material/select"; import * as i6 from "@angular/material/core"; import * as i7 from "@angular/material/checkbox"; import * as i8 from "@angular/material/radio"; import * as i9 from "@angular/material/button"; import * as i10 from "@angular/material/datepicker"; export class MaterialFormsComponent extends BaseFormsComponent { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MaterialFormsComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: MaterialFormsComponent, isStandalone: true, selector: "material-forms", providers: [provideNativeDateAdapter()], usesInheritance: true, ngImport: i0, template: "<form [formGroup]=\"form\" [attr.name]=\"formName()\" (ngSubmit)=\"onSubmit()\">\n @for (control of controls(); track $index) {\n @if (control.uiFramework === \"material\" || !control.uiFramework) {\n @if (form.get(control.name)) {\n <section [ngClass]=\"control.class\" [ngStyle]=\"control.style\">\n @if (control.type === \"input\") {\n <mat-form-field appearance=\"outline\">\n @if (control.label) {\n <mat-label>{{ control.label }}</mat-label>\n }\n <input matInput [formControlName]=\"control.name\" [id]=\"control.name\" type=\"text\" />\n </mat-form-field>\n }\n @if (control.type === \"select\") {\n <mat-form-field appearance=\"outline\">\n @if (control.label) {\n <mat-label>{{ control.label }}</mat-label>\n }\n <mat-select [formControlName]=\"control.name\" [id]=\"control.name\">\n @for (option of control.options ?? []; track $index) {\n <mat-option [value]=\"option\">{{ option }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n @if (control.type === \"checkbox\") {\n <mat-checkbox [formControlName]=\"control.name\" [id]=\"control.name\">\n {{ control.label }}\n </mat-checkbox>\n }\n @if (control.type === \"radio\") {\n <mat-radio-group [formControlName]=\"control.name\">\n @for (option of control.options ?? []; track $index) {\n <mat-radio-button [id]=\"control.name + '-' + option\" [value]=\"option\">\n {{ option }}\n </mat-radio-button>\n }\n </mat-radio-group>\n }\n @if (control.type === \"datepicker\") {\n @if (control.datePickerMode === \"single\") {\n <mat-form-field appearance=\"outline\">\n <mat-label>Choose a date</mat-label>\n <input matInput [matDatepicker]=\"datepicker\" [formControlName]=\"control.name\" />\n <mat-datepicker-toggle matIconSuffix [for]=\"datepicker\"></mat-datepicker-toggle>\n <mat-datepicker #datepicker></mat-datepicker>\n </mat-form-field>\n } @else if (control.datePickerMode === \"range\") {\n <mat-form-field>\n <mat-label>Enter a date range</mat-label>\n <mat-date-range-input [rangePicker]=\"picker\" [formGroup]=\"form\" ]>\n <input matStartDate formControlName=\"{{ control.name }}_Start\" placeholder=\"Start date\" />\n <input matEndDate formControlName=\"{{ control.name }}_End\" placeholder=\"End date\" />\n </mat-date-range-input>\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-date-range-picker #picker></mat-date-range-picker>\n\n <!-- @if (form.controls.start.hasError('matStartDateInvalid')) {\n <mat-error>Invalid start date</mat-error>\n }\n @if (form.controls.end.hasError('matEndDateInvalid')) {\n <mat-error>Invalid end date</mat-error>\n } -->\n </mat-form-field>\n }\n }\n\n <validation-messages [control]=\"form.get(control.name)\" [controlName]=\"control.name\"\n [config]=\"control\"></validation-messages>\n </section>\n }\n }\n }\n <button mat-raised-button color=\"primary\" type=\"submit\" [disabled]=\"form.invalid\">\n Submit\n </button>\n</form>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i7.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i8.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i8.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i9.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: ValidationMessagesComponent, selector: "validation-messages", inputs: ["control", "controlName", "config"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i10.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i10.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i10.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: i10.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i10.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i10.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i10.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "ngmodule", type: MatFormFieldModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MaterialFormsComponent, decorators: [{ type: Component, args: [{ selector: 'material-forms', standalone: true, imports: [ BaseFormsComponent, CommonModule, ReactiveFormsModule, MatInputModule, MatSelectModule, MatCheckboxModule, MatRadioModule, MatButtonModule, ValidationMessagesComponent, MatDatepickerModule, MatFormFieldModule, ], providers: [provideNativeDateAdapter()], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form [formGroup]=\"form\" [attr.name]=\"formName()\" (ngSubmit)=\"onSubmit()\">\n @for (control of controls(); track $index) {\n @if (control.uiFramework === \"material\" || !control.uiFramework) {\n @if (form.get(control.name)) {\n <section [ngClass]=\"control.class\" [ngStyle]=\"control.style\">\n @if (control.type === \"input\") {\n <mat-form-field appearance=\"outline\">\n @if (control.label) {\n <mat-label>{{ control.label }}</mat-label>\n }\n <input matInput [formControlName]=\"control.name\" [id]=\"control.name\" type=\"text\" />\n </mat-form-field>\n }\n @if (control.type === \"select\") {\n <mat-form-field appearance=\"outline\">\n @if (control.label) {\n <mat-label>{{ control.label }}</mat-label>\n }\n <mat-select [formControlName]=\"control.name\" [id]=\"control.name\">\n @for (option of control.options ?? []; track $index) {\n <mat-option [value]=\"option\">{{ option }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n @if (control.type === \"checkbox\") {\n <mat-checkbox [formControlName]=\"control.name\" [id]=\"control.name\">\n {{ control.label }}\n </mat-checkbox>\n }\n @if (control.type === \"radio\") {\n <mat-radio-group [formControlName]=\"control.name\">\n @for (option of control.options ?? []; track $index) {\n <mat-radio-button [id]=\"control.name + '-' + option\" [value]=\"option\">\n {{ option }}\n </mat-radio-button>\n }\n </mat-radio-group>\n }\n @if (control.type === \"datepicker\") {\n @if (control.datePickerMode === \"single\") {\n <mat-form-field appearance=\"outline\">\n <mat-label>Choose a date</mat-label>\n <input matInput [matDatepicker]=\"datepicker\" [formControlName]=\"control.name\" />\n <mat-datepicker-toggle matIconSuffix [for]=\"datepicker\"></mat-datepicker-toggle>\n <mat-datepicker #datepicker></mat-datepicker>\n </mat-form-field>\n } @else if (control.datePickerMode === \"range\") {\n <mat-form-field>\n <mat-label>Enter a date range</mat-label>\n <mat-date-range-input [rangePicker]=\"picker\" [formGroup]=\"form\" ]>\n <input matStartDate formControlName=\"{{ control.name }}_Start\" placeholder=\"Start date\" />\n <input matEndDate formControlName=\"{{ control.name }}_End\" placeholder=\"End date\" />\n </mat-date-range-input>\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-date-range-picker #picker></mat-date-range-picker>\n\n <!-- @if (form.controls.start.hasError('matStartDateInvalid')) {\n <mat-error>Invalid start date</mat-error>\n }\n @if (form.controls.end.hasError('matEndDateInvalid')) {\n <mat-error>Invalid end date</mat-error>\n } -->\n </mat-form-field>\n }\n }\n\n <validation-messages [control]=\"form.get(control.name)\" [controlName]=\"control.name\"\n [config]=\"control\"></validation-messages>\n </section>\n }\n }\n }\n <button mat-raised-button color=\"primary\" type=\"submit\" [disabled]=\"form.invalid\">\n Submit\n </button>\n</form>" }] }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWF0ZXJpYWwtZm9ybS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9uZ0Zvcm0vc3JjL2FwcC9tYXRlcmlhbC1mb3JtL21hdGVyaWFsLWZvcm0uY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vbmdGb3JtL3NyYy9hcHAvbWF0ZXJpYWwtZm9ybS9tYXRlcmlhbC1mb3JtLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0VHO0FBQ0gsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFDTCxTQUFTLEVBQ1QsdUJBQXVCLEdBQ3hCLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFDTCxtQkFBbUIsR0FDcEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDekQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzNELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQy9ELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDM0QsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sc0RBQXNELENBQUM7QUFDbkcsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDbEUsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDbEUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7Ozs7Ozs7Ozs7OztBQXVCdkQsTUFBTSxPQUFPLHNCQUF1QixTQUFRLGtCQUFrQjt3R0FBakQsc0JBQXNCOzRGQUF0QixzQkFBc0IsNkRBSnRCLENBQUMsd0JBQXdCLEVBQUUsQ0FBQyxpRENyR3pDLDZ0R0E0RU8sMkNEY0gsWUFBWSxpTkFDWixtQkFBbUIsNDhCQUNuQixjQUFjLHV2QkFDZCxlQUFlLG1yQkFDZixpQkFBaUIsb1lBQ2pCLGNBQWMsb2xCQUNkLGVBQWUsNE5BQ2YsMkJBQTJCLDZHQUMzQixtQkFBbUIsMmxDQUNuQixrQkFBa0I7OzRGQU1ULHNCQUFzQjtrQkFwQmxDLFNBQVM7K0JBQ0UsZ0JBQWdCLGNBQ2QsSUFBSSxXQUNQO3dCQUNQLGtCQUFrQjt3QkFDbEIsWUFBWTt3QkFDWixtQkFBbUI7d0JBQ25CLGNBQWM7d0JBQ2QsZUFBZTt3QkFDZixpQkFBaUI7d0JBQ2pCLGNBQWM7d0JBQ2QsZUFBZTt3QkFDZiwyQkFBMkI7d0JBQzNCLG1CQUFtQjt3QkFDbkIsa0JBQWtCO3FCQUNuQixhQUNVLENBQUMsd0JBQXdCLEVBQUUsQ0FBQyxtQkFFdEIsdUJBQXVCLENBQUMsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGNvbXBvbmVudFxuICogQG5hbWUgTWF0ZXJpYWxGb3Jtc0NvbXBvbmVudFxuICogQGRlc2NyaXB0aW9uXG4gKiBBIHN0YW5kYWxvbmUgQW5ndWxhciBjb21wb25lbnQgdGhhdCByZW5kZXJzIGEgZHluYW1pYyBmb3JtIHVzaW5nIEFuZ3VsYXIgTWF0ZXJpYWwgY29tcG9uZW50cy5cbiAqIFRoZSBmb3JtIGNvbmZpZ3VyYXRpb24gaXMgcHJvdmlkZWQgdmlhIGlucHV0cywgYW5kIHRoZSBmb3JtIHN1Ym1pc3Npb24gaXMgaGFuZGxlZCB2aWEgYW4gb3V0cHV0IGV2ZW50LlxuICogXG4gKiBAc2VsZWN0b3IgbWF0ZXJpYWwtZm9ybXNcbiAqIEBzdGFuZGFsb25lIHRydWVcbiAqIEBpbXBvcnRzXG4gKiAtIENvbW1vbk1vZHVsZVxuICogLSBSZWFjdGl2ZUZvcm1zTW9kdWxlXG4gKiAtIE1hdElucHV0TW9kdWxlXG4gKiAtIE1hdFNlbGVjdE1vZHVsZVxuICogLSBNYXRDaGVja2JveE1vZHVsZVxuICogLSBNYXRSYWRpb01vZHVsZVxuICogLSBNYXRCdXR0b25Nb2R1bGVcbiAqIC0gVmFsaWRhdGlvbk1lc3NhZ2VzQ29tcG9uZW50XG4gKiAtIE1hdERhdGVwaWNrZXJNb2R1bGVcbiAqIC0gTWF0Rm9ybUZpZWxkTW9kdWxlXG4gKiBAcHJvdmlkZXJzIHByb3ZpZGVOYXRpdmVEYXRlQWRhcHRlclxuICogQHRlbXBsYXRlVXJsIC4vbWF0ZXJpYWwtZm9ybS5jb21wb25lbnQuaHRtbFxuICogQHN0eWxlVXJsIC4vbWF0ZXJpYWwtZm9ybS5jb21wb25lbnQuY3NzXG4gKiBAY2hhbmdlRGV0ZWN0aW9uIENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaFxuICogXG4gKiBAaW5wdXRzXG4gKiBAcHJvcGVydHkge3N0cmluZ30gZm9ybU5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgZm9ybS5cbiAqIEBwcm9wZXJ0eSB7Rm9ybUNvbnRyb2xDb25maWdbXX0gY29udHJvbHMgLSBUaGUgY29uZmlndXJhdGlvbiBmb3IgdGhlIGZvcm0gY29udHJvbHMuXG4gKiBcbiAqIEBvdXRwdXRzXG4gKiBAcHJvcGVydHkge0V2ZW50RW1pdHRlcjxSZWNvcmQ8c3RyaW5nLCBhbnk+Pn0gZm9ybVN1Ym1pdCAtIEV2ZW50IGVtaXR0ZWQgd2hlbiB0aGUgZm9ybSBpcyBzdWJtaXR0ZWQuXG4gKiBcbiAqIEBjbGFzc1xuICogQG5hbWUgTWF0ZXJpYWxGb3Jtc0NvbXBvbmVudFxuICogQGRlc2NyaXB0aW9uXG4gKiBUaGlzIGNsYXNzIGRlZmluZXMgdGhlIE1hdGVyaWFsRm9ybXNDb21wb25lbnQgd2hpY2ggaW5pdGlhbGl6ZXMgdGhlIGZvcm0gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGNvbnRyb2xzIGNvbmZpZ3VyYXRpb24sXG4gKiBoYW5kbGVzIGZvcm0gc3VibWlzc2lvbiwgYW5kIHByb3ZpZGVzIHZhbGlkYXRpb24gZm9yIHRoZSBmb3JtIGNvbnRyb2xzLlxuICogXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwYXJhbSB7Rm9ybUJ1aWxkZXJ9IGZiIC0gQW5ndWxhciBGb3JtQnVpbGRlciBzZXJ2aWNlIHRvIGNyZWF0ZSBmb3JtIGdyb3VwcyBhbmQgY29udHJvbHMuXG4gKiBAcGFyYW0ge0NoYW5nZURldGVjdG9yUmVmfSBjZHIgLSBBbmd1bGFyIENoYW5nZURldGVjdG9yUmVmIHNlcnZpY2UgdG8gbWFudWFsbHkgdHJpZ2dlciBjaGFuZ2UgZGV0ZWN0aW9uLlxuICogXG4gKiBAbWV0aG9kXG4gKiBAbmFtZSBuZ09uSW5pdFxuICogQGRlc2NyaXB0aW9uXG4gKiBMaWZlY3ljbGUgaG9vayB0aGF0IGlzIGNhbGxlZCBhZnRlciB0aGUgY29tcG9uZW50J3MgdmlldyBoYXMgYmVlbiBpbml0aWFsaXplZC4gSXQgaW5pdGlhbGl6ZXMgdGhlIGZvcm0uXG4gKiBcbiAqIEBtZXRob2RcbiAqIEBuYW1lIGluaXRpYWxpemVGb3JtXG4gKiBAZGVzY3JpcHRpb25cbiAqIEluaXRpYWxpemVzIHRoZSBmb3JtIGNvbnRyb2xzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLiBTdXBwb3J0cyBkYXRlcGlja2VyIHJhbmdlIGNvbnRyb2xzLlxuICogXG4gKiBAbWV0aG9kXG4gKiBAbmFtZSBnZXRWYWxpZGF0b3JzXG4gKiBAZGVzY3JpcHRpb25cbiAqIFJldHVybnMgYW4gYXJyYXkgb2YgQW5ndWxhciB2YWxpZGF0b3JzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb250cm9sIGNvbmZpZ3VyYXRpb24uXG4gKiBAcGFyYW0ge0Zvcm1Db250cm9sQ29uZmlnfSBjb250cm9sIC0gVGhlIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBmb3JtIGNvbnRyb2wuXG4gKiBAcmV0dXJucyB7VmFsaWRhdG9yc1tdfSAtIEFuIGFycmF5IG9mIEFuZ3VsYXIgdmFsaWRhdG9ycy5cbiAqIFxuICogQG1ldGhvZFxuICogQG5hbWUgb25TdWJtaXRcbiAqIEBkZXNjcmlwdGlvblxuICogSGFuZGxlcyB0aGUgZm9ybSBzdWJtaXNzaW9uLiBNYXJrcyBhbGwgY29udHJvbHMgYXMgdG91Y2hlZCBhbmQgZGlydHksIHVwZGF0ZXMgdGhlIGZvcm0ncyB2YWxpZGl0eSxcbiAqIGVtaXRzIHRoZSBmb3JtIHZhbHVlcyBpZiB0aGUgZm9ybSBpcyB2YWxpZCwgYW5kIHJlc2V0cyB0aGUgZm9ybS4gVHJpZ2dlcnMgY2hhbmdlIGRldGVjdGlvbi5cbiAqL1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7XG4gIENvbXBvbmVudCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgUmVhY3RpdmVGb3Jtc01vZHVsZSxcbn0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgTWF0SW5wdXRNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pbnB1dCc7XG5pbXBvcnQgeyBNYXRTZWxlY3RNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9zZWxlY3QnO1xuaW1wb3J0IHsgTWF0Q2hlY2tib3hNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9jaGVja2JveCc7XG5pbXBvcnQgeyBNYXRSYWRpb01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3JhZGlvJztcbmltcG9ydCB7IE1hdEJ1dHRvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XG5pbXBvcnQgeyBWYWxpZGF0aW9uTWVzc2FnZXNDb21wb25lbnQgfSBmcm9tICcuLi92YWxpZGF0aW9uLW1lc3NhZ2VzL3ZhbGlkYXRpb24tbWVzc2FnZXMuY29tcG9uZW50JztcbmltcG9ydCB7IE1hdERhdGVwaWNrZXJNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9kYXRlcGlja2VyJztcbmltcG9ydCB7IE1hdEZvcm1GaWVsZE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2Zvcm0tZmllbGQnO1xuaW1wb3J0IHsgcHJvdmlkZU5hdGl2ZURhdGVBZGFwdGVyIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvY29yZSc7XG5pbXBvcnQgeyBCYXNlRm9ybXNDb21wb25lbnQgfSBmcm9tICcuLi9iYXNlLmNvbXBvbmVudCc7XG5cblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbWF0ZXJpYWwtZm9ybXMnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgQmFzZUZvcm1zQ29tcG9uZW50LFxuICAgIENvbW1vbk1vZHVsZSxcbiAgICBSZWFjdGl2ZUZvcm1zTW9kdWxlLFxuICAgIE1hdElucHV0TW9kdWxlLFxuICAgIE1hdFNlbGVjdE1vZHVsZSxcbiAgICBNYXRDaGVja2JveE1vZHVsZSxcbiAgICBNYXRSYWRpb01vZHVsZSxcbiAgICBNYXRCdXR0b25Nb2R1bGUsXG4gICAgVmFsaWRhdGlvbk1lc3NhZ2VzQ29tcG9uZW50LFxuICAgIE1hdERhdGVwaWNrZXJNb2R1bGUsXG4gICAgTWF0Rm9ybUZpZWxkTW9kdWxlLFxuICBdLFxuICBwcm92aWRlcnM6IFtwcm92aWRlTmF0aXZlRGF0ZUFkYXB0ZXIoKV0sXG4gIHRlbXBsYXRlVXJsOiAnLi9tYXRlcmlhbC1mb3JtLmNvbXBvbmVudC5odG1sJyxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIE1hdGVyaWFsRm9ybXNDb21wb25lbnQgZXh0ZW5kcyBCYXNlRm9ybXNDb21wb25lbnQge30iLCI8Zm9ybSBbZm9ybUdyb3VwXT1cImZvcm1cIiBbYXR0ci5uYW1lXT1cImZvcm1OYW1lKClcIiAobmdTdWJtaXQpPVwib25TdWJtaXQoKVwiPlxuICBAZm9yIChjb250cm9sIG9mIGNvbnRyb2xzKCk7IHRyYWNrICRpbmRleCkge1xuICBAaWYgKGNvbnRyb2wudWlGcmFtZXdvcmsgPT09IFwibWF0ZXJpYWxcIiB8fCAhY29udHJvbC51aUZyYW1ld29yaykge1xuICBAaWYgKGZvcm0uZ2V0KGNvbnRyb2wubmFtZSkpIHtcbiAgPHNlY3Rpb24gW25nQ2xhc3NdPVwiY29udHJvbC5jbGFzc1wiIFtuZ1N0eWxlXT1cImNvbnRyb2wuc3R5bGVcIj5cbiAgICBAaWYgKGNvbnRyb2wudHlwZSA9PT0gXCJpbnB1dFwiKSB7XG4gICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCI+XG4gICAgICBAaWYgKGNvbnRyb2wubGFiZWwpIHtcbiAgICAgIDxtYXQtbGFiZWw+e3sgY29udHJvbC5sYWJlbCB9fTwvbWF0LWxhYmVsPlxuICAgICAgfVxuICAgICAgPGlucHV0IG1hdElucHV0IFtmb3JtQ29udHJvbE5hbWVdPVwiY29udHJvbC5uYW1lXCIgW2lkXT1cImNvbnRyb2wubmFtZVwiIHR5cGU9XCJ0ZXh0XCIgLz5cbiAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgIH1cbiAgICBAaWYgKGNvbnRyb2wudHlwZSA9PT0gXCJzZWxlY3RcIikge1xuICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiPlxuICAgICAgQGlmIChjb250cm9sLmxhYmVsKSB7XG4gICAgICA8bWF0LWxhYmVsPnt7IGNvbnRyb2wubGFiZWwgfX08L21hdC1sYWJlbD5cbiAgICAgIH1cbiAgICAgIDxtYXQtc2VsZWN0IFtmb3JtQ29udHJvbE5hbWVdPVwiY29udHJvbC5uYW1lXCIgW2lkXT1cImNvbnRyb2wubmFtZVwiPlxuICAgICAgICBAZm9yIChvcHRpb24gb2YgY29udHJvbC5vcHRpb25zID8/IFtdOyB0cmFjayAkaW5kZXgpIHtcbiAgICAgICAgPG1hdC1vcHRpb24gW3ZhbHVlXT1cIm9wdGlvblwiPnt7IG9wdGlvbiB9fTwvbWF0LW9wdGlvbj5cbiAgICAgICAgfVxuICAgICAgPC9tYXQtc2VsZWN0PlxuICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgfVxuICAgIEBpZiAoY29udHJvbC50eXBlID09PSBcImNoZWNrYm94XCIpIHtcbiAgICA8bWF0LWNoZWNrYm94IFtmb3JtQ29udHJvbE5hbWVdPVwiY29udHJvbC5uYW1lXCIgW2lkXT1cImNvbnRyb2wubmFtZVwiPlxuICAgICAge3sgY29udHJvbC5sYWJlbCB9fVxuICAgIDwvbWF0LWNoZWNrYm94PlxuICAgIH1cbiAgICBAaWYgKGNvbnRyb2wudHlwZSA9PT0gXCJyYWRpb1wiKSB7XG4gICAgPG1hdC1yYWRpby1ncm91cCBbZm9ybUNvbnRyb2xOYW1lXT1cImNvbnRyb2wubmFtZVwiPlxuICAgICAgQGZvciAob3B0aW9uIG9mIGNvbnRyb2wub3B0aW9ucyA/PyBbXTsgdHJhY2sgJGluZGV4KSB7XG4gICAgICA8bWF0LXJhZGlvLWJ1dHRvbiBbaWRdPVwiY29udHJvbC5uYW1lICsgJy0nICsgb3B0aW9uXCIgW3ZhbHVlXT1cIm9wdGlvblwiPlxuICAgICAgICB7eyBvcHRpb24gfX1cbiAgICAgIDwvbWF0LXJhZGlvLWJ1dHRvbj5cbiAgICAgIH1cbiAgICA8L21hdC1yYWRpby1ncm91cD5cbiAgICB9XG4gICAgQGlmIChjb250cm9sLnR5cGUgPT09IFwiZGF0ZXBpY2tlclwiKSB7XG4gICAgQGlmIChjb250cm9sLmRhdGVQaWNrZXJNb2RlID09PSBcInNpbmdsZVwiKSB7XG4gICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCI+XG4gICAgICA8bWF0LWxhYmVsPkNob29zZSBhIGRhdGU8L21hdC1sYWJlbD5cbiAgICAgIDxpbnB1dCBtYXRJbnB1dCBbbWF0RGF0ZXBpY2tlcl09XCJkYXRlcGlja2VyXCIgW2Zvcm1Db250cm9sTmFtZV09XCJjb250cm9sLm5hbWVcIiAvPlxuICAgICAgPG1hdC1kYXRlcGlja2VyLXRvZ2dsZSBtYXRJY29uU3VmZml4IFtmb3JdPVwiZGF0ZXBpY2tlclwiPjwvbWF0LWRhdGVwaWNrZXItdG9nZ2xlPlxuICAgICAgPG1hdC1kYXRlcGlja2VyICNkYXRlcGlja2VyPjwvbWF0LWRhdGVwaWNrZXI+XG4gICAgPC9tYXQtZm9ybS1maWVsZD5cbiAgICB9IEBlbHNlIGlmIChjb250cm9sLmRhdGVQaWNrZXJNb2RlID09PSBcInJhbmdlXCIpIHtcbiAgICA8bWF0LWZvcm0tZmllbGQ+XG4gICAgICA8bWF0LWxhYmVsPkVudGVyIGEgZGF0ZSByYW5nZTwvbWF0LWxhYmVsPlxuICAgICAgPG1hdC1kYXRlLXJhbmdlLWlucHV0IFtyYW5nZVBpY2tlcl09XCJwaWNrZXJcIiBbZm9ybUdyb3VwXT1cImZvcm1cIiBdPlxuICAgICAgICA8aW5wdXQgbWF0U3RhcnREYXRlIGZvcm1Db250cm9sTmFtZT1cInt7IGNvbnRyb2wubmFtZSB9fV9TdGFydFwiIHBsYWNlaG9sZGVyPVwiU3RhcnQgZGF0ZVwiIC8+XG4gICAgICAgIDxpbnB1dCBtYXRFbmREYXRlIGZvcm1Db250cm9sTmFtZT1cInt7IGNvbnRyb2wubmFtZSB9fV9FbmRcIiBwbGFjZWhvbGRlcj1cIkVuZCBkYXRlXCIgLz5cbiAgICAgIDwvbWF0LWRhdGUtcmFuZ2UtaW5wdXQ+XG4gICAgICA8bWF0LWRhdGVwaWNrZXItdG9nZ2xlIG1hdEljb25TdWZmaXggW2Zvcl09XCJwaWNrZXJcIj48L21hdC1kYXRlcGlja2VyLXRvZ2dsZT5cbiAgICAgIDxtYXQtZGF0ZS1yYW5nZS1waWNrZXIgI3BpY2tlcj48L21hdC1kYXRlLXJhbmdlLXBpY2tlcj5cblxuICAgICAgPCEtLSBAaWYgKGZvcm0uY29udHJvbHMuc3RhcnQuaGFzRXJyb3IoJ21hdFN0YXJ0RGF0ZUludmFsaWQnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG1hdC1lcnJvcj5JbnZhbGlkIHN0YXJ0IGRhdGU8L21hdC1lcnJvcj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQGlmIChmb3JtLmNvbnRyb2xzLmVuZC5oYXNFcnJvcignbWF0RW5kRGF0ZUludmFsaWQnKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG1hdC1lcnJvcj5JbnZhbGlkIGVuZCBkYXRlPC9tYXQtZXJyb3I+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSAtLT5cbiAgICA8L21hdC1mb3JtLWZpZWxkPlxuICAgIH1cbiAgICB9XG5cbiAgICA8dmFsaWRhdGlvbi1tZXNzYWdlcyBbY29udHJvbF09XCJmb3JtLmdldChjb250cm9sLm5hbWUpXCIgW2NvbnRyb2xOYW1lXT1cImNvbnRyb2wubmFtZVwiXG4gICAgICBbY29uZmlnXT1cImNvbnRyb2xcIj48L3ZhbGlkYXRpb24tbWVzc2FnZXM+XG4gIDwvc2VjdGlvbj5cbiAgfVxuICB9XG4gIH1cbiAgPGJ1dHRvbiBtYXQtcmFpc2VkLWJ1dHRvbiBjb2xvcj1cInByaW1hcnlcIiB0eXBlPVwic3VibWl0XCIgW2Rpc2FibGVkXT1cImZvcm0uaW52YWxpZFwiPlxuICAgIFN1Ym1pdFxuICA8L2J1dHRvbj5cbjwvZm9ybT4iXX0=