@ckeditor/ckeditor5-angular
Version:
Official Angular component for CKEditor 5 – the best browser-based rich text editor.
420 lines • 51.7 kB
JavaScript
import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { first } from 'rxjs/operators';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { uid } from '@ckeditor/ckeditor5-integrations-common';
import { appendAllIntegrationPluginsToConfig } from './plugins/append-all-integration-plugins-to-config';
import * as i0 from "@angular/core";
const ANGULAR_INTEGRATION_READ_ONLY_LOCK_ID = 'Lock from Angular integration (@ckeditor/ckeditor5-angular)';
class CKEditorComponent {
/**
* The reference to the DOM element created by the component.
*/
elementRef;
/**
* The constructor of the editor to be used for the instance of the component.
* It can be e.g. the `ClassicEditorBuild`, `InlineEditorBuild` or some custom editor.
*/
editor;
/**
* The configuration of the editor.
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editorconfig-EditorConfig.html
* to learn more.
*/
config = {};
/**
* The initial data of the editor. Useful when not using the ngModel.
* See https://angular.io/api/forms/NgModel to learn more.
*/
data = '';
/**
* Tag name of the editor component.
*
* The default tag is 'div'.
*/
tagName = 'div';
// TODO Change to ContextWatchdog<Editor, HTMLElement> after new ckeditor5 alpha release
/**
* The context watchdog.
*/
watchdog;
/**
* Config for the EditorWatchdog.
*/
editorWatchdogConfig;
/**
* Allows disabling the two-way data binding mechanism. Disabling it can boost performance for large documents.
*
* When a component is connected using the [(ngModel)] or [formControl] directives and this value is set to true then none of the data
* will ever be synchronized.
*
* An integrator must call `editor.data.get()` manually once the application needs the editor's data.
* An editor instance can be received in the `ready()` callback.
*/
disableTwoWayDataBinding = false;
/**
* When set `true`, the editor becomes read-only.
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html#member-isReadOnly
* to learn more.
*/
set disabled(isDisabled) {
this.setDisabledState(isDisabled);
}
get disabled() {
if (this.editorInstance) {
return this.editorInstance.isReadOnly;
}
return this.initiallyDisabled;
}
/**
* Fires when the editor is ready. It corresponds with the `editor#ready`
* https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html#event-ready
* event.
*/
ready = new EventEmitter();
/**
* Fires when the content of the editor has changed. It corresponds with the `editor.model.document#change`
* https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_model_document-Document.html#event-change
* event.
*/
change = new EventEmitter();
/**
* Fires when the editing view of the editor is blurred. It corresponds with the `editor.editing.view.document#blur`
* https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_document-Document.html#event-event:blur
* event.
*/
blur = new EventEmitter();
/**
* Fires when the editing view of the editor is focused. It corresponds with the `editor.editing.view.document#focus`
* https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_view_document-Document.html#event-event:focus
* event.
*/
focus = new EventEmitter();
/**
* Fires when the editor component crashes.
*/
error = new EventEmitter();
/**
* The instance of the editor created by this component.
*/
get editorInstance() {
let editorWatchdog = this.editorWatchdog;
if (this.watchdog) {
// Temporarily use the `_watchdogs` internal map as the `getItem()` method throws
// an error when the item is not registered yet.
// See https://github.com/ckeditor/ckeditor5-angular/issues/177.
// TODO should be able to change when new chages in Watcdog are released.
editorWatchdog = this.watchdog._watchdogs.get(this.id);
}
if (editorWatchdog) {
return editorWatchdog.editor;
}
return null;
}
/**
* The editor watchdog. It is created when the context watchdog is not passed to the component.
* It keeps the editor running.
*/
editorWatchdog;
/**
* If the component is read–only before the editor instance is created, it remembers that state,
* so the editor can become read–only once it is ready.
*/
initiallyDisabled = false;
/**
* An instance of https://angular.io/api/core/NgZone to allow the interaction with the editor
* withing the Angular event loop.
*/
ngZone;
/**
* A callback executed when the content of the editor changes. Part of the
* `ControlValueAccessor` (https://angular.io/api/forms/ControlValueAccessor) interface.
*
* Note: Unset unless the component uses the `ngModel`.
*/
cvaOnChange;
/**
* A callback executed when the editor has been blurred. Part of the
* `ControlValueAccessor` (https://angular.io/api/forms/ControlValueAccessor) interface.
*
* Note: Unset unless the component uses the `ngModel`.
*/
cvaOnTouched;
/**
* Reference to the source element used by the editor.
*/
editorElement;
/**
* A lock flag preventing from calling the `cvaOnChange()` during setting editor data.
*/
isEditorSettingData = false;
id = uid();
getId() {
return this.id;
}
constructor(elementRef, ngZone) {
this.ngZone = ngZone;
this.elementRef = elementRef;
this.checkVersion();
}
checkVersion() {
// To avoid issues with the community typings and CKEditor 5, let's treat window as any. See #342.
const { CKEDITOR_VERSION } = window;
if (!CKEDITOR_VERSION) {
return console.warn('Cannot find the "CKEDITOR_VERSION" in the "window" scope.');
}
const [major] = CKEDITOR_VERSION.split('.').map(Number);
if (major >= 42 || CKEDITOR_VERSION.startsWith('0.0.0')) {
return;
}
console.warn('The <CKEditor> component requires using CKEditor 5 in version 42+ or nightly build.');
}
// Implementing the OnChanges interface. Whenever the `data` property is changed, update the editor content.
ngOnChanges(changes) {
if (Object.prototype.hasOwnProperty.call(changes, 'data') && changes.data && !changes.data.isFirstChange()) {
this.writeValue(changes.data.currentValue);
}
}
// Implementing the AfterViewInit interface.
ngAfterViewInit() {
this.attachToWatchdog();
}
// Implementing the OnDestroy interface.
async ngOnDestroy() {
if (this.watchdog) {
await this.watchdog.remove(this.id);
}
else if (this.editorWatchdog && this.editorWatchdog.editor) {
await this.editorWatchdog.destroy();
this.editorWatchdog = undefined;
}
}
// Implementing the ControlValueAccessor interface (only when binding to ngModel).
writeValue(value) {
// This method is called with the `null` value when the form resets.
// A component's responsibility is to restore to the initial state.
if (value === null) {
value = '';
}
// If already initialized.
if (this.editorInstance) {
// The lock mechanism prevents from calling `cvaOnChange()` during changing
// the editor state. See #139
this.isEditorSettingData = true;
this.editorInstance.data.set(value);
this.isEditorSettingData = false;
}
// If not, wait for it to be ready; store the data.
else {
// If the editor element is already available, then update its content.
this.data = value;
// If not, then wait until it is ready
// and change data only for the first `ready` event.
this.ready
.pipe(first())
.subscribe(editor => {
editor.data.set(this.data);
});
}
}
// Implementing the ControlValueAccessor interface (only when binding to ngModel).
registerOnChange(callback) {
this.cvaOnChange = callback;
}
// Implementing the ControlValueAccessor interface (only when binding to ngModel).
registerOnTouched(callback) {
this.cvaOnTouched = callback;
}
// Implementing the ControlValueAccessor interface (only when binding to ngModel).
setDisabledState(isDisabled) {
// If already initialized.
if (this.editorInstance) {
if (isDisabled) {
this.editorInstance.enableReadOnlyMode(ANGULAR_INTEGRATION_READ_ONLY_LOCK_ID);
}
else {
this.editorInstance.disableReadOnlyMode(ANGULAR_INTEGRATION_READ_ONLY_LOCK_ID);
}
}
// Store the state anyway to use it once the editor is created.
this.initiallyDisabled = isDisabled;
}
/**
* Creates the editor instance, sets initial editor data, then integrates
* the editor with the Angular component. This method does not use the `editor.data.set()`
* because of the issue in the collaboration mode (#6).
*/
attachToWatchdog() {
// TODO: elementOrData parameter type can be simplified to HTMLElemen after templated Watchdog will be released.
const creator = ((elementOrData, config) => {
return this.ngZone.runOutsideAngular(async () => {
this.elementRef.nativeElement.appendChild(elementOrData);
const editor = await this.editor.create(elementOrData, config);
if (this.initiallyDisabled) {
editor.enableReadOnlyMode(ANGULAR_INTEGRATION_READ_ONLY_LOCK_ID);
}
this.ngZone.run(() => {
this.ready.emit(editor);
});
this.setUpEditorEvents(editor);
return editor;
});
});
const destructor = async (editor) => {
await editor.destroy();
this.elementRef.nativeElement.removeChild(this.editorElement);
};
const emitError = (e) => {
// Do not run change detection by re-entering the Angular zone if the `error`
// emitter doesn't have any subscribers.
// Subscribers are pushed onto the list whenever `error` is listened inside the template:
// `<ckeditor (error)="onError(...)"></ckeditor>`.
if (hasObservers(this.error)) {
this.ngZone.run(() => this.error.emit(e));
}
else {
// Print error to the console when there are no subscribers to the `error` event.
console.error(e);
}
};
const element = document.createElement(this.tagName);
const config = this.getConfig();
this.editorElement = element;
// Based on the presence of the watchdog decide how to initialize the editor.
if (this.watchdog) {
// When the context watchdog is passed add the new item to it based on the passed configuration.
this.watchdog.add({
id: this.id,
type: 'editor',
creator,
destructor,
sourceElementOrData: element,
config
}).catch(e => {
emitError(e);
});
this.watchdog.on('itemError', (_, { itemId }) => {
if (itemId === this.id) {
emitError();
}
});
}
else {
// In the other case create the watchdog by hand to keep the editor running.
const editorWatchdog = new this.editor.EditorWatchdog(this.editor, this.editorWatchdogConfig);
editorWatchdog.setCreator(creator);
editorWatchdog.setDestructor(destructor);
editorWatchdog.on('error', emitError);
this.editorWatchdog = editorWatchdog;
this.ngZone.runOutsideAngular(() => {
// Note: must be called outside of the Angular zone too because `create` is calling
// `_startErrorHandling` within a microtask which sets up `error` listener on the window.
editorWatchdog.create(element, config).catch(e => {
emitError(e);
});
});
}
}
getConfig() {
if (this.data && this.config.initialData) {
throw new Error('Editor data should be provided either using `config.initialData` or `data` properties.');
}
const config = { ...this.config };
// Merge two possible ways of providing data into the `config.initialData` field.
const initialData = this.config.initialData || this.data;
if (initialData) {
// Define the `config.initialData` only when the initial content is specified.
config.initialData = initialData;
}
return appendAllIntegrationPluginsToConfig(config);
}
/**
* Integrates the editor with the component by attaching related event listeners.
*/
setUpEditorEvents(editor) {
const modelDocument = editor.model.document;
const viewDocument = editor.editing.view.document;
modelDocument.on('change:data', evt => {
this.ngZone.run(() => {
if (this.disableTwoWayDataBinding) {
return;
}
if (this.cvaOnChange && !this.isEditorSettingData) {
const data = editor.data.get();
this.cvaOnChange(data);
}
this.change.emit({ event: evt, editor });
});
});
viewDocument.on('focus', evt => {
this.ngZone.run(() => {
this.focus.emit({ event: evt, editor });
});
});
viewDocument.on('blur', evt => {
this.ngZone.run(() => {
if (this.cvaOnTouched) {
this.cvaOnTouched();
}
this.blur.emit({ event: evt, editor });
});
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CKEditorComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: CKEditorComponent, selector: "ckeditor", inputs: { editor: "editor", config: "config", data: "data", tagName: "tagName", watchdog: "watchdog", editorWatchdogConfig: "editorWatchdogConfig", disableTwoWayDataBinding: "disableTwoWayDataBinding", disabled: "disabled" }, outputs: { ready: "ready", change: "change", blur: "blur", focus: "focus", error: "error" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
// eslint-disable-next-line @typescript-eslint/no-use-before-define
useExisting: forwardRef(() => CKEditorComponent),
multi: true
}
], usesOnChanges: true, ngImport: i0, template: '<ng-template></ng-template>', isInline: true });
}
export { CKEditorComponent };
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CKEditorComponent, decorators: [{
type: Component,
args: [{
selector: 'ckeditor',
template: '<ng-template></ng-template>',
// Integration with @angular/forms.
providers: [
{
provide: NG_VALUE_ACCESSOR,
// eslint-disable-next-line @typescript-eslint/no-use-before-define
useExisting: forwardRef(() => CKEditorComponent),
multi: true
}
]
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { editor: [{
type: Input
}], config: [{
type: Input
}], data: [{
type: Input
}], tagName: [{
type: Input
}], watchdog: [{
type: Input
}], editorWatchdogConfig: [{
type: Input
}], disableTwoWayDataBinding: [{
type: Input
}], disabled: [{
type: Input
}], ready: [{
type: Output
}], change: [{
type: Output
}], blur: [{
type: Output
}], focus: [{
type: Output
}], error: [{
type: Output
}] } });
function hasObservers(emitter) {
// Cast to `any` because `observed` property is available in RxJS >= 7.2.0.
// Fallback to checking `observers` list if this property is not defined.
return emitter.observed || emitter.observers.length > 0;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2tlZGl0b3IuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NrZWRpdG9yL2NrZWRpdG9yLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFXQSxPQUFPLEVBQ04sU0FBUyxFQUNULEtBQUssRUFDTCxNQUFNLEVBRU4sWUFBWSxFQUNaLFVBQVUsRUFFVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdkMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFpQm5ELE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUM5RCxPQUFPLEVBQUUsbUNBQW1DLEVBQUUsTUFBTSxvREFBb0QsQ0FBQzs7QUFFekcsTUFBTSxxQ0FBcUMsR0FBRyw2REFBNkQsQ0FBQztBQWlCNUcsTUFjYSxpQkFBaUI7SUFDN0I7O09BRUc7SUFDSyxVQUFVLENBQTJCO0lBRTdDOzs7T0FHRztJQUNhLE1BQU0sQ0FHcEI7SUFFRjs7OztPQUlHO0lBQ2EsTUFBTSxHQUFpQixFQUFFLENBQUM7SUFFMUM7OztPQUdHO0lBQ2EsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUUxQjs7OztPQUlHO0lBQ2EsT0FBTyxHQUFHLEtBQUssQ0FBQztJQUVoQyx3RkFBd0Y7SUFDeEY7O09BRUc7SUFDYSxRQUFRLENBQW1CO0lBRTNDOztPQUVHO0lBQ2Esb0JBQW9CLENBQWtCO0lBRXREOzs7Ozs7OztPQVFHO0lBQ2Esd0JBQXdCLEdBQUcsS0FBSyxDQUFDO0lBRWpEOzs7O09BSUc7SUFDSCxJQUFvQixRQUFRLENBQUUsVUFBbUI7UUFDaEQsSUFBSSxDQUFDLGdCQUFnQixDQUFFLFVBQVUsQ0FBRSxDQUFDO0lBQ3JDLENBQUM7SUFFRCxJQUFXLFFBQVE7UUFDbEIsSUFBSyxJQUFJLENBQUMsY0FBYyxFQUFHO1lBQzFCLE9BQU8sSUFBSSxDQUFD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LEVBQUUsaUJBQWlCOzRCQUMxQixtRUFBbUU7NEJBQ25FLFdBQVcsRUFBRSxVQUFVLENBQUUsR0FBRyxFQUFFLGtCQUFrQixDQUFFOzRCQUNsRCxLQUFLLEVBQUUsSUFBSTt5QkFDWDtxQkFDRDtpQkFDRDtzSEFXZ0IsTUFBTTtzQkFBckIsS0FBSztnQkFVVSxNQUFNO3NCQUFyQixLQUFLO2dCQU1VLElBQUk7c0JBQW5CLEtBQUs7Z0JBT1UsT0FBTztzQkFBdEIsS0FBSztnQkFNVSxRQUFRO3NCQUF2QixLQUFLO2dCQUtVLG9CQUFvQjtzQkFBbkMsS0FBSztnQkFXVSx3QkFBd0I7c0JBQXZDLEtBQUs7Z0JBT2MsUUFBUTtzQkFBM0IsS0FBSztnQkFpQlcsS0FBSztzQkFBckIsTUFBTTtnQkFPVSxNQUFNO3NCQUF0QixNQUFNO2dCQU9VLElBQUk7c0JBQXBCLE1BQU07Z0JBT1UsS0FBSztzQkFBckIsTUFBTTtnQkFLVSxLQUFLO3NCQUFyQixNQUFNOztBQXNVUixTQUFTLFlBQVksQ0FBSyxPQUF3QjtJQUNqRCwyRUFBMkU7SUFDM0UseUVBQXlFO0lBQ3pFLE9BQVMsT0FBZ0IsQ0FBQyxRQUFRLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0FBQ3BFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlIENvcHlyaWdodCAoYykgMjAwMy0yMDI0LCBDS1NvdXJjZSBIb2xkaW5nIHNwLiB6IG8uby4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqIEZvciBsaWNlbnNpbmcsIHNlZSBMSUNFTlNFLm1kLlxuICovXG5cbmltcG9ydCB0eXBlIHtcblx0QWZ0ZXJWaWV3SW5pdCxcblx0T25DaGFuZ2VzLFxuXHRPbkRlc3Ryb3ksXG5cdFNpbXBsZUNoYW5nZXNcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuXHRDb21wb25lbnQsXG5cdElucHV0LFxuXHRPdXRwdXQsXG5cdE5nWm9uZSxcblx0RXZlbnRFbWl0dGVyLFxuXHRmb3J3YXJkUmVmLFxuXHRFbGVtZW50UmVmXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgZmlyc3QgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBOR19WQUxVRV9BQ0NFU1NPUiB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9jb25zaXN0ZW50LXR5cGUtaW1wb3J0c1xuaW1wb3J0IHR5cGUge1xuXHRDb250ZXh0V2F0Y2hkb2csXG5cdEVkaXRvcldhdGNoZG9nLFxuXHRXYXRjaGRvZ0NvbmZpZyxcblx0RWRpdG9yLFxuXHRFZGl0b3JDb25maWcsXG5cdEdldEV2ZW50SW5mbyxcblx0RG9jdW1lbnRDaGFuZ2VFdmVudCxcblx0RWRpdG9yQ3JlYXRvckZ1bmN0aW9uLFxuXHRWaWV3RG9jdW1lbnRCbHVyRXZlbnQsXG5cdFZpZXdEb2N1bWVudEZvY3VzRXZlbnRcbn0gZnJvbSAnY2tlZGl0b3I1JztcbmltcG9ydCB0eXBlIHsgQ29udHJvbFZhbHVlQWNjZXNzb3IgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5cbmltcG9ydCB7IHVpZCB9IGZyb20gJ0Bja2VkaXRvci9ja2VkaXRvcjUtaW50ZWdyYXRpb25zLWNvbW1vbic7XG5pbXBvcnQgeyBhcHBlbmRBbGxJbnRlZ3JhdGlvblBsdWdpbnNUb0NvbmZpZyB9IGZyb20gJy4vcGx1Z2lucy9hcHBlbmQtYWxsLWludGVncmF0aW9uLXBsdWdpbnMtdG8tY29uZmlnJztcblxuY29uc3QgQU5HVUxBUl9JTlRFR1JBVElPTl9SRUFEX09OTFlfTE9DS19JRCA9ICdMb2NrIGZyb20gQW5ndWxhciBpbnRlZ3JhdGlvbiAoQGNrZWRpdG9yL2NrZWRpdG9yNS1hbmd1bGFyKSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQmx1ckV2ZW50PFRFZGl0b3IgZXh0ZW5kcyBFZGl0b3IgPSBFZGl0b3I+IHtcblx0ZXZlbnQ6IEdldEV2ZW50SW5mbzxWaWV3RG9jdW1lbnRCbHVyRXZlbnQ+O1xuXHRlZGl0b3I6IFRFZGl0b3I7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRm9jdXNFdmVudDxURWRpdG9yIGV4dGVuZHMgRWRpdG9yID0gRWRpdG9yPiB7XG5cdGV2ZW50OiBHZXRFdmVudEluZm88Vmlld0RvY3VtZW50Rm9jdXNFdmVudD47XG5cdGVkaXRvcjogVEVkaXRvcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDaGFuZ2VFdmVudDxURWRpdG9yIGV4dGVuZHMgRWRpdG9yID0gRWRpdG9yPiB7XG5cdGV2ZW50OiBHZXRFdmVudEluZm88RG9jdW1lbnRDaGFuZ2VFdmVudD47XG5cdGVkaXRvcjogVEVkaXRvcjtcbn1cblxuQENvbXBvbmVudCgge1xuXHRzZWxlY3RvcjogJ2NrZWRpdG9yJyxcblx0dGVtcGxhdGU6ICc8bmctdGVtcGxhdGU+PC9uZy10ZW1wbGF0ZT4nLFxuXG5cdC8vIEludGVncmF0aW9uIHdpdGggQGFuZ3VsYXIvZm9ybXMuXG5cdHByb3ZpZGVyczogW1xuXHRcdHtcblx0XHRcdHByb3ZpZGU6IE5HX1ZBTFVFX0FDQ0VTU09SLFxuXHRcdFx0Ly8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby11c2UtYmVmb3JlLWRlZmluZVxuXHRcdFx0dXNlRXhpc3Rpbmc6IGZvcndhcmRSZWYoICgpID0+IENLRWRpdG9yQ29tcG9uZW50ICksXG5cdFx0XHRtdWx0aTogdHJ1ZVxuXHRcdH1cblx0XVxufSApXG5leHBvcnQgY2xhc3MgQ0tFZGl0b3JDb21wb25lbnQ8VEVkaXRvciBleHRlbmRzIEVkaXRvciA9IEVkaXRvcj4gaW1wbGVtZW50cyBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3ksIE9uQ2hhbmdlcywgQ29udHJvbFZhbHVlQWNjZXNzb3Ige1xuXHQvKipcblx0ICogVGhlIHJlZmVyZW5jZSB0byB0aGUgRE9NIGVsZW1lbnQgY3JlYXRlZCBieSB0aGUgY29tcG9uZW50LlxuXHQgKi9cblx0cHJpdmF0ZSBlbGVtZW50UmVmITogRWxlbWVudFJlZjxIVE1MRWxlbWVudD47XG5cblx0LyoqXG5cdCAqIFRoZSBjb25zdHJ1Y3RvciBvZiB0aGUgZWRpdG9yIHRvIGJlIHVzZWQgZm9yIHRoZSBpbnN0YW5jZSBvZiB0aGUgY29tcG9uZW50LlxuXHQgKiBJdCBjYW4gYmUgZS5nLiB0aGUgYENsYXNzaWNFZGl0b3JCdWlsZGAsIGBJbmxpbmVFZGl0b3JCdWlsZGAgb3Igc29tZSBjdXN0b20gZWRpdG9yLlxuXHQgKi9cblx0QElucHV0KCkgcHVibGljIGVkaXRvcj86IHtcblx0XHRjcmVhdGUoIHNvdXJjZUVsZW1lbnRPckRhdGE6IEhUTUxFbGVtZW50IHwgc3RyaW5nLCBjb25maWc/OiBFZGl0b3JDb25maWcgKTogUHJvbWlzZTxURWRpdG9yPjtcblx0XHRFZGl0b3JXYXRjaGRvZzogdHlwZW9mIEVkaXRvcldhdGNoZG9nO1xuXHR9O1xuXG5cdC8qKlxuXHQgKiBUaGUgY29uZmlndXJhdGlvbiBvZiB0aGUgZWRpdG9yLlxuXHQgKiBTZWUgaHR0cHM6Ly9ja2VkaXRvci5jb20vZG9jcy9ja2VkaXRvcjUvbGF0ZXN0L2FwaS9tb2R1bGVfY29yZV9lZGl0b3JfZWRpdG9yY29uZmlnLUVkaXRvckNvbmZpZy5odG1sXG5cdCAqIHRvIGxlYXJuIG1vcmUuXG5cdCAqL1xuXHRASW5wdXQoKSBwdWJsaWMgY29uZmlnOiBFZGl0b3JDb25maWcgPSB7fTtcblxuXHQvKipcblx0ICogVGhlIGluaXRpYWwgZGF0YSBvZiB0aGUgZWRpdG9yLiBVc2VmdWwgd2hlbiBub3QgdXNpbmcgdGhlIG5nTW9kZWwuXG5cdCAqIFNlZSBodHRwczovL2FuZ3VsYXIuaW8vYXBpL2Zvcm1zL05nTW9kZWwgdG8gbGVhcm4gbW9yZS5cblx0ICovXG5cdEBJbnB1dCgpIHB1YmxpYyBkYXRhID0gJyc7XG5cblx0LyoqXG5cdCAqIFRhZyBuYW1lIG9mIHRoZSBlZGl0b3IgY29tcG9uZW50LlxuXHQgKlxuXHQgKiBUaGUgZGVmYXVsdCB0YWcgaXMgJ2RpdicuXG5cdCAqL1xuXHRASW5wdXQoKSBwdWJsaWMgdGFnTmFtZSA9ICdkaXYnO1xuXG5cdC8vIFRPRE8gQ2hhbmdlIHRvIENvbnRleHRXYXRjaGRvZzxFZGl0b3IsIEhUTUxFbGVtZW50PiBhZnRlciBuZXcgY2tlZGl0b3I1IGFscGhhIHJlbGVhc2Vcblx0LyoqXG5cdCAqIFRoZSBjb250ZXh0IHdhdGNoZG9nLlxuXHQgKi9cblx0QElucHV0KCkgcHVibGljIHdhdGNoZG9nPzogQ29udGV4dFdhdGNoZG9nO1xuXG5cdC8qKlxuXHQgKiBDb25maWcgZm9yIHRoZSBFZGl0b3JXYXRjaGRvZy5cblx0ICovXG5cdEBJbnB1dCgpIHB1YmxpYyBlZGl0b3JXYXRjaGRvZ0NvbmZpZz86IFdhdGNoZG9nQ29uZmlnO1xuXG5cdC8qKlxuXHQgKiBBbGxvd3MgZGlzYWJsaW5nIHRoZSB0d28td2F5IGRhdGEgYmluZGluZyBtZWNoYW5pc20uIERpc2FibGluZyBpdCBjYW4gYm9vc3QgcGVyZm9ybWFuY2UgZm9yIGxhcmdlIGRvY3VtZW50cy5cblx0ICpcblx0ICogV2hlbiBhIGNvbXBvbmVudCBpcyBjb25uZWN0ZWQgdXNpbmcgdGhlIFsobmdNb2RlbCldIG9yIFtmb3JtQ29udHJvbF0gZGlyZWN0aXZlcyBhbmQgdGhpcyB2YWx1ZSBpcyBzZXQgdG8gdHJ1ZSB0aGVuIG5vbmUgb2YgdGhlIGRhdGFcblx0ICogd2lsbCBldmVyIGJlIHN5bmNocm9uaXplZC5cblx0ICpcblx0ICogQW4gaW50ZWdyYXRvciBtdXN0IGNhbGwgYGVkaXRvci5kYXRhLmdldCgpYCBtYW51YWxseSBvbmNlIHRoZSBhcHBsaWNhdGlvbiBuZWVkcyB0aGUgZWRpdG9yJ3MgZGF0YS5cblx0ICogQW4gZWRpdG9yIGluc3RhbmNlIGNhbiBiZSByZWNlaXZlZCBpbiB0aGUgYHJlYWR5KClgIGNhbGxiYWNrLlxuXHQgKi9cblx0QElucHV0KCkgcHVibGljIGRpc2FibGVUd29XYXlEYXRhQmluZGluZyA9IGZhbHNlO1xuXG5cdC8qKlxuXHQgKiBXaGVuIHNldCBgdHJ1ZWAsIHRoZSBlZGl0b3IgYmVjb21lcyByZWFkLW9ubHkuXG5cdCAqIFNlZSBodHRwczovL2NrZWRpdG9yLmNvbS9kb2NzL2NrZWRpdG9yNS9sYXRlc3QvYXBpL21vZHVsZV9jb3JlX2VkaXRvcl9lZGl0b3ItRWRpdG9yLmh0bWwjbWVtYmVyLWlzUmVhZE9ubHlcblx0ICogdG8gbGVhcm4gbW9yZS5cblx0ICovXG5cdEBJbnB1dCgpIHB1YmxpYyBzZXQgZGlzYWJsZWQoIGlzRGlzYWJsZWQ6IGJvb2xlYW4gKSB7XG5cdFx0dGhpcy5zZXREaXNhYmxlZFN0YXRlKCBpc0Rpc2FibGVkICk7XG5cdH1cblxuXHRwdWJsaWMgZ2V0IGRpc2FibGVkKCk6IGJvb2xlYW4ge1xuXHRcdGlmICggdGhpcy5lZGl0b3JJbnN0YW5jZSApIHtcblx0XHRcdHJldHVybiB0aGlzLmVkaXRvckluc3RhbmNlLmlzUmVhZE9ubHk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXMuaW5pdGlhbGx5RGlzYWJsZWQ7XG5cdH1cblxuXHQvKipcblx0ICogRmlyZXMgd2hlbiB0aGUgZWRpdG9yIGlzIHJlYWR5LiBJdCBjb3JyZXNwb25kcyB3aXRoIHRoZSBgZWRpdG9yI3JlYWR5YFxuXHQgKiBodHRwczovL2NrZWRpdG9yLmNvbS9kb2NzL2NrZWRpdG9yNS9sYXRlc3QvYXBpL21vZHVsZV9jb3JlX2VkaXRvcl9lZGl0b3ItRWRpdG9yLmh0bWwjZXZlbnQtcmVhZHlcblx0ICogZXZlbnQuXG5cdCAqL1xuXHRAT3V0cHV0KCkgcHVibGljIHJlYWR5ID0gbmV3IEV2ZW50RW1pdHRlcjxURWRpdG9yPigpO1xuXG5cdC8qKlxuXHQgKiBGaXJlcyB3aGVuIHRoZSBjb250ZW50IG9mIHRoZSBlZGl0b3IgaGFzIGNoYW5nZWQuIEl0IGNvcnJlc3BvbmRzIHdpdGggdGhlIGBlZGl0b3IubW9kZWwuZG9jdW1lbnQjY2hhbmdlYFxuXHQgKiBodHRwczovL2NrZWRpdG9yLmNvbS9kb2NzL2NrZWRpdG9yNS9sYXRlc3QvYXBpL21vZHVsZV9lbmdpbmVfbW9kZWxfZG9jdW1lbnQtRG9jdW1lbnQuaHRtbCNldmVudC1jaGFuZ2Vcblx0ICogZXZlbnQuXG5cdCAqL1xuXHRAT3V0cHV0KCkgcHVibGljIGNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8Q2hhbmdlRXZlbnQ8VEVkaXRvcj4+KCk7XG5cblx0LyoqXG5cdCAqIEZpcmVzIHdoZW4gdGhlIGVkaXRpbmcgdmlldyBvZiB0aGUgZWRpdG9yIGlzIGJsdXJyZWQuIEl0IGNvcnJlc3BvbmRzIHdpdGggdGhlIGBlZGl0b3IuZWRpdGluZy52aWV3LmRvY3VtZW50I2JsdXJgXG5cdCAqIGh0dHBzOi8vY2tlZGl0b3IuY29tL2RvY3MvY2tlZGl0b3I1L2xhdGVzdC9hcGkvbW9kdWxlX2VuZ2luZV92aWV3X2RvY3VtZW50LURvY3VtZW50Lmh0bWwjZXZlbnQtZXZlbnQ6Ymx1clxuXHQgKiBldmVudC5cblx0ICovXG5cdEBPdXRwdXQoKSBwdWJsaWMgYmx1ciA9IG5ldyBFdmVudEVtaXR0ZXI8Qmx1ckV2ZW50PFRFZGl0b3I+PigpO1xuXG5cdC8qKlxuXHQgKiBGaXJlcyB3aGVuIHRoZSBlZGl0aW5nIHZpZXcgb2YgdGhlIGVkaXRvciBpcyBmb2N1c2VkLiBJdCBjb3JyZXNwb25kcyB3aXRoIHRoZSBgZWRpdG9yLmVkaXRpbmcudmlldy5kb2N1bWVudCNmb2N1c2Bcblx0ICogaHR0cHM6Ly9ja2VkaXRvci5jb20vZG9jcy9ja2VkaXRvcjUvbGF0ZXN0L2FwaS9tb2R1bGVfZW5naW5lX3ZpZXdfZG9jdW1lbnQtRG9jdW1lbnQuaHRtbCNldmVudC1ldmVudDpmb2N1c1xuXHQgKiBldmVudC5cblx0ICovXG5cdEBPdXRwdXQoKSBwdWJsaWMgZm9jdXMgPSBuZXcgRXZlbnRFbWl0dGVyPEZvY3VzRXZlbnQ8VEVkaXRvcj4+KCk7XG5cblx0LyoqXG5cdCAqIEZpcmVzIHdoZW4gdGhlIGVkaXRvciBjb21wb25lbnQgY3Jhc2hlcy5cblx0ICovXG5cdEBPdXRwdXQoKSBwdWJsaWMgZXJyb3IgPSBuZXcgRXZlbnRFbWl0dGVyPHVua25vd24+KCk7XG5cblx0LyoqXG5cdCAqIFRoZSBpbnN0YW5jZSBvZiB0aGUgZWRpdG9yIGNyZWF0ZWQgYnkgdGhpcyBjb21wb25lbnQuXG5cdCAqL1xuXHRwdWJsaWMgZ2V0IGVkaXRvckluc3RhbmNlKCk6IFRFZGl0b3IgfCBudWxsIHtcblx0XHRsZXQgZWRpdG9yV2F0Y2hkb2cgPSB0aGlzLmVkaXRvcldhdGNoZG9nO1xuXG5cdFx0aWYgKCB0aGlzLndhdGNoZG9nICkge1xuXHRcdFx0Ly8gVGVtcG9yYXJpbHkgdXNlIHRoZSBgX3dhdGNoZG9nc2AgaW50ZXJuYWwgbWFwIGFzIHRoZSBgZ2V0SXRlbSgpYCBtZXRob2QgdGhyb3dzXG5cdFx0XHQvLyBhbiBlcnJvciB3aGVuIHRoZSBpdGVtIGlzIG5vdCByZWdpc3RlcmVkIHlldC5cblx0XHRcdC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vY2tlZGl0b3IvY2tlZGl0b3I1LWFuZ3VsYXIvaXNzdWVzLzE3Ny5cblx0XHRcdC8vIFRPRE8gc2hvdWxkIGJlIGFibGUgdG8gY2hhbmdlIHdoZW4gbmV3IGNoYWdlcyBpbiBXYXRjZG9nIGFyZSByZWxlYXNlZC5cblx0XHRcdGVkaXRvcldhdGNoZG9nID0gKCB0aGlzLndhdGNoZG9nIGFzIGFueSApLl93YXRjaGRvZ3MuZ2V0KCB0aGlzLmlkICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBlZGl0b3JXYXRjaGRvZyApIHtcblx0XHRcdHJldHVybiBlZGl0b3JXYXRjaGRvZy5lZGl0b3I7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cblxuXHQvKipcblx0ICogVGhlIGVkaXRvciB3YXRjaGRvZy4gSXQgaXMgY3JlYXRlZCB3aGVuIHRoZSBjb250ZXh0IHdhdGNoZG9nIGlzIG5vdCBwYXNzZWQgdG8gdGhlIGNvbXBvbmVudC5cblx0ICogSXQga2VlcHMgdGhlIGVkaXRvciBydW5uaW5nLlxuXHQgKi9cblx0cHJpdmF0ZSBlZGl0b3JXYXRjaGRvZz86IEVkaXRvcldhdGNoZG9nPFRFZGl0b3I+O1xuXG5cdC8qKlxuXHQgKiBJZiB0aGUgY29tcG9uZW50IGlzIHJlYWTigJNvbmx5IGJlZm9yZSB0aGUgZWRpdG9yIGluc3RhbmNlIGlzIGNyZWF0ZWQsIGl0IHJlbWVtYmVycyB0aGF0IHN0YXRlLFxuXHQgKiBzbyB0aGUgZWRpdG9yIGNhbiBiZWNvbWUgcmVhZOKAk29ubHkgb25jZSBpdCBpcyByZWFkeS5cblx0ICovXG5cdHByaXZhdGUgaW5pdGlhbGx5RGlzYWJsZWQgPSBmYWxzZTtcblxuXHQvKipcblx0ICogQW4gaW5zdGFuY2Ugb2YgaHR0cHM6Ly9hbmd1bGFyLmlvL2FwaS9jb3JlL05nWm9uZSB0byBhbGxvdyB0aGUgaW50ZXJhY3Rpb24gd2l0aCB0aGUgZWRpdG9yXG5cdCAqIHdpdGhpbmcgdGhlIEFuZ3VsYXIgZXZlbnQgbG9vcC5cblx0ICovXG5cdHByaXZhdGUgbmdab25lOiBOZ1pvbmU7XG5cblx0LyoqXG5cdCAqIEEgY2FsbGJhY2sgZXhlY3V0ZWQgd2hlbiB0aGUgY29udGVudCBvZiB0aGUgZWRpdG9yIGNoYW5nZXMuIFBhcnQgb2YgdGhlXG5cdCAqIGBDb250cm9sVmFsdWVBY2Nlc3NvcmAgKGh0dHBzOi8vYW5ndWxhci5pby9hcGkvZm9ybXMvQ29udHJvbFZhbHVlQWNjZXNzb3IpIGludGVyZmFjZS5cblx0ICpcblx0ICogTm90ZTogVW5zZXQgdW5sZXNzIHRoZSBjb21wb25lbnQgdXNlcyB0aGUgYG5nTW9kZWxgLlxuXHQgKi9cblx0cHJpdmF0ZSBjdmFPbkNoYW5nZT86ICggZGF0YTogc3RyaW5nICkgPT4gdm9pZDtcblxuXHQvKipcblx0ICogQSBjYWxsYmFjayBleGVjdXRlZCB3aGVuIHRoZSBlZGl0b3IgaGFzIGJlZW4gYmx1cnJlZC4gUGFydCBvZiB0aGVcblx0ICogYENvbnRyb2xWYWx1ZUFjY2Vzc29yYCAoaHR0cHM6Ly9hbmd1bGFyLmlvL2FwaS9mb3Jtcy9Db250cm9sVmFsdWVBY2Nlc3NvcikgaW50ZXJmYWNlLlxuXHQgKlxuXHQgKiBOb3RlOiBVbnNldCB1bmxlc3MgdGhlIGNvbXBvbmVudCB1c2VzIHRoZSBgbmdNb2RlbGAuXG5cdCAqL1xuXHRwcml2YXRlIGN2YU9uVG91Y2hlZD86ICgpID0+IHZvaWQ7XG5cblx0LyoqXG5cdCAqIFJlZmVyZW5jZSB0byB0aGUgc291cmNlIGVsZW1lbnQgdXNlZCBieSB0aGUgZWRpdG9yLlxuXHQgKi9cblx0cHJpdmF0ZSBlZGl0b3JFbGVtZW50PzogSFRNTEVsZW1lbnQ7XG5cblx0LyoqXG5cdCAqIEEgbG9jayBmbGFnIHByZXZlbnRpbmcgZnJvbSBjYWxsaW5nIHRoZSBgY3ZhT25DaGFuZ2UoKWAgZHVyaW5nIHNldHRpbmcgZWRpdG9yIGRhdGEuXG5cdCAqL1xuXHRwcml2YXRlIGlzRWRpdG9yU2V0dGluZ0RhdGEgPSBmYWxzZTtcblxuXHRwcml2YXRlIGlkID0gdWlkKCk7XG5cblx0cHVibGljIGdldElkKCk6IHN0cmluZyB7XG5cdFx0cmV0dXJuIHRoaXMuaWQ7XG5cdH1cblxuXHRwdWJsaWMgY29uc3RydWN0b3IoIGVsZW1lbnRSZWY6IEVsZW1lbnRSZWYsIG5nWm9uZTogTmdab25lICkge1xuXHRcdHRoaXMubmdab25lID0gbmdab25lO1xuXHRcdHRoaXMuZWxlbWVudFJlZiA9IGVsZW1lbnRSZWY7XG5cblx0XHR0aGlzLmNoZWNrVmVyc2lvbigpO1xuXHR9XG5cblx0cHJpdmF0ZSBjaGVja1ZlcnNpb24oKSB7XG5cdFx0Ly8gVG8gYXZvaWQgaXNzdWVzIHdpdGggdGhlIGNvbW11bml0eSB0eXBpbmdzIGFuZCBDS0VkaXRvciA1LCBsZXQncyB0cmVhdCB3aW5kb3cgYXMgYW55LiBTZWUgIzM0Mi5cblx0XHRjb25zdCB7IENLRURJVE9SX1ZFUlNJT04gfSA9ICggd2luZG93IGFzIGFueSApO1xuXG5cdFx0aWYgKCAhQ0tFRElUT1JfVkVSU0lPTiApIHtcblx0XHRcdHJldHVybiBjb25zb2xlLndhcm4oICdDYW5ub3QgZmluZCB0aGUgXCJDS0VESVRPUl9WRVJTSU9OXCIgaW4gdGhlIFwid2luZG93XCIgc2NvcGUuJyApO1xuXHRcdH1cblxuXHRcdGNvbnN0IFsgbWFqb3IgXSA9IENLRURJVE9SX1ZFUlNJT04uc3BsaXQoICcuJyApLm1hcCggTnVtYmVyICk7XG5cblx0XHRpZiAoIG1ham9yID49IDQyIHx8IENLRURJVE9SX1ZFUlNJT04uc3RhcnRzV2l0aCggJzAuMC4wJyApICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGNvbnNvbGUud2FybiggJ1RoZSA8Q0tFZGl0b3I+IGNvbXBvbmVudCByZXF1aXJlcyB1c2luZyBDS0VkaXRvciA1IGluIHZlcnNpb24gNDIrIG9yIG5pZ2h0bHkgYnVpbGQuJyApO1xuXHR9XG5cblx0Ly8gSW1wbGVtZW50aW5nIHRoZSBPbkNoYW5nZXMgaW50ZXJmYWNlLiBXaGVuZXZlciB0aGUgYGRhdGFgIHByb3BlcnR5IGlzIGNoYW5nZWQsIHVwZGF0ZSB0aGUgZWRpdG9yIGNvbnRlbnQuXG5cdHB1YmxpYyBuZ09uQ2hhbmdlcyggY2hhbmdlczogU2ltcGxlQ2hhbmdlcyApOiB2b2lkIHtcblx0XHRpZiAoIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggY2hhbmdlcywgJ2RhdGEnICkgJiYgY2hhbmdlcy5kYXRhICYmICFjaGFuZ2VzLmRhdGEuaXNGaXJzdENoYW5nZSgpICkge1xuXHRcdFx0dGhpcy53cml0ZVZhbHVlKCBjaGFuZ2VzLmRhdGEuY3VycmVudFZhbHVlICk7XG5cdFx0fVxuXHR9XG5cblx0Ly8gSW1wbGVtZW50aW5nIHRoZSBBZnRlclZpZXdJbml0IGludGVyZmFjZS5cblx0cHVibGljIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcblx0XHR0aGlzLmF0dGFjaFRvV2F0Y2hkb2coKTtcblx0fVxuXG5cdC8vIEltcGxlbWVudGluZyB0aGUgT25EZXN0cm95IGludGVyZmFjZS5cblx0cHVibGljIGFzeW5jIG5nT25EZXN0cm95KCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICggdGhpcy53YXRjaGRvZyApIHtcblx0XHRcdGF3YWl0IHRoaXMud2F0Y2hkb2cucmVtb3ZlKCB0aGlzLmlkICk7XG5cdFx0fSBlbHNlIGlmICggdGhpcy5lZGl0b3JXYXRjaGRvZyAmJiB0aGlzLmVkaXRvcldhdGNoZG9nLmVkaXRvciApIHtcblx0XHRcdGF3YWl0IHRoaXMuZWRpdG9yV2F0Y2hkb2cuZGVzdHJveSgpO1xuXG5cdFx0XHR0aGlzLmVkaXRvcldhdGNoZG9nID0gdW5kZWZpbmVkO1xuXHRcdH1cblx0fVxuXG5cdC8vIEltcGxlbWVudGluZyB0aGUgQ29udHJvbFZhbHVlQWNjZXNzb3IgaW50ZXJmYWNlIChvbmx5IHdoZW4gYmluZGluZyB0byBuZ01vZGVsKS5cblx0cHVibGljIHdyaXRlVmFsdWUoIHZhbHVlOiBzdHJpbmcgfCBudWxsICk6IHZvaWQge1xuXHRcdC8vIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCB3aXRoIHRoZSBgbnVsbGAgdmFsdWUgd2hlbiB0aGUgZm9ybSByZXNldHMuXG5cdFx0Ly8gQSBjb21wb25lbnQncyByZXNwb25zaWJpbGl0eSBpcyB0byByZXN0b3JlIHRvIHRoZSBpbml0aWFsIHN0YXRlLlxuXHRcdGlmICggdmFsdWUgPT09IG51bGwgKSB7XG5cdFx0XHR2YWx1ZSA9ICcnO1xuXHRcdH1cblxuXHRcdC8vIElmIGFscmVhZHkgaW5pdGlhbGl6ZWQuXG5cdFx0aWYgKCB0aGlzLmVkaXRvckluc3RhbmNlICkge1xuXHRcdFx0Ly8gVGhlIGxvY2sgbWVjaGFuaXNtIHByZXZlbnRzIGZyb20gY2FsbGluZyBgY3ZhT25DaGFuZ2UoKWAgZHVyaW5nIGNoYW5naW5nXG5cdFx0XHQvLyB0aGUgZWRpdG9yIHN0YXRlLiBTZWUgIzEzOVxuXHRcdFx0dGhpcy5pc0VkaXRvclNldHRpbmdEYXRhID0gdHJ1ZTtcblx0XHRcdHRoaXMuZWRpdG9ySW5zdGFuY2UuZGF0YS5zZXQoIHZhbHVlICk7XG5cdFx0XHR0aGlzLmlzRWRpdG9yU2V0dGluZ0RhdGEgPSBmYWxzZTtcblx0XHR9XG5cdFx0Ly8gSWYgbm90LCB3YWl0IGZvciBpdCB0byBiZSByZWFkeTsgc3RvcmUgdGhlIGRhdGEuXG5cdFx0ZWxzZSB7XG5cdFx0XHQvLyBJZiB0aGUgZWRpdG9yIGVsZW1lbnQgaXMgYWxyZWFkeSBhdmFpbGFibGUsIHRoZW4gdXBkYXRlIGl0cyBjb250ZW50LlxuXHRcdFx0dGhpcy5kYXRhID0gdmFsdWU7XG5cblx0XHRcdC8vIElmIG5vdCwgdGhlbiB3YWl0IHVudGlsIGl0IGlzIHJlYWR5XG5cdFx0XHQvLyBhbmQgY2hhbmdlIGRhdGEgb25seSBmb3IgdGhlIGZpcnN0IGByZWFkeWAgZXZlbnQuXG5cdFx0XHR0aGlzLnJlYWR5XG5cdFx0XHRcdC5waXBlKCBmaXJzdCgpIClcblx0XHRcdFx0LnN1YnNjcmliZSggZWRpdG9yID0+IHtcblx0XHRcdFx0XHRlZGl0b3IuZGF0YS5zZXQoIHRoaXMuZGF0YSApO1xuXHRcdFx0XHR9ICk7XG5cdFx0fVxuXHR9XG5cblx0Ly8gSW1wbGVtZW50aW5nIHRoZSBDb250cm9sVmFsdWVBY2Nlc3NvciBpbnRlcmZhY2UgKG9ubHkgd2hlbiBiaW5kaW5nIHRvIG5nTW9kZWwpLlxuXHRwdWJsaWMgcmVnaXN0ZXJPbkNoYW5nZSggY2FsbGJhY2s6ICggZGF0YTogc3RyaW5nICkgPT4gdm9pZCApOiB2b2lkIHtcblx0XHR0aGlzLmN2YU9uQ2hhbmdlID0gY2FsbGJhY2s7XG5cdH1cblxuXHQvLyBJbXBsZW1lbnRpbmcgdGhlIENvbnRyb2xWYWx1ZUFjY2Vzc29yIGludGVyZmFjZSAob25seSB3aGVuIGJpbmRpbmcgdG8gbmdNb2RlbCkuXG5cdHB1YmxpYyByZWdpc3Rlck9uVG91Y2hlZCggY2FsbGJhY2s6ICgpID0+IHZvaWQgKTogdm9pZCB7XG5cdFx0dGhpcy5jdmFPblRvdWNoZWQgPSBjYWxsYmFjaztcblx0fVxuXG5cdC8vIEltcGxlbWVudGluZyB0aGUgQ29udHJvbFZhbHVlQWNjZXNzb3IgaW50ZXJmYWNlIChvbmx5IHdoZW4gYmluZGluZyB0byBuZ01vZGVsKS5cblx0cHVibGljIHNldERpc2FibGVkU3RhdGUoIGlzRGlzYWJsZWQ6IGJvb2xlYW4gKTogdm9pZCB7XG5cdFx0Ly8gSWYgYWxyZWFkeSBpbml0aWFsaXplZC5cblx0XHRpZiAoIHRoaXMuZWRpdG9ySW5zdGFuY2UgKSB7XG5cdFx0XHRpZiAoIGlzRGlzYWJsZWQgKSB7XG5cdFx0XHRcdHRoaXMuZWRpdG9ySW5zdGFuY2UuZW5hYmxlUmVhZE9ubHlNb2RlKCBBTkdVTEFSX0lOVEVHUkFUSU9OX1JFQURfT05MWV9MT0NLX0lEICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0aGlzLmVkaXRvckluc3RhbmNlLmRpc2FibGVSZWFkT25seU1vZGUoIEFOR1VMQVJfSU5URUdSQVRJT05fUkVBRF9PTkxZX0xPQ0tfSUQgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBTdG9yZSB0aGUgc3RhdGUgYW55d2F5IHRvIHVzZSBpdCBvbmNlIHRoZSBlZGl0b3IgaXMgY3JlYXRlZC5cblx0XHR0aGlzLmluaXRpYWxseURpc2FibGVkID0gaXNEaXNhYmxlZDtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIHRoZSBlZGl0b3IgaW5zdGFuY2UsIHNldHMgaW5pdGlhbCBlZGl0b3IgZGF0YSwgdGhlbiBpbnRlZ3JhdGVzXG5cdCAqIHRoZSBlZGl0b3Igd2l0aCB0aGUgQW5ndWxhciBjb21wb25lbnQuIFRoaXMgbWV0aG9kIGRvZXMgbm90IHVzZSB0aGUgYGVkaXRvci5kYXRhLnNldCgpYFxuXHQgKiBiZWNhdXNlIG9mIHRoZSBpc3N1ZSBpbiB0aGUgY29sbGFib3JhdGlvbiBtb2RlICgjNikuXG5cdCAqL1xuXHRwcml2YXRlIGF0dGFjaFRvV2F0Y2hkb2coKSB7XG5cdFx0Ly8gVE9ETzogZWxlbWVudE9yRGF0YSBwYXJhbWV0ZXIgdHlwZSBjYW4gYmUgc2ltcGxpZmllZCB0byBIVE1MRWxlbWVuIGFmdGVyIHRlbXBsYXRlZCBXYXRjaGRvZyB3aWxsIGJlIHJlbGVhc2VkLlxuXHRcdGNvbnN0IGNyZWF0b3I6IEVkaXRvckNyZWF0b3JGdW5jdGlvbjxURWRpdG9yPiA9ICggKCBlbGVtZW50T3JEYXRhLCBjb25maWcgKSA9PiB7XG5cdFx0XHRyZXR1cm4gdGhpcy5uZ1pvbmUucnVuT3V0c2lkZUFuZ3VsYXIoIGFzeW5jICgpID0+IHtcblx0XHRcdFx0dGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQuYXBwZW5kQ2hpbGQoIGVsZW1lbnRPckRhdGEgYXMgSFRNTEVsZW1lbnQgKTtcblxuXHRcdFx0XHRjb25zdCBlZGl0b3IgPSBhd2FpdCB0aGlzLmVkaXRvciEuY3JlYXRlKCBlbGVtZW50T3JEYXRhIGFzIEhUTUxFbGVtZW50LCBjb25maWcgKTtcblxuXHRcdFx0XHRpZiAoIHRoaXMuaW5pdGlhbGx5RGlzYWJsZWQgKSB7XG5cdFx0XHRcdFx0ZWRpdG9yLmVuYWJsZVJlYWRPbmx5TW9kZSggQU5HVUxBUl9JTlRFR1JBVElPTl9SRUFEX09OTFlfTE9DS19JRCApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5uZ1pvbmUucnVuKCAoKSA9PiB7XG5cdFx0XHRcdFx0dGhpcy5yZWFkeS5lbWl0KCBlZGl0b3IgKTtcblx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdHRoaXMuc2V0VXBFZGl0b3JFdmVudHMoIGVkaXRvciApO1xuXG5cdFx0XHRcdHJldHVybiBlZGl0b3I7XG5cdFx0XHR9ICk7XG5cdFx0fSApO1xuXG5cdFx0Y29uc3QgZGVzdHJ1Y3RvciA9IGFzeW5jICggZWRpdG9yOiBFZGl0b3IgKSA9PiB7XG5cdFx0XHRhd2FpdCBlZGl0b3IuZGVzdHJveSgpO1xuXG5cdFx0XHR0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5yZW1vdmVDaGlsZCggdGhpcy5lZGl0b3JFbGVtZW50ISApO1xuXHRcdH07XG5cblx0XHRjb25zdCBlbWl0RXJyb3IgPSAoIGU/OiB1bmtub3duICkgPT4ge1xuXHRcdFx0Ly8gRG8gbm90IHJ1biBjaGFuZ2UgZGV0ZWN0aW9uIGJ5IHJlLWVudGVyaW5nIHRoZSBBbmd1bGFyIHpvbmUgaWYgdGhlIGBlcnJvcmBcblx0XHRcdC8vIGVtaXR0ZXIgZG9lc24ndCBoYXZlIGFueSBzdWJzY3JpYmVycy5cblx0XHRcdC8vIFN1YnNjcmliZXJzIGFyZSBwdXNoZWQgb250byB0aGUgbGlzdCB3aGVuZXZlciBgZXJyb3JgIGlzIGxpc3RlbmVkIGluc2lkZSB0aGUgdGVtcGxhdGU6XG5cdFx0XHQvLyBgPGNrZWRpdG9yIChlcnJvcik9XCJvbkVycm9yKC4uLilcIj48L2NrZWRpdG9yPmAuXG5cdFx0XHRpZiAoIGhhc09ic2VydmVycyggdGhpcy5lcnJvciApICkge1xuXHRcdFx0XHR0aGlzLm5nWm9uZS5ydW4oICgpID0+IHRoaXMuZXJyb3IuZW1pdCggZSApICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQvLyBQcmludCBlcnJvciB0byB0aGUgY29uc29sZSB3aGVuIHRoZXJlIGFyZSBubyBzdWJzY3JpYmVycyB0byB0aGUgYGVycm9yYCBldmVudC5cblx0XHRcdFx0Y29uc29sZS5lcnJvciggZSApO1xuXHRcdFx0fVxuXHRcdH07XG5cdFx0Y29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIHRoaXMudGFnTmFtZSApO1xuXHRcdGNvbnN0IGNvbmZpZyA9IHRoaXMuZ2V0Q29uZmlnKCk7XG5cblx0XHR0aGlzLmVkaXRvckVsZW1lbnQgPSBlbGVtZW50O1xuXG5cdFx0Ly8gQmFzZWQgb24gdGhlIHByZXNlbmNlIG9mIHRoZSB3YXRjaGRvZyBkZWNpZGUgaG93IHRvIGluaXRpYWxpemUgdGhlIGVkaXRvci5cblx0XHRpZiAoIHRoaXMud2F0Y2hkb2cgKSB7XG5cdFx0XHQvLyBXaGVuIHRoZSBjb250ZXh0IHdhdGNoZG9nIGlzIHBhc3NlZCBhZGQgdGhlIG5ldyBpdGVtIHRvIGl0IGJhc2VkIG9uIHRoZSBwYXNzZWQgY29uZmlndXJhdGlvbi5cblx0XHRcdHRoaXMud2F0Y2hkb2cuYWRkKCB7XG5cdFx0XHRcdGlkOiB0aGlzLmlkLFxuXHRcdFx0XHR0eXBlOiAnZWRpdG9yJyxcblx0XHRcdFx0Y3JlYXRvcixcblx0XHRcdFx0ZGVzdHJ1Y3Rvcixcblx0XHRcdFx0c291cmNlRWxlbWVudE9yRGF0YTogZWxlbWVudCxcblx0XHRcdFx0Y29uZmlnXG5cdFx0XHR9ICkuY2F0Y2goIGUgPT4ge1xuXHRcdFx0XHRlbWl0RXJyb3IoIGUgKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0dGhpcy53YXRjaGRvZy5vbiggJ2l0ZW1FcnJvcicsICggXywgeyBpdGVtSWQgfSApID0+IHtcblx0XHRcdFx0aWYgKCBpdGVtSWQgPT09IHRoaXMuaWQgKSB7XG5cdFx0XHRcdFx0ZW1pdEVycm9yKCk7XG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gSW4gdGhlIG90aGVyIGNhc2UgY3JlYXRlIHRoZSB3YXRjaGRvZyBieSBoYW5kIHRvIGtlZXAgdGhlIGVkaXRvciBydW5uaW5nLlxuXHRcdFx0Y29uc3QgZWRpdG9yV2F0Y2hkb2cgPSBuZXcgdGhpcy5lZGl0b3IhLkVkaXRvcldhdGNoZG9nKFxuXHRcdFx0XHR0aGlzLmVkaXRvciEsXG5cdFx0XHRcdHRoaXMuZWRpdG9yV2F0Y2hkb2dDb25maWdcblx0XHRcdCk7XG5cblx0XHRcdGVkaXRvcldhdGNoZG9nLnNldENyZWF0b3IoIGNyZWF0b3IgKTtcblx0XHRcdGVkaXRvcldhdGNoZG9nLnNldERlc3RydWN0b3IoIGRlc3RydWN0b3IgKTtcblx0XHRcdGVkaXRvcldhdGNoZG9nLm9uKCAnZXJyb3InLCBlbWl0RXJyb3IgKTtcblxuXHRcdFx0dGhpcy5lZGl0b3JXYXRjaGRvZyA9IGVkaXRvcldhdGNoZG9nO1xuXHRcdFx0dGhpcy5uZ1pvbmUucnVuT3V0c2lkZUFuZ3VsYXIoICgpID0+IHtcblx0XHRcdFx0Ly8gTm90ZTogbXVzdCBiZSBjYWxsZWQgb3V0c2lkZSBvZiB0aGUgQW5ndWxhciB6b25lIHRvbyBiZWNhdXNlIGBjcmVhdGVgIGlzIGNhbGxpbmdcblx0XHRcdFx0Ly8gYF9zdGFydEVycm9ySGFuZGxpbmdgIHdpdGhpbiBhIG1pY3JvdGFzayB3aGljaCBzZXRzIHVwIGBlcnJvcmAgbGlzdGVuZXIgb24gdGhlIHdpbmRvdy5cblx0XHRcdFx0ZWRpdG9yV2F0Y2hkb2cuY3JlYXRlKCBlbGVtZW50LCBjb25maWcgKS5jYXRjaCggZSA9PiB7XG5cdFx0XHRcdFx0ZW1pdEVycm9yKCBlICk7XG5cdFx0XHRcdH0gKTtcblx0XHRcdH0gKTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIGdldENvbmZpZygpIHtcblx0XHRpZiAoIHRoaXMuZGF0YSAmJiB0aGlzLmNvbmZpZy5pbml0aWFsRGF0YSApIHtcblx0XHRcdHRocm93IG5ldyBFcnJvciggJ0VkaXRvciBkYXRhIHNob3VsZCBiZSBwcm92aWRlZCBlaXRoZXIgdXNpbmcgYGNvbmZpZy5pbml0aWFsRGF0YWAgb3IgYGRhdGFgIHByb3BlcnRpZXMuJyApO1xuXHRcdH1cblxuXHRcdGNvbnN0IGNvbmZpZyA9IHsgLi4udGhpcy5jb25maWcgfTtcblxuXHRcdC8vIE1lcmdlIHR3byBwb3NzaWJsZSB3YXlzIG9mIHByb3ZpZGluZyBkYXRhIGludG8gdGhlIGBjb25maWcuaW5pdGlhbERhdGFgIGZpZWxkLlxuXHRcdGNvbnN0IGluaXRpYWxEYXRhID0gdGhpcy5jb25maWcuaW5pdGlhbERhdGEgfHwgdGhpcy5kYXRhO1xuXG5cdFx0aWYgKCBpbml0aWFsRGF0YSApIHtcblx0XHRcdC8vIERlZmluZSB0aGUgYGNvbmZpZy5pbml0aWFsRGF0YWAgb25seSB3aGVuIHRoZSBpbml0aWFsIGNvbnRlbnQgaXMgc3BlY2lmaWVkLlxuXHRcdFx0Y29uZmlnLmluaXRpYWxEYXRhID0gaW5pdGlhbERhdGE7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGFwcGVuZEFsbEludGVncmF0aW9uUGx1Z2luc1RvQ29uZmlnKCBjb25maWcgKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBJbnRlZ3JhdGVzIHRoZSBlZGl0b3Igd2l0aCB0aGUgY29tcG9uZW50IGJ5IGF0dGFjaGluZyByZWxhdGVkIGV2ZW50IGxpc3RlbmVycy5cblx0ICovXG5cdHByaXZhdGUgc2