UNPKG

52.9 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11Object.defineProperty(exports, "__esModule", { value: true });
12exports.TextEditorSyncAdapter = void 0;
13const convert_1 = require("../convert");
14const languageclient_1 = require("../languageclient");
15const apply_edit_adapter_1 = require("./apply-edit-adapter");
16const atom_1 = require("atom");
17const Utils = require("../utils");
18/**
19 * Public: Synchronizes the documents between Atom and the language server by notifying each end of changes, opening,
20 * closing and other events as well as sending and applying changes either in whole or in part depending on what the
21 * language server supports.
22 */
23class DocumentSyncAdapter {
24 /**
25 * Public: Create a new {DocumentSyncAdapter} for the given language server.
26 *
27 * @param _connection A {LanguageClientConnection} to the language server to be kept in sync.
28 * @param documentSync The document syncing options.
29 * @param _editorSelector A predicate function that takes a {TextEditor} and returns a {boolean} indicating whether
30 * this adapter should care about the contents of the editor.
31 * @param _getLanguageIdFromEditor A function that returns a {string} of `languageId` used for `textDocument/didOpen`
32 * notification.
33 */
34 constructor(_connection, _editorSelector, documentSync, _reportBusyWhile, _getLanguageIdFromEditor) {
35 this._connection = _connection;
36 this._editorSelector = _editorSelector;
37 this._reportBusyWhile = _reportBusyWhile;
38 this._getLanguageIdFromEditor = _getLanguageIdFromEditor;
39 this._disposable = new atom_1.CompositeDisposable();
40 this._editors = new WeakMap();
41 this._versions = new Map();
42 if (typeof documentSync === "object") {
43 this._documentSync = documentSync;
44 }
45 else {
46 this._documentSync = {
47 change: documentSync || languageclient_1.TextDocumentSyncKind.Full,
48 };
49 }
50 this._disposable.add(atom.textEditors.observe(this.observeTextEditor.bind(this)));
51 }
52 /**
53 * Public: Determine whether this adapter can be used to adapt a language server based on the serverCapabilities
54 * matrix textDocumentSync capability either being Full or Incremental.
55 *
56 * @param serverCapabilities The {ServerCapabilities} of the language server to consider.
57 * @returns A {Boolean} indicating adapter can adapt the server based on the given serverCapabilities.
58 */
59 static canAdapt(serverCapabilities) {
60 return this.canAdaptV2(serverCapabilities) || this.canAdaptV3(serverCapabilities);
61 }
62 static canAdaptV2(serverCapabilities) {
63 return (serverCapabilities.textDocumentSync === languageclient_1.TextDocumentSyncKind.Incremental ||
64 serverCapabilities.textDocumentSync === languageclient_1.TextDocumentSyncKind.Full);
65 }
66 static canAdaptV3(serverCapabilities) {
67 const options = serverCapabilities.textDocumentSync;
68 return (options !== null &&
69 typeof options === "object" &&
70 (options.change === languageclient_1.TextDocumentSyncKind.Incremental || options.change === languageclient_1.TextDocumentSyncKind.Full));
71 }
72 /** Dispose this adapter ensuring any resources are freed and events unhooked. */
73 dispose() {
74 this._disposable.dispose();
75 }
76 /**
77 * Examine a {TextEditor} and decide if we wish to observe it. If so ensure that we stop observing it when it is
78 * closed or otherwise destroyed.
79 *
80 * @param editor A {TextEditor} to consider for observation.
81 */
82 observeTextEditor(editor) {
83 const listener = editor.observeGrammar((_grammar) => this._handleGrammarChange(editor));
84 this._disposable.add(editor.onDidDestroy(() => {
85 this._disposable.remove(listener);
86 listener.dispose();
87 }));
88 this._disposable.add(listener);
89 if (!this._editors.has(editor) && this._editorSelector(editor)) {
90 this._handleNewEditor(editor);
91 }
92 }
93 _handleGrammarChange(editor) {
94 const sync = this._editors.get(editor);
95 if (sync != null && !this._editorSelector(editor)) {
96 this._editors.delete(editor);
97 this._disposable.remove(sync);
98 sync.didClose();
99 sync.dispose();
100 }
101 else if (sync == null && this._editorSelector(editor)) {
102 this._handleNewEditor(editor);
103 }
104 }
105 _handleNewEditor(editor) {
106 const sync = new TextEditorSyncAdapter(editor, this._connection, this._documentSync, this._versions, this._reportBusyWhile, this._getLanguageIdFromEditor);
107 this._editors.set(editor, sync);
108 this._disposable.add(sync);
109 this._disposable.add(editor.onDidDestroy(() => {
110 const destroyedSync = this._editors.get(editor);
111 if (destroyedSync) {
112 this._editors.delete(editor);
113 this._disposable.remove(destroyedSync);
114 destroyedSync.dispose();
115 }
116 }));
117 }
118 getEditorSyncAdapter(editor) {
119 return this._editors.get(editor);
120 }
121}
122exports.default = DocumentSyncAdapter;
123/** Public: Keep a single {TextEditor} in sync with a given language server. */
124class TextEditorSyncAdapter {
125 /**
126 * Public: Create a {TextEditorSyncAdapter} in sync with a given language server.
127 *
128 * @param _editor A {TextEditor} to keep in sync.
129 * @param _connection A {LanguageClientConnection} to a language server to keep in sync.
130 * @param _documentSync The document syncing options.
131 */
132 constructor(_editor, _connection, _documentSync, _versions, _reportBusyWhile, _getLanguageIdFromEditor) {
133 this._editor = _editor;
134 this._connection = _connection;
135 this._documentSync = _documentSync;
136 this._versions = _versions;
137 this._reportBusyWhile = _reportBusyWhile;
138 this._getLanguageIdFromEditor = _getLanguageIdFromEditor;
139 this._disposable = new atom_1.CompositeDisposable();
140 this._fakeDidChangeWatchedFiles = atom.project.onDidChangeFiles == null;
141 const changeTracking = this.setupChangeTracking(_documentSync);
142 if (changeTracking != null) {
143 this._disposable.add(changeTracking);
144 }
145 // These handlers are attached only if server supports them
146 if (_documentSync.willSave) {
147 this._disposable.add(_editor.getBuffer().onWillSave(this.willSave.bind(this)));
148 }
149 if (_documentSync.willSaveWaitUntil) {
150 this._disposable.add(_editor.getBuffer().onWillSave(this.willSaveWaitUntil.bind(this)));
151 }
152 // Send close notifications unless it's explicitly disabled
153 if (_documentSync.openClose !== false) {
154 this._disposable.add(_editor.onDidDestroy(this.didClose.bind(this)));
155 }
156 this._disposable.add(_editor.onDidSave(this.didSave.bind(this)), _editor.onDidChangePath(this.didRename.bind(this)));
157 this._currentUri = this.getEditorUri();
158 if (_documentSync.openClose !== false) {
159 this.didOpen();
160 }
161 }
162 /** The change tracking disposable listener that will ensure that changes are sent to the language server as appropriate. */
163 setupChangeTracking(documentSync) {
164 switch (documentSync.change) {
165 case languageclient_1.TextDocumentSyncKind.Full:
166 return this._editor.onDidChange(this.sendFullChanges.bind(this));
167 case languageclient_1.TextDocumentSyncKind.Incremental:
168 return this._editor.getBuffer().onDidChangeText(this.sendIncrementalChanges.bind(this));
169 }
170 return null;
171 }
172 /** Dispose this adapter ensuring any resources are freed and events unhooked. */
173 dispose() {
174 this._disposable.dispose();
175 }
176 /** Get the languageId field that will be sent to the language server by simply using the `_getLanguageIdFromEditor`. */
177 getLanguageId() {
178 return this._getLanguageIdFromEditor(this._editor);
179 }
180 /**
181 * Public: Create a {VersionedTextDocumentIdentifier} for the document observed by this adapter including both the Uri
182 * and the current Version.
183 */
184 getVersionedTextDocumentIdentifier() {
185 return {
186 uri: this.getEditorUri(),
187 version: this._getVersion(this._editor.getPath() || ""),
188 };
189 }
190 /** Public: Send the entire document to the language server. This is used when operating in Full (1) sync mode. */
191 sendFullChanges() {
192 if (!this._isPrimaryAdapter()) {
193 return;
194 } // Multiple editors, we are not first
195 this._bumpVersion();
196 this._connection.didChangeTextDocument({
197 textDocument: this.getVersionedTextDocumentIdentifier(),
198 contentChanges: [{ text: this._editor.getText() }],
199 });
200 }
201 /**
202 * Public: Send the incremental text changes to the language server. This is used when operating in Incremental (2) sync mode.
203 *
204 * @param event The event fired by Atom to indicate the document has stopped changing including a list of changes
205 * since the last time this event fired for this text editor. NOTE: The order of changes in the event is guaranteed
206 * top to bottom. Language server expects this in reverse.
207 */
208 sendIncrementalChanges(event) {
209 if (event.changes.length > 0) {
210 if (!this._isPrimaryAdapter()) {
211 return;
212 } // Multiple editors, we are not first
213 this._bumpVersion();
214 this._connection.didChangeTextDocument({
215 textDocument: this.getVersionedTextDocumentIdentifier(),
216 contentChanges: event.changes.map(TextEditorSyncAdapter.textEditToContentChange).reverse(),
217 });
218 }
219 }
220 /**
221 * Public: Convert an Atom {TextEditEvent} to a language server {TextDocumentContentChangeEvent} object.
222 *
223 * @param change The Atom {TextEditEvent} to convert.
224 * @returns A {TextDocumentContentChangeEvent} that represents the converted {TextEditEvent}.
225 */
226 static textEditToContentChange(change) {
227 return {
228 range: convert_1.default.atomRangeToLSRange(change.oldRange),
229 rangeLength: change.oldText.length,
230 text: change.newText,
231 };
232 }
233 _isPrimaryAdapter() {
234 const lowestIdForBuffer = Math.min(...atom.workspace
235 .getTextEditors()
236 .filter((t) => t.getBuffer() === this._editor.getBuffer())
237 .map((t) => t.id));
238 return lowestIdForBuffer === this._editor.id;
239 }
240 _bumpVersion() {
241 const filePath = this._editor.getPath();
242 if (filePath == null) {
243 return;
244 }
245 this._versions.set(filePath, this._getVersion(filePath) + 1);
246 }
247 /**
248 * Ensure when the document is opened we send notification to the language server so it can load it in and keep track
249 * of diagnostics etc.
250 */
251 didOpen() {
252 const filePath = this._editor.getPath();
253 if (filePath == null) {
254 return;
255 } // Not yet saved
256 if (!this._isPrimaryAdapter()) {
257 return;
258 } // Multiple editors, we are not first
259 this._connection.didOpenTextDocument({
260 textDocument: {
261 uri: this.getEditorUri(),
262 languageId: this.getLanguageId().toLowerCase(),
263 version: this._getVersion(filePath),
264 text: this._editor.getText(),
265 },
266 });
267 }
268 _getVersion(filePath) {
269 return this._versions.get(filePath) || 1;
270 }
271 /** Called when the {TextEditor} is closed and sends the 'didCloseTextDocument' notification to the connected language server. */
272 didClose() {
273 if (this._editor.getPath() == null) {
274 return;
275 } // Not yet saved
276 const fileStillOpen = atom.workspace.getTextEditors().find((t) => t.getBuffer() === this._editor.getBuffer());
277 if (fileStillOpen) {
278 return; // Other windows or editors still have this file open
279 }
280 this._connection.didCloseTextDocument({ textDocument: { uri: this.getEditorUri() } });
281 }
282 /** Called just before the {TextEditor} saves and sends the 'willSaveTextDocument' notification to the connected language server. */
283 willSave() {
284 if (!this._isPrimaryAdapter()) {
285 return;
286 }
287 const uri = this.getEditorUri();
288 this._connection.willSaveTextDocument({
289 textDocument: { uri },
290 reason: languageclient_1.TextDocumentSaveReason.Manual,
291 });
292 }
293 /**
294 * Called just before the {TextEditor} saves, sends the 'willSaveWaitUntilTextDocument' request to the connected
295 * language server and waits for the response before saving the buffer.
296 */
297 willSaveWaitUntil() {
298 return __awaiter(this, void 0, void 0, function* () {
299 if (!this._isPrimaryAdapter()) {
300 return Promise.resolve();
301 }
302 const buffer = this._editor.getBuffer();
303 const uri = this.getEditorUri();
304 const title = this._editor.getLongTitle();
305 const applyEditsOrTimeout = Utils.promiseWithTimeout(2500, // 2.5 seconds timeout
306 this._connection.willSaveWaitUntilTextDocument({
307 textDocument: { uri },
308 reason: languageclient_1.TextDocumentSaveReason.Manual,
309 }))
310 .then((edits) => {
311 const cursor = this._editor.getCursorBufferPosition();
312 apply_edit_adapter_1.default.applyEdits(buffer, convert_1.default.convertLsTextEdits(edits));
313 this._editor.setCursorBufferPosition(cursor);
314 })
315 .catch((err) => {
316 atom.notifications.addError("On-save action failed", {
317 description: `Failed to apply edits to ${title}`,
318 detail: err.message,
319 });
320 return;
321 });
322 const withBusySignal = this._reportBusyWhile(`Applying on-save edits for ${title}`, () => applyEditsOrTimeout);
323 return withBusySignal || applyEditsOrTimeout;
324 });
325 }
326 /**
327 * Called when the {TextEditor} saves and sends the 'didSaveTextDocument' notification to the connected language
328 * server. Note: Right now this also sends the `didChangeWatchedFiles` notification as well but that will be sent from
329 * elsewhere soon.
330 */
331 didSave() {
332 if (!this._isPrimaryAdapter()) {
333 return;
334 }
335 const uri = this.getEditorUri();
336 const didSaveNotification = {
337 textDocument: { uri, version: this._getVersion(uri) },
338 };
339 if (typeof this._documentSync.save === "object" && this._documentSync.save.includeText) {
340 didSaveNotification.text = this._editor.getText();
341 }
342 this._connection.didSaveTextDocument(didSaveNotification);
343 if (this._fakeDidChangeWatchedFiles) {
344 this._connection.didChangeWatchedFiles({
345 changes: [{ uri, type: languageclient_1.FileChangeType.Changed }],
346 });
347 }
348 }
349 didRename() {
350 if (!this._isPrimaryAdapter()) {
351 return;
352 }
353 const oldUri = this._currentUri;
354 this._currentUri = this.getEditorUri();
355 if (!oldUri) {
356 return; // Didn't previously have a name
357 }
358 if (this._documentSync.openClose !== false) {
359 this._connection.didCloseTextDocument({ textDocument: { uri: oldUri } });
360 }
361 if (this._fakeDidChangeWatchedFiles) {
362 this._connection.didChangeWatchedFiles({
363 changes: [
364 { uri: oldUri, type: languageclient_1.FileChangeType.Deleted },
365 { uri: this._currentUri, type: languageclient_1.FileChangeType.Created },
366 ],
367 });
368 }
369 // Send an equivalent open event for this editor, which will now use the new
370 // file path.
371 if (this._documentSync.openClose !== false) {
372 this.didOpen();
373 }
374 }
375 /** Public: Obtain the current {TextEditor} path and convert it to a Uri. */
376 getEditorUri() {
377 return convert_1.default.pathToUri(this._editor.getPath() || "");
378 }
379}
380exports.TextEditorSyncAdapter = TextEditorSyncAdapter;
381//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9jdW1lbnQtc3luYy1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vbGliL2FkYXB0ZXJzL2RvY3VtZW50LXN5bmMtYWRhcHRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQSx3Q0FBZ0M7QUFDaEMsc0RBVTBCO0FBQzFCLDZEQUFtRDtBQUNuRCwrQkFBMEc7QUFDMUcsa0NBQWlDO0FBRWpDOzs7O0dBSUc7QUFDSCxNQUFxQixtQkFBbUI7SUFpQ3RDOzs7Ozs7Ozs7T0FTRztJQUNILFlBQ1UsV0FBcUMsRUFDckMsZUFBZ0QsRUFDeEQsWUFBd0UsRUFDaEUsZ0JBQXVDLEVBQ3ZDLHdCQUF3RDtRQUp4RCxnQkFBVyxHQUFYLFdBQVcsQ0FBMEI7UUFDckMsb0JBQWUsR0FBZixlQUFlLENBQWlDO1FBRWhELHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBdUI7UUFDdkMsNkJBQXdCLEdBQXhCLHdCQUF3QixDQUFnQztRQS9DMUQsZ0JBQVcsR0FBRyxJQUFJLDBCQUFtQixFQUFFLENBQUE7UUFFdkMsYUFBUSxHQUErQyxJQUFJLE9BQU8sRUFBRSxDQUFBO1FBQ3BFLGNBQVMsR0FBd0IsSUFBSSxHQUFHLEVBQUUsQ0FBQTtRQThDaEQsSUFBSSxPQUFPLFlBQVksS0FBSyxRQUFRLEVBQUU7WUFDcEMsSUFBSSxDQUFDLGFBQWEsR0FBRyxZQUFZLENBQUE7U0FDbEM7YUFBTTtZQUNMLElBQUksQ0FBQyxhQUFhLEdBQUc7Z0JBQ25CLE1BQU0sRUFBRSxZQUFZLElBQUkscUNBQW9CLENBQUMsSUFBSTthQUNsRCxDQUFBO1NBQ0Y7UUFDRCxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUNuRixDQUFDO0lBcEREOzs7Ozs7T0FNRztJQUNJLE1BQU0sQ0FBQyxRQUFRLENBQUMsa0JBQXNDO1FBQzNELE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtJQUNuRixDQUFDO0lBRU8sTUFBTSxDQUFDLFVBQVUsQ0FBQyxrQkFBc0M7UUFDOUQsT0FBTyxDQUNMLGtCQUFrQixDQUFDLGdCQUFnQixLQUFLLHFDQUFvQixDQUFDLFdBQVc7WUFDeEUsa0JBQWtCLENBQUMsZ0JBQWdCLEtBQUsscUNBQW9CLENBQUMsSUFBSSxDQUNsRSxDQUFBO0lBQ0gsQ0FBQztJQUVPLE1BQU0sQ0FBQyxVQUFVLENBQUMsa0JBQXNDO1FBQzlELE1BQU0sT0FBTyxHQUFHLGtCQUFrQixDQUFDLGdCQUFnQixDQUFBO1FBQ25ELE9BQU8sQ0FDTCxPQUFPLEtBQUssSUFBSTtZQUNoQixPQUFPLE9BQU8sS0FBSyxRQUFRO1lBQzNCLENBQUMsT0FBTyxDQUFDLE1BQU0sS0FBSyxxQ0FBb0IsQ0FBQyxXQUFXLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxxQ0FBb0IsQ0FBQyxJQUFJLENBQUMsQ0FDdEcsQ0FBQTtJQUNILENBQUM7SUE2QkQsaUZBQWlGO0lBQzFFLE9BQU87UUFDWixJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sRUFBRSxDQUFBO0lBQzVCLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLGlCQUFpQixDQUFDLE1BQWtCO1FBQ3pDLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO1FBQ3ZGLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUNsQixNQUFNLENBQUMsWUFBWSxDQUFDLEdBQUcsRUFBRTtZQUN2QixJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQTtZQUNqQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUE7UUFDcEIsQ0FBQyxDQUFDLENBQ0gsQ0FBQTtRQUNELElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQzlCLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxFQUFFO1lBQzlELElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQTtTQUM5QjtJQUNILENBQUM7SUFFTyxvQkFBb0IsQ0FBQyxNQUFrQjtRQUM3QyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUN0QyxJQUFJLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxFQUFFO1lBQ2pELElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1lBQzVCLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFBO1lBQzdCLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQTtZQUNmLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQTtTQUNmO2FBQU0sSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLEVBQUU7WUFDdkQsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFBO1NBQzlCO0lBQ0gsQ0FBQztJQUVPLGdCQUFnQixDQUFDLE1BQWtCO1FBQ3pDLE1BQU0sSUFBSSxHQUFHLElBQUkscUJBQXFCLENBQ3BDLE1BQU0sRUFDTixJQUFJLENBQUMsV0FBVyxFQUNoQixJQUFJLENBQUMsYUFBYSxFQUNsQixJQUFJLENBQUMsU0FBUyxFQUNkLElBQUksQ0FBQyxnQkFBZ0IsRUFDckIsSUFBSSxDQUFDLHdCQUF3QixDQUM5QixDQUFBO1FBQ0QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFBO1FBQy9CLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBQzFCLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUNsQixNQUFNLENBQUMsWUFBWSxDQUFDLEdBQUcsRUFBRTtZQUN2QixNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUMvQyxJQUFJLGFBQWEsRUFBRTtnQkFDakIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUE7Z0JBQzVCLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFBO2dCQUN0QyxhQUFhLENBQUMsT0FBTyxFQUFFLENBQUE7YUFDeEI7UUFDSCxDQUFDLENBQUMsQ0FDSCxDQUFBO0lBQ0gsQ0FBQztJQUVNLG9CQUFvQixDQUFDLE1BQWtCO1FBQzVDLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDbEMsQ0FBQztDQUNGO0FBM0hELHNDQTJIQztBQUVELCtFQUErRTtBQUMvRSxNQUFhLHFCQUFxQjtJQUtoQzs7Ozs7O09BTUc7SUFDSCxZQUNVLE9BQW1CLEVBQ25CLFdBQXFDLEVBQ3JDLGFBQXNDLEVBQ3RDLFNBQThCLEVBQzlCLGdCQUF1QyxFQUN2Qyx3QkFBd0Q7UUFMeEQsWUFBTyxHQUFQLE9BQU8sQ0FBWTtRQUNuQixnQkFBVyxHQUFYLFdBQVcsQ0FBMEI7UUFDckMsa0JBQWEsR0FBYixhQUFhLENBQXlCO1FBQ3RDLGNBQVMsR0FBVCxTQUFTLENBQXFCO1FBQzlCLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBdUI7UUFDdkMsNkJBQXdCLEdBQXhCLHdCQUF3QixDQUFnQztRQWpCMUQsZ0JBQVcsR0FBRyxJQUFJLDBCQUFtQixFQUFFLENBQUE7UUFtQjdDLElBQUksQ0FBQywwQkFBMEIsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixJQUFJLElBQUksQ0FBQTtRQUV2RSxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsYUFBYSxDQUFDLENBQUE7UUFDOUQsSUFBSSxjQUFjLElBQUksSUFBSSxFQUFFO1lBQzFCLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFBO1NBQ3JDO1FBRUQsMkRBQTJEO1FBQzNELElBQUksYUFBYSxDQUFDLFFBQVEsRUFBRTtZQUMxQixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtTQUMvRTtRQUNELElBQUksYUFBYSxDQUFDLGlCQUFpQixFQUFFO1lBQ25DLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7U0FDeEY7UUFDRCwyREFBMkQ7UUFDM0QsSUFBSSxhQUFhLENBQUMsU0FBUyxLQUFLLEtBQUssRUFBRTtZQUNyQyxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtTQUNyRTtRQUNELElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUVwSCxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQTtRQUV0QyxJQUFJLGFBQWEsQ0FBQyxTQUFTLEtBQUssS0FBSyxFQUFFO1lBQ3JDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQTtTQUNmO0lBQ0gsQ0FBQztJQUVELDRIQUE0SDtJQUNySCxtQkFBbUIsQ0FBQyxZQUFxQztRQUM5RCxRQUFRLFlBQVksQ0FBQyxNQUFNLEVBQUU7WUFDM0IsS0FBSyxxQ0FBb0IsQ0FBQyxJQUFJO2dCQUM1QixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUE7WUFDbEUsS0FBSyxxQ0FBb0IsQ0FBQyxXQUFXO2dCQUNuQyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtTQUMxRjtRQUNELE9BQU8sSUFBSSxDQUFBO0lBQ2IsQ0FBQztJQUVELGlGQUFpRjtJQUMxRSxPQUFPO1FBQ1osSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQTtJQUM1QixDQUFDO0lBRUQsd0hBQXdIO0lBQ2pILGFBQWE7UUFDbEIsT0FBTyxJQUFJLENBQUMsd0JBQXdCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQ3BELENBQUM7SUFFRDs7O09BR0c7SUFDSSxrQ0FBa0M7UUFDdkMsT0FBTztZQUNMLEdBQUcsRUFBRSxJQUFJLENBQUMsWUFBWSxFQUFFO1lBQ3hCLE9BQU8sRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1NBQ3hELENBQUE7SUFDSCxDQUFDO0lBRUQsa0hBQWtIO0lBQzNHLGVBQWU7UUFDcEIsSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxFQUFFO1lBQzdCLE9BQU07U0FDUCxDQUFDLHFDQUFxQztRQUV2QyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUE7UUFDbkIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxxQkFBcUIsQ0FBQztZQUNyQyxZQUFZLEVBQUUsSUFBSSxDQUFDLGtDQUFrQyxFQUFFO1lBQ3ZELGNBQWMsRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQztTQUNuRCxDQUFDLENBQUE7SUFDSixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksc0JBQXNCLENBQUMsS0FBaUM7UUFDN0QsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7WUFDNUIsSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxFQUFFO2dCQUM3QixPQUFNO2FBQ1AsQ0FBQyxxQ0FBcUM7WUFFdkMsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFBO1lBQ25CLElBQUksQ0FBQyxXQUFXLENBQUMscUJBQXFCLENBQUM7Z0JBQ3JDLFlBQVksRUFBRSxJQUFJLENBQUMsa0NBQWtDLEVBQUU7Z0JBQ3ZELGNBQWMsRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLE9BQU8sRUFBRTthQUMzRixDQUFDLENBQUE7U0FDSDtJQUNILENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLE1BQU0sQ0FBQyx1QkFBdUIsQ0FBQyxNQUFrQjtRQUN0RCxPQUFPO1lBQ0wsS0FBSyxFQUFFLGlCQUFPLENBQUMsa0JBQWtCLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQztZQUNsRCxXQUFXLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNO1lBQ2xDLElBQUksRUFBRSxNQUFNLENBQUMsT0FBTztTQUNyQixDQUFBO0lBQ0gsQ0FBQztJQUVPLGlCQUFpQjtRQUN2QixNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxHQUFHLENBQ2hDLEdBQUcsSUFBSSxDQUFDLFNBQVM7YUFDZCxjQUFjLEVBQUU7YUFDaEIsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLEtBQUssSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQzthQUN6RCxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FDcEIsQ0FBQTtRQUNELE9BQU8saUJBQWlCLEtBQUssSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUE7SUFDOUMsQ0FBQztJQUVPLFlBQVk7UUFDbEIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQTtRQUN2QyxJQUFJLFFBQVEsSUFBSSxJQUFJLEVBQUU7WUFDcEIsT0FBTTtTQUNQO1FBQ0QsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUE7SUFDOUQsQ0FBQztJQUVEOzs7T0FHRztJQUNLLE9BQU87UUFDYixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFBO1FBQ3ZDLElBQUksUUFBUSxJQUFJLElBQUksRUFBRTtZQUNwQixPQUFNO1NBQ1AsQ0FBQyxnQkFBZ0I7UUFFbEIsSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxFQUFFO1lBQzdCLE9BQU07U0FDUCxDQUFDLHFDQUFxQztRQUV2QyxJQUFJLENBQUMsV0FBVyxDQUFDLG1CQUFtQixDQUFDO1lBQ25DLFlBQVksRUFBRTtnQkFDWixHQUFHLEVBQUUsSUFBSSxDQUFDLFlBQVksRUFBRTtnQkFDeEIsVUFBVSxFQUFFLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQyxXQUFXLEVBQUU7Z0JBQzlDLE9BQU8sRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQztnQkFDbkMsSUFBSSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFO2FBQzdCO1NBQ0YsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVPLFdBQVcsQ0FBQyxRQUFnQjtRQUNsQyxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUMxQyxDQUFDO0lBRUQsaUlBQWlJO0lBQzFILFFBQVE7UUFDYixJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLElBQUksSUFBSSxFQUFFO1lBQ2xDLE9BQU07U0FDUCxDQUFDLGdCQUFnQjtRQUVsQixNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLGNBQWMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxLQUFLLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQTtRQUM3RyxJQUFJLGFBQWEsRUFBRTtZQUNqQixPQUFNLENBQUMscURBQXFEO1NBQzdEO1FBRUQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLFlBQVksRUFBRSxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsWUFBWSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUE7SUFDdkYsQ0FBQztJQUVELG9JQUFvSTtJQUM3SCxRQUFRO1FBQ2IsSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxFQUFFO1lBQzdCLE9BQU07U0FDUDtRQUVELE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQTtRQUMvQixJQUFJLENBQUMsV0FBVyxDQUFDLG9CQUFvQixDQUFDO1lBQ3BDLFlBQVksRUFBRSxFQUFFLEdBQUcsRUFBRTtZQUNyQixNQUFNLEVBQUUsdUNBQXNCLENBQUMsTUFBTTtTQUN0QyxDQUFDLENBQUE7SUFDSixDQUFDO0lBRUQ7OztPQUdHO0lBQ1UsaUJBQWlCOztZQUM1QixJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLEVBQUU7Z0JBQzdCLE9BQU8sT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFBO2FBQ3pCO1lBRUQsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQTtZQUN2QyxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUE7WUFDL0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQTtZQUV6QyxNQUFNLG1CQUFtQixHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FDbEQsSUFBSSxFQUFFLHNCQUFzQjtZQUM1QixJQUFJLENBQUMsV0FBVyxDQUFDLDZCQUE2QixDQUFDO2dCQUM3QyxZQUFZLEVBQUUsRUFBRSxHQUFHLEVBQUU7Z0JBQ3JCLE1BQU0sRUFBRSx1Q0FBc0IsQ0FBQyxNQUFNO2FBQ3RDLENBQUMsQ0FDSDtpQkFDRSxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtnQkFDZCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHVCQUF1QixFQUFFLENBQUE7Z0JBQ3JELDRCQUFnQixDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsaUJBQU8sQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO2dCQUN0RSxJQUFJLENBQUMsT0FBTyxDQUFDLHVCQUF1QixDQUFDLE1BQU0sQ0FBQyxDQUFBO1lBQzlDLENBQUMsQ0FBQztpQkFDRCxLQUFLLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRTtnQkFDYixJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsRUFBRTtvQkFDbkQsV0FBVyxFQUFFLDRCQUE0QixLQUFLLEVBQUU7b0JBQ2hELE1BQU0sRUFBRSxHQUFHLENBQUMsT0FBTztpQkFDcEIsQ0FBQyxDQUFBO2dCQUNGLE9BQU07WUFDUixDQUFDLENBQUMsQ0FBQTtZQUVKLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyw4QkFBOEIsS0FBSyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsbUJBQW1CLENBQUMsQ0FBQTtZQUM5RyxPQUFPLGNBQWMsSUFBSSxtQkFBbUIsQ0FBQTtRQUM5QyxDQUFDO0tBQUE7SUFFRDs7OztPQUlHO0lBQ0ksT0FBTztRQUNaLElBQUksQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsRUFBRTtZQUM3QixPQUFNO1NBQ1A7UUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUE7UUFDL0IsTUFBTSxtQkFBbUIsR0FBRztZQUMxQixZQUFZLEVBQUUsRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLEVBQUU7U0FDekIsQ0FBQTtRQUM5QixJQUFJLE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRTtZQUN0RixtQkFBbUIsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQTtTQUNsRDtRQUNELElBQUksQ0FBQyxXQUFXLENBQUMsbUJBQW1CLENBQUMsbUJBQW1CLENBQUMsQ0FBQTtRQUN6RCxJQUFJLElBQUksQ0FBQywwQkFBMEIsRUFBRTtZQUNuQyxJQUFJLENBQUMsV0FBVyxDQUFDLHFCQUFxQixDQUFDO2dCQUNyQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsK0JBQWMsQ0FBQyxPQUFPLEVBQUUsQ0FBQzthQUNqRCxDQUFDLENBQUE7U0FDSDtJQUNILENBQUM7SUFFTSxTQUFTO1FBQ2QsSUFBSSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxFQUFFO1lBQzdCLE9BQU07U0FDUDtRQUVELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUE7UUFDL0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUE7UUFDdEMsSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUNYLE9BQU0sQ0FBQyxnQ0FBZ0M7U0FDeEM7UUFFRCxJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsU0FBUyxLQUFLLEtBQUssRUFBRTtZQUMxQyxJQUFJLENBQUMsV0FBVyxDQUFDLG9CQUFvQixDQUFDLEVBQUUsWUFBWSxFQUFFLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQTtTQUN6RTtRQUVELElBQUksSUFBSSxDQUFDLDBCQUEwQixFQUFFO1lBQ25DLElBQUksQ0FBQyxXQUFXLENBQUMscUJBQXFCLENBQUM7Z0JBQ3JDLE9BQU8sRUFBRTtvQkFDUCxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLCtCQUFjLENBQUMsT0FBTyxFQUFFO29CQUM3QyxFQUFFLEdBQUcsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSwrQkFBYyxDQUFDLE9BQU8sRUFBRTtpQkFDeEQ7YUFDRixDQUFDLENBQUE7U0FDSDtRQUVELDRFQUE0RTtRQUM1RSxhQUFhO1FBQ2IsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsS0FBSyxLQUFLLEVBQUU7WUFDMUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFBO1NBQ2Y7SUFDSCxDQUFDO0lBRUQsNEVBQTRFO0lBQ3JFLFlBQVk7UUFDakIsT0FBTyxpQkFBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBQ3hELENBQUM7Q0FDRjtBQXpTRCxzREF5U0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQ29udmVydCBmcm9tIFwiLi4vY29udmVydFwiXG5pbXBvcnQge1xuICBMYW5ndWFnZUNsaWVudENvbm5lY3Rpb24sXG4gIEZpbGVDaGFuZ2VUeXBlLFxuICBUZXh0RG9jdW1lbnRTYXZlUmVhc29uLFxuICBUZXh0RG9jdW1lbnRTeW5jS2luZCxcbiAgVGV4dERvY3VtZW50U3luY09wdGlvbnMsXG4gIFRleHREb2N1bWVudENvbnRlbnRDaGFuZ2VFdmVudCxcbiAgVmVyc2lvbmVkVGV4dERvY3VtZW50SWRlbnRpZmllcixcbiAgU2VydmVyQ2FwYWJpbGl0aWVzLFxuICBEaWRTYXZlVGV4dERvY3VtZW50UGFyYW1zLFxufSBmcm9tIFwiLi4vbGFuZ3VhZ2VjbGllbnRcIlxuaW1wb3J0IEFwcGx5RWRpdEFkYXB0ZXIgZnJvbSBcIi4vYXBwbHktZWRpdC1hZGFwdGVyXCJcbmltcG9ydCB7IENvbXBvc2l0ZURpc3Bvc2FibGUsIERpc3Bvc2FibGUsIFRleHRFZGl0b3IsIEJ1ZmZlclN0b3BwZWRDaGFuZ2luZ0V2ZW50LCBUZXh0Q2hhbmdlIH0gZnJvbSBcImF0b21cIlxuaW1wb3J0ICogYXMgVXRpbHMgZnJvbSBcIi4uL3V0aWxzXCJcblxuLyoqXG4gKiBQdWJsaWM6IFN5bmNocm9uaXplcyB0aGUgZG9jdW1lbnRzIGJldHdlZW4gQXRvbSBhbmQgdGhlIGxhbmd1YWdlIHNlcnZlciBieSBub3RpZnlpbmcgZWFjaCBlbmQgb2YgY2hhbmdlcywgb3BlbmluZyxcbiAqIGNsb3NpbmcgYW5kIG90aGVyIGV2ZW50cyBhcyB3ZWxsIGFzIHNlbmRpbmcgYW5kIGFwcGx5aW5nIGNoYW5nZXMgZWl0aGVyIGluIHdob2xlIG9yIGluIHBhcnQgZGVwZW5kaW5nIG9uIHdoYXQgdGhlXG4gKiBsYW5ndWFnZSBzZXJ2ZXIgc3VwcG9ydHMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIERvY3VtZW50U3luY0FkYXB0ZXIge1xuICBwcml2YXRlIF9kaXNwb3NhYmxlID0gbmV3IENvbXBvc2l0ZURpc3Bvc2FibGUoKVxuICBwdWJsaWMgX2RvY3VtZW50U3luYzogVGV4dERvY3VtZW50U3luY09wdGlvbnNcbiAgcHJpdmF0ZSBfZWRpdG9yczogV2Vha01hcDxUZXh0RWRpdG9yLCBUZXh0RWRpdG9yU3luY0FkYXB0ZXI+ID0gbmV3IFdlYWtNYXAoKVxuICBwcml2YXRlIF92ZXJzaW9uczogTWFwPHN0cmluZywgbnVtYmVyPiA9IG5ldyBNYXAoKVxuXG4gIC8qKlxuICAgKiBQdWJsaWM6IERldGVybWluZSB3aGV0aGVyIHRoaXMgYWRhcHRlciBjYW4gYmUgdXNlZCB0byBhZGFwdCBhIGxhbmd1YWdlIHNlcnZlciBiYXNlZCBvbiB0aGUgc2VydmVyQ2FwYWJpbGl0aWVzXG4gICAqIG1hdHJpeCB0ZXh0RG9jdW1lbnRTeW5jIGNhcGFiaWxpdHkgZWl0aGVyIGJlaW5nIEZ1bGwgb3IgSW5jcmVtZW50YWwuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJ2ZXJDYXBhYmlsaXRpZXMgVGhlIHtTZXJ2ZXJDYXBhYmlsaXRpZXN9IG9mIHRoZSBsYW5ndWFnZSBzZXJ2ZXIgdG8gY29uc2lkZXIuXG4gICAqIEByZXR1cm5zIEEge0Jvb2xlYW59IGluZGljYXRpbmcgYWRhcHRlciBjYW4gYWRhcHQgdGhlIHNlcnZlciBiYXNlZCBvbiB0aGUgZ2l2ZW4gc2VydmVyQ2FwYWJpbGl0aWVzLlxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBjYW5BZGFwdChzZXJ2ZXJDYXBhYmlsaXRpZXM6IFNlcnZlckNhcGFiaWxpdGllcyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNhbkFkYXB0VjIoc2VydmVyQ2FwYWJpbGl0aWVzKSB8fCB0aGlzLmNhbkFkYXB0VjMoc2VydmVyQ2FwYWJpbGl0aWVzKVxuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY2FuQWRhcHRWMihzZXJ2ZXJDYXBhYmlsaXRpZXM6IFNlcnZlckNhcGFiaWxpdGllcyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICBzZXJ2ZXJDYXBhYmlsaXRpZXMudGV4dERvY3VtZW50U3luYyA9PT0gVGV4dERvY3VtZW50U3luY0tpbmQuSW5jcmVtZW50YWwgfHxcbiAgICAgIHNlcnZlckNhcGFiaWxpdGllcy50ZXh0RG9jdW1lbnRTeW5jID09PSBUZXh0RG9jdW1lbnRTeW5jS2luZC5GdWxsXG4gICAgKVxuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgY2FuQWRhcHRWMyhzZXJ2ZXJDYXBhYmlsaXRpZXM6IFNlcnZlckNhcGFiaWxpdGllcyk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IG9wdGlvbnMgPSBzZXJ2ZXJDYXBhYmlsaXRpZXMudGV4dERvY3VtZW50U3luY1xuICAgIHJldHVybiAoXG4gICAgICBvcHRpb25zICE9PSBudWxsICYmXG4gICAgICB0eXBlb2Ygb3B0aW9ucyA9PT0gXCJvYmplY3RcIiAmJlxuICAgICAgKG9wdGlvbnMuY2hhbmdlID09PSBUZXh0RG9jdW1lbnRTeW5jS2luZC5JbmNyZW1lbnRhbCB8fCBvcHRpb25zLmNoYW5nZSA9PT0gVGV4dERvY3VtZW50U3luY0tpbmQuRnVsbClcbiAgICApXG4gIH1cblxuICAvKipcbiAgICogUHVibGljOiBDcmVhdGUgYSBuZXcge0RvY3VtZW50U3luY0FkYXB0ZXJ9IGZvciB0aGUgZ2l2ZW4gbGFuZ3VhZ2Ugc2VydmVyLlxuICAgKlxuICAgKiBAcGFyYW0gX2Nvbm5lY3Rpb24gQSB7TGFuZ3VhZ2VDbGllbnRDb25uZWN0aW9ufSB0byB0aGUgbGFuZ3VhZ2Ugc2VydmVyIHRvIGJlIGtlcHQgaW4gc3luYy5cbiAgICogQHBhcmFtIGRvY3VtZW50U3luYyBUaGUgZG9jdW1lbnQgc3luY2luZyBvcHRpb25zLlxuICAgKiBAcGFyYW0gX2VkaXRvclNlbGVjdG9yIEEgcHJlZGljYXRlIGZ1bmN0aW9uIHRoYXQgdGFrZXMgYSB7VGV4dEVkaXRvcn0gYW5kIHJldHVybnMgYSB7Ym9vbGVhbn0gaW5kaWNhdGluZyB3aGV0aGVyXG4gICAqICAgdGhpcyBhZGFwdGVyIHNob3VsZCBjYXJlIGFib3V0IHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLlxuICAgKiBAcGFyYW0gX2dldExhbmd1YWdlSWRGcm9tRWRpdG9yIEEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEge3N0cmluZ30gb2YgYGxhbmd1YWdlSWRgIHVzZWQgZm9yIGB0ZXh0RG9jdW1lbnQvZGlkT3BlbmBcbiAgICogICBub3RpZmljYXRpb24uXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIF9jb25uZWN0aW9uOiBMYW5ndWFnZUNsaWVudENvbm5lY3Rpb24sXG4gICAgcHJpdmF0ZSBfZWRpdG9yU2VsZWN0b3I6IChlZGl0b3I6IFRleHRFZGl0b3IpID0+IGJvb2xlYW4sXG4gICAgZG9jdW1lbnRTeW5jOiBUZXh0RG9jdW1lbnRTeW5jT3B0aW9ucyB8IFRleHREb2N1bWVudFN5bmNLaW5kIHwgdW5kZWZpbmVkLFxuICAgIHByaXZhdGUgX3JlcG9ydEJ1c3lXaGlsZTogVXRpbHMuUmVwb3J0QnVzeVdoaWxlLFxuICAgIHByaXZhdGUgX2dldExhbmd1YWdlSWRGcm9tRWRpdG9yOiAoZWRpdG9yOiBUZXh0RWRpdG9yKSA9PiBzdHJpbmdcbiAgKSB7XG4gICAgaWYgKHR5cGVvZiBkb2N1bWVudFN5bmMgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgIHRoaXMuX2RvY3VtZW50U3luYyA9IGRvY3VtZW50U3luY1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9kb2N1bWVudFN5bmMgPSB7XG4gICAgICAgIGNoYW5nZTogZG9jdW1lbnRTeW5jIHx8IFRleHREb2N1bWVudFN5bmNLaW5kLkZ1bGwsXG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuX2Rpc3Bvc2FibGUuYWRkKGF0b20udGV4dEVkaXRvcnMub2JzZXJ2ZSh0aGlzLm9ic2VydmVUZXh0RWRpdG9yLmJpbmQodGhpcykpKVxuICB9XG5cbiAgLyoqIERpc3Bvc2UgdGhpcyBhZGFwdGVyIGVuc3VyaW5nIGFueSByZXNvdXJjZXMgYXJlIGZyZWVkIGFuZCBldmVudHMgdW5ob29rZWQuICovXG4gIHB1YmxpYyBkaXNwb3NlKCk6IHZvaWQge1xuICAgIHRoaXMuX2Rpc3Bvc2FibGUuZGlzcG9zZSgpXG4gIH1cblxuICAvKipcbiAgICogRXhhbWluZSBhIHtUZXh0RWRpdG9yfSBhbmQgZGVjaWRlIGlmIHdlIHdpc2ggdG8gb2JzZXJ2ZSBpdC4gSWYgc28gZW5zdXJlIHRoYXQgd2Ugc3RvcCBvYnNlcnZpbmcgaXQgd2hlbiBpdCBpc1xuICAgKiBjbG9zZWQgb3Igb3RoZXJ3aXNlIGRlc3Ryb3llZC5cbiAgICpcbiAgICogQHBhcmFtIGVkaXRvciBBIHtUZXh0RWRpdG9yfSB0byBjb25zaWRlciBmb3Igb2JzZXJ2YXRpb24uXG4gICAqL1xuICBwdWJsaWMgb2JzZXJ2ZVRleHRFZGl0b3IoZWRpdG9yOiBUZXh0RWRpdG9yKTogdm9pZCB7XG4gICAgY29uc3QgbGlzdGVuZXIgPSBlZGl0b3Iub2JzZXJ2ZUdyYW1tYXIoKF9ncmFtbWFyKSA9PiB0aGlzLl9oYW5kbGVHcmFtbWFyQ2hhbmdlKGVkaXRvcikpXG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoXG4gICAgICBlZGl0b3Iub25EaWREZXN0cm95KCgpID0+IHtcbiAgICAgICAgdGhpcy5fZGlzcG9zYWJsZS5yZW1vdmUobGlzdGVuZXIpXG4gICAgICAgIGxpc3RlbmVyLmRpc3Bvc2UoKVxuICAgICAgfSlcbiAgICApXG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQobGlzdGVuZXIpXG4gICAgaWYgKCF0aGlzLl9lZGl0b3JzLmhhcyhlZGl0b3IpICYmIHRoaXMuX2VkaXRvclNlbGVjdG9yKGVkaXRvcikpIHtcbiAgICAgIHRoaXMuX2hhbmRsZU5ld0VkaXRvcihlZGl0b3IpXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfaGFuZGxlR3JhbW1hckNoYW5nZShlZGl0b3I6IFRleHRFZGl0b3IpOiB2b2lkIHtcbiAgICBjb25zdCBzeW5jID0gdGhpcy5fZWRpdG9ycy5nZXQoZWRpdG9yKVxuICAgIGlmIChzeW5jICE9IG51bGwgJiYgIXRoaXMuX2VkaXRvclNlbGVjdG9yKGVkaXRvcikpIHtcbiAgICAgIHRoaXMuX2VkaXRvcnMuZGVsZXRlKGVkaXRvcilcbiAgICAgIHRoaXMuX2Rpc3Bvc2FibGUucmVtb3ZlKHN5bmMpXG4gICAgICBzeW5jLmRpZENsb3NlKClcbiAgICAgIHN5bmMuZGlzcG9zZSgpXG4gICAgfSBlbHNlIGlmIChzeW5jID09IG51bGwgJiYgdGhpcy5fZWRpdG9yU2VsZWN0b3IoZWRpdG9yKSkge1xuICAgICAgdGhpcy5faGFuZGxlTmV3RWRpdG9yKGVkaXRvcilcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9oYW5kbGVOZXdFZGl0b3IoZWRpdG9yOiBUZXh0RWRpdG9yKTogdm9pZCB7XG4gICAgY29uc3Qgc3luYyA9IG5ldyBUZXh0RWRpdG9yU3luY0FkYXB0ZXIoXG4gICAgICBlZGl0b3IsXG4gICAgICB0aGlzLl9jb25uZWN0aW9uLFxuICAgICAgdGhpcy5fZG9jdW1lbnRTeW5jLFxuICAgICAgdGhpcy5fdmVyc2lvbnMsXG4gICAgICB0aGlzLl9yZXBvcnRCdXN5V2hpbGUsXG4gICAgICB0aGlzLl9nZXRMYW5ndWFnZUlkRnJvbUVkaXRvclxuICAgIClcbiAgICB0aGlzLl9lZGl0b3JzLnNldChlZGl0b3IsIHN5bmMpXG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoc3luYylcbiAgICB0aGlzLl9kaXNwb3NhYmxlLmFkZChcbiAgICAgIGVkaXRvci5vbkRpZERlc3Ryb3koKCkgPT4ge1xuICAgICAgICBjb25zdCBkZXN0cm95ZWRTeW5jID0gdGhpcy5fZWRpdG9ycy5nZXQoZWRpdG9yKVxuICAgICAgICBpZiAoZGVzdHJveWVkU3luYykge1xuICAgICAgICAgIHRoaXMuX2VkaXRvcnMuZGVsZXRlKGVkaXRvcilcbiAgICAgICAgICB0aGlzLl9kaXNwb3NhYmxlLnJlbW92ZShkZXN0cm95ZWRTeW5jKVxuICAgICAgICAgIGRlc3Ryb3llZFN5bmMuZGlzcG9zZSgpXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgKVxuICB9XG5cbiAgcHVibGljIGdldEVkaXRvclN5bmNBZGFwdGVyKGVkaXRvcjogVGV4dEVkaXRvcik6IFRleHRFZGl0b3JTeW5jQWRhcHRlciB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuX2VkaXRvcnMuZ2V0KGVkaXRvcilcbiAgfVxufVxuXG4vKiogUHVibGljOiBLZWVwIGEgc2luZ2xlIHtUZXh0RWRpdG9yfSBpbiBzeW5jIHdpdGggYSBnaXZlbiBsYW5ndWFnZSBzZXJ2ZXIuICovXG5leHBvcnQgY2xhc3MgVGV4dEVkaXRvclN5bmNBZGFwdGVyIHtcbiAgcHJpdmF0ZSBfZGlzcG9zYWJsZSA9IG5ldyBDb21wb3NpdGVEaXNwb3NhYmxlKClcbiAgcHJpdmF0ZSBfY3VycmVudFVyaTogc3RyaW5nXG4gIHByaXZhdGUgX2Zha2VEaWRDaGFuZ2VXYXRjaGVkRmlsZXM6IGJvb2xlYW5cblxuICAvKipcbiAgICogUHVibGljOiBDcmVhdGUgYSB7VGV4dEVkaXRvclN5bmNBZGFwdGVyfSBpbiBzeW5jIHdpdGggYSBnaXZlbiBsYW5ndWFnZSBzZXJ2ZXIuXG4gICAqXG4gICAqIEBwYXJhbSBfZWRpdG9yIEEge1RleHRFZGl0b3J9IHRvIGtlZXAgaW4gc3luYy5cbiAgICogQHBhcmFtIF9jb25uZWN0aW9uIEEge0xhbmd1YWdlQ2xpZW50Q29ubmVjdGlvbn0gdG8gYSBsYW5ndWFnZSBzZXJ2ZXIgdG8ga2VlcCBpbiBzeW5jLlxuICAgKiBAcGFyYW0gX2RvY3VtZW50U3luYyBUaGUgZG9jdW1lbnQgc3luY2luZyBvcHRpb25zLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBfZWRpdG9yOiBUZXh0RWRpdG9yLFxuICAgIHByaXZhdGUgX2Nvbm5lY3Rpb246IExhbmd1YWdlQ2xpZW50Q29ubmVjdGlvbixcbiAgICBwcml2YXRlIF9kb2N1bWVudFN5bmM6IFRleHREb2N1bWVudFN5bmNPcHRpb25zLFxuICAgIHByaXZhdGUgX3ZlcnNpb25zOiBNYXA8c3RyaW5nLCBudW1iZXI+LFxuICAgIHByaXZhdGUgX3JlcG9ydEJ1c3lXaGlsZTogVXRpbHMuUmVwb3J0QnVzeVdoaWxlLFxuICAgIHByaXZhdGUgX2dldExhbmd1YWdlSWRGcm9tRWRpdG9yOiAoZWRpdG9yOiBUZXh0RWRpdG9yKSA9PiBzdHJpbmdcbiAgKSB7XG4gICAgdGhpcy5fZmFrZURpZENoYW5nZVdhdGNoZWRGaWxlcyA9IGF0b20ucHJvamVjdC5vbkRpZENoYW5nZUZpbGVzID09IG51bGxcblxuICAgIGNvbnN0IGNoYW5nZVRyYWNraW5nID0gdGhpcy5zZXR1cENoYW5nZVRyYWNraW5nKF9kb2N1bWVudFN5bmMpXG4gICAgaWYgKGNoYW5nZVRyYWNraW5nICE9IG51bGwpIHtcbiAgICAgIHRoaXMuX2Rpc3Bvc2FibGUuYWRkKGNoYW5nZVRyYWNraW5nKVxuICAgIH1cblxuICAgIC8vIFRoZXNlIGhhbmRsZXJzIGFyZSBhdHRhY2hlZCBvbmx5IGlmIHNlcnZlciBzdXBwb3J0cyB0aGVtXG4gICAgaWYgKF9kb2N1bWVudFN5bmMud2lsbFNhdmUpIHtcbiAgICAgIHRoaXMuX2Rpc3Bvc2FibGUuYWRkKF9lZGl0b3IuZ2V0QnVmZmVyKCkub25XaWxsU2F2ZSh0aGlzLndpbGxTYXZlLmJpbmQodGhpcykpKVxuICAgIH1cbiAgICBpZiAoX2RvY3VtZW50U3luYy53aWxsU2F2ZVdhaXRVbnRpbCkge1xuICAgICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoX2VkaXRvci5nZXRCdWZmZXIoKS5vbldpbGxTYXZlKHRoaXMud2lsbFNhdmVXYWl0VW50aWwuYmluZCh0aGlzKSkpXG4gICAgfVxuICAgIC8vIFNlbmQgY2xvc2Ugbm90aWZpY2F0aW9ucyB1bmxlc3MgaXQncyBleHBsaWNpdGx5IGRpc2FibGVkXG4gICAgaWYgKF9kb2N1bWVudFN5bmMub3BlbkNsb3NlICE9PSBmYWxzZSkge1xuICAgICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoX2VkaXRvci5vbkRpZERlc3Ryb3kodGhpcy5kaWRDbG9zZS5iaW5kKHRoaXMpKSlcbiAgICB9XG4gICAgdGhpcy5fZGlzcG9zYWJsZS5hZGQoX2VkaXRvci5vbkRpZFNhdmUodGhpcy5kaWRTYXZlLmJpbmQodGhpcykpLCBfZWRpdG9yLm9uRGlkQ2hhbmdlUGF0aCh0aGlzLmRpZFJlbmFtZS5iaW5kKHRoaXMpKSlcblxuICAgIHRoaXMuX2N1cnJlbnRVcmkgPSB0aGlzLmdldEVkaXRvclVyaSgpXG5cbiAgICBpZiAoX2RvY3VtZW50U3luYy5vcGVuQ2xvc2UgIT09IGZhbHNlKSB7XG4gICAgICB0aGlzLmRpZE9wZW4oKVxuICAgIH1cbiAgfVxuXG4gIC8qKiBUaGUgY2hhbmdlIHRyYWNraW5nIGRpc3Bvc2FibGUgbGlzdGVuZXIgdGhhdCB3aWxsIGVuc3VyZSB0aGF0IGNoYW5nZXMgYXJlIHNlbnQgdG8gdGhlIGxhbmd1YWdlIHNlcnZlciBhcyBhcHByb3ByaWF0ZS4gKi9cbiAgcHVibGljIHNldHVwQ2hhbmdlVHJhY2tpbmcoZG9jdW1lbnRTeW5jOiBUZXh0RG9jdW1lbnRTeW5jT3B0aW9ucyk6IERpc3Bvc2FibGUgfCBudWxsIHtcbiAgICBzd2l0Y2ggKGRvY3VtZW50U3luYy5jaGFuZ2UpIHtcbiAgICAgIGNhc2UgVGV4dERvY3VtZW50U3luY0tpbmQuRnVsbDpcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VkaXRvci5vbkRpZENoYW5nZSh0aGlzLnNlbmRGdWxsQ2hhbmdlcy5iaW5kKHRoaXMpKVxuICAgICAgY2FzZSBUZXh0RG9jdW1lbnRTeW5jS2luZC5JbmNyZW1lbnRhbDpcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VkaXRvci5nZXRCdWZmZXIoKS5vbkRpZENoYW5nZVRleHQodGhpcy5zZW5kSW5jcmVtZW50YWxDaGFuZ2VzLmJpbmQodGhpcykpXG4gICAgfVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvKiogRGlzcG9zZSB0aGlzIGFkYXB0ZXIgZW5zdXJpbmcgYW55IHJlc291cmNlcyBhcmUgZnJlZWQgYW5kIGV2ZW50cyB1bmhvb2tlZC4gKi9cbiAgcHVibGljIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5fZGlzcG9zYWJsZS5kaXNwb3NlKClcbiAgfVxuXG4gIC8qKiBHZXQgdGhlIGxhbmd1YWdlSWQgZmllbGQgdGhhdCB3aWxsIGJlIHNlbnQgdG8gdGhlIGxhbmd1YWdlIHNlcnZlciBieSBzaW1wbHkgdXNpbmcgdGhlIGBfZ2V0TGFuZ3VhZ2VJZEZyb21FZGl0b3JgLiAqL1xuICBwdWJsaWMgZ2V0TGFuZ3VhZ2VJZCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9nZXRMYW5ndWFnZUlkRnJvbUVkaXRvcih0aGlzLl9lZGl0b3IpXG4gIH1cblxuICAvKipcbiAgICogUHVibGljOiBDcmVhdGUgYSB7VmVyc2lvbmVkVGV4dERvY3VtZW50SWRlbnRpZmllcn0gZm9yIHRoZSBkb2N1bWVudCBvYnNlcnZlZCBieSB0aGlzIGFkYXB0ZXIgaW5jbHVkaW5nIGJvdGggdGhlIFVyaVxuICAgKiBhbmQgdGhlIGN1cnJlbnQgVmVyc2lvbi5cbiAgICovXG4gIHB1YmxpYyBnZXRWZXJzaW9uZWRUZXh0RG9jdW1lbnRJZGVudGlmaWVyKCk6IFZlcnNpb25lZFRleHREb2N1bWVudElkZW50aWZpZXIge1xuICAgIHJldHVybiB7XG4gICAgICB1cmk6IHRoaXMuZ2V0RWRpdG9yVXJpKCksXG4gICAgICB2ZXJzaW9uOiB0aGlzLl9nZXRWZXJzaW9uKHRoaXMuX2VkaXRvci5nZXRQYXRoKCkgfHwgXCJcIiksXG4gICAgfVxuICB9XG5cbiAgLyoqIFB1YmxpYzogU2VuZCB0aGUgZW50aXJlIGRvY3VtZW50IHRvIHRoZSBsYW5ndWFnZSBzZXJ2ZXIuIFRoaXMgaXMgdXNlZCB3aGVuIG9wZXJhdGluZyBpbiBGdWxsICgxKSBzeW5jIG1vZGUuICovXG4gIHB1YmxpYyBzZW5kRnVsbENoYW5nZXMoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLl9pc1ByaW1hcnlBZGFwdGVyKCkpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTXVsdGlwbGUgZWRpdG9ycywgd2UgYXJlIG5vdCBmaXJzdFxuXG4gICAgdGhpcy5fYnVtcFZlcnNpb24oKVxuICAgIHRoaXMuX2Nvbm5lY3Rpb24uZGlkQ2hhbmdlVGV4dERvY3VtZW50KHtcbiAgICAgIHRleHREb2N1bWVudDogdGhpcy5nZXRWZXJzaW9uZWRUZXh0RG9jdW1lbnRJZGVudGlmaWVyKCksXG4gICAgICBjb250ZW50Q2hhbmdlczogW3sgdGV4dDogdGhpcy5fZWRpdG9yLmdldFRleHQoKSB9XSxcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFB1YmxpYzogU2VuZCB0aGUgaW5jcmVtZW50YWwgdGV4dCBjaGFuZ2VzIHRvIHRoZSBsYW5ndWFnZSBzZXJ2ZXIuIFRoaXMgaXMgdXNlZCB3aGVuIG9wZXJhdGluZyBpbiBJbmNyZW1lbnRhbCAoMikgc3luYyBtb2RlLlxuICAgKlxuICAgKiBAcGFyYW0gZXZlbnQgVGhlIGV2ZW50IGZpcmVkIGJ5IEF0b20gdG8gaW5kaWNhdGUgdGhlIGRvY3VtZW50IGhhcyBzdG9wcGVkIGNoYW5naW5nIGluY2x1ZGluZyBhIGxpc3Qgb2YgY2hhbmdlc1xuICAgKiAgIHNpbmNlIHRoZSBsYXN0IHRpbWUgdGhpcyBldmVudCBmaXJlZCBmb3IgdGhpcyB0ZXh0IGVkaXRvci4gTk9URTogVGhlIG9yZGVyIG9mIGNoYW5nZXMgaW4gdGhlIGV2ZW50IGlzIGd1YXJhbnRlZWRcbiAgICogICB0b3AgdG8gYm90dG9tLiBMYW5ndWFnZSBzZXJ2ZXIgZXhwZWN0cyB0aGlzIGluIHJldmVyc2UuXG4gICAqL1xuICBwdWJsaWMgc2VuZEluY3JlbWVudGFsQ2hhbmdlcyhldmVudDogQnVmZmVyU3RvcHBlZENoYW5naW5nRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoZXZlbnQuY2hhbmdlcy5sZW5ndGggPiAwKSB7XG4gICAgICBpZiAoIXRoaXMuX2lzUHJpbWFyeUFkYXB0ZXIoKSkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH0gLy8gTXVsdGlwbGUgZWRpdG9ycywgd2UgYXJlIG5vdCBmaXJzdFxuXG4gICAgICB0aGlzLl9idW1wVmVyc2lvbigpXG4gICAgICB0aGlzLl9jb25uZWN0aW9uLmRpZENoYW5nZVRleHREb2N1bWVudCh7XG4gICAgICAgIHRleHREb2N1bWVudDogdGhpcy5nZXRWZXJzaW9uZWRUZXh0RG9jdW1lbnRJZGVudGlmaWVyKCksXG4gICAgICAgIGNvbnRlbnRDaGFuZ2VzOiBldmVudC5jaGFuZ2VzLm1hcChUZXh0RWRpdG9yU3luY0FkYXB0ZXIudGV4dEVkaXRUb0NvbnRlbnRDaGFuZ2UpLnJldmVyc2UoKSxcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFB1YmxpYzogQ29udmVydCBhbiBBdG9tIHtUZXh0RWRpdEV2ZW50fSB0byBhIGxhbmd1YWdlIHNlcnZlciB7VGV4dERvY3VtZW50Q29udGVudENoYW5nZUV2ZW50fSBvYmplY3QuXG4gICAqXG4gICAqIEBwYXJhbSBjaGFuZ2UgVGhlIEF0b20ge1RleHRFZGl0RXZlbnR9IHRvIGNvbnZlcnQuXG4gICAqIEByZXR1cm5zIEEge1RleHREb2N1bWVudENvbnRlbnRDaGFuZ2VFdmVudH0gdGhhdCByZXByZXNlbnRzIHRoZSBjb252ZXJ0ZWQge1RleHRFZGl0RXZlbnR9LlxuICAgKi9cbiAgcHVibGljIHN0YXRpYyB0ZXh0RWRpdFRvQ29udGVudENoYW5nZShjaGFuZ2U6IFRleHRDaGFuZ2UpOiBUZXh0RG9jdW1lbnRDb250ZW50Q2hhbmdlRXZlbnQge1xuICAgIHJldHVybiB7XG4gICAgICByYW5nZTogQ29udmVydC5hdG9tUmFuZ2VUb0xTUmFuZ2UoY2hhbmdlLm9sZFJhbmdlKSxcbiAgICAgIHJhbmdlTGVuZ3RoOiBjaGFuZ2Uub2xkVGV4dC5sZW5ndGgsXG4gICAgICB0ZXh0OiBjaGFuZ2UubmV3VGV4dCxcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9pc1ByaW1hcnlBZGFwdGVyKCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGxvd2VzdElkRm9yQnVmZmVyID0gTWF0aC5taW4oXG4gICAgICAuLi5hdG9tLndvcmtzcGFjZVxuICAgICAgICAuZ2V0VGV4dEVkaXRvcnMoKVxuICAgICAgICAuZmlsdGVyKCh0KSA9PiB0LmdldEJ1ZmZlcigpID09PSB0aGlzLl9lZGl0b3IuZ2V0QnVmZmVyKCkpXG4gICAgICAgIC5tYXAoKHQpID0+IHQuaWQpXG4gICAgKVxuICAgIHJldHVybiBsb3dlc3RJZEZvckJ1ZmZlciA9PT0gdGhpcy5fZWRpdG9yLmlkXG4gIH1cblxuICBwcml2YXRlIF9idW1wVmVyc2lvbigpOiB2b2lkIHtcbiAgICBjb25zdCBmaWxlUGF0aCA9IHRoaXMuX2VkaXRvci5nZXRQYXRoKClcbiAgICBpZiAoZmlsZVBhdGggPT0gbnVsbCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIHRoaXMuX3ZlcnNpb25zLnNldChmaWxlUGF0aCwgdGhpcy5fZ2V0VmVyc2lvbihmaWxlUGF0aCkgKyAxKVxuICB9XG5cbiAgLyoqXG4gICAqIEVuc3VyZSB3aGVuIHRoZSBkb2N1bWVudCBpcyBvcGVuZWQgd2Ugc2VuZCBub3RpZmljYXRpb24gdG8gdGhlIGxhbmd1YWdlIHNlcnZlciBzbyBpdCBjYW4gbG9hZCBpdCBpbiBhbmQga2VlcCB0cmFja1xuICAgKiBvZiBkaWFnbm9zdGljcyBldGMuXG4gICAqL1xuICBwcml2YXRlIGRpZE9wZW4oKTogdm9pZCB7XG4gICAgY29uc3QgZmlsZVBhdGggPSB0aGlzLl9lZGl0b3IuZ2V0UGF0aCgpXG4gICAgaWYgKGZpbGVQYXRoID09IG51bGwpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTm90IHlldCBzYXZlZFxuXG4gICAgaWYgKCF0aGlzLl9pc1ByaW1hcnlBZGFwdGVyKCkpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTXVsdGlwbGUgZWRpdG9ycywgd2UgYXJlIG5vdCBmaXJzdFxuXG4gICAgdGhpcy5fY29ubmVjdGlvbi5kaWRPcGVuVGV4dERvY3VtZW50KHtcbiAgICAgIHRleHREb2N1bWVudDoge1xuICAgICAgICB1cmk6IHRoaXMuZ2V0RWRpdG9yVXJpKCksXG4gICAgICAgIGxhbmd1YWdlSWQ6IHRoaXMuZ2V0TGFuZ3VhZ2VJZCgpLnRvTG93ZXJDYXNlKCksXG4gICAgICAgIHZlcnNpb246IHRoaXMuX2dldFZlcnNpb24oZmlsZVBhdGgpLFxuICAgICAgICB0ZXh0OiB0aGlzLl9lZGl0b3IuZ2V0VGV4dCgpLFxuICAgICAgfSxcbiAgICB9KVxuICB9XG5cbiAgcHJpdmF0ZSBfZ2V0VmVyc2lvbihmaWxlUGF0aDogc3RyaW5nKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fdmVyc2lvbnMuZ2V0KGZpbGVQYXRoKSB8fCAxXG4gIH1cblxuICAvKiogQ2FsbGVkIHdoZW4gdGhlIHtUZXh0RWRpdG9yfSBpcyBjbG9zZWQgYW5kIHNlbmRzIHRoZSAnZGlkQ2xvc2VUZXh0RG9jdW1lbnQnIG5vdGlmaWNhdGlvbiB0byB0aGUgY29ubmVjdGVkIGxhbmd1YWdlIHNlcnZlci4gKi9cbiAgcHVibGljIGRpZENsb3NlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLl9lZGl0b3IuZ2V0UGF0aCgpID09IG51bGwpIHtcbiAgICAgIHJldHVyblxuICAgIH0gLy8gTm90IHlldCBzYXZlZFxuXG4gICAgY29uc3QgZmlsZVN0aWxsT3BlbiA9IGF0b20ud29ya3NwYWNlLmdldFRleHRFZGl0b3JzKCkuZmluZCgodCkgPT4gdC5nZXRCdWZmZXIoKSA9PT0gdGhpcy5fZWRpdG9yLmdldEJ1ZmZlcigpKVxuICAgIGlmIChmaWxlU3RpbGxPcGVuKSB7XG4gICAgICByZXR1cm4gLy8gT3RoZXIgd2luZG93cyBvciBlZGl0b3JzIHN0aWxsIGhhdmUgdGhpcyBmaWxlIG9wZW5cbiAgICB9XG5cbiAgICB0aGlzLl9jb25uZWN0aW9uLmRpZENsb3NlVGV4dERvY3VtZW50KHsgdGV4dERvY3VtZW50OiB7IHVyaTogdGhpcy5nZXRFZGl0b3JVcmkoKSB9IH0pXG4gIH1cblxuICAvKiogQ2FsbGVkIGp1c3QgYmVmb3JlIHRoZSB7VGV4dEVkaXRvcn0gc2F2ZXMgYW5kIHNlbmRzIHRoZSAnd2lsbFNhdmVUZXh0RG9jdW1lbnQnIG5vdGlmaWNhdGlvbiB0byB0aGUgY29ubmVjdGVkIGxhbmd1YWdlIHNlcnZlci4gKi9cbiAgcHVibGljIHdpbGxTYXZlKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5faXNQcmltYXJ5QWRhcHRlcigpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCB1cmkgPSB0aGlzLmdldEVkaXRvclVyaSgpXG4gICAgdGhpcy5fY29ubmVjdGlvbi53aWxsU2F2ZVRleHREb2N1bWVudCh7XG4gICAgICB0ZXh0RG9jdW1lbnQ6IHsgdXJpIH0sXG4gICAgICByZWFzb246IFRleHREb2N1bWVudFNhdmVSZWFzb24uTWFudWFsLFxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQ2FsbGVkIGp1c3QgYmVmb3JlIHRoZSB7VGV4dEVkaXRvcn0gc2F2ZXMsIHNlbmRzIHRoZSAnd2lsbFNhdmVXYWl0VW50aWxUZXh0RG9jdW1lbnQnIHJlcXVlc3QgdG8gdGhlIGNvbm5lY3RlZFxuICAgKiBsYW5ndWFnZSBzZXJ2ZXIgYW5kIHdhaXRzIGZvciB0aGUgcmVzcG9uc2UgYmVmb3JlIHNhdmluZyB0aGUgYnVmZmVyLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHdpbGxTYXZlV2FpdFVudGlsKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghdGhpcy5faXNQcmltYXJ5QWRhcHRlcigpKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgICB9XG5cbiAgICBjb25zdCBidWZmZXIgPSB0aGlzLl9lZGl0b3IuZ2V0QnVmZmVyKClcbiAgICBjb25zdCB1cmkgPSB0aGlzLmdldEVkaXRvclVyaSgpXG4gICAgY29uc3QgdGl0bGUgPSB0aGlzLl9lZGl0b3IuZ2V0TG9uZ1RpdGxlKClcblxuICAgIGNvbnN0IGFwcGx5RWRpdHNPclRpbWVvdXQgPSBVdGlscy5wcm9taXNlV2l0aFRpbWVvdXQoXG4gICAgICAyNTAwLCAvLyAyLjUgc2Vjb25kcyB0aW1lb3V0XG4gICAgICB0aGlzLl9jb25uZWN0aW9uLndpbGxTYXZlV2FpdFVudGlsVGV4dERvY3VtZW50KHtcbiAgICAgICAgdGV4dERvY3VtZW50OiB7IHVyaSB9LFxuICAgICAgICByZWFzb246IFRleHREb2N1bWVudFNhdmVSZWFzb24uTWFudWFsLFxuICAgICAgfSlcbiAgICApXG4gICAgICAudGhlbigoZWRpdHMpID0+IHtcbiAgICAgICAgY29uc3QgY3Vyc29yID0gdGhpcy5fZWRpdG9yLmdldEN1cnNvckJ1ZmZlclBvc2l0aW9uKClcbiAgICAgICAgQXBwbHlFZGl0QWRhcHRlci5hcHBseUVkaXRzKGJ1ZmZlciwgQ29udmVydC5jb252ZXJ0THNUZXh0RWRpdHMoZWRpdHMpKVxuICAgICAgICB0aGlzLl9lZGl0b3Iuc2V0Q3Vyc29yQnVmZmVyUG9zaXRpb24oY3Vyc29yKVxuICAgICAgfSlcbiAgICAgIC5jYXRjaCgoZXJyKSA9PiB7XG4gICAgICAgIGF0b20ubm90aWZpY2F0aW9ucy5hZGRFcnJvcihcIk9uLXNhdmUgYWN0aW9uIGZhaWxlZFwiLCB7XG4gICAgICAgICAgZGVzY3JpcHRpb246IGBGYWlsZWQgdG8gYXBwbHkgZWRpdHMgdG8gJHt0aXRsZX1gLFxuICAgICAgICAgIGRldGFpbDogZXJyLm1lc3NhZ2UsXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVyblxuICAgICAgfSlcblxuICAgIGNvbnN0IHdpdGhCdXN5U2lnbmFsID0gdGhpcy5fcmVwb3J0QnVzeVdoaWxlKGBBcHBseWluZyBvbi1zYXZlIGVkaXRzIGZvciAke3RpdGxlfWAsICgpID0+IGFwcGx5RWRpdHNPclRpbWVvdXQpXG4gICAgcmV0dXJuIHdpdGhCdXN5U2lnbmFsIHx8IGFwcGx5RWRpdHNPclRpbWVvdXRcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxsZWQgd2hlbiB0aGUge1RleHRFZGl0b3J9IHNhdmVzIGFuZCBzZW5kcyB0aGUgJ2RpZFNhdmVUZXh0RG9jdW1lbnQnIG5vdGlmaWNhdGlvbiB0byB0aGUgY29ubmVjdGVkIGxhbmd1YWdlXG4gICAqIHNlcnZlci4gTm90ZTogUmlnaHQgbm93IHRoaXMgYWxzbyBzZW5kcyB0aGUgYGRpZENoYW5nZVdhdGNoZWRGaWxlc2Agbm90aWZpY2F0aW9uIGFzIHdlbGwgYnV0IHRoYXQgd2lsbCBiZSBzZW50IGZyb21cbiAgICogZWxzZXdoZXJlIHNvb24uXG4gICAqL1xuICBwdWJsaWMgZGlkU2F2ZSgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuX2lzUHJpbWFyeUFkYXB0ZXIoKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgdXJpID0gdGhpcy5nZXRFZGl0b3JVcmkoKVxuICAgIGNvbnN0IGRpZFNhdmVOb3RpZmljYXRpb24gPSB7XG4gICAgICB0ZXh0RG9jdW1lbnQ6IHsgdXJpLCB2ZXJzaW9uOiB0aGlzLl9nZXRWZXJzaW9uKHVyaSkgfSxcbiAgICB9IGFzIERpZFNhdmVUZXh0RG9jdW1lbnRQYXJhbXNcbiAgICBpZiAodHlwZW9mIHRoaXMuX2RvY3VtZW50U3luYy5zYXZlID09PSBcIm9iamVjdFwiICYmIHRoaXMuX2RvY3VtZW50U3luYy5zYXZlLmluY2x1ZGVUZXh0KSB7XG4gICAgICBkaWRTYXZlTm90aWZpY2F0aW9uLnRleHQgPSB0aGlzLl9lZGl0b3IuZ2V0VGV4dCgpXG4gICAgfVxuICAgIHRoaXMuX2Nvbm5lY3Rpb24uZGlkU2F2ZVRleHREb2N1bWVudChkaWRTYXZlTm90aWZpY2F0aW9uKVxuICAgIGlmICh0aGlzLl9mYWtlRGlkQ2hhbmdlV2F0Y2hlZEZpbGVzKSB7XG4gICAgICB0aGlzLl9jb25uZWN0aW9uLmRpZENoYW5nZVdhdGNoZWRGaWxlcyh7XG4gICAgICAgIGNoYW5nZXM6IFt7IHVyaSwgdHlwZTogRmlsZUNoYW5nZVR5cGUuQ2hhbmdlZCB9XSxcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgcHVibGljIGRpZFJlbmFtZSgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuX2lzUHJpbWFyeUFkYXB0ZXIoKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3Qgb2xkVXJpID0gdGhpcy5fY3VycmVudFVyaVxuICAgIHRoaXMuX2N1cnJlbnRVcmkgPSB0aGlzLmdldEVkaXRvclVyaSgpXG4gICAgaWYgKCFvbGRVcmkpIHtcbiAgICAgIHJldHVybiAvLyBEaWRuJ3QgcHJldmlvdXNseSBoYXZlIGEgbmFtZVxuICAgIH1cblxuICAgIGlmICh0aGlzLl9kb2N1bWVudFN5bmMub3BlbkNsb3NlICE9PSBmYWxzZSkge1xuICAgICAgdGhpcy5fY29ubmVjdGlvbi5kaWRDbG9zZVRleHREb2N1bWVudCh7IHRleHREb2N1bWVudDogeyB1cmk6IG9sZFVyaSB9IH0pXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX2Zha2VEaWRDaGFuZ2VXYXRjaGVkRmlsZXMpIHtcbiAgICAgIHRoaXMuX2Nvbm5lY3Rpb24uZGlkQ2hhbmdlV2F0Y2hlZEZpbGVzKHtcbiAgICAgICAgY2hhbmdlczogW1xuICAgICAgICAgIHsgdXJpOiBvbGRVcmksIHR5cGU6IEZpbGVDaGFuZ2VUeXBlLkRlbGV0ZWQgfSxcbiAgICAgICAgICB7IHVyaTogdGhpcy5fY3VycmVudFVyaSwgdHlwZTogRmlsZUNoYW5nZVR5cGUuQ3JlYXRlZCB9LFxuICAgICAgICBdLFxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBTZW5kIGFuIGVxdWl2YWxlbnQgb3BlbiBldmVudCBmb3IgdGhpcyBlZGl0b3IsIHdoaWNoIHdpbGwgbm93IHVzZSB0aGUgbmV3XG4gICAgLy8gZmlsZSBwYXRoLlxuICAgIGlmICh0aGlzLl9kb2N1bWVudFN5bmMub3BlbkNsb3NlICE9PSBmYWxzZSkge1xuICAgICAgdGhpcy5kaWRPcGVuKClcbiAgICB9XG4gIH1cblxuICAvKiogUHVibGljOiBPYnRhaW4gdGhlIGN1cnJlbnQge1RleHRFZGl0b3J9IHBhdGggYW5kIGNvbnZlcnQgaXQgdG8gYSBVcmkuICovXG4gIHB1YmxpYyBnZXRFZGl0b3JVcmkoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gQ29udmVydC5wYXRoVG9VcmkodGhpcy5fZWRpdG9yLmdldFBhdGgoKSB8fCBcIlwiKVxuICB9XG59XG4iXX0=
\No newline at end of file