obsidian-dev-utils 82.0.0-beta.1 → 82.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/lib/cjs/library.cjs +2 -2
  3. package/dist/lib/cjs/obsidian/components/rename-delete-handler-component.cjs +10 -1
  4. package/dist/lib/cjs/obsidian/components/rename-delete-handler-component.d.cts +3 -0
  5. package/dist/lib/cjs/obsidian/editor-lock.cjs +265 -43
  6. package/dist/lib/cjs/obsidian/editor-lock.d.cts +93 -11
  7. package/dist/lib/cjs/obsidian/editor.cjs +16 -31
  8. package/dist/lib/cjs/obsidian/editor.d.cts +13 -9
  9. package/dist/lib/cjs/obsidian/file-manager.cjs +5 -1
  10. package/dist/lib/cjs/obsidian/file-manager.d.cts +9 -0
  11. package/dist/lib/cjs/obsidian/i18n/locales/en.cjs +6 -2
  12. package/dist/lib/cjs/obsidian/i18n/locales/en.d.cts +4 -0
  13. package/dist/lib/cjs/obsidian/i18n/locales/translations-map.d.cts +4 -0
  14. package/dist/lib/cjs/obsidian/markdown-code-block-processor.cjs +4 -1
  15. package/dist/lib/cjs/obsidian/markdown-code-block-processor.d.cts +13 -0
  16. package/dist/lib/cjs/obsidian/modals/minimizable-modal.cjs +3 -1
  17. package/dist/lib/cjs/obsidian/plugin/obsidian-plugin-repo-paths.cjs +2 -1
  18. package/dist/lib/cjs/obsidian/plugin/obsidian-plugin-repo-paths.d.cts +2 -0
  19. package/dist/lib/cjs/obsidian/plugin/plugin.cjs +21 -1
  20. package/dist/lib/cjs/obsidian/plugin/plugin.d.cts +15 -0
  21. package/dist/lib/cjs/obsidian/vault.cjs +67 -37
  22. package/dist/lib/cjs/obsidian/vault.d.cts +7 -6
  23. package/dist/lib/cjs/script-utils/bundlers/esbuild-impl/obsidian-plugin-builder.cjs +29 -2
  24. package/dist/lib/cjs/script-utils/bundlers/esbuild-impl/obsidian-plugin-builder.d.cts +4 -0
  25. package/dist/lib/esm/library.mjs +2 -2
  26. package/dist/lib/esm/obsidian/components/rename-delete-handler-component.d.mts +3 -0
  27. package/dist/lib/esm/obsidian/components/rename-delete-handler-component.mjs +10 -1
  28. package/dist/lib/esm/obsidian/editor-lock.d.mts +93 -11
  29. package/dist/lib/esm/obsidian/editor-lock.mjs +270 -48
  30. package/dist/lib/esm/obsidian/editor.d.mts +13 -9
  31. package/dist/lib/esm/obsidian/editor.mjs +17 -30
  32. package/dist/lib/esm/obsidian/file-manager.d.mts +9 -0
  33. package/dist/lib/esm/obsidian/file-manager.mjs +5 -1
  34. package/dist/lib/esm/obsidian/i18n/locales/en.d.mts +4 -0
  35. package/dist/lib/esm/obsidian/i18n/locales/en.mjs +6 -2
  36. package/dist/lib/esm/obsidian/i18n/locales/translations-map.d.mts +4 -0
  37. package/dist/lib/esm/obsidian/markdown-code-block-processor.d.mts +13 -0
  38. package/dist/lib/esm/obsidian/markdown-code-block-processor.mjs +4 -1
  39. package/dist/lib/esm/obsidian/modals/minimizable-modal.mjs +3 -1
  40. package/dist/lib/esm/obsidian/plugin/obsidian-plugin-repo-paths.d.mts +2 -0
  41. package/dist/lib/esm/obsidian/plugin/obsidian-plugin-repo-paths.mjs +2 -1
  42. package/dist/lib/esm/obsidian/plugin/plugin.d.mts +15 -0
  43. package/dist/lib/esm/obsidian/plugin/plugin.mjs +21 -1
  44. package/dist/lib/esm/obsidian/vault.d.mts +7 -6
  45. package/dist/lib/esm/obsidian/vault.mjs +67 -40
  46. package/dist/lib/esm/script-utils/bundlers/esbuild-impl/obsidian-plugin-builder.d.mts +4 -0
  47. package/dist/lib/esm/script-utils/bundlers/esbuild-impl/obsidian-plugin-builder.mjs +33 -3
  48. package/dist/styles.css +20 -1
  49. package/package.json +1 -1
@@ -125,56 +125,154 @@ var __copyProps = (to, from, except, desc) => {
125
125
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
126
126
  var editor_lock_exports = {};
127
127
  __export(editor_lock_exports, {
128
+ EditorLockComponent: () => EditorLockComponent,
128
129
  isEditorLockedForPath: () => isEditorLockedForPath,
129
130
  lockEditorForPath: () => lockEditorForPath,
131
+ requestEditorUnlockForPath: () => requestEditorUnlockForPath,
130
132
  unlockEditorForPath: () => unlockEditorForPath
131
133
  });
132
134
  module.exports = __toCommonJS(editor_lock_exports);
133
135
  var import_implementations = require('@obsidian-typings/obsidian-public-latest/implementations');
134
136
  var import_obsidian = require('obsidian');
137
+ var import_async = require('../async.cjs');
135
138
  var import_disposable = require('../disposable.cjs');
136
139
  var import_function = require('../function.cjs');
137
140
  var import_obsidian_dev_utils_state = require('../obsidian-dev-utils-state.cjs');
141
+ var import_type_guards = require('../type-guards.cjs');
142
+ var import_component_ex = require('./components/component-ex.cjs');
138
143
  var import_editor = require('./editor.cjs');
139
144
  var import_file_system = require('./file-system.cjs');
140
145
  var import_i18n = require('./i18n/i18n.cjs');
146
+ var import_confirm = require('./modals/confirm.cjs');
147
+ var import_plugin_id = require('./plugin/plugin-id.cjs');
141
148
  const EDITOR_LOCK_STATE_KEY = "editorLock";
142
149
  const LOCK_ICON_ID = "lock";
150
+ const LOCK_INDICATOR_CSS_CLASS = "obsidian-dev-utils-lock-indicator";
151
+ const STATUS_BAR_ITEM_CSS_CLASS = "status-bar-item";
152
+ const STATUS_BAR_CSS_SELECTOR = ".status-bar";
153
+ const UNLOCK_ICON_ID = "unlock";
154
+ class EditorLockEventsComponent extends import_component_ex.ComponentEx {
155
+ constructor(app, onChange) {
156
+ super();
157
+ this.app = app;
158
+ this.onChange = onChange;
159
+ }
160
+ app;
161
+ onChange;
162
+ onload() {
163
+ super.onload();
164
+ this.registerEvent(this.app.workspace.on("active-leaf-change", this.onChange));
165
+ this.registerEvent(this.app.workspace.on("layout-change", this.onChange));
166
+ this.registerEvent(this.app.workspace.on("file-open", this.onChange));
167
+ }
168
+ }
143
169
  class EditorPathLockManager {
144
- eventRefs = [];
145
- lockCountByPath = /* @__PURE__ */ new Map();
146
- lockedIconElByView = /* @__PURE__ */ new Map();
170
+ abortControllersByPath = /* @__PURE__ */ new Map();
171
+ eventsComponent;
172
+ indicatorsByView = /* @__PURE__ */ new Map();
173
+ lockCountByPluginIdByPath = /* @__PURE__ */ new Map();
174
+ statusBarItemEl = null;
147
175
  isLocked(app, pathOrFile) {
148
- return (this.lockCountByPath.get((0, import_file_system.getPath)(app, pathOrFile)) ?? 0) > 0;
176
+ return this.isPathLocked((0, import_file_system.getPath)(app, pathOrFile));
149
177
  }
150
- lock(app, pathOrFile) {
178
+ lock(app, pathOrFile, pluginId, abortController) {
151
179
  const path = (0, import_file_system.getPath)(app, pathOrFile);
152
- this.lockCountByPath.set(path, (this.lockCountByPath.get(path) ?? 0) + 1);
153
- this.ensureEventsRegistered(app);
180
+ let lockCountByPluginId = this.lockCountByPluginIdByPath.get(path);
181
+ if (!lockCountByPluginId) {
182
+ lockCountByPluginId = /* @__PURE__ */ new Map();
183
+ this.lockCountByPluginIdByPath.set(path, lockCountByPluginId);
184
+ }
185
+ lockCountByPluginId.set(pluginId, (lockCountByPluginId.get(pluginId) ?? 0) + 1);
186
+ if (abortController) {
187
+ let abortControllers = this.abortControllersByPath.get(path);
188
+ if (!abortControllers) {
189
+ abortControllers = /* @__PURE__ */ new Set();
190
+ this.abortControllersByPath.set(path, abortControllers);
191
+ }
192
+ abortControllers.add(abortController);
193
+ }
194
+ this.ensureSubscribed(app);
154
195
  this.reconcile(app);
155
196
  return new import_disposable.CallbackDisposable({
156
197
  callback: () => {
157
- this.unlockPath(app, path);
198
+ if (abortController) {
199
+ const abortControllers = this.abortControllersByPath.get(path);
200
+ (0, import_type_guards.assertNonNullable)(abortControllers);
201
+ abortControllers.delete(abortController);
202
+ if (abortControllers.size === 0) {
203
+ this.abortControllersByPath.delete(path);
204
+ }
205
+ }
206
+ this.unlockPath(app, path, pluginId);
158
207
  },
159
208
  multipleDisposeBehavior: import_disposable.MultipleDisposeBehavior.Ignore
160
209
  });
161
210
  }
162
- unlock(app, pathOrFile) {
163
- this.unlockPath(app, (0, import_file_system.getPath)(app, pathOrFile));
211
+ /**
212
+ * Aborts every {@link AbortController} currently registered for the note at the given path, which
213
+ * cancels the operations holding the lock. Each operation releases its own lock in its `finally`
214
+ * when it observes the abort, so this does not unlock the path directly. A no-op when no controller
215
+ * is registered for the path.
216
+ *
217
+ * @param app - The Obsidian app instance.
218
+ * @param pathOrFile - The path or file of the note to request an unlock for.
219
+ */
220
+ requestUnlock(app, pathOrFile) {
221
+ const abortControllers = this.abortControllersByPath.get((0, import_file_system.getPath)(app, pathOrFile));
222
+ if (!abortControllers) {
223
+ return;
224
+ }
225
+ for (const controller of abortControllers) {
226
+ controller.abort();
227
+ }
228
+ }
229
+ unlock(app, pathOrFile, pluginId) {
230
+ this.unlockPath(app, (0, import_file_system.getPath)(app, pathOrFile), pluginId);
164
231
  }
165
- ensureEventsRegistered(app) {
166
- if (this.eventRefs.length > 0) {
232
+ unlockAllForPlugin(app, pluginId) {
233
+ for (const [path, lockCountByPluginId] of this.lockCountByPluginIdByPath) {
234
+ if (lockCountByPluginId.delete(pluginId) && lockCountByPluginId.size === 0) {
235
+ this.lockCountByPluginIdByPath.delete(path);
236
+ }
237
+ }
238
+ this.reconcileAndCleanup(app);
239
+ }
240
+ createIndicators(app, view, path, tooltip) {
241
+ const actionIconEl = view.addAction(LOCK_ICON_ID, tooltip, import_function.noop);
242
+ this.registerUnlockMenu(app, actionIconEl, () => path);
243
+ let tabIconEl = null;
244
+ const tabStatusContainerEl = view.leaf.tabHeaderStatusContainerEl;
245
+ if (tabStatusContainerEl) {
246
+ tabIconEl = tabStatusContainerEl.createSpan({ cls: LOCK_INDICATOR_CSS_CLASS });
247
+ (0, import_obsidian.setIcon)(tabIconEl, LOCK_ICON_ID);
248
+ (0, import_obsidian.setTooltip)(tabIconEl, tooltip);
249
+ this.registerUnlockMenu(app, tabIconEl, () => path);
250
+ }
251
+ return {
252
+ actionIconEl,
253
+ tabIconEl
254
+ };
255
+ }
256
+ ensureSubscribed(app) {
257
+ if (this.eventsComponent) {
167
258
  return;
168
259
  }
169
- const reconcile = () => {
260
+ this.eventsComponent = new EditorLockEventsComponent(app, () => {
170
261
  this.reconcile(app);
171
- };
172
- this.eventRefs.push(
173
- app.workspace.on("active-leaf-change", reconcile),
174
- app.workspace.on("layout-change", reconcile),
175
- // Same-leaf navigation to another note fires no leaf/layout change; without this it stays read-only.
176
- app.workspace.on("file-open", reconcile)
177
- );
262
+ });
263
+ this.eventsComponent.load();
264
+ }
265
+ isPathLocked(path) {
266
+ return (this.lockCountByPluginIdByPath.get(path)?.size ?? 0) > 0;
267
+ }
268
+ lockingPluginNames(app, path) {
269
+ const lockCountByPluginId = this.lockCountByPluginIdByPath.get(path);
270
+ (0, import_type_guards.assertNonNullable)(lockCountByPluginId);
271
+ return Array.from(lockCountByPluginId.keys()).map((pluginId) => app.plugins.manifests[pluginId]?.name ?? pluginId);
272
+ }
273
+ lockTooltip(app, path) {
274
+ const header = (0, import_i18n.t)(($) => $.obsidianDevUtils.editorLock.lockedByTooltip);
275
+ return [header, ...this.lockingPluginNames(app, path)].join("\n");
178
276
  }
179
277
  reconcile(app) {
180
278
  const viewsToLock = /* @__PURE__ */ new Set();
@@ -187,61 +285,185 @@ class EditorPathLockManager {
187
285
  if (path === void 0) {
188
286
  continue;
189
287
  }
190
- if ((this.lockCountByPath.get(path) ?? 0) > 0) {
288
+ if (this.isPathLocked(path)) {
191
289
  viewsToLock.add(view);
192
- if (!this.lockedIconElByView.has(view)) {
193
- (0, import_editor.lockEditor)(view.editor);
194
- const iconEl = view.addAction(LOCK_ICON_ID, (0, import_i18n.t)(($) => $.obsidianDevUtils.editorLock.lockedNoteTooltip), import_function.noop);
195
- this.lockedIconElByView.set(view, iconEl);
290
+ const tooltip = this.lockTooltip(app, path);
291
+ (0, import_editor.toggleEditorReadOnly)(view.editor, true);
292
+ const indicators = this.indicatorsByView.get(view);
293
+ if (indicators) {
294
+ this.updateIndicatorTooltips(indicators, tooltip);
295
+ } else {
296
+ this.indicatorsByView.set(view, this.createIndicators(app, view, path, tooltip));
196
297
  }
197
298
  }
198
299
  }
199
- for (const [view, iconEl] of this.lockedIconElByView) {
300
+ for (const [view, indicators] of this.indicatorsByView) {
200
301
  if (!viewsToLock.has(view)) {
201
- (0, import_editor.unlockEditor)(view.editor);
202
- iconEl.remove();
203
- this.lockedIconElByView.delete(view);
302
+ (0, import_editor.toggleEditorReadOnly)(view.editor, false);
303
+ indicators.actionIconEl.remove();
304
+ indicators.tabIconEl?.remove();
305
+ this.indicatorsByView.delete(view);
204
306
  }
205
307
  }
308
+ this.updateStatusBar(app);
206
309
  }
207
- unlockPath(app, path) {
208
- const count = this.lockCountByPath.get(path) ?? 0;
310
+ reconcileAndCleanup(app) {
311
+ this.reconcile(app);
312
+ if (this.lockCountByPluginIdByPath.size === 0) {
313
+ this.eventsComponent?.unload();
314
+ this.eventsComponent = void 0;
315
+ }
316
+ }
317
+ registerUnlockMenu(app, el, getContextPath) {
318
+ el.addEventListener("contextmenu", (evt) => {
319
+ evt.preventDefault();
320
+ const path = getContextPath();
321
+ if (path === void 0) {
322
+ return;
323
+ }
324
+ const menu = new import_obsidian.Menu();
325
+ menu.addItem((item) => {
326
+ item.setTitle((0, import_i18n.t)(($) => $.obsidianDevUtils.editorLock.unlockMenuItem)).setIcon(UNLOCK_ICON_ID).onClick((0, import_async.convertAsyncToSync)(async () => {
327
+ const isConfirmed = await (0, import_confirm.confirm)({
328
+ app,
329
+ message: this.unlockConfirmMessage(app, path),
330
+ title: (0, import_i18n.t)(($) => $.obsidianDevUtils.editorLock.unlockConfirmTitle)
331
+ });
332
+ if (isConfirmed) {
333
+ this.requestUnlock(app, path);
334
+ }
335
+ }));
336
+ });
337
+ menu.showAtMouseEvent(evt);
338
+ });
339
+ }
340
+ unlockConfirmMessage(app, path) {
341
+ const header = (0, import_i18n.t)(($) => $.obsidianDevUtils.editorLock.unlockConfirmMessage);
342
+ return [header, ...this.lockingPluginNames(app, path)].join("\n");
343
+ }
344
+ unlockPath(app, path, pluginId) {
345
+ const lockCountByPluginId = this.lockCountByPluginIdByPath.get(path);
346
+ if (!lockCountByPluginId) {
347
+ return;
348
+ }
349
+ const count = lockCountByPluginId.get(pluginId) ?? 0;
209
350
  if (count <= 0) {
210
351
  return;
211
352
  }
212
353
  if (count === 1) {
213
- this.lockCountByPath.delete(path);
354
+ lockCountByPluginId.delete(pluginId);
214
355
  } else {
215
- this.lockCountByPath.set(path, count - 1);
356
+ lockCountByPluginId.set(pluginId, count - 1);
216
357
  }
217
- this.reconcile(app);
218
- if (this.lockCountByPath.size === 0) {
219
- this.unregisterEvents(app);
358
+ if (lockCountByPluginId.size === 0) {
359
+ this.lockCountByPluginIdByPath.delete(path);
220
360
  }
361
+ this.reconcileAndCleanup(app);
221
362
  }
222
- unregisterEvents(app) {
223
- for (const eventRef of this.eventRefs) {
224
- app.workspace.offref(eventRef);
363
+ updateIndicatorTooltips(indicators, tooltip) {
364
+ (0, import_obsidian.setTooltip)(indicators.actionIconEl, tooltip);
365
+ if (indicators.tabIconEl) {
366
+ (0, import_obsidian.setTooltip)(indicators.tabIconEl, tooltip);
225
367
  }
226
- this.eventRefs = [];
368
+ }
369
+ updateStatusBar(app) {
370
+ const activeView = app.workspace.getActiveViewOfType(import_obsidian.MarkdownView);
371
+ const activePath = activeView?.file?.path;
372
+ const isActiveLocked = activePath !== void 0 && this.isPathLocked(activePath);
373
+ if (!isActiveLocked || !activeView) {
374
+ this.statusBarItemEl?.remove();
375
+ this.statusBarItemEl = null;
376
+ return;
377
+ }
378
+ if (!this.statusBarItemEl) {
379
+ const statusBarEl = activeView.containerEl.ownerDocument.body.querySelector(STATUS_BAR_CSS_SELECTOR);
380
+ if (!statusBarEl) {
381
+ return;
382
+ }
383
+ this.statusBarItemEl = statusBarEl.createDiv({ cls: [STATUS_BAR_ITEM_CSS_CLASS, LOCK_INDICATOR_CSS_CLASS] });
384
+ (0, import_obsidian.setIcon)(this.statusBarItemEl, LOCK_ICON_ID);
385
+ this.registerUnlockMenu(app, this.statusBarItemEl, () => app.workspace.getActiveViewOfType(import_obsidian.MarkdownView)?.file?.path);
386
+ }
387
+ (0, import_obsidian.setTooltip)(this.statusBarItemEl, this.lockTooltip(app, activePath));
388
+ }
389
+ }
390
+ class EditorLockComponent extends import_component_ex.ComponentEx {
391
+ app;
392
+ pluginId;
393
+ /**
394
+ * Creates an editor-lock handle owned by a plugin.
395
+ *
396
+ * @param app - The Obsidian app instance.
397
+ * @param pluginId - The id of the owning plugin. Defaults to the current plugin context's id; pass
398
+ * it explicitly (e.g. from a plugin's `manifest.id`) when constructing before the plugin context is
399
+ * initialized, such as inside `PluginBase.onload`.
400
+ */
401
+ constructor(app, pluginId = (0, import_plugin_id.getPluginId)()) {
402
+ super();
403
+ this.app = app;
404
+ this.pluginId = pluginId;
405
+ }
406
+ /**
407
+ * Checks whether the note at the given path is currently locked by any plugin.
408
+ *
409
+ * @param pathOrFile - The path or file of the note to check.
410
+ * @returns `true` if the note has at least one active lock, `false` otherwise.
411
+ */
412
+ isLockedForPath(pathOrFile) {
413
+ return getManager().isLocked(this.app, pathOrFile);
414
+ }
415
+ /**
416
+ * Locks the note at the given path on behalf of this plugin, making it read-only in every current
417
+ * and future {@link MarkdownView} until the lock is released. Reference-counted: balance each call
418
+ * with a dispose of the returned {@link Disposable} (ideally via `using`) or {@link unlockForPath}.
419
+ *
420
+ * @param pathOrFile - The path or file of the note to lock.
421
+ * @param options - Optional locking options. Pass an
422
+ * {@link EditorLockComponentLockForPathOptions.abortController} to make the lock cancelable via the
423
+ * indicator's right-click "unlock" menu or {@link requestEditorUnlockForPath}.
424
+ * @returns A {@link Disposable} that releases this lock when disposed. Disposing more than once is a no-op.
425
+ */
426
+ lockForPath(pathOrFile, options) {
427
+ return getManager().lock(this.app, pathOrFile, this.pluginId, options?.abortController);
428
+ }
429
+ /**
430
+ * Releases every lock still held by this plugin when the component (and thus the plugin) unloads,
431
+ * so no note is left stuck read-only by an operation that never completed.
432
+ */
433
+ onunload() {
434
+ getManager().unlockAllForPlugin(this.app, this.pluginId);
435
+ super.onunload();
436
+ }
437
+ /**
438
+ * Releases one lock previously acquired for the note at the given path via {@link lockForPath}.
439
+ *
440
+ * @param pathOrFile - The path or file of the note to unlock.
441
+ */
442
+ unlockForPath(pathOrFile) {
443
+ getManager().unlock(this.app, pathOrFile, this.pluginId);
227
444
  }
228
445
  }
229
446
  function isEditorLockedForPath(app, pathOrFile) {
230
447
  return getManager().isLocked(app, pathOrFile);
231
448
  }
232
449
  function lockEditorForPath(app, pathOrFile) {
233
- return getManager().lock(app, pathOrFile);
450
+ return getManager().lock(app, pathOrFile, (0, import_plugin_id.getPluginId)());
451
+ }
452
+ function requestEditorUnlockForPath(app, pathOrFile) {
453
+ getManager().requestUnlock(app, pathOrFile);
234
454
  }
235
455
  function unlockEditorForPath(app, pathOrFile) {
236
- getManager().unlock(app, pathOrFile);
456
+ getManager().unlock(app, pathOrFile, (0, import_plugin_id.getPluginId)());
237
457
  }
238
458
  function getManager() {
239
459
  return (0, import_obsidian_dev_utils_state.getObsidianDevUtilsState)(EDITOR_LOCK_STATE_KEY, new EditorPathLockManager()).value;
240
460
  }
241
461
  // Annotate the CommonJS export names for ESM import in node:
242
462
  0 && (module.exports = {
463
+ EditorLockComponent,
243
464
  isEditorLockedForPath,
244
465
  lockEditorForPath,
466
+ requestEditorUnlockForPath,
245
467
  unlockEditorForPath
246
468
  });
247
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vc3JjL29ic2lkaWFuL2VkaXRvci1sb2NrLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogTm90ZS1zY29wZWQsIHJlZmVyZW5jZS1jb3VudGVkIGVkaXRvciBsb2NraW5nLlxuICpcbiAqIFdoZXJlIHtAbGluayBsb2NrRWRpdG9yfSAvIHtAbGluayB1bmxvY2tFZGl0b3J9IGZyb20gYC4vZWRpdG9yLnRzYCB0b2dnbGUgdGhlIHJlYWQtb25seSBzdGF0ZSBvZiBhXG4gKiBzaW5nbGUge0BsaW5rIEVkaXRvcn0gaW5zdGFuY2UsIHRoZSBoZWxwZXJzIGhlcmUgbG9jayBhIG5vdGUgYnkgaXRzICoqcGF0aCoqOiB3aGlsZSBhIHBhdGggaXNcbiAqIGxvY2tlZCwgZXZlcnkgY3VycmVudCBhbmQgZnV0dXJlIHtAbGluayBNYXJrZG93blZpZXd9IG9mIHRoYXQgbm90ZSAoaW4gYW55IHdpbmRvdywgaW5jbHVkaW5nXG4gKiBwb3BvdXRzKSBpcyBtYWRlIHJlYWQtb25seSBhbmQgc2hvd3MgYSBsb2NrIGljb24uIExvY2tzIGFyZSByZWZlcmVuY2UtY291bnRlZCwgc28gbmVzdGVkIG9yXG4gKiBjb25jdXJyZW50IG9wZXJhdGlvbnMgb24gdGhlIHNhbWUgbm90ZSBhcmUgc2FmZSBcdTIwMTQgdGhlIG5vdGUgaXMgdW5sb2NrZWQgb25seSB3aGVuIHRoZSBsYXN0IGxvY2sgaXNcbiAqIHJlbGVhc2VkLlxuICpcbiAqIFRoZSBhY3F1aXJlcnMgcmV0dXJuIGEge0BsaW5rIERpc3Bvc2FibGV9LCBzbyB0aGUgcHJlZmVycmVkIGNhbGwgc3R5bGUgaXMgYSBgdXNpbmdgIGRlY2xhcmF0aW9uXG4gKiB0aGF0IHJlbGVhc2VzIGF1dG9tYXRpY2FsbHkgYXQgc2NvcGUgZXhpdCAoaW5jbHVkaW5nIG9uIHRocm93KTpcbiAqXG4gKiBgYGB0c1xuICogdXNpbmcgX2xvY2sgPSBsb2NrRWRpdG9yRm9yUGF0aChhcHAsIHBhdGgpO1xuICogLy8gLi4uIGxvbmctcnVubmluZyB3b3JrIChwcm9jZXNzLCBwcm9jZXNzRnJvbnRNYXR0ZXIsIG1lcmdlL3NwbGl0KSAuLi5cbiAqIC8vIGF1dG8tdW5sb2NrZWQgYXQgc2NvcGUgZXhpdFxuICogYGBgXG4gKlxuICogVGhlIGV4cGxpY2l0IHtAbGluayB1bmxvY2tFZGl0b3JGb3JQYXRofSBwYWlyIHJlbWFpbnMgYXZhaWxhYmxlIGZvciBub24tYHVzaW5nYCBjYWxsIHNpdGVzLlxuICovXG5cbmltcG9ydCB0eXBlIHtcbiAgQXBwLFxuICBFdmVudFJlZlxufSBmcm9tICdvYnNpZGlhbic7XG5cbmltcG9ydCB7IFZpZXdUeXBlIH0gZnJvbSAnQG9ic2lkaWFuLXR5cGluZ3Mvb2JzaWRpYW4tcHVibGljLWxhdGVzdC9pbXBsZW1lbnRhdGlvbnMnO1xuaW1wb3J0IHsgTWFya2Rvd25WaWV3IH0gZnJvbSAnb2JzaWRpYW4nO1xuXG5pbXBvcnQgdHlwZSB7IFBhdGhPckZpbGUgfSBmcm9tICcuL2ZpbGUtc3lzdGVtLnRzJztcblxuaW1wb3J0IHtcbiAgQ2FsbGJhY2tEaXNwb3NhYmxlLFxuICBNdWx0aXBsZURpc3Bvc2VCZWhhdmlvclxufSBmcm9tICcuLi9kaXNwb3NhYmxlLnRzJztcbmltcG9ydCB7IG5vb3AgfSBmcm9tICcuLi9mdW5jdGlvbi50cyc7XG5pbXBvcnQgeyBnZXRPYnNpZGlhbkRldlV0aWxzU3RhdGUgfSBmcm9tICcuLi9vYnNpZGlhbi1kZXYtdXRpbHMtc3RhdGUudHMnO1xuaW1wb3J0IHtcbiAgbG9ja0VkaXRvcixcbiAgdW5sb2NrRWRpdG9yXG59IGZyb20gJy4vZWRpdG9yLnRzJztcbmltcG9ydCB7IGdldFBhdGggfSBmcm9tICcuL2ZpbGUtc3lzdGVtLnRzJztcbmltcG9ydCB7IHQgfSBmcm9tICcuL2kxOG4vaTE4bi50cyc7XG5cbmNvbnN0IEVESVRPUl9MT0NLX1NUQVRFX0tFWSA9ICdlZGl0b3JMb2NrJztcbmNvbnN0IExPQ0tfSUNPTl9JRCA9ICdsb2NrJztcblxuLyoqXG4gKiBUcmFja3MgcmVmZXJlbmNlLWNvdW50ZWQsIHBhdGgtc2NvcGVkIGVkaXRvciBsb2NrcyBhbmQga2VlcHMgZXZlcnkgb3BlbiB7QGxpbmsgTWFya2Rvd25WaWV3fSBpblxuICogc3luYyB3aXRoIHRoZSBsb2NrZWQtcGF0aCBzZXQuIEEgc2luZ2xlIGluc3RhbmNlIGxpdmVzIG9uIHRoZSBzaGFyZWQgYG9ic2lkaWFuLWRldi11dGlsc2Agc3RhdGUuXG4gKi9cbmNsYXNzIEVkaXRvclBhdGhMb2NrTWFuYWdlciB7XG4gIHByaXZhdGUgZXZlbnRSZWZzOiBFdmVudFJlZltdID0gW107XG4gIHByaXZhdGUgcmVhZG9ubHkgbG9ja0NvdW50QnlQYXRoID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2NrZWRJY29uRWxCeVZpZXcgPSBuZXcgTWFwPE1hcmtkb3duVmlldywgSFRNTEVsZW1lbnQ+KCk7XG5cbiAgcHVibGljIGlzTG9ja2VkKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICh0aGlzLmxvY2tDb3VudEJ5UGF0aC5nZXQoZ2V0UGF0aChhcHAsIHBhdGhPckZpbGUpKSA/PyAwKSA+IDA7XG4gIH1cblxuICBwdWJsaWMgbG9jayhhcHA6IEFwcCwgcGF0aE9yRmlsZTogUGF0aE9yRmlsZSk6IERpc3Bvc2FibGUge1xuICAgIGNvbnN0IHBhdGggPSBnZXRQYXRoKGFwcCwgcGF0aE9yRmlsZSk7XG4gICAgdGhpcy5sb2NrQ291bnRCeVBhdGguc2V0KHBhdGgsICh0aGlzLmxvY2tDb3VudEJ5UGF0aC5nZXQocGF0aCkgPz8gMCkgKyAxKTtcbiAgICB0aGlzLmVuc3VyZUV2ZW50c1JlZ2lzdGVyZWQoYXBwKTtcbiAgICB0aGlzLnJlY29uY2lsZShhcHApO1xuXG4gICAgcmV0dXJuIG5ldyBDYWxsYmFja0Rpc3Bvc2FibGUoe1xuICAgICAgY2FsbGJhY2s6ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy51bmxvY2tQYXRoKGFwcCwgcGF0aCk7XG4gICAgICB9LFxuICAgICAgbXVsdGlwbGVEaXNwb3NlQmVoYXZpb3I6IE11bHRpcGxlRGlzcG9zZUJlaGF2aW9yLklnbm9yZVxuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIHVubG9jayhhcHA6IEFwcCwgcGF0aE9yRmlsZTogUGF0aE9yRmlsZSk6IHZvaWQge1xuICAgIHRoaXMudW5sb2NrUGF0aChhcHAsIGdldFBhdGgoYXBwLCBwYXRoT3JGaWxlKSk7XG4gIH1cblxuICBwcml2YXRlIGVuc3VyZUV2ZW50c1JlZ2lzdGVyZWQoYXBwOiBBcHApOiB2b2lkIHtcbiAgICBpZiAodGhpcy5ldmVudFJlZnMubGVuZ3RoID4gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHJlY29uY2lsZSA9ICgpOiB2b2lkID0+IHtcbiAgICAgIHRoaXMucmVjb25jaWxlKGFwcCk7XG4gICAgfTtcbiAgICB0aGlzLmV2ZW50UmVmcy5wdXNoKFxuICAgICAgYXBwLndvcmtzcGFjZS5vbignYWN0aXZlLWxlYWYtY2hhbmdlJywgcmVjb25jaWxlKSxcbiAgICAgIGFwcC53b3Jrc3BhY2Uub24oJ2xheW91dC1jaGFuZ2UnLCByZWNvbmNpbGUpLFxuICAgICAgLy8gU2FtZS1sZWFmIG5hdmlnYXRpb24gdG8gYW5vdGhlciBub3RlIGZpcmVzIG5vIGxlYWYvbGF5b3V0IGNoYW5nZTsgd2l0aG91dCB0aGlzIGl0IHN0YXlzIHJlYWQtb25seS5cbiAgICAgIGFwcC53b3Jrc3BhY2Uub24oJ2ZpbGUtb3BlbicsIHJlY29uY2lsZSlcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSByZWNvbmNpbGUoYXBwOiBBcHApOiB2b2lkIHtcbiAgICBjb25zdCB2aWV3c1RvTG9jayA9IG5ldyBTZXQ8TWFya2Rvd25WaWV3PigpO1xuICAgIGZvciAoY29uc3QgbGVhZiBvZiBhcHAud29ya3NwYWNlLmdldExlYXZlc09mVHlwZShWaWV3VHlwZS5NYXJrZG93bikpIHtcbiAgICAgIGNvbnN0IHZpZXcgPSBsZWFmLnZpZXc7XG4gICAgICBpZiAoISh2aWV3IGluc3RhbmNlb2YgTWFya2Rvd25WaWV3KSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcGF0aCA9IHZpZXcuZmlsZT8ucGF0aDtcbiAgICAgIGlmIChwYXRoID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmICgodGhpcy5sb2NrQ291bnRCeVBhdGguZ2V0KHBhdGgpID8/IDApID4gMCkge1xuICAgICAgICB2aWV3c1RvTG9jay5hZGQodmlldyk7XG4gICAgICAgIGlmICghdGhpcy5sb2NrZWRJY29uRWxCeVZpZXcuaGFzKHZpZXcpKSB7XG4gICAgICAgICAgbG9ja0VkaXRvcih2aWV3LmVkaXRvcik7XG4gICAgICAgICAgY29uc3QgaWNvbkVsID0gdmlldy5hZGRBY3Rpb24oTE9DS19JQ09OX0lELCB0KCgkKSA9PiAkLm9ic2lkaWFuRGV2VXRpbHMuZWRpdG9yTG9jay5sb2NrZWROb3RlVG9vbHRpcCksIG5vb3ApO1xuICAgICAgICAgIHRoaXMubG9ja2VkSWNvbkVsQnlWaWV3LnNldCh2aWV3LCBpY29uRWwpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBbdmlldywgaWNvbkVsXSBvZiB0aGlzLmxvY2tlZEljb25FbEJ5Vmlldykge1xuICAgICAgaWYgKCF2aWV3c1RvTG9jay5oYXModmlldykpIHtcbiAgICAgICAgdW5sb2NrRWRpdG9yKHZpZXcuZWRpdG9yKTtcbiAgICAgICAgaWNvbkVsLnJlbW92ZSgpO1xuICAgICAgICB0aGlzLmxvY2tlZEljb25FbEJ5Vmlldy5kZWxldGUodmlldyk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB1bmxvY2tQYXRoKGFwcDogQXBwLCBwYXRoOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBjb3VudCA9IHRoaXMubG9ja0NvdW50QnlQYXRoLmdldChwYXRoKSA/PyAwO1xuICAgIGlmIChjb3VudCA8PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKGNvdW50ID09PSAxKSB7XG4gICAgICB0aGlzLmxvY2tDb3VudEJ5UGF0aC5kZWxldGUocGF0aCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMubG9ja0NvdW50QnlQYXRoLnNldChwYXRoLCBjb3VudCAtIDEpO1xuICAgIH1cblxuICAgIHRoaXMucmVjb25jaWxlKGFwcCk7XG5cbiAgICBpZiAodGhpcy5sb2NrQ291bnRCeVBhdGguc2l6ZSA9PT0gMCkge1xuICAgICAgdGhpcy51bnJlZ2lzdGVyRXZlbnRzKGFwcCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB1bnJlZ2lzdGVyRXZlbnRzKGFwcDogQXBwKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBldmVudFJlZiBvZiB0aGlzLmV2ZW50UmVmcykge1xuICAgICAgYXBwLndvcmtzcGFjZS5vZmZyZWYoZXZlbnRSZWYpO1xuICAgIH1cbiAgICB0aGlzLmV2ZW50UmVmcyA9IFtdO1xuICB9XG59XG5cbi8qKlxuICogQ2hlY2tzIHdoZXRoZXIgdGhlIG5vdGUgYXQgdGhlIGdpdmVuIHBhdGggaXMgY3VycmVudGx5IGxvY2tlZC5cbiAqXG4gKiBAcGFyYW0gYXBwIC0gVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZS5cbiAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIGB0cnVlYCBpZiB0aGUgbm90ZSBoYXMgYXQgbGVhc3Qgb25lIGFjdGl2ZSBsb2NrLCBgZmFsc2VgIG90aGVyd2lzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzRWRpdG9yTG9ja2VkRm9yUGF0aChhcHA6IEFwcCwgcGF0aE9yRmlsZTogUGF0aE9yRmlsZSk6IGJvb2xlYW4ge1xuICByZXR1cm4gZ2V0TWFuYWdlcigpLmlzTG9ja2VkKGFwcCwgcGF0aE9yRmlsZSk7XG59XG5cbi8qKlxuICogTG9ja3MgdGhlIG5vdGUgYXQgdGhlIGdpdmVuIHBhdGgsIG1ha2luZyBpdCByZWFkLW9ubHkgaW4gZXZlcnkgY3VycmVudCBhbmQgZnV0dXJlXG4gKiB7QGxpbmsgTWFya2Rvd25WaWV3fSB1bnRpbCB0aGUgbG9jayBpcyByZWxlYXNlZC5cbiAqXG4gKiBUaGUgbG9jayBpcyByZWZlcmVuY2UtY291bnRlZDogZWFjaCBjYWxsIG11c3QgYmUgYmFsYW5jZWQgYnkgZXhhY3RseSBvbmUgcmVsZWFzZSAoZWl0aGVyIGRpc3Bvc2luZ1xuICogdGhlIHJldHVybmVkIHtAbGluayBEaXNwb3NhYmxlfSBcdTIwMTQgaWRlYWxseSB2aWEgYSBgdXNpbmdgIGRlY2xhcmF0aW9uIFx1MjAxNCBvciBhIG1hdGNoaW5nXG4gKiB7QGxpbmsgdW5sb2NrRWRpdG9yRm9yUGF0aH0gY2FsbCkuXG4gKlxuICogQHBhcmFtIGFwcCAtIFRoZSBPYnNpZGlhbiBhcHAgaW5zdGFuY2UuXG4gKiBAcGFyYW0gcGF0aE9yRmlsZSAtIFRoZSBwYXRoIG9yIGZpbGUgb2YgdGhlIG5vdGUgdG8gbG9jay5cbiAqIEByZXR1cm5zIEEge0BsaW5rIERpc3Bvc2FibGV9IHRoYXQgcmVsZWFzZXMgdGhpcyBsb2NrIHdoZW4gZGlzcG9zZWQuIERpc3Bvc2luZyBtb3JlIHRoYW4gb25jZSBpcyBhIG5vLW9wLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbG9ja0VkaXRvckZvclBhdGgoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUpOiBEaXNwb3NhYmxlIHtcbiAgcmV0dXJuIGdldE1hbmFnZXIoKS5sb2NrKGFwcCwgcGF0aE9yRmlsZSk7XG59XG5cbi8qKlxuICogUmVsZWFzZXMgb25lIGxvY2sgcHJldmlvdXNseSBhY3F1aXJlZCBmb3IgdGhlIG5vdGUgYXQgdGhlIGdpdmVuIHBhdGggdmlhIHtAbGluayBsb2NrRWRpdG9yRm9yUGF0aH0uXG4gKlxuICogV2hlbiB0aGUgbGFzdCBsb2NrIGlzIHJlbGVhc2VkIHRoZSBub3RlIGJlY29tZXMgZnVsbHkgZWRpdGFibGUgYWdhaW4gYW5kIGl0cyBsb2NrIGljb24gaXMgcmVtb3ZlZC5cbiAqIENhbGxpbmcgdGhpcyB3aGVuIHRoZSBub3RlIGlzIG5vdCBsb2NrZWQgaXMgYSBuby1vcC5cbiAqXG4gKiBAcGFyYW0gYXBwIC0gVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZS5cbiAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byB1bmxvY2suXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1bmxvY2tFZGl0b3JGb3JQYXRoKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogdm9pZCB7XG4gIGdldE1hbmFnZXIoKS51bmxvY2soYXBwLCBwYXRoT3JGaWxlKTtcbn1cblxuZnVuY3Rpb24gZ2V0TWFuYWdlcigpOiBFZGl0b3JQYXRoTG9ja01hbmFnZXIge1xuICByZXR1cm4gZ2V0T2JzaWRpYW5EZXZVdGlsc1N0YXRlPEVkaXRvclBhdGhMb2NrTWFuYWdlcj4oRURJVE9SX0xPQ0tfU1RBVEVfS0VZLCBuZXcgRWRpdG9yUGF0aExvY2tNYW5hZ2VyKCkpLnZhbHVlO1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQTZCQSw2QkFBeUI7QUFDekIsc0JBQTZCO0FBSTdCLHdCQUdPO0FBQ1Asc0JBQXFCO0FBQ3JCLHNDQUF5QztBQUN6QyxvQkFHTztBQUNQLHlCQUF3QjtBQUN4QixrQkFBa0I7QUFFbEIsTUFBTSx3QkFBd0I7QUFDOUIsTUFBTSxlQUFlO0FBTXJCLE1BQU0sc0JBQXNCO0FBQUEs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
469
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vc3JjL29ic2lkaWFuL2VkaXRvci1sb2NrLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogTm90ZS1zY29wZWQsIHJlZmVyZW5jZS1jb3VudGVkIGVkaXRvciBsb2NraW5nLlxuICpcbiAqIFdoZXJlIHtAbGluayB0b2dnbGVFZGl0b3JSZWFkT25seX0gZnJvbSBgLi9lZGl0b3IudHNgIHRvZ2dsZXMgdGhlIHJlYWQtb25seSBzdGF0ZSBvZiBhIHNpbmdsZVxuICoge0BsaW5rIEVkaXRvcn0gaW5zdGFuY2UsIHRoZSBoZWxwZXJzIGhlcmUgbG9jayBhIG5vdGUgYnkgaXRzICoqcGF0aCoqOiB3aGlsZSBhIHBhdGggaXNcbiAqIGxvY2tlZCwgZXZlcnkgY3VycmVudCBhbmQgZnV0dXJlIHtAbGluayBNYXJrZG93blZpZXd9IG9mIHRoYXQgbm90ZSAoaW4gYW55IHdpbmRvdywgaW5jbHVkaW5nXG4gKiBwb3BvdXRzKSBpcyBtYWRlIHJlYWQtb25seSBhbmQgc2hvd3MgYSBsb2NrIGluZGljYXRvciBpbiBpdHMgdGFiIGhlYWRlciwgaXRzIHZpZXcgYWN0aW9uIGJhciwgYW5kXG4gKiB0aGUgc3RhdHVzIGJhciAod2hpbGUgdGhlIG5vdGUgaXMgYWN0aXZlKS4gTG9ja3MgYXJlIHJlZmVyZW5jZS1jb3VudGVkIHBlciBsb2NraW5nIHBsdWdpbiwgc29cbiAqIG5lc3RlZCBvciBjb25jdXJyZW50IG9wZXJhdGlvbnMgb24gdGhlIHNhbWUgbm90ZSBhcmUgc2FmZSBcdTIwMTQgdGhlIG5vdGUgaXMgdW5sb2NrZWQgb25seSB3aGVuIHRoZVxuICogbGFzdCBsb2NrIGlzIHJlbGVhc2VkIFx1MjAxNCBhbmQgdGhlIGluZGljYXRvcnMnIHRvb2x0aXAgbGlzdHMgd2hpY2ggcGx1Z2lucyBjdXJyZW50bHkgaG9sZCBhIGxvY2suXG4gKlxuICogVGhlIGFjcXVpcmVycyByZXR1cm4gYSB7QGxpbmsgRGlzcG9zYWJsZX0sIHNvIHRoZSBwcmVmZXJyZWQgY2FsbCBzdHlsZSBpcyBhIGB1c2luZ2AgZGVjbGFyYXRpb25cbiAqIHRoYXQgcmVsZWFzZXMgYXV0b21hdGljYWxseSBhdCBzY29wZSBleGl0IChpbmNsdWRpbmcgb24gdGhyb3cpOlxuICpcbiAqIGBgYHRzXG4gKiB1c2luZyBfbG9jayA9IGxvY2tFZGl0b3JGb3JQYXRoKGFwcCwgcGF0aCk7XG4gKiAvLyAuLi4gbG9uZy1ydW5uaW5nIHdvcmsgKHByb2Nlc3MsIHByb2Nlc3NGcm9udE1hdHRlciwgbWVyZ2Uvc3BsaXQpIC4uLlxuICogLy8gYXV0by11bmxvY2tlZCBhdCBzY29wZSBleGl0XG4gKiBgYGBcbiAqXG4gKiBUaGUgZXhwbGljaXQge0BsaW5rIHVubG9ja0VkaXRvckZvclBhdGh9IHBhaXIgcmVtYWlucyBhdmFpbGFibGUgZm9yIG5vbi1gdXNpbmdgIGNhbGwgc2l0ZXMuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBBcHAgfSBmcm9tICdvYnNpZGlhbic7XG5cbmltcG9ydCB7IFZpZXdUeXBlIH0gZnJvbSAnQG9ic2lkaWFuLXR5cGluZ3Mvb2JzaWRpYW4tcHVibGljLWxhdGVzdC9pbXBsZW1lbnRhdGlvbnMnO1xuaW1wb3J0IHtcbiAgTWFya2Rvd25WaWV3LFxuICBNZW51LFxuICBzZXRJY29uLFxuICBzZXRUb29sdGlwXG59IGZyb20gJ29ic2lkaWFuJztcblxuaW1wb3J0IHR5cGUgeyBQYXRoT3JGaWxlIH0gZnJvbSAnLi9maWxlLXN5c3RlbS50cyc7XG5cbmltcG9ydCB7IGNvbnZlcnRBc3luY1RvU3luYyB9IGZyb20gJy4uL2FzeW5jLnRzJztcbmltcG9ydCB7XG4gIENhbGxiYWNrRGlzcG9zYWJsZSxcbiAgTXVsdGlwbGVEaXNwb3NlQmVoYXZpb3Jcbn0gZnJvbSAnLi4vZGlzcG9zYWJsZS50cyc7XG5pbXBvcnQgeyBub29wIH0gZnJvbSAnLi4vZnVuY3Rpb24udHMnO1xuaW1wb3J0IHsgZ2V0T2JzaWRpYW5EZXZVdGlsc1N0YXRlIH0gZnJvbSAnLi4vb2JzaWRpYW4tZGV2LXV0aWxzLXN0YXRlLnRzJztcbmltcG9ydCB7IGFzc2VydE5vbk51bGxhYmxlIH0gZnJvbSAnLi4vdHlwZS1ndWFyZHMudHMnO1xuaW1wb3J0IHsgQ29tcG9uZW50RXggfSBmcm9tICcuL2NvbXBvbmVudHMvY29tcG9uZW50LWV4LnRzJztcbmltcG9ydCB7IHRvZ2dsZUVkaXRvclJlYWRPbmx5IH0gZnJvbSAnLi9lZGl0b3IudHMnO1xuaW1wb3J0IHsgZ2V0UGF0aCB9IGZyb20gJy4vZmlsZS1zeXN0ZW0udHMnO1xuaW1wb3J0IHsgdCB9IGZyb20gJy4vaTE4bi9pMThuLnRzJztcbmltcG9ydCB7IGNvbmZpcm0gfSBmcm9tICcuL21vZGFscy9jb25maXJtLnRzJztcbmltcG9ydCB7IGdldFBsdWdpbklkIH0gZnJvbSAnLi9wbHVnaW4vcGx1Z2luLWlkLnRzJztcblxuY29uc3QgRURJVE9SX0xPQ0tfU1RBVEVfS0VZID0gJ2VkaXRvckxvY2snO1xuY29uc3QgTE9DS19JQ09OX0lEID0gJ2xvY2snO1xuY29uc3QgTE9DS19JTkRJQ0FUT1JfQ1NTX0NMQVNTID0gJ29ic2lkaWFuLWRldi11dGlscy1sb2NrLWluZGljYXRvcic7XG5jb25zdCBTVEFUVVNfQkFSX0lURU1fQ1NTX0NMQVNTID0gJ3N0YXR1cy1iYXItaXRlbSc7XG5jb25zdCBTVEFUVVNfQkFSX0NTU19TRUxFQ1RPUiA9ICcuc3RhdHVzLWJhcic7XG5jb25zdCBVTkxPQ0tfSUNPTl9JRCA9ICd1bmxvY2snO1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHtAbGluayBFZGl0b3JMb2NrQ29tcG9uZW50LmxvY2tGb3JQYXRofS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBFZGl0b3JMb2NrQ29tcG9uZW50TG9ja0ZvclBhdGhPcHRpb25zIHtcbiAgLyoqXG4gICAqIEFuIG9wdGlvbmFsIHtAbGluayBBYm9ydENvbnRyb2xsZXJ9IGFzc29jaWF0ZWQgd2l0aCB0aGUgbG9jay4gV2hlbiB0aGUgbG9jayBpbmRpY2F0b3IgaXNcbiAgICogcmlnaHQtY2xpY2tlZCBhbmQgdGhlIHVzZXIgY29uZmlybXMgYW4gdW5sb2NrIChvciB7QGxpbmsgcmVxdWVzdEVkaXRvclVubG9ja0ZvclBhdGh9IGlzIGNhbGxlZCBmb3JcbiAgICogdGhlIHBhdGgpLCB0aGlzIGNvbnRyb2xsZXIgaXMgYWJvcnRlZCBzbyB0aGUgb3BlcmF0aW9uIGhvbGRpbmcgdGhlIGxvY2sgY2FuIGNhbmNlbCBpdHNlbGYgYW5kXG4gICAqIHJlbGVhc2UgdGhlIGxvY2sgaW4gaXRzIG93biBjbGVhbnVwLlxuICAgKi9cbiAgcmVhZG9ubHkgYWJvcnRDb250cm9sbGVyPzogQWJvcnRDb250cm9sbGVyO1xufVxuXG5pbnRlcmZhY2UgTG9ja0luZGljYXRvcnMge1xuICByZWFkb25seSBhY3Rpb25JY29uRWw6IEhUTUxFbGVtZW50O1xuICByZWFkb25seSB0YWJJY29uRWw6IEhUTUxFbGVtZW50IHwgbnVsbDtcbn1cblxuLyoqXG4gKiBTdWJzY3JpYmVzIHRvIHRoZSB3b3Jrc3BhY2UgZXZlbnRzIHRoYXQgc2hvdWxkIHRyaWdnZXIgYSBsb2NrIHJlY29uY2lsZS4gSW1wbGVtZW50ZWQgYXMgYVxuICoge0BsaW5rIENvbXBvbmVudEV4fSBzbyB0aGUgc3Vic2NyaXB0aW9ucyBhcmUgcmVnaXN0ZXJlZCBvbiBgbG9hZCgpYCBhbmQgYXV0b21hdGljYWxseSByZW1vdmVkIG9uXG4gKiBgdW5sb2FkKClgLCBpbnN0ZWFkIG9mIGJlaW5nIHRyYWNrZWQgYW5kIHRvcm4gZG93biBieSBoYW5kLlxuICovXG5jbGFzcyBFZGl0b3JMb2NrRXZlbnRzQ29tcG9uZW50IGV4dGVuZHMgQ29tcG9uZW50RXgge1xuICBwdWJsaWMgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBhcHA6IEFwcCwgcHJpdmF0ZSByZWFkb25seSBvbkNoYW5nZTogKCkgPT4gdm9pZCkge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBwdWJsaWMgb3ZlcnJpZGUgb25sb2FkKCk6IHZvaWQge1xuICAgIHN1cGVyLm9ubG9hZCgpO1xuICAgIHRoaXMucmVnaXN0ZXJFdmVudCh0aGlzLmFwcC53b3Jrc3BhY2Uub24oJ2FjdGl2ZS1sZWFmLWNoYW5nZScsIHRoaXMub25DaGFuZ2UpKTtcbiAgICB0aGlzLnJlZ2lzdGVyRXZlbnQodGhpcy5hcHAud29ya3NwYWNlLm9uKCdsYXlvdXQtY2hhbmdlJywgdGhpcy5vbkNoYW5nZSkpO1xuICAgIC8vIFNhbWUtbGVhZiBuYXZpZ2F0aW9uIHRvIGFub3RoZXIgbm90ZSBmaXJlcyBubyBsZWFmL2xheW91dCBjaGFuZ2U7IHdpdGhvdXQgdGhpcyBpdCBzdGF5cyByZWFkLW9ubHkuXG4gICAgdGhpcy5yZWdpc3RlckV2ZW50KHRoaXMuYXBwLndvcmtzcGFjZS5vbignZmlsZS1vcGVuJywgdGhpcy5vbkNoYW5nZSkpO1xuICB9XG59XG5cbi8qKlxuICogVHJhY2tzIHJlZmVyZW5jZS1jb3VudGVkLCBwYXRoLXNjb3BlZCBlZGl0b3IgbG9ja3MgYW5kIGtlZXBzIGV2ZXJ5IG9wZW4ge0BsaW5rIE1hcmtkb3duVmlld30gaW5cbiAqIHN5bmMgd2l0aCB0aGUgbG9ja2VkLXBhdGggc2V0LiBBIHNpbmdsZSBpbnN0YW5jZSBsaXZlcyBvbiB0aGUgc2hhcmVkIGBvYnNpZGlhbi1kZXYtdXRpbHNgIHN0YXRlLlxuICogRm9yIGVhY2ggbG9ja2VkIHBhdGggdGhlIGxvY2sgY291bnQgaXMgdHJhY2tlZCBwZXIgbG9ja2luZyBwbHVnaW4sIHNvIHRoZSBpbmRpY2F0b3JzIGNhbiByZXBvcnRcbiAqIHdoaWNoIHBsdWdpbnMgY3VycmVudGx5IGhvbGQgYSBsb2NrLlxuICovXG5jbGFzcyBFZGl0b3JQYXRoTG9ja01hbmFnZXIge1xuICBwcml2YXRlIHJlYWRvbmx5IGFib3J0Q29udHJvbGxlcnNCeVBhdGggPSBuZXcgTWFwPHN0cmluZywgU2V0PEFib3J0Q29udHJvbGxlcj4+KCk7XG4gIHByaXZhdGUgZXZlbnRzQ29tcG9uZW50OiBFZGl0b3JMb2NrRXZlbnRzQ29tcG9uZW50IHwgdW5kZWZpbmVkO1xuICBwcml2YXRlIHJlYWRvbmx5IGluZGljYXRvcnNCeVZpZXcgPSBuZXcgTWFwPE1hcmtkb3duVmlldywgTG9ja0luZGljYXRvcnM+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgbG9ja0NvdW50QnlQbHVnaW5JZEJ5UGF0aCA9IG5ldyBNYXA8c3RyaW5nLCBNYXA8c3RyaW5nLCBudW1iZXI+PigpO1xuICBwcml2YXRlIHN0YXR1c0Jhckl0ZW1FbDogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcblxuICBwdWJsaWMgaXNMb2NrZWQoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc1BhdGhMb2NrZWQoZ2V0UGF0aChhcHAsIHBhdGhPckZpbGUpKTtcbiAgfVxuXG4gIHB1YmxpYyBsb2NrKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlLCBwbHVnaW5JZDogc3RyaW5nLCBhYm9ydENvbnRyb2xsZXI/OiBBYm9ydENvbnRyb2xsZXIpOiBEaXNwb3NhYmxlIHtcbiAgICBjb25zdCBwYXRoID0gZ2V0UGF0aChhcHAsIHBhdGhPckZpbGUpO1xuICAgIGxldCBsb2NrQ291bnRCeVBsdWdpbklkID0gdGhpcy5sb2NrQ291bnRCeVBsdWdpbklkQnlQYXRoLmdldChwYXRoKTtcbiAgICBpZiAoIWxvY2tDb3VudEJ5UGx1Z2luSWQpIHtcbiAgICAgIGxvY2tDb3VudEJ5UGx1Z2luSWQgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xuICAgICAgdGhpcy5sb2NrQ291bnRCeVBsdWdpbklkQnlQYXRoLnNldChwYXRoLCBsb2NrQ291bnRCeVBsdWdpbklkKTtcbiAgICB9XG4gICAgbG9ja0NvdW50QnlQbHVnaW5JZC5zZXQocGx1Z2luSWQsIChsb2NrQ291bnRCeVBsdWdpbklkLmdldChwbHVnaW5JZCkgPz8gMCkgKyAxKTtcbiAgICBpZiAoYWJvcnRDb250cm9sbGVyKSB7XG4gICAgICBsZXQgYWJvcnRDb250cm9sbGVycyA9IHRoaXMuYWJvcnRDb250cm9sbGVyc0J5UGF0aC5nZXQocGF0aCk7XG4gICAgICBpZiAoIWFib3J0Q29udHJvbGxlcnMpIHtcbiAgICAgICAgYWJvcnRDb250cm9sbGVycyA9IG5ldyBTZXQ8QWJvcnRDb250cm9sbGVyPigpO1xuICAgICAgICB0aGlzLmFib3J0Q29udHJvbGxlcnNCeVBhdGguc2V0KHBhdGgsIGFib3J0Q29udHJvbGxlcnMpO1xuICAgICAgfVxuICAgICAgYWJvcnRDb250cm9sbGVycy5hZGQoYWJvcnRDb250cm9sbGVyKTtcbiAgICB9XG4gICAgdGhpcy5lbnN1cmVTdWJzY3JpYmVkKGFwcCk7XG4gICAgdGhpcy5yZWNvbmNpbGUoYXBwKTtcblxuICAgIHJldHVybiBuZXcgQ2FsbGJhY2tEaXNwb3NhYmxlKHtcbiAgICAgIGNhbGxiYWNrOiAoKTogdm9pZCA9PiB7XG4gICAgICAgIGlmIChhYm9ydENvbnRyb2xsZXIpIHtcbiAgICAgICAgICBjb25zdCBhYm9ydENvbnRyb2xsZXJzID0gdGhpcy5hYm9ydENvbnRyb2xsZXJzQnlQYXRoLmdldChwYXRoKTtcbiAgICAgICAgICAvLyBUaGUgc2V0IHdhcyBjcmVhdGVkIHdoZW4gdGhpcyBsb2NrIGFkZGVkIGl0cyBjb250cm9sbGVyIGFuZCBpcyBvbmx5IHJlbW92ZWQgaGVyZS5cbiAgICAgICAgICAvLyBJdCBpcyB0aGVyZWZvcmUgYWx3YXlzIHByZXNlbnQgd2hpbGUgdGhpcyAoc2luZ2xlLXJ1bikgZGlzcG9zYWJsZSBjYWxsYmFjayBleGVjdXRlcy5cbiAgICAgICAgICBhc3NlcnROb25OdWxsYWJsZShhYm9ydENvbnRyb2xsZXJzKTtcbiAgICAgICAgICBhYm9ydENvbnRyb2xsZXJzLmRlbGV0ZShhYm9ydENvbnRyb2xsZXIpO1xuICAgICAgICAgIGlmIChhYm9ydENvbnRyb2xsZXJzLnNpemUgPT09IDApIHtcbiAgICAgICAgICAgIHRoaXMuYWJvcnRDb250cm9sbGVyc0J5UGF0aC5kZWxldGUocGF0aCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMudW5sb2NrUGF0aChhcHAsIHBhdGgsIHBsdWdpbklkKTtcbiAgICAgIH0sXG4gICAgICBtdWx0aXBsZURpc3Bvc2VCZWhhdmlvcjogTXVsdGlwbGVEaXNwb3NlQmVoYXZpb3IuSWdub3JlXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQWJvcnRzIGV2ZXJ5IHtAbGluayBBYm9ydENvbnRyb2xsZXJ9IGN1cnJlbnRseSByZWdpc3RlcmVkIGZvciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCwgd2hpY2hcbiAgICogY2FuY2VscyB0aGUgb3BlcmF0aW9ucyBob2xkaW5nIHRoZSBsb2NrLiBFYWNoIG9wZXJhdGlvbiByZWxlYXNlcyBpdHMgb3duIGxvY2sgaW4gaXRzIGBmaW5hbGx5YFxuICAgKiB3aGVuIGl0IG9ic2VydmVzIHRoZSBhYm9ydCwgc28gdGhpcyBkb2VzIG5vdCB1bmxvY2sgdGhlIHBhdGggZGlyZWN0bHkuIEEgbm8tb3Agd2hlbiBubyBjb250cm9sbGVyXG4gICAqIGlzIHJlZ2lzdGVyZWQgZm9yIHRoZSBwYXRoLlxuICAgKlxuICAgKiBAcGFyYW0gYXBwIC0gVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZS5cbiAgICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIHJlcXVlc3QgYW4gdW5sb2NrIGZvci5cbiAgICovXG4gIHB1YmxpYyByZXF1ZXN0VW5sb2NrKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogdm9pZCB7XG4gICAgY29uc3QgYWJvcnRDb250cm9sbGVycyA9IHRoaXMuYWJvcnRDb250cm9sbGVyc0J5UGF0aC5nZXQoZ2V0UGF0aChhcHAsIHBhdGhPckZpbGUpKTtcbiAgICBpZiAoIWFib3J0Q29udHJvbGxlcnMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZm9yIChjb25zdCBjb250cm9sbGVyIG9mIGFib3J0Q29udHJvbGxlcnMpIHtcbiAgICAgIGNvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgdW5sb2NrKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlLCBwbHVnaW5JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy51bmxvY2tQYXRoKGFwcCwgZ2V0UGF0aChhcHAsIHBhdGhPckZpbGUpLCBwbHVnaW5JZCk7XG4gIH1cblxuICBwdWJsaWMgdW5sb2NrQWxsRm9yUGx1Z2luKGFwcDogQXBwLCBwbHVnaW5JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBbcGF0aCwgbG9ja0NvdW50QnlQbHVnaW5JZF0gb2YgdGhpcy5sb2NrQ291bnRCeVBsdWdpbklkQnlQYXRoKSB7XG4gICAgICBpZiAobG9ja0NvdW50QnlQbHVnaW5JZC5kZWxldGUocGx1Z2luSWQpICYmIGxvY2tDb3VudEJ5UGx1Z2luSWQuc2l6ZSA9PT0gMCkge1xuICAgICAgICB0aGlzLmxvY2tDb3VudEJ5UGx1Z2luSWRCeVBhdGguZGVsZXRlKHBhdGgpO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnJlY29uY2lsZUFuZENsZWFudXAoYXBwKTtcbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlSW5kaWNhdG9ycyhhcHA6IEFwcCwgdmlldzogTWFya2Rvd25WaWV3LCBwYXRoOiBzdHJpbmcsIHRvb2x0aXA6IHN0cmluZyk6IExvY2tJbmRpY2F0b3JzIHtcbiAgICBjb25zdCBhY3Rpb25JY29uRWwgPSB2aWV3LmFkZEFjdGlvbihMT0NLX0lDT05fSUQsIHRvb2x0aXAsIG5vb3ApO1xuICAgIHRoaXMucmVnaXN0ZXJVbmxvY2tNZW51KGFwcCwgYWN0aW9uSWNvbkVsLCAoKSA9PiBwYXRoKTtcblxuICAgIGxldCB0YWJJY29uRWw6IEhUTUxFbGVtZW50IHwgbnVsbCA9IG51bGw7XG4gICAgY29uc3QgdGFiU3RhdHVzQ29udGFpbmVyRWwgPSB2aWV3LmxlYWYudGFiSGVhZGVyU3RhdHVzQ29udGFpbmVyRWw7XG4gICAgaWYgKHRhYlN0YXR1c0NvbnRhaW5lckVsKSB7XG4gICAgICB0YWJJY29uRWwgPSB0YWJTdGF0dXNDb250YWluZXJFbC5jcmVhdGVTcGFuKHsgY2xzOiBMT0NLX0lORElDQVRPUl9DU1NfQ0xBU1MgfSk7XG4gICAgICBzZXRJY29uKHRhYkljb25FbCwgTE9DS19JQ09OX0lEKTtcbiAgICAgIHNldFRvb2x0aXAodGFiSWNvbkVsLCB0b29sdGlwKTtcbiAgICAgIHRoaXMucmVnaXN0ZXJVbmxvY2tNZW51KGFwcCwgdGFiSWNvbkVsLCAoKSA9PiBwYXRoKTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgYWN0aW9uSWNvbkVsLFxuICAgICAgdGFiSWNvbkVsXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgZW5zdXJlU3Vic2NyaWJlZChhcHA6IEFwcCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmV2ZW50c0NvbXBvbmVudCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmV2ZW50c0NvbXBvbmVudCA9IG5ldyBFZGl0b3JMb2NrRXZlbnRzQ29tcG9uZW50KGFwcCwgKCkgPT4ge1xuICAgICAgdGhpcy5yZWNvbmNpbGUoYXBwKTtcbiAgICB9KTtcbiAgICB0aGlzLmV2ZW50c0NvbXBvbmVudC5sb2FkKCk7XG4gIH1cblxuICBwcml2YXRlIGlzUGF0aExvY2tlZChwYXRoOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKHRoaXMubG9ja0NvdW50QnlQbHVnaW5JZEJ5UGF0aC5nZXQocGF0aCk/LnNpemUgPz8gMCkgPiAwO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2NraW5nUGx1Z2luTmFtZXMoYXBwOiBBcHAsIHBhdGg6IHN0cmluZyk6IHN0cmluZ1tdIHtcbiAgICBjb25zdCBsb2NrQ291bnRCeVBsdWdpbklkID0gdGhpcy5sb2NrQ291bnRCeVBsdWdpbklkQnlQYXRoLmdldChwYXRoKTtcbiAgICAvLyBgbG9ja2luZ1BsdWdpbk5hbWVzYCBpcyBvbmx5IGV2ZXIgY2FsbGVkIGZvciBhIGxvY2tlZCBwYXRoLCBzbyB0aGUgcGVyLXBsdWdpbiBtYXAgaXMgYWx3YXlzIHByZXNlbnQuXG4gICAgYXNzZXJ0Tm9uTnVsbGFibGUobG9ja0NvdW50QnlQbHVnaW5JZCk7XG4gICAgcmV0dXJuIEFycmF5LmZyb20obG9ja0NvdW50QnlQbHVnaW5JZC5rZXlzKCkpLm1hcCgocGx1Z2luSWQpID0+IGFwcC5wbHVnaW5zLm1hbmlmZXN0c1twbHVnaW5JZF0/Lm5hbWUgPz8gcGx1Z2luSWQpO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2NrVG9vbHRpcChhcHA6IEFwcCwgcGF0aDogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBoZWFkZXIgPSB0KCgkKSA9PiAkLm9ic2lkaWFuRGV2VXRpbHMuZWRpdG9yTG9jay5sb2NrZWRCeVRvb2x0aXApO1xuICAgIC8vIEEgcnVudGltZS1sZW5ndGggbGlzdCBvZiBwbHVnaW4gbmFtZXMsIG9uZSBwZXIgbGluZSB1bmRlciB0aGUgaGVhZGVyLlxuICAgIHJldHVybiBbaGVhZGVyLCAuLi50aGlzLmxvY2tpbmdQbHVnaW5OYW1lcyhhcHAsIHBhdGgpXS5qb2luKCdcXG4nKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVjb25jaWxlKGFwcDogQXBwKTogdm9pZCB7XG4gICAgY29uc3Qgdmlld3NUb0xvY2sgPSBuZXcgU2V0PE1hcmtkb3duVmlldz4oKTtcbiAgICBmb3IgKGNvbnN0IGxlYWYgb2YgYXBwLndvcmtzcGFjZS5nZXRMZWF2ZXNPZlR5cGUoVmlld1R5cGUuTWFya2Rvd24pKSB7XG4gICAgICBjb25zdCB2aWV3ID0gbGVhZi52aWV3O1xuICAgICAgaWYgKCEodmlldyBpbnN0YW5jZW9mIE1hcmtkb3duVmlldykpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBhdGggPSB2aWV3LmZpbGU/LnBhdGg7XG4gICAgICBpZiAocGF0aCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5pc1BhdGhMb2NrZWQocGF0aCkpIHtcbiAgICAgICAgdmlld3NUb0xvY2suYWRkKHZpZXcpO1xuICAgICAgICBjb25zdCB0b29sdGlwID0gdGhpcy5sb2NrVG9vbHRpcChhcHAsIHBhdGgpO1xuICAgICAgICAvLyBSZS1hcHBseSB0aGUgcmVhZC1vbmx5IHRvZ2dsZSBvbiBldmVyeSByZWNvbmNpbGUsIG5vdCBvbmx5IHRoZSBmaXJzdCB0aW1lIGEgdmlldyBpcyB0cmFja2VkLlxuICAgICAgICAvLyBUaGUgdG9nZ2xlIGlzIGlkZW1wb3RlbnQsIHNvIHJlLWFwcGx5aW5nIHRvIGFuIGFscmVhZHktbG9ja2VkIHZpZXcgaXMgY2hlYXAuXG4gICAgICAgIC8vIEEgdmlldyBvcGVuZWQgYWZ0ZXIgdGhlIGxvY2sgaXMgcmVjb25jaWxlZCBzeW5jaHJvbm91c2x5LCBiZWZvcmUgaXRzIENvZGVNaXJyb3IgaXMgcmVhZHkuXG4gICAgICAgIC8vIEl0cyBmaXJzdCB0b2dnbGUgaXMgdGhlcmVmb3JlIGEgbm8tb3A7IHJlLWFwcGx5aW5nIG1ha2VzIHRoZSBsb2NrIHRha2UgaG9sZCBvbmNlIGl0IHNldHRsZXMuXG4gICAgICAgIHRvZ2dsZUVkaXRvclJlYWRPbmx5KHZpZXcuZWRpdG9yLCB0cnVlKTtcbiAgICAgICAgY29uc3QgaW5kaWNhdG9ycyA9IHRoaXMuaW5kaWNhdG9yc0J5Vmlldy5nZXQodmlldyk7XG4gICAgICAgIGlmIChpbmRpY2F0b3JzKSB7XG4gICAgICAgICAgdGhpcy51cGRhdGVJbmRpY2F0b3JUb29sdGlwcyhpbmRpY2F0b3JzLCB0b29sdGlwKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLmluZGljYXRvcnNCeVZpZXcuc2V0KHZpZXcsIHRoaXMuY3JlYXRlSW5kaWNhdG9ycyhhcHAsIHZpZXcsIHBhdGgsIHRvb2x0aXApKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGZvciAoY29uc3QgW3ZpZXcsIGluZGljYXRvcnNdIG9mIHRoaXMuaW5kaWNhdG9yc0J5Vmlldykge1xuICAgICAgaWYgKCF2aWV3c1RvTG9jay5oYXModmlldykpIHtcbiAgICAgICAgdG9nZ2xlRWRpdG9yUmVhZE9ubHkodmlldy5lZGl0b3IsIGZhbHNlKTtcbiAgICAgICAgaW5kaWNhdG9ycy5hY3Rpb25JY29uRWwucmVtb3ZlKCk7XG4gICAgICAgIGluZGljYXRvcnMudGFiSWNvbkVsPy5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy5pbmRpY2F0b3JzQnlWaWV3LmRlbGV0ZSh2aWV3KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLnVwZGF0ZVN0YXR1c0JhcihhcHApO1xuICB9XG5cbiAgcHJpdmF0ZSByZWNvbmNpbGVBbmRDbGVhbnVwKGFwcDogQXBwKTogdm9pZCB7XG4gICAgdGhpcy5yZWNvbmNpbGUoYXBwKTtcbiAgICBpZiAodGhpcy5sb2NrQ291bnRCeVBsdWdpbklkQnlQYXRoLnNpemUgPT09IDApIHtcbiAgICAgIHRoaXMuZXZlbnRzQ29tcG9uZW50Py51bmxvYWQoKTtcbiAgICAgIHRoaXMuZXZlbnRzQ29tcG9uZW50ID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgcmVnaXN0ZXJVbmxvY2tNZW51KGFwcDogQXBwLCBlbDogSFRNTEVsZW1lbnQsIGdldENvbnRleHRQYXRoOiAoKSA9PiBzdHJpbmcgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICAvLyBBIHBsYWluIGxpc3RlbmVyIGRpZXMgd2l0aCB0aGUgZWxlbWVudCB3aGVuIGl0IGlzIGAucmVtb3ZlKClgZCBvbiB1bmxvY2ssIHNvIGl0IG5ldmVyIGxlYWtzLlxuICAgIGVsLmFkZEV2ZW50TGlzdGVuZXIoJ2NvbnRleHRtZW51JywgKGV2dCkgPT4ge1xuICAgICAgZXZ0LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBjb25zdCBwYXRoID0gZ2V0Q29udGV4dFBhdGgoKTtcbiAgICAgIGlmIChwYXRoID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgbWVudSA9IG5ldyBNZW51KCk7XG4gICAgICBtZW51LmFkZEl0ZW0oKGl0ZW0pID0+IHtcbiAgICAgICAgaXRlbVxuICAgICAgICAgIC5zZXRUaXRsZSh0KCgkKSA9PiAkLm9ic2lkaWFuRGV2VXRpbHMuZWRpdG9yTG9jay51bmxvY2tNZW51SXRlbSkpXG4gICAgICAgICAgLnNldEljb24oVU5MT0NLX0lDT05fSUQpXG4gICAgICAgICAgLm9uQ2xpY2soY29udmVydEFzeW5jVG9TeW5jKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGlzQ29uZmlybWVkID0gYXdhaXQgY29uZmlybSh7XG4gICAgICAgICAgICAgIGFwcCxcbiAgICAgICAgICAgICAgbWVzc2FnZTogdGhpcy51bmxvY2tDb25maXJtTWVzc2FnZShhcHAsIHBhdGgpLFxuICAgICAgICAgICAgICB0aXRsZTogdCgoJCkgPT4gJC5vYnNpZGlhbkRldlV0aWxzLmVkaXRvckxvY2sudW5sb2NrQ29uZmlybVRpdGxlKVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBpZiAoaXNDb25maXJtZWQpIHtcbiAgICAgICAgICAgICAgdGhpcy5yZXF1ZXN0VW5sb2NrKGFwcCwgcGF0aCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSkpO1xuICAgICAgfSk7XG4gICAgICBtZW51LnNob3dBdE1vdXNlRXZlbnQoZXZ0KTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgdW5sb2NrQ29uZmlybU1lc3NhZ2UoYXBwOiBBcHAsIHBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgaGVhZGVyID0gdCgoJCkgPT4gJC5vYnNpZGlhbkRldlV0aWxzLmVkaXRvckxvY2sudW5sb2NrQ29uZmlybU1lc3NhZ2UpO1xuICAgIC8vIEEgcnVudGltZS1sZW5ndGggbGlzdCBvZiB0aGUgcGx1Z2lucyBjdXJyZW50bHkgaG9sZGluZyBhIGxvY2ssIG9uZSBwZXIgbGluZSB1bmRlciB0aGUgaGVhZGVyLlxuICAgIHJldHVybiBbaGVhZGVyLCAuLi50aGlzLmxvY2tpbmdQbHVnaW5OYW1lcyhhcHAsIHBhdGgpXS5qb2luKCdcXG4nKTtcbiAgfVxuXG4gIHByaXZhdGUgdW5sb2NrUGF0aChhcHA6IEFwcCwgcGF0aDogc3RyaW5nLCBwbHVnaW5JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgbG9ja0NvdW50QnlQbHVnaW5JZCA9IHRoaXMubG9ja0NvdW50QnlQbHVnaW5JZEJ5UGF0aC5nZXQocGF0aCk7XG4gICAgaWYgKCFsb2NrQ291bnRCeVBsdWdpbklkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgY291bnQgPSBsb2NrQ291bnRCeVBsdWdpbklkLmdldChwbHVnaW5JZCkgPz8gMDtcbiAgICBpZiAoY291bnQgPD0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChjb3VudCA9PT0gMSkge1xuICAgICAgbG9ja0NvdW50QnlQbHVnaW5JZC5kZWxldGUocGx1Z2luSWQpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2NrQ291bnRCeVBsdWdpbklkLnNldChwbHVnaW5JZCwgY291bnQgLSAxKTtcbiAgICB9XG5cbiAgICBpZiAobG9ja0NvdW50QnlQbHVnaW5JZC5zaXplID09PSAwKSB7XG4gICAgICB0aGlzLmxvY2tDb3VudEJ5UGx1Z2luSWRCeVBhdGguZGVsZXRlKHBhdGgpO1xuICAgIH1cblxuICAgIHRoaXMucmVjb25jaWxlQW5kQ2xlYW51cChhcHApO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVJbmRpY2F0b3JUb29sdGlwcyhpbmRpY2F0b3JzOiBMb2NrSW5kaWNhdG9ycywgdG9vbHRpcDogc3RyaW5nKTogdm9pZCB7XG4gICAgc2V0VG9vbHRpcChpbmRpY2F0b3JzLmFjdGlvbkljb25FbCwgdG9vbHRpcCk7XG4gICAgaWYgKGluZGljYXRvcnMudGFiSWNvbkVsKSB7XG4gICAgICBzZXRUb29sdGlwKGluZGljYXRvcnMudGFiSWNvbkVsLCB0b29sdGlwKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZVN0YXR1c0JhcihhcHA6IEFwcCk6IHZvaWQge1xuICAgIGNvbnN0IGFjdGl2ZVZpZXcgPSBhcHAud29ya3NwYWNlLmdldEFjdGl2ZVZpZXdPZlR5cGUoTWFya2Rvd25WaWV3KTtcbiAgICBjb25zdCBhY3RpdmVQYXRoID0gYWN0aXZlVmlldz8uZmlsZT8ucGF0aDtcbiAgICBjb25zdCBpc0FjdGl2ZUxvY2tlZCA9IGFjdGl2ZVBhdGggIT09IHVuZGVmaW5lZCAmJiB0aGlzLmlzUGF0aExvY2tlZChhY3RpdmVQYXRoKTtcblxuICAgIGlmICghaXNBY3RpdmVMb2NrZWQgfHwgIWFjdGl2ZVZpZXcpIHtcbiAgICAgIHRoaXMuc3RhdHVzQmFySXRlbUVsPy5yZW1vdmUoKTtcbiAgICAgIHRoaXMuc3RhdHVzQmFySXRlbUVsID0gbnVsbDtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuc3RhdHVzQmFySXRlbUVsKSB7XG4gICAgICBjb25zdCBzdGF0dXNCYXJFbCA9IGFjdGl2ZVZpZXcuY29udGFpbmVyRWwub3duZXJEb2N1bWVudC5ib2R5LnF1ZXJ5U2VsZWN0b3I8SFRNTEVsZW1lbnQ+KFNUQVRVU19CQVJfQ1NTX1NFTEVDVE9SKTtcbiAgICAgIGlmICghc3RhdHVzQmFyRWwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5zdGF0dXNCYXJJdGVtRWwgPSBzdGF0dXNCYXJFbC5jcmVhdGVEaXYoeyBjbHM6IFtTVEFUVVNfQkFSX0lURU1fQ1NTX0NMQVNTLCBMT0NLX0lORElDQVRPUl9DU1NfQ0xBU1NdIH0pO1xuICAgICAgc2V0SWNvbih0aGlzLnN0YXR1c0Jhckl0ZW1FbCwgTE9DS19JQ09OX0lEKTtcbiAgICAgIC8vIFRoZSBzaW5nbGUgc3RhdHVzLWJhciBpdGVtIGlzIHJldXNlZCBhY3Jvc3MgYWN0aXZlLW5vdGUgc3dpdGNoZXMuXG4gICAgICAvLyBJdCBtdXN0IHJlc29sdmUgdGhlIGN1cnJlbnRseSBhY3RpdmUgbm90ZSdzIHBhdGggYXQgY2xpY2sgdGltZSByYXRoZXIgdGhhbiBjYXB0dXJpbmcgb25lIHBhdGguXG4gICAgICB0aGlzLnJlZ2lzdGVyVW5sb2NrTWVudShhcHAsIHRoaXMuc3RhdHVzQmFySXRlbUVsLCAoKSA9PiBhcHAud29ya3NwYWNlLmdldEFjdGl2ZVZpZXdPZlR5cGUoTWFya2Rvd25WaWV3KT8uZmlsZT8ucGF0aCk7XG4gICAgfVxuXG4gICAgc2V0VG9vbHRpcCh0aGlzLnN0YXR1c0Jhckl0ZW1FbCwgdGhpcy5sb2NrVG9vbHRpcChhcHAsIGFjdGl2ZVBhdGgpKTtcbiAgfVxufVxuXG4vKipcbiAqIEEgcGVyLXBsdWdpbiBoYW5kbGUgZm9yIG5vdGUtc2NvcGVkIGVkaXRvciBsb2NraW5nLiBBZGQgaXQgYXMgYSBjaGlsZCBvZiB5b3VyIHBsdWdpblxuICogKGB0aGlzLmFkZENoaWxkKG5ldyBFZGl0b3JMb2NrQ29tcG9uZW50KHRoaXMuYXBwKSlgKSBzbyB0aGF0IGFueSBsb2NrcyBpdCBzdGlsbCBob2xkcyBhcmUgcmVsZWFzZWRcbiAqIGF1dG9tYXRpY2FsbHkgd2hlbiB0aGUgcGx1Z2luIHVubG9hZHMgXHUyMDE0IGEgbm90ZSBjYW4gbmV2ZXIgYmUgbGVmdCBzdHVjayByZWFkLW9ubHkgYmVjYXVzZSB0aGVcbiAqIHBsdWdpbiB0aGF0IGxvY2tlZCBpdCB3YXMgZGlzYWJsZWQgb3IgcmVsb2FkZWQgbWlkLW9wZXJhdGlvbi5cbiAqXG4gKiBMb2NrcyBhcmUgcmVmZXJlbmNlLWNvdW50ZWQgYW5kIGF0dHJpYnV0ZWQgdG8gdGhpcyBwbHVnaW4sIHNvIHRoZSBsb2NrIGluZGljYXRvcnMnIHRvb2x0aXAgbmFtZXNcbiAqIGl0IGFtb25nIHRoZSBwbHVnaW5zIGN1cnJlbnRseSBob2xkaW5nIGEgbG9jay5cbiAqL1xuZXhwb3J0IGNsYXNzIEVkaXRvckxvY2tDb21wb25lbnQgZXh0ZW5kcyBDb21wb25lbnRFeCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgYXBwOiBBcHA7XG4gIHByaXZhdGUgcmVhZG9ubHkgcGx1Z2luSWQ6IHN0cmluZztcblxuICAvKipcbiAgICogQ3JlYXRlcyBhbiBlZGl0b3ItbG9jayBoYW5kbGUgb3duZWQgYnkgYSBwbHVnaW4uXG4gICAqXG4gICAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICAgKiBAcGFyYW0gcGx1Z2luSWQgLSBUaGUgaWQgb2YgdGhlIG93bmluZyBwbHVnaW4uIERlZmF1bHRzIHRvIHRoZSBjdXJyZW50IHBsdWdpbiBjb250ZXh0J3MgaWQ7IHBhc3NcbiAgICogaXQgZXhwbGljaXRseSAoZS5nLiBmcm9tIGEgcGx1Z2luJ3MgYG1hbmlmZXN0LmlkYCkgd2hlbiBjb25zdHJ1Y3RpbmcgYmVmb3JlIHRoZSBwbHVnaW4gY29udGV4dCBpc1xuICAgKiBpbml0aWFsaXplZCwgc3VjaCBhcyBpbnNpZGUgYFBsdWdpbkJhc2Uub25sb2FkYC5cbiAgICovXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihhcHA6IEFwcCwgcGx1Z2luSWQ6IHN0cmluZyA9IGdldFBsdWdpbklkKCkpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMuYXBwID0gYXBwO1xuICAgIHRoaXMucGx1Z2luSWQgPSBwbHVnaW5JZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3Mgd2hldGhlciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCBpcyBjdXJyZW50bHkgbG9ja2VkIGJ5IGFueSBwbHVnaW4uXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byBjaGVjay5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBub3RlIGhhcyBhdCBsZWFzdCBvbmUgYWN0aXZlIGxvY2ssIGBmYWxzZWAgb3RoZXJ3aXNlLlxuICAgKi9cbiAgcHVibGljIGlzTG9ja2VkRm9yUGF0aChwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGdldE1hbmFnZXIoKS5pc0xvY2tlZCh0aGlzLmFwcCwgcGF0aE9yRmlsZSk7XG4gIH1cblxuICAvKipcbiAgICogTG9ja3MgdGhlIG5vdGUgYXQgdGhlIGdpdmVuIHBhdGggb24gYmVoYWxmIG9mIHRoaXMgcGx1Z2luLCBtYWtpbmcgaXQgcmVhZC1vbmx5IGluIGV2ZXJ5IGN1cnJlbnRcbiAgICogYW5kIGZ1dHVyZSB7QGxpbmsgTWFya2Rvd25WaWV3fSB1bnRpbCB0aGUgbG9jayBpcyByZWxlYXNlZC4gUmVmZXJlbmNlLWNvdW50ZWQ6IGJhbGFuY2UgZWFjaCBjYWxsXG4gICAqIHdpdGggYSBkaXNwb3NlIG9mIHRoZSByZXR1cm5lZCB7QGxpbmsgRGlzcG9zYWJsZX0gKGlkZWFsbHkgdmlhIGB1c2luZ2ApIG9yIHtAbGluayB1bmxvY2tGb3JQYXRofS5cbiAgICpcbiAgICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIGxvY2suXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gT3B0aW9uYWwgbG9ja2luZyBvcHRpb25zLiBQYXNzIGFuXG4gICAqIHtAbGluayBFZGl0b3JMb2NrQ29tcG9uZW50TG9ja0ZvclBhdGhPcHRpb25zLmFib3J0Q29udHJvbGxlcn0gdG8gbWFrZSB0aGUgbG9jayBjYW5jZWxhYmxlIHZpYSB0aGVcbiAgICogaW5kaWNhdG9yJ3MgcmlnaHQtY2xpY2sgXCJ1bmxvY2tcIiBtZW51IG9yIHtAbGluayByZXF1ZXN0RWRpdG9yVW5sb2NrRm9yUGF0aH0uXG4gICAqIEByZXR1cm5zIEEge0BsaW5rIERpc3Bvc2FibGV9IHRoYXQgcmVsZWFzZXMgdGhpcyBsb2NrIHdoZW4gZGlzcG9zZWQuIERpc3Bvc2luZyBtb3JlIHRoYW4gb25jZSBpcyBhIG5vLW9wLlxuICAgKi9cbiAgcHVibGljIGxvY2tGb3JQYXRoKHBhdGhPckZpbGU6IFBhdGhPckZpbGUsIG9wdGlvbnM/OiBFZGl0b3JMb2NrQ29tcG9uZW50TG9ja0ZvclBhdGhPcHRpb25zKTogRGlzcG9zYWJsZSB7XG4gICAgcmV0dXJuIGdldE1hbmFnZXIoKS5sb2NrKHRoaXMuYXBwLCBwYXRoT3JGaWxlLCB0aGlzLnBsdWdpbklkLCBvcHRpb25zPy5hYm9ydENvbnRyb2xsZXIpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2VzIGV2ZXJ5IGxvY2sgc3RpbGwgaGVsZCBieSB0aGlzIHBsdWdpbiB3aGVuIHRoZSBjb21wb25lbnQgKGFuZCB0aHVzIHRoZSBwbHVnaW4pIHVubG9hZHMsXG4gICAqIHNvIG5vIG5vdGUgaXMgbGVmdCBzdHVjayByZWFkLW9ubHkgYnkgYW4gb3BlcmF0aW9uIHRoYXQgbmV2ZXIgY29tcGxldGVkLlxuICAgKi9cbiAgcHVibGljIG92ZXJyaWRlIG9udW5sb2FkKCk6IHZvaWQge1xuICAgIGdldE1hbmFnZXIoKS51bmxvY2tBbGxGb3JQbHVnaW4odGhpcy5hcHAsIHRoaXMucGx1Z2luSWQpO1xuICAgIHN1cGVyLm9udW5sb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogUmVsZWFzZXMgb25lIGxvY2sgcHJldmlvdXNseSBhY3F1aXJlZCBmb3IgdGhlIG5vdGUgYXQgdGhlIGdpdmVuIHBhdGggdmlhIHtAbGluayBsb2NrRm9yUGF0aH0uXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byB1bmxvY2suXG4gICAqL1xuICBwdWJsaWMgdW5sb2NrRm9yUGF0aChwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogdm9pZCB7XG4gICAgZ2V0TWFuYWdlcigpLnVubG9jayh0aGlzLmFwcCwgcGF0aE9yRmlsZSwgdGhpcy5wbHVnaW5JZCk7XG4gIH1cbn1cblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCBpcyBjdXJyZW50bHkgbG9ja2VkLlxuICpcbiAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIGNoZWNrLlxuICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBub3RlIGhhcyBhdCBsZWFzdCBvbmUgYWN0aXZlIGxvY2ssIGBmYWxzZWAgb3RoZXJ3aXNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNFZGl0b3JMb2NrZWRGb3JQYXRoKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogYm9vbGVhbiB7XG4gIHJldHVybiBnZXRNYW5hZ2VyKCkuaXNMb2NrZWQoYXBwLCBwYXRoT3JGaWxlKTtcbn1cblxuLyoqXG4gKiBMb2NrcyB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCwgbWFraW5nIGl0IHJlYWQtb25seSBpbiBldmVyeSBjdXJyZW50IGFuZCBmdXR1cmVcbiAqIHtAbGluayBNYXJrZG93blZpZXd9IHVudGlsIHRoZSBsb2NrIGlzIHJlbGVhc2VkLCBhbmQgc2hvd2luZyBhIGxvY2sgaW5kaWNhdG9yIGluIHRoZSB0YWIgaGVhZGVyLFxuICogdmlldyBhY3Rpb24gYmFyLCBhbmQgKHdoaWxlIGFjdGl2ZSkgdGhlIHN0YXR1cyBiYXIuIFRoZSBpbmRpY2F0b3JzJyB0b29sdGlwIGxpc3RzIHRoZSBwbHVnaW5zXG4gKiB0aGF0IGN1cnJlbnRseSBob2xkIGEgbG9jayBvbiB0aGUgbm90ZS5cbiAqXG4gKiBUaGUgbG9jayBpcyByZWZlcmVuY2UtY291bnRlZCBwZXIgY2FsbGluZyBwbHVnaW46IGVhY2ggY2FsbCBtdXN0IGJlIGJhbGFuY2VkIGJ5IGV4YWN0bHkgb25lXG4gKiByZWxlYXNlIChlaXRoZXIgZGlzcG9zaW5nIHRoZSByZXR1cm5lZCB7QGxpbmsgRGlzcG9zYWJsZX0gXHUyMDE0IGlkZWFsbHkgdmlhIGEgYHVzaW5nYCBkZWNsYXJhdGlvbiBcdTIwMTQgb3JcbiAqIGEgbWF0Y2hpbmcge0BsaW5rIHVubG9ja0VkaXRvckZvclBhdGh9IGNhbGwpLlxuICpcbiAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIGxvY2suXG4gKiBAcmV0dXJucyBBIHtAbGluayBEaXNwb3NhYmxlfSB0aGF0IHJlbGVhc2VzIHRoaXMgbG9jayB3aGVuIGRpc3Bvc2VkLiBEaXNwb3NpbmcgbW9yZSB0aGFuIG9uY2UgaXMgYSBuby1vcC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxvY2tFZGl0b3JGb3JQYXRoKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogRGlzcG9zYWJsZSB7XG4gIHJldHVybiBnZXRNYW5hZ2VyKCkubG9jayhhcHAsIHBhdGhPckZpbGUsIGdldFBsdWdpbklkKCkpO1xufVxuXG4vKipcbiAqIFJlcXVlc3RzIGFuIHVubG9jayBvZiB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCBieSBhYm9ydGluZyBldmVyeSB7QGxpbmsgQWJvcnRDb250cm9sbGVyfSB0aGF0IHdhc1xuICogYXNzb2NpYXRlZCB3aXRoIGEgbG9jayBvbiBpdCAodmlhIHtAbGluayBFZGl0b3JMb2NrQ29tcG9uZW50LmxvY2tGb3JQYXRofSdzXG4gKiB7QGxpbmsgRWRpdG9yTG9ja0NvbXBvbmVudExvY2tGb3JQYXRoT3B0aW9ucy5hYm9ydENvbnRyb2xsZXJ9KS4gVGhlIG9wZXJhdGlvbnMgaG9sZGluZyB0aGUgbG9ja1xuICogb2JzZXJ2ZSB0aGUgYWJvcnQgYW5kIHJlbGVhc2UgdGhlaXIgb3duIGxvY2tzLiBBIG5vLW9wIHdoZW4gbm8gYWJvcnRhYmxlIGxvY2sgaXMgcmVnaXN0ZXJlZCBmb3IgdGhlXG4gKiBwYXRoLlxuICpcbiAqIFRoaXMgbGV0cyBhIGNvbnN1bWluZyBwbHVnaW4gd2lyZSBpdHMgb3duIFwidW5sb2NrIGFjdGl2ZSBub3RlXCIgY29tbWFuZCB3aXRob3V0IHJlYWNoaW5nIGludG8gdGhlXG4gKiBsb2NrIG1hbmFnZXIgZGlyZWN0bHkuXG4gKlxuICogQHBhcmFtIGFwcCAtIFRoZSBPYnNpZGlhbiBhcHAgaW5zdGFuY2UuXG4gKiBAcGFyYW0gcGF0aE9yRmlsZSAtIFRoZSBwYXRoIG9yIGZpbGUgb2YgdGhlIG5vdGUgdG8gcmVxdWVzdCBhbiB1bmxvY2sgZm9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVxdWVzdEVkaXRvclVubG9ja0ZvclBhdGgoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUpOiB2b2lkIHtcbiAgZ2V0TWFuYWdlcigpLnJlcXVlc3RVbmxvY2soYXBwLCBwYXRoT3JGaWxlKTtcbn1cblxuLyoqXG4gKiBSZWxlYXNlcyBvbmUgbG9jayBwcmV2aW91c2x5IGFjcXVpcmVkIGZvciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCB2aWEge0BsaW5rIGxvY2tFZGl0b3JGb3JQYXRofS5cbiAqXG4gKiBXaGVuIHRoZSBsYXN0IGxvY2sgaXMgcmVsZWFzZWQgdGhlIG5vdGUgYmVjb21lcyBmdWxseSBlZGl0YWJsZSBhZ2FpbiBhbmQgaXRzIGxvY2sgaW5kaWNhdG9ycyBhcmVcbiAqIHJlbW92ZWQuIENhbGxpbmcgdGhpcyB3aGVuIHRoZSBub3RlIGlzIG5vdCBsb2NrZWQgaXMgYSBuby1vcC5cbiAqXG4gKiBAcGFyYW0gYXBwIC0gVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZS5cbiAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byB1bmxvY2suXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1bmxvY2tFZGl0b3JGb3JQYXRoKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogdm9pZCB7XG4gIGdldE1hbmFnZXIoKS51bmxvY2soYXBwLCBwYXRoT3JGaWxlLCBnZXRQbHVnaW5JZCgpKTtcbn1cblxuZnVuY3Rpb24gZ2V0TWFuYWdlcigpOiBFZGl0b3JQYXRoTG9ja01hbmFnZXIge1xuICByZXR1cm4gZ2V0T2JzaWRpYW5EZXZVdGlsc1N0YXRlPEVkaXRvclBhdGhMb2NrTWFuYWdlcj4oRURJVE9SX0xPQ0tfU1RBVEVf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
@@ -4,12 +4,13 @@
4
4
  *
5
5
  * Note-scoped, reference-counted editor locking.
6
6
  *
7
- * Where {@link lockEditor} / {@link unlockEditor} from `./editor.ts` toggle the read-only state of a
8
- * single {@link Editor} instance, the helpers here lock a note by its **path**: while a path is
7
+ * Where {@link toggleEditorReadOnly} from `./editor.ts` toggles the read-only state of a single
8
+ * {@link Editor} instance, the helpers here lock a note by its **path**: while a path is
9
9
  * locked, every current and future {@link MarkdownView} of that note (in any window, including
10
- * popouts) is made read-only and shows a lock icon. Locks are reference-counted, so nested or
11
- * concurrent operations on the same note are safe — the note is unlocked only when the last lock is
12
- * released.
10
+ * popouts) is made read-only and shows a lock indicator in its tab header, its view action bar, and
11
+ * the status bar (while the note is active). Locks are reference-counted per locking plugin, so
12
+ * nested or concurrent operations on the same note are safe — the note is unlocked only when the
13
+ * last lock is released — and the indicators' tooltip lists which plugins currently hold a lock.
13
14
  *
14
15
  * The acquirers return a {@link Disposable}, so the preferred call style is a `using` declaration
15
16
  * that releases automatically at scope exit (including on throw):
@@ -24,6 +25,71 @@
24
25
  */
25
26
  import type { App } from 'obsidian' with { 'resolution-mode': 'import' };
26
27
  import type { PathOrFile } from './file-system.cjs';
28
+ import { ComponentEx } from './components/component-ex.cjs';
29
+ /**
30
+ * Options for {@link EditorLockComponent.lockForPath}.
31
+ */
32
+ export interface EditorLockComponentLockForPathOptions {
33
+ /**
34
+ * An optional {@link AbortController} associated with the lock. When the lock indicator is
35
+ * right-clicked and the user confirms an unlock (or {@link requestEditorUnlockForPath} is called for
36
+ * the path), this controller is aborted so the operation holding the lock can cancel itself and
37
+ * release the lock in its own cleanup.
38
+ */
39
+ readonly abortController?: AbortController;
40
+ }
41
+ /**
42
+ * A per-plugin handle for note-scoped editor locking. Add it as a child of your plugin
43
+ * (`this.addChild(new EditorLockComponent(this.app))`) so that any locks it still holds are released
44
+ * automatically when the plugin unloads — a note can never be left stuck read-only because the
45
+ * plugin that locked it was disabled or reloaded mid-operation.
46
+ *
47
+ * Locks are reference-counted and attributed to this plugin, so the lock indicators' tooltip names
48
+ * it among the plugins currently holding a lock.
49
+ */
50
+ export declare class EditorLockComponent extends ComponentEx {
51
+ private readonly app;
52
+ private readonly pluginId;
53
+ /**
54
+ * Creates an editor-lock handle owned by a plugin.
55
+ *
56
+ * @param app - The Obsidian app instance.
57
+ * @param pluginId - The id of the owning plugin. Defaults to the current plugin context's id; pass
58
+ * it explicitly (e.g. from a plugin's `manifest.id`) when constructing before the plugin context is
59
+ * initialized, such as inside `PluginBase.onload`.
60
+ */
61
+ constructor(app: App, pluginId?: string);
62
+ /**
63
+ * Checks whether the note at the given path is currently locked by any plugin.
64
+ *
65
+ * @param pathOrFile - The path or file of the note to check.
66
+ * @returns `true` if the note has at least one active lock, `false` otherwise.
67
+ */
68
+ isLockedForPath(pathOrFile: PathOrFile): boolean;
69
+ /**
70
+ * Locks the note at the given path on behalf of this plugin, making it read-only in every current
71
+ * and future {@link MarkdownView} until the lock is released. Reference-counted: balance each call
72
+ * with a dispose of the returned {@link Disposable} (ideally via `using`) or {@link unlockForPath}.
73
+ *
74
+ * @param pathOrFile - The path or file of the note to lock.
75
+ * @param options - Optional locking options. Pass an
76
+ * {@link EditorLockComponentLockForPathOptions.abortController} to make the lock cancelable via the
77
+ * indicator's right-click "unlock" menu or {@link requestEditorUnlockForPath}.
78
+ * @returns A {@link Disposable} that releases this lock when disposed. Disposing more than once is a no-op.
79
+ */
80
+ lockForPath(pathOrFile: PathOrFile, options?: EditorLockComponentLockForPathOptions): Disposable;
81
+ /**
82
+ * Releases every lock still held by this plugin when the component (and thus the plugin) unloads,
83
+ * so no note is left stuck read-only by an operation that never completed.
84
+ */
85
+ onunload(): void;
86
+ /**
87
+ * Releases one lock previously acquired for the note at the given path via {@link lockForPath}.
88
+ *
89
+ * @param pathOrFile - The path or file of the note to unlock.
90
+ */
91
+ unlockForPath(pathOrFile: PathOrFile): void;
92
+ }
27
93
  /**
28
94
  * Checks whether the note at the given path is currently locked.
29
95
  *
@@ -34,22 +100,38 @@ import type { PathOrFile } from './file-system.cjs';
34
100
  export declare function isEditorLockedForPath(app: App, pathOrFile: PathOrFile): boolean;
35
101
  /**
36
102
  * Locks the note at the given path, making it read-only in every current and future
37
- * {@link MarkdownView} until the lock is released.
103
+ * {@link MarkdownView} until the lock is released, and showing a lock indicator in the tab header,
104
+ * view action bar, and (while active) the status bar. The indicators' tooltip lists the plugins
105
+ * that currently hold a lock on the note.
38
106
  *
39
- * The lock is reference-counted: each call must be balanced by exactly one release (either disposing
40
- * the returned {@link Disposable} — ideally via a `using` declaration — or a matching
41
- * {@link unlockEditorForPath} call).
107
+ * The lock is reference-counted per calling plugin: each call must be balanced by exactly one
108
+ * release (either disposing the returned {@link Disposable} — ideally via a `using` declaration — or
109
+ * a matching {@link unlockEditorForPath} call).
42
110
  *
43
111
  * @param app - The Obsidian app instance.
44
112
  * @param pathOrFile - The path or file of the note to lock.
45
113
  * @returns A {@link Disposable} that releases this lock when disposed. Disposing more than once is a no-op.
46
114
  */
47
115
  export declare function lockEditorForPath(app: App, pathOrFile: PathOrFile): Disposable;
116
+ /**
117
+ * Requests an unlock of the note at the given path by aborting every {@link AbortController} that was
118
+ * associated with a lock on it (via {@link EditorLockComponent.lockForPath}'s
119
+ * {@link EditorLockComponentLockForPathOptions.abortController}). The operations holding the lock
120
+ * observe the abort and release their own locks. A no-op when no abortable lock is registered for the
121
+ * path.
122
+ *
123
+ * This lets a consuming plugin wire its own "unlock active note" command without reaching into the
124
+ * lock manager directly.
125
+ *
126
+ * @param app - The Obsidian app instance.
127
+ * @param pathOrFile - The path or file of the note to request an unlock for.
128
+ */
129
+ export declare function requestEditorUnlockForPath(app: App, pathOrFile: PathOrFile): void;
48
130
  /**
49
131
  * Releases one lock previously acquired for the note at the given path via {@link lockEditorForPath}.
50
132
  *
51
- * When the last lock is released the note becomes fully editable again and its lock icon is removed.
52
- * Calling this when the note is not locked is a no-op.
133
+ * When the last lock is released the note becomes fully editable again and its lock indicators are
134
+ * removed. Calling this when the note is not locked is a no-op.
53
135
  *
54
136
  * @param app - The Obsidian app instance.
55
137
  * @param pathOrFile - The path or file of the note to unlock.