obsidian-dev-utils 83.3.0 → 84.0.0
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.
- package/CHANGELOG.md +26 -0
- package/dist/dev/main.js +1780 -1187
- package/dist/lib/cjs/generated-during-build.cjs +2 -2
- package/dist/lib/cjs/obsidian/components/plugin-notice-component.cjs +6 -4
- package/dist/lib/cjs/obsidian/components/rename-delete-handler-component.cjs +10 -10
- package/dist/lib/cjs/obsidian/components/rename-delete-handler-component.d.cts +3 -3
- package/dist/lib/cjs/obsidian/editor.cjs +1 -1
- package/dist/lib/cjs/obsidian/editor.d.cts +2 -2
- package/dist/lib/cjs/obsidian/file-manager.cjs +9 -9
- package/dist/lib/cjs/obsidian/file-manager.d.cts +9 -9
- package/dist/lib/cjs/obsidian/i18n/locales/en.cjs +8 -8
- package/dist/lib/cjs/obsidian/i18n/locales/en.d.cts +7 -7
- package/dist/lib/cjs/obsidian/i18n/locales/translations-map.d.cts +7 -7
- package/dist/lib/cjs/obsidian/index.cjs +7 -4
- package/dist/lib/cjs/obsidian/index.d.cts +2 -1
- package/dist/lib/cjs/obsidian/markdown-code-block-processor.cjs +7 -7
- package/dist/lib/cjs/obsidian/markdown-code-block-processor.d.cts +9 -9
- package/dist/lib/cjs/obsidian/plugin/plugin.cjs +21 -21
- package/dist/lib/cjs/obsidian/plugin/plugin.d.cts +15 -15
- package/dist/lib/cjs/obsidian/resource-lock.cjs +899 -0
- package/dist/lib/cjs/obsidian/resource-lock.d.cts +227 -0
- package/dist/lib/cjs/obsidian/vault-transaction.cjs +371 -0
- package/dist/lib/cjs/obsidian/vault-transaction.d.cts +146 -0
- package/dist/lib/cjs/obsidian/vault.cjs +4 -4
- package/dist/lib/cjs/obsidian/vault.d.cts +3 -3
- package/dist/lib/esm/generated-during-build.mjs +2 -2
- package/dist/lib/esm/obsidian/components/plugin-notice-component.mjs +10 -5
- package/dist/lib/esm/obsidian/components/rename-delete-handler-component.d.mts +3 -3
- package/dist/lib/esm/obsidian/components/rename-delete-handler-component.mjs +10 -10
- package/dist/lib/esm/obsidian/editor.d.mts +2 -2
- package/dist/lib/esm/obsidian/editor.mjs +1 -1
- package/dist/lib/esm/obsidian/file-manager.d.mts +9 -9
- package/dist/lib/esm/obsidian/file-manager.mjs +9 -9
- package/dist/lib/esm/obsidian/i18n/locales/en.d.mts +7 -7
- package/dist/lib/esm/obsidian/i18n/locales/en.mjs +8 -8
- package/dist/lib/esm/obsidian/i18n/locales/translations-map.d.mts +7 -7
- package/dist/lib/esm/obsidian/index.d.mts +2 -1
- package/dist/lib/esm/obsidian/index.mjs +5 -3
- package/dist/lib/esm/obsidian/markdown-code-block-processor.d.mts +9 -9
- package/dist/lib/esm/obsidian/markdown-code-block-processor.mjs +7 -7
- package/dist/lib/esm/obsidian/plugin/plugin.d.mts +15 -15
- package/dist/lib/esm/obsidian/plugin/plugin.mjs +21 -21
- package/dist/lib/esm/obsidian/resource-lock.d.mts +227 -0
- package/dist/lib/esm/obsidian/resource-lock.mjs +799 -0
- package/dist/lib/esm/obsidian/vault-transaction.d.mts +146 -0
- package/dist/lib/esm/obsidian/vault-transaction.mjs +271 -0
- package/dist/lib/esm/obsidian/vault.d.mts +3 -3
- package/dist/lib/esm/obsidian/vault.mjs +4 -4
- package/dist/styles.css +8 -1
- package/obsidian/resource-lock/package.json +6 -0
- package/obsidian/vault-transaction/package.json +6 -0
- package/package.json +1 -1
- package/dist/lib/cjs/obsidian/editor-lock.cjs +0 -545
- package/dist/lib/cjs/obsidian/editor-lock.d.cts +0 -142
- package/dist/lib/esm/obsidian/editor-lock.d.mts +0 -142
- package/dist/lib/esm/obsidian/editor-lock.mjs +0 -442
- package/obsidian/editor-lock/package.json +0 -6
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
/*
|
|
2
|
+
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
3
|
+
if you want to view the source, please visit the github repository of this plugin
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function initCjs() {
|
|
7
|
+
// eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
|
|
8
|
+
const globalThisRecord = globalThis;
|
|
9
|
+
globalThisRecord['__name'] ??= name;
|
|
10
|
+
const originalRequire = require;
|
|
11
|
+
if (originalRequire && !originalRequire.__isPatched) {
|
|
12
|
+
// eslint-disable-next-line no-global-assign, no-implicit-globals -- We need to patch the `require()` function.
|
|
13
|
+
require = Object.assign(
|
|
14
|
+
(id) => requirePatched(id),
|
|
15
|
+
originalRequire,
|
|
16
|
+
{
|
|
17
|
+
__isPatched: true
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const newFuncs = {
|
|
23
|
+
__extractDefault() {
|
|
24
|
+
return extractDefault;
|
|
25
|
+
},
|
|
26
|
+
process() {
|
|
27
|
+
const browserProcess = {
|
|
28
|
+
browser: true,
|
|
29
|
+
cwd() {
|
|
30
|
+
return '/';
|
|
31
|
+
},
|
|
32
|
+
env: {},
|
|
33
|
+
platform: 'android'
|
|
34
|
+
};
|
|
35
|
+
return browserProcess;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
for (const key of Object.keys(newFuncs)) {
|
|
40
|
+
globalThisRecord[key] ??= newFuncs[key]?.();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function name(obj) {
|
|
44
|
+
return obj;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function extractDefault(module) {
|
|
48
|
+
return module && module.__esModule && 'default' in module ? module.default : module;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const OBSIDIAN_BUILT_IN_MODULE_NAMES = [
|
|
52
|
+
'obsidian',
|
|
53
|
+
'@codemirror/autocomplete',
|
|
54
|
+
'@codemirror/collab',
|
|
55
|
+
'@codemirror/commands',
|
|
56
|
+
'@codemirror/language',
|
|
57
|
+
'@codemirror/lint',
|
|
58
|
+
'@codemirror/search',
|
|
59
|
+
'@codemirror/state',
|
|
60
|
+
'@codemirror/text',
|
|
61
|
+
'@codemirror/view',
|
|
62
|
+
'@lezer/common',
|
|
63
|
+
'@lezer/lr',
|
|
64
|
+
'@lezer/highlight'];
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
const DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES = [
|
|
68
|
+
'@codemirror/closebrackets',
|
|
69
|
+
'@codemirror/comment',
|
|
70
|
+
'@codemirror/fold',
|
|
71
|
+
'@codemirror/gutter',
|
|
72
|
+
'@codemirror/highlight',
|
|
73
|
+
'@codemirror/history',
|
|
74
|
+
'@codemirror/matchbrackets',
|
|
75
|
+
'@codemirror/panel',
|
|
76
|
+
'@codemirror/rangeset',
|
|
77
|
+
'@codemirror/rectangular-selection',
|
|
78
|
+
'@codemirror/stream-parser',
|
|
79
|
+
'@codemirror/tooltip'];
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
function requirePatched(id) {
|
|
83
|
+
if (OBSIDIAN_BUILT_IN_MODULE_NAMES.includes(id) || DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES.includes(id)) {
|
|
84
|
+
return originalRequire?.(id);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated, obsidianmd/no-global-this -- Need access to app. Actively use globalThis.
|
|
88
|
+
if (globalThis.app.isMobile) {
|
|
89
|
+
if (id === 'process' || id === 'node:process') {
|
|
90
|
+
// eslint-disable-next-line no-console -- Valid usage.
|
|
91
|
+
console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Fake process object is returned instead.`);
|
|
92
|
+
// eslint-disable-next-line obsidianmd/no-global-this -- Actively use globalThis.
|
|
93
|
+
return globalThis.process;
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
const module = originalRequire?.(id);
|
|
97
|
+
if (module) {
|
|
98
|
+
return extractDefault(module);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// eslint-disable-next-line no-console -- Valid usage.
|
|
103
|
+
console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Empty object is returned instead.`);
|
|
104
|
+
return {};
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
|
|
108
|
+
"use strict";
|
|
109
|
+
var __defProp = Object.defineProperty;
|
|
110
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
111
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
112
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
113
|
+
var __export = (target, all) => {
|
|
114
|
+
for (var name in all)
|
|
115
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
116
|
+
};
|
|
117
|
+
var __copyProps = (to, from, except, desc) => {
|
|
118
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
119
|
+
for (let key of __getOwnPropNames(from))
|
|
120
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
121
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
122
|
+
}
|
|
123
|
+
return to;
|
|
124
|
+
};
|
|
125
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
126
|
+
var resource_lock_exports = {};
|
|
127
|
+
__export(resource_lock_exports, {
|
|
128
|
+
ResourceLockComponent: () => ResourceLockComponent,
|
|
129
|
+
ResourceLockedError: () => ResourceLockedError,
|
|
130
|
+
isResourceLockedForPath: () => isResourceLockedForPath,
|
|
131
|
+
lockResourceForPath: () => lockResourceForPath,
|
|
132
|
+
requestResourceUnlockForPath: () => requestResourceUnlockForPath,
|
|
133
|
+
unlockResourceForPath: () => unlockResourceForPath
|
|
134
|
+
});
|
|
135
|
+
module.exports = __toCommonJS(resource_lock_exports);
|
|
136
|
+
var import_implementations = require('@obsidian-typings/obsidian-public-latest/implementations');
|
|
137
|
+
var import_obsidian = require('obsidian');
|
|
138
|
+
var import_async = require('../async.cjs');
|
|
139
|
+
var import_disposable = require('../disposable.cjs');
|
|
140
|
+
var import_function = require('../function.cjs');
|
|
141
|
+
var import_obsidian_dev_utils_state = require('../obsidian-dev-utils-state.cjs');
|
|
142
|
+
var import_type_guards = require('../type-guards.cjs');
|
|
143
|
+
var import_component_ex = require('./components/component-ex.cjs');
|
|
144
|
+
var import_monkey_around_component = require('./components/monkey-around-component.cjs');
|
|
145
|
+
var import_editor = require('./editor.cjs');
|
|
146
|
+
var import_file_system = require('./file-system.cjs');
|
|
147
|
+
var import_html_element = require('./html-element.cjs');
|
|
148
|
+
var import_i18n = require('./i18n/i18n.cjs');
|
|
149
|
+
var import_confirm = require('./modals/confirm.cjs');
|
|
150
|
+
var import_vault = require('./vault.cjs');
|
|
151
|
+
const BEEP_DURATION_SECONDS = 0.08;
|
|
152
|
+
const BEEP_FREQUENCY_HZ = 660;
|
|
153
|
+
const BEEP_GAIN = 0.05;
|
|
154
|
+
const BEEP_THROTTLE_MILLISECONDS = 200;
|
|
155
|
+
const RESOURCE_LOCK_STATE_KEY = "resourceLock";
|
|
156
|
+
const LOCK_ICON_ID = "lock";
|
|
157
|
+
const LOCK_INDICATOR_CSS_CLASS = "obsidian-dev-utils-lock-indicator";
|
|
158
|
+
const LOCK_INDICATOR_FLASH_CSS_CLASS = "obsidian-dev-utils-lock-indicator-flash";
|
|
159
|
+
const STATUS_BAR_ITEM_CSS_CLASS = "status-bar-item";
|
|
160
|
+
const STATUS_BAR_CSS_SELECTOR = ".status-bar";
|
|
161
|
+
const UNLOCK_ICON_ID = "unlock";
|
|
162
|
+
class ResourceLockEventsComponent extends import_component_ex.ComponentEx {
|
|
163
|
+
app;
|
|
164
|
+
onChange;
|
|
165
|
+
onExternalMutation;
|
|
166
|
+
onFileMenu;
|
|
167
|
+
constructor(params) {
|
|
168
|
+
super();
|
|
169
|
+
this.app = params.app;
|
|
170
|
+
this.onChange = params.onChange;
|
|
171
|
+
this.onExternalMutation = params.onExternalMutation;
|
|
172
|
+
this.onFileMenu = params.onFileMenu;
|
|
173
|
+
}
|
|
174
|
+
onload() {
|
|
175
|
+
super.onload();
|
|
176
|
+
this.registerEvent(this.app.workspace.on("active-leaf-change", this.onChange));
|
|
177
|
+
this.registerEvent(this.app.workspace.on("layout-change", this.onChange));
|
|
178
|
+
this.registerEvent(this.app.workspace.on("file-open", this.onChange));
|
|
179
|
+
this.registerEvent(this.app.workspace.on("file-menu", this.onFileMenu));
|
|
180
|
+
this.registerEvent(this.app.vault.on("create", (file) => {
|
|
181
|
+
this.onExternalMutation(file.path);
|
|
182
|
+
}));
|
|
183
|
+
this.registerEvent(this.app.vault.on("delete", (file) => {
|
|
184
|
+
this.onExternalMutation(file.path);
|
|
185
|
+
}));
|
|
186
|
+
this.registerEvent(this.app.vault.on("rename", (file, oldPath) => {
|
|
187
|
+
this.onExternalMutation(oldPath);
|
|
188
|
+
this.onExternalMutation(file.path);
|
|
189
|
+
}));
|
|
190
|
+
this.registerEvent(this.app.metadataCache.on("deleted", (file) => {
|
|
191
|
+
this.onExternalMutation(file.path);
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
class ResourceLockMutationBlockerComponent extends import_monkey_around_component.MonkeyAroundComponent {
|
|
196
|
+
shouldBlockMutation;
|
|
197
|
+
constructor(params) {
|
|
198
|
+
super();
|
|
199
|
+
this.shouldBlockMutation = params.shouldBlockMutation;
|
|
200
|
+
}
|
|
201
|
+
onload() {
|
|
202
|
+
super.onload();
|
|
203
|
+
this.registerMethodPatch({
|
|
204
|
+
methodName: "append",
|
|
205
|
+
obj: import_obsidian.Vault.prototype,
|
|
206
|
+
patchHandler: ({ fallback, originalArgs: [file] }) => {
|
|
207
|
+
this.assertNotBlocked([file.path]);
|
|
208
|
+
return fallback();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
this.registerMethodPatch({
|
|
212
|
+
methodName: "copy",
|
|
213
|
+
obj: import_obsidian.Vault.prototype,
|
|
214
|
+
patchHandler: ({ fallback, originalArgs: [, newPath] }) => {
|
|
215
|
+
this.assertNotBlocked([newPath]);
|
|
216
|
+
return fallback();
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
this.registerMethodPatch({
|
|
220
|
+
methodName: "create",
|
|
221
|
+
obj: import_obsidian.Vault.prototype,
|
|
222
|
+
patchHandler: ({ fallback, originalArgs: [path] }) => {
|
|
223
|
+
this.assertNotBlocked([path]);
|
|
224
|
+
return fallback();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
this.registerMethodPatch({
|
|
228
|
+
methodName: "createBinary",
|
|
229
|
+
obj: import_obsidian.Vault.prototype,
|
|
230
|
+
patchHandler: ({ fallback, originalArgs: [path] }) => {
|
|
231
|
+
this.assertNotBlocked([path]);
|
|
232
|
+
return fallback();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
this.registerMethodPatch({
|
|
236
|
+
methodName: "createFolder",
|
|
237
|
+
obj: import_obsidian.Vault.prototype,
|
|
238
|
+
patchHandler: ({ fallback, originalArgs: [path] }) => {
|
|
239
|
+
this.assertNotBlocked([path]);
|
|
240
|
+
return fallback();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
this.registerMethodPatch({
|
|
244
|
+
methodName: "delete",
|
|
245
|
+
obj: import_obsidian.Vault.prototype,
|
|
246
|
+
patchHandler: ({ fallback, originalArgs: [file] }) => {
|
|
247
|
+
this.assertNotBlocked([file.path]);
|
|
248
|
+
return fallback();
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
this.registerMethodPatch({
|
|
252
|
+
methodName: "modify",
|
|
253
|
+
obj: import_obsidian.Vault.prototype,
|
|
254
|
+
patchHandler: ({ fallback, originalArgs: [file] }) => {
|
|
255
|
+
this.assertNotBlocked([file.path]);
|
|
256
|
+
return fallback();
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
this.registerMethodPatch({
|
|
260
|
+
methodName: "modifyBinary",
|
|
261
|
+
obj: import_obsidian.Vault.prototype,
|
|
262
|
+
patchHandler: ({ fallback, originalArgs: [file] }) => {
|
|
263
|
+
this.assertNotBlocked([file.path]);
|
|
264
|
+
return fallback();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
this.registerMethodPatch({
|
|
268
|
+
methodName: "process",
|
|
269
|
+
obj: import_obsidian.Vault.prototype,
|
|
270
|
+
patchHandler: ({ fallback, originalArgs: [file] }) => {
|
|
271
|
+
this.assertNotBlocked([file.path]);
|
|
272
|
+
return fallback();
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
this.registerMethodPatch({
|
|
276
|
+
methodName: "rename",
|
|
277
|
+
obj: import_obsidian.Vault.prototype,
|
|
278
|
+
patchHandler: ({ fallback, originalArgs: [file, newPath] }) => {
|
|
279
|
+
this.assertNotBlocked([file.path, newPath]);
|
|
280
|
+
return fallback();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
this.registerMethodPatch({
|
|
284
|
+
methodName: "trash",
|
|
285
|
+
obj: import_obsidian.Vault.prototype,
|
|
286
|
+
patchHandler: ({ fallback, originalArgs: [file] }) => {
|
|
287
|
+
this.assertNotBlocked([file.path]);
|
|
288
|
+
return fallback();
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
this.registerMethodPatch({
|
|
292
|
+
methodName: "renameFile",
|
|
293
|
+
obj: import_obsidian.FileManager.prototype,
|
|
294
|
+
patchHandler: ({ fallback, originalArgs: [file, newPath] }) => {
|
|
295
|
+
this.assertNotBlocked([file.path, newPath]);
|
|
296
|
+
return fallback();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
this.registerMethodPatch({
|
|
300
|
+
methodName: "trashFile",
|
|
301
|
+
obj: import_obsidian.FileManager.prototype,
|
|
302
|
+
patchHandler: ({ fallback, originalArgs: [file] }) => {
|
|
303
|
+
this.assertNotBlocked([file.path]);
|
|
304
|
+
return fallback();
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
assertNotBlocked(paths) {
|
|
309
|
+
for (const path of paths) {
|
|
310
|
+
if (this.shouldBlockMutation(path)) {
|
|
311
|
+
throw new ResourceLockedError(path);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
class ResourceLockManager {
|
|
317
|
+
audioContext = null;
|
|
318
|
+
bypassPathSets = /* @__PURE__ */ new Set();
|
|
319
|
+
eventsComponent = null;
|
|
320
|
+
indicatorsByView = /* @__PURE__ */ new Map();
|
|
321
|
+
lastBeepMilliseconds = 0;
|
|
322
|
+
lockEntriesByPath = /* @__PURE__ */ new Map();
|
|
323
|
+
mutationBlockerComponent = null;
|
|
324
|
+
statusBarItemEl = null;
|
|
325
|
+
bypass(app, pathsOrFiles) {
|
|
326
|
+
const bypassedPaths = new Set(pathsOrFiles.map((pathOrFile) => (0, import_file_system.getPath)(app, pathOrFile)));
|
|
327
|
+
this.bypassPathSets.add(bypassedPaths);
|
|
328
|
+
return new import_disposable.CallbackDisposable({
|
|
329
|
+
callback: () => {
|
|
330
|
+
this.bypassPathSets.delete(bypassedPaths);
|
|
331
|
+
},
|
|
332
|
+
multipleDisposeBehavior: import_disposable.MultipleDisposeBehavior.Ignore
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
isLocked(app, pathOrFile) {
|
|
336
|
+
return this.isPathLocked((0, import_file_system.getPath)(app, pathOrFile));
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Checks whether the given path is locked directly or is covered by a `subtree`-locked ancestor
|
|
340
|
+
* folder.
|
|
341
|
+
*
|
|
342
|
+
* @param app - The Obsidian app instance.
|
|
343
|
+
* @param pathOrFile - The path or file to check.
|
|
344
|
+
* @returns `true` if the path is itself locked or lies under a `subtree`-locked folder.
|
|
345
|
+
*/
|
|
346
|
+
isLockedByAncestor(app, pathOrFile) {
|
|
347
|
+
return this.resolveLockOwnerPath(app, (0, import_file_system.getPath)(app, pathOrFile)) !== null;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Checks whether the given path is covered by a mutation-blocking lock (one taken with
|
|
351
|
+
* `blocksMutations`) directly or on a `subtree`-locked ancestor folder.
|
|
352
|
+
*
|
|
353
|
+
* @param app - The Obsidian app instance.
|
|
354
|
+
* @param pathOrFile - The path or file to check.
|
|
355
|
+
* @returns `true` if a mutation of the path is currently blocked.
|
|
356
|
+
*/
|
|
357
|
+
isMutationBlockedByAncestor(app, pathOrFile) {
|
|
358
|
+
const path = (0, import_file_system.getPath)(app, pathOrFile);
|
|
359
|
+
const exactEntries = this.lockEntriesByPath.get(path);
|
|
360
|
+
if (exactEntries?.some((entry) => entry.blocksMutations)) {
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
for (const [lockedPath, entries] of this.lockEntriesByPath) {
|
|
364
|
+
if (entries.some((entry) => entry.mode === "subtree" && entry.blocksMutations) && (0, import_vault.isChild)({ app, childPathOrFile: path, parentPathOrFile: lockedPath })) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
lock(app, pathOrFile, pluginId, options = {}) {
|
|
371
|
+
const path = (0, import_file_system.getPath)(app, pathOrFile);
|
|
372
|
+
const entry = {
|
|
373
|
+
abortController: options.abortController ?? null,
|
|
374
|
+
blocksMutations: options.blocksMutations ?? false,
|
|
375
|
+
mode: options.mode ?? "file",
|
|
376
|
+
pluginId
|
|
377
|
+
};
|
|
378
|
+
let entries = this.lockEntriesByPath.get(path);
|
|
379
|
+
if (!entries) {
|
|
380
|
+
entries = [];
|
|
381
|
+
this.lockEntriesByPath.set(path, entries);
|
|
382
|
+
}
|
|
383
|
+
entries.push(entry);
|
|
384
|
+
this.ensureSubscribed(app);
|
|
385
|
+
if (entry.blocksMutations) {
|
|
386
|
+
this.ensureBlockerInstalled(app);
|
|
387
|
+
}
|
|
388
|
+
this.reconcile(app);
|
|
389
|
+
return new import_disposable.CallbackDisposable({
|
|
390
|
+
callback: () => {
|
|
391
|
+
this.removeEntry(app, path, entry);
|
|
392
|
+
},
|
|
393
|
+
multipleDisposeBehavior: import_disposable.MultipleDisposeBehavior.Ignore
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Aborts every {@link AbortController} currently registered for the note at the given path, which
|
|
398
|
+
* cancels the operations holding the lock. Each operation releases its own lock in its `finally`
|
|
399
|
+
* when it observes the abort, so this does not unlock the path directly. A no-op when no controller
|
|
400
|
+
* is registered for the path.
|
|
401
|
+
*
|
|
402
|
+
* @param app - The Obsidian app instance.
|
|
403
|
+
* @param pathOrFile - The path or file of the note to request an unlock for.
|
|
404
|
+
*/
|
|
405
|
+
requestUnlock(app, pathOrFile) {
|
|
406
|
+
const entries = this.lockEntriesByPath.get((0, import_file_system.getPath)(app, pathOrFile));
|
|
407
|
+
if (!entries) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
for (const entry of entries) {
|
|
411
|
+
entry.abortController?.abort();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
unlock(app, pathOrFile, pluginId) {
|
|
415
|
+
const path = (0, import_file_system.getPath)(app, pathOrFile);
|
|
416
|
+
const entries = this.lockEntriesByPath.get(path);
|
|
417
|
+
if (!entries) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const index = entries.findIndex((entry) => entry.pluginId === pluginId);
|
|
421
|
+
if (index === -1) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
entries.splice(index, 1);
|
|
425
|
+
if (entries.length === 0) {
|
|
426
|
+
this.lockEntriesByPath.delete(path);
|
|
427
|
+
}
|
|
428
|
+
this.reconcileAndCleanup(app);
|
|
429
|
+
}
|
|
430
|
+
unlockAllForPlugin(app, pluginId) {
|
|
431
|
+
for (const [path, entries] of this.lockEntriesByPath) {
|
|
432
|
+
const remaining = entries.filter((entry) => entry.pluginId !== pluginId);
|
|
433
|
+
if (remaining.length === 0) {
|
|
434
|
+
this.lockEntriesByPath.delete(path);
|
|
435
|
+
} else if (remaining.length !== entries.length) {
|
|
436
|
+
this.lockEntriesByPath.set(path, remaining);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
this.reconcileAndCleanup(app);
|
|
440
|
+
}
|
|
441
|
+
addUnlockMenuItem(app, menu, path) {
|
|
442
|
+
menu.addItem((item) => {
|
|
443
|
+
item.setTitle((0, import_i18n.t)(($) => $.obsidianDevUtils.resourceLock.unlockMenuItem)).setIcon(UNLOCK_ICON_ID).onClick((0, import_async.convertAsyncToSync)(async () => {
|
|
444
|
+
const isConfirmed = await (0, import_confirm.confirm)({
|
|
445
|
+
app,
|
|
446
|
+
message: this.unlockConfirmMessage(app, path),
|
|
447
|
+
title: (0, import_i18n.t)(($) => $.obsidianDevUtils.resourceLock.unlockConfirmTitle)
|
|
448
|
+
});
|
|
449
|
+
if (isConfirmed) {
|
|
450
|
+
this.requestUnlock(app, path);
|
|
451
|
+
}
|
|
452
|
+
}));
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
beep() {
|
|
456
|
+
const nowMilliseconds = Date.now();
|
|
457
|
+
if (nowMilliseconds - this.lastBeepMilliseconds < BEEP_THROTTLE_MILLISECONDS) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
this.lastBeepMilliseconds = nowMilliseconds;
|
|
461
|
+
const AudioContextClass = window.AudioContext;
|
|
462
|
+
if (!AudioContextClass) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
this.audioContext ??= new AudioContextClass();
|
|
466
|
+
const audioContext = this.audioContext;
|
|
467
|
+
const oscillator = audioContext.createOscillator();
|
|
468
|
+
const gainNode = audioContext.createGain();
|
|
469
|
+
oscillator.type = "sine";
|
|
470
|
+
oscillator.frequency.value = BEEP_FREQUENCY_HZ;
|
|
471
|
+
gainNode.gain.value = BEEP_GAIN;
|
|
472
|
+
oscillator.connect(gainNode);
|
|
473
|
+
gainNode.connect(audioContext.destination);
|
|
474
|
+
oscillator.start();
|
|
475
|
+
oscillator.stop(audioContext.currentTime + BEEP_DURATION_SECONDS);
|
|
476
|
+
}
|
|
477
|
+
createIndicators(app, view, path, tooltip) {
|
|
478
|
+
const actionIconEl = view.addAction(LOCK_ICON_ID, tooltip, import_function.noop);
|
|
479
|
+
this.registerUnlockMenu(app, actionIconEl, () => path);
|
|
480
|
+
let tabIconEl = null;
|
|
481
|
+
const tabStatusContainerEl = view.leaf.tabHeaderStatusContainerEl;
|
|
482
|
+
if (tabStatusContainerEl) {
|
|
483
|
+
tabIconEl = tabStatusContainerEl.createSpan({ cls: LOCK_INDICATOR_CSS_CLASS });
|
|
484
|
+
(0, import_obsidian.setIcon)(tabIconEl, LOCK_ICON_ID);
|
|
485
|
+
(0, import_obsidian.setTooltip)(tabIconEl, tooltip);
|
|
486
|
+
this.registerUnlockMenu(app, tabIconEl, () => path);
|
|
487
|
+
}
|
|
488
|
+
const onTypeAttempt = () => {
|
|
489
|
+
this.flashIndicators(view);
|
|
490
|
+
this.beep();
|
|
491
|
+
};
|
|
492
|
+
view.contentEl.addEventListener("beforeinput", onTypeAttempt);
|
|
493
|
+
return {
|
|
494
|
+
actionIconEl,
|
|
495
|
+
disposeTypeListener: () => {
|
|
496
|
+
view.contentEl.removeEventListener("beforeinput", onTypeAttempt);
|
|
497
|
+
},
|
|
498
|
+
tabIconEl
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
ensureBlockerInstalled(app) {
|
|
502
|
+
if (this.mutationBlockerComponent) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
this.mutationBlockerComponent = new ResourceLockMutationBlockerComponent({
|
|
506
|
+
shouldBlockMutation: (path) => this.shouldBlockMutation(app, path)
|
|
507
|
+
});
|
|
508
|
+
this.mutationBlockerComponent.load();
|
|
509
|
+
}
|
|
510
|
+
ensureSubscribed(app) {
|
|
511
|
+
if (this.eventsComponent) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
this.eventsComponent = new ResourceLockEventsComponent({
|
|
515
|
+
app,
|
|
516
|
+
onChange: () => {
|
|
517
|
+
this.reconcile(app);
|
|
518
|
+
},
|
|
519
|
+
onExternalMutation: (path) => {
|
|
520
|
+
this.handleExternalMutation(app, path);
|
|
521
|
+
},
|
|
522
|
+
onFileMenu: (menu, file) => {
|
|
523
|
+
this.handleFileMenu(app, menu, file);
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
this.eventsComponent.load();
|
|
527
|
+
}
|
|
528
|
+
flashElement(el) {
|
|
529
|
+
el.removeClass(LOCK_INDICATOR_FLASH_CSS_CLASS);
|
|
530
|
+
el.getBoundingClientRect();
|
|
531
|
+
el.addClass(LOCK_INDICATOR_FLASH_CSS_CLASS);
|
|
532
|
+
}
|
|
533
|
+
flashIndicators(view) {
|
|
534
|
+
const indicators = this.indicatorsByView.get(view);
|
|
535
|
+
for (const el of [indicators?.actionIconEl, indicators?.tabIconEl, this.statusBarItemEl]) {
|
|
536
|
+
if (el) {
|
|
537
|
+
this.flashElement(el);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Reacts to a vault/metadata event on a path. If the path is mutation-blocked and NOT covered by an
|
|
543
|
+
* active bypass scope, the change bypassed the blocker patch (sync / filesystem / a raw-adapter
|
|
544
|
+
* write) — an intruder. Abort the `abortController` of every mutation-blocking lock covering the path
|
|
545
|
+
* so the owning operation cancels and rolls back. A no-op for the owner's own (bypassed) mutations,
|
|
546
|
+
* for unblocked paths, and for blocking locks with no `abortController`.
|
|
547
|
+
*
|
|
548
|
+
* @param app - The Obsidian app instance.
|
|
549
|
+
* @param path - The path the event fired on.
|
|
550
|
+
*/
|
|
551
|
+
handleExternalMutation(app, path) {
|
|
552
|
+
if (!this.isMutationBlockedByAncestor(app, path)) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (this.isBypassed(app, path)) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
for (const [lockedPath, entries] of this.lockEntriesByPath) {
|
|
559
|
+
for (const entry of entries) {
|
|
560
|
+
if (!entry.blocksMutations || !entry.abortController) {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const coversPath = lockedPath === path || entry.mode === "subtree" && (0, import_vault.isChild)({ app, childPathOrFile: path, parentPathOrFile: lockedPath });
|
|
564
|
+
if (coversPath) {
|
|
565
|
+
entry.abortController.abort();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
handleFileMenu(app, menu, file) {
|
|
571
|
+
const ownerPath = this.resolveLockOwnerPath(app, file.path);
|
|
572
|
+
if (ownerPath === null) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
this.addUnlockMenuItem(app, menu, ownerPath);
|
|
576
|
+
}
|
|
577
|
+
hasBlockingLock() {
|
|
578
|
+
for (const entries of this.lockEntriesByPath.values()) {
|
|
579
|
+
if (entries.some((entry) => entry.blocksMutations)) {
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Checks whether the path is covered by an active {@link ResourceLockManager.bypass} scope — the
|
|
587
|
+
* owner declaring that its own mutations of the path (and, for a folder, its subtree) are sanctioned.
|
|
588
|
+
*
|
|
589
|
+
* @param app - The Obsidian app instance.
|
|
590
|
+
* @param path - The path being mutated.
|
|
591
|
+
* @returns `true` if some active bypass scope covers the path.
|
|
592
|
+
*/
|
|
593
|
+
isBypassed(app, path) {
|
|
594
|
+
for (const bypassedPaths of this.bypassPathSets) {
|
|
595
|
+
for (const bypassedPath of bypassedPaths) {
|
|
596
|
+
if ((0, import_vault.isChildOrSelf)({ app, childPathOrFile: path, parentPathOrFile: bypassedPath })) {
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
isPathLocked(path) {
|
|
604
|
+
return (this.lockEntriesByPath.get(path)?.length ?? 0) > 0;
|
|
605
|
+
}
|
|
606
|
+
lockingPluginNames(app, path) {
|
|
607
|
+
const entries = this.lockEntriesByPath.get(path);
|
|
608
|
+
(0, import_type_guards.assertNonNullable)(entries);
|
|
609
|
+
const pluginIds = [...new Set(entries.map((entry) => entry.pluginId))];
|
|
610
|
+
return pluginIds.map((pluginId) => app.plugins.manifests[pluginId]?.name ?? pluginId);
|
|
611
|
+
}
|
|
612
|
+
lockTooltip(app, path) {
|
|
613
|
+
const header = (0, import_i18n.t)(($) => $.obsidianDevUtils.resourceLock.lockedByTooltip);
|
|
614
|
+
return [header, ...this.lockingPluginNames(app, path)].join("\n");
|
|
615
|
+
}
|
|
616
|
+
reconcile(app) {
|
|
617
|
+
const viewsToLock = /* @__PURE__ */ new Set();
|
|
618
|
+
for (const leaf of app.workspace.getLeavesOfType(import_implementations.ViewType.Markdown)) {
|
|
619
|
+
const view = leaf.view;
|
|
620
|
+
if (!(view instanceof import_obsidian.MarkdownView)) {
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
const path = view.file?.path;
|
|
624
|
+
if (path === void 0) {
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
const ownerPath = this.resolveLockOwnerPath(app, path);
|
|
628
|
+
if (ownerPath !== null) {
|
|
629
|
+
viewsToLock.add(view);
|
|
630
|
+
const tooltip = this.lockTooltip(app, ownerPath);
|
|
631
|
+
(0, import_editor.toggleEditorReadOnly)(view.editor, true);
|
|
632
|
+
const indicators = this.indicatorsByView.get(view);
|
|
633
|
+
if (indicators) {
|
|
634
|
+
this.updateIndicatorTooltips(indicators, tooltip);
|
|
635
|
+
} else {
|
|
636
|
+
this.indicatorsByView.set(view, this.createIndicators(app, view, ownerPath, tooltip));
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
for (const [view, indicators] of this.indicatorsByView) {
|
|
641
|
+
if (!viewsToLock.has(view)) {
|
|
642
|
+
(0, import_editor.toggleEditorReadOnly)(view.editor, false);
|
|
643
|
+
indicators.disposeTypeListener();
|
|
644
|
+
indicators.actionIconEl.remove();
|
|
645
|
+
indicators.tabIconEl?.remove();
|
|
646
|
+
this.indicatorsByView.delete(view);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
this.updateStatusBar(app);
|
|
650
|
+
}
|
|
651
|
+
reconcileAndCleanup(app) {
|
|
652
|
+
this.reconcile(app);
|
|
653
|
+
if (this.lockEntriesByPath.size === 0) {
|
|
654
|
+
this.eventsComponent?.unload();
|
|
655
|
+
this.eventsComponent = null;
|
|
656
|
+
}
|
|
657
|
+
if (!this.hasBlockingLock()) {
|
|
658
|
+
this.mutationBlockerComponent?.unload();
|
|
659
|
+
this.mutationBlockerComponent = null;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
registerUnlockMenu(app, el, getContextPath) {
|
|
663
|
+
el.addEventListener("contextmenu", (evt) => {
|
|
664
|
+
evt.preventDefault();
|
|
665
|
+
const path = getContextPath();
|
|
666
|
+
if (path === void 0) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const menu = new import_obsidian.Menu();
|
|
670
|
+
this.addUnlockMenuItem(app, menu, path);
|
|
671
|
+
menu.showAtMouseEvent(evt);
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
removeEntry(app, path, entry) {
|
|
675
|
+
const entries = this.lockEntriesByPath.get(path);
|
|
676
|
+
if (!entries) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const index = entries.indexOf(entry);
|
|
680
|
+
if (index === -1) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
entries.splice(index, 1);
|
|
684
|
+
if (entries.length === 0) {
|
|
685
|
+
this.lockEntriesByPath.delete(path);
|
|
686
|
+
}
|
|
687
|
+
this.reconcileAndCleanup(app);
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Resolves which locked path (if any) covers the given path: the path itself when directly locked,
|
|
691
|
+
* otherwise the longest `subtree`-locked ancestor folder that contains it (longest so a nested
|
|
692
|
+
* subtree lock wins deterministically over an outer one).
|
|
693
|
+
*
|
|
694
|
+
* @param app - The Obsidian app instance.
|
|
695
|
+
* @param path - The path to resolve an owner lock for.
|
|
696
|
+
* @returns The covering locked path, or `null` when nothing covers it.
|
|
697
|
+
*/
|
|
698
|
+
resolveLockOwnerPath(app, path) {
|
|
699
|
+
if (this.isPathLocked(path)) {
|
|
700
|
+
return path;
|
|
701
|
+
}
|
|
702
|
+
let bestOwnerPath = null;
|
|
703
|
+
for (const [lockedPath, entries] of this.lockEntriesByPath) {
|
|
704
|
+
const hasSubtreeLock = entries.some((entry) => entry.mode === "subtree");
|
|
705
|
+
if (hasSubtreeLock && (0, import_vault.isChild)({ app, childPathOrFile: path, parentPathOrFile: lockedPath }) && (bestOwnerPath === null || lockedPath.length > bestOwnerPath.length)) {
|
|
706
|
+
bestOwnerPath = lockedPath;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return bestOwnerPath;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Decides whether a mutation of the path must be blocked: `true` when the path is mutation-blocked
|
|
713
|
+
* and not covered by any active {@link ResourceLockManager.bypass} scope.
|
|
714
|
+
*
|
|
715
|
+
* @param app - The Obsidian app instance.
|
|
716
|
+
* @param path - The path being mutated.
|
|
717
|
+
* @returns `true` to reject the mutation, `false` to allow it.
|
|
718
|
+
*/
|
|
719
|
+
shouldBlockMutation(app, path) {
|
|
720
|
+
if (!this.isMutationBlockedByAncestor(app, path)) {
|
|
721
|
+
return false;
|
|
722
|
+
}
|
|
723
|
+
return !this.isBypassed(app, path);
|
|
724
|
+
}
|
|
725
|
+
unlockConfirmMessage(app, path) {
|
|
726
|
+
const fragment = createFragment();
|
|
727
|
+
fragment.appendText((0, import_i18n.t)(($) => $.obsidianDevUtils.resourceLock.unlockConfirmMessage));
|
|
728
|
+
for (const name of this.lockingPluginNames(app, path)) {
|
|
729
|
+
fragment.createEl("br");
|
|
730
|
+
(0, import_html_element.appendCodeBlock)(fragment, name);
|
|
731
|
+
}
|
|
732
|
+
return fragment;
|
|
733
|
+
}
|
|
734
|
+
updateIndicatorTooltips(indicators, tooltip) {
|
|
735
|
+
(0, import_obsidian.setTooltip)(indicators.actionIconEl, tooltip);
|
|
736
|
+
if (indicators.tabIconEl) {
|
|
737
|
+
(0, import_obsidian.setTooltip)(indicators.tabIconEl, tooltip);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
updateStatusBar(app) {
|
|
741
|
+
const activeView = app.workspace.getActiveViewOfType(import_obsidian.MarkdownView);
|
|
742
|
+
const activePath = activeView?.file?.path;
|
|
743
|
+
const activeOwnerPath = activePath === void 0 ? null : this.resolveLockOwnerPath(app, activePath);
|
|
744
|
+
if (activeOwnerPath === null || !activeView) {
|
|
745
|
+
this.statusBarItemEl?.remove();
|
|
746
|
+
this.statusBarItemEl = null;
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
if (!this.statusBarItemEl) {
|
|
750
|
+
const statusBarEl = activeView.containerEl.ownerDocument.body.querySelector(STATUS_BAR_CSS_SELECTOR);
|
|
751
|
+
if (!statusBarEl) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
this.statusBarItemEl = statusBarEl.createDiv({ cls: [STATUS_BAR_ITEM_CSS_CLASS, LOCK_INDICATOR_CSS_CLASS] });
|
|
755
|
+
(0, import_obsidian.setIcon)(this.statusBarItemEl, LOCK_ICON_ID);
|
|
756
|
+
this.registerUnlockMenu(app, this.statusBarItemEl, () => {
|
|
757
|
+
const path = app.workspace.getActiveViewOfType(import_obsidian.MarkdownView)?.file?.path;
|
|
758
|
+
return path === void 0 ? void 0 : this.resolveLockOwnerPath(app, path) ?? void 0;
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
(0, import_obsidian.setTooltip)(this.statusBarItemEl, this.lockTooltip(app, activeOwnerPath));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
class ResourceLockComponent extends import_component_ex.ComponentEx {
|
|
765
|
+
app;
|
|
766
|
+
pluginId;
|
|
767
|
+
/**
|
|
768
|
+
* Creates an resource-lock handle owned by a plugin.
|
|
769
|
+
*
|
|
770
|
+
* @param app - The Obsidian app instance.
|
|
771
|
+
* @param pluginId - The id of the owning plugin (e.g. its `manifest.id`). Locks are attributed to
|
|
772
|
+
* it for reference-counting and the indicators' "locked by" tooltip.
|
|
773
|
+
*/
|
|
774
|
+
constructor(app, pluginId) {
|
|
775
|
+
super();
|
|
776
|
+
this.app = app;
|
|
777
|
+
this.pluginId = pluginId;
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Opens an **ambient bypass scope**: while the returned {@link Disposable} is not disposed, this
|
|
781
|
+
* plugin's own mutations of the given paths (and, for a folder, its whole subtree) pass through the
|
|
782
|
+
* mutation blocker instead of throwing {@link ResourceLockedError}. Any mutation of a blocked path
|
|
783
|
+
* NOT covered by an active bypass scope is still rejected. Use it with a `using` declaration around
|
|
784
|
+
* the operation that legitimately mutates resources it has locked with
|
|
785
|
+
* {@link ResourceLockComponentLockForPathOptions.shouldBlockMutations}:
|
|
786
|
+
*
|
|
787
|
+
* ```ts
|
|
788
|
+
* using _bypass = component.bypassBlockedMutations([sourceFile, targetFile]);
|
|
789
|
+
* // ... the operation's own vault mutations of those paths are allowed ...
|
|
790
|
+
* // scope end: the paths are enforced again
|
|
791
|
+
* ```
|
|
792
|
+
*
|
|
793
|
+
* @param pathsOrFiles - The paths (or files/folders) whose mutation by this operation is sanctioned.
|
|
794
|
+
* @returns A {@link Disposable} that ends the bypass scope when disposed.
|
|
795
|
+
*/
|
|
796
|
+
bypassBlockedMutations(pathsOrFiles) {
|
|
797
|
+
return getManager().bypass(this.app, pathsOrFiles);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Checks whether the path is locked directly or lies under a `subtree`-locked ancestor folder.
|
|
801
|
+
*
|
|
802
|
+
* @param pathOrFile - The path or file to check.
|
|
803
|
+
* @returns `true` if the path is itself locked or is covered by a `subtree` lock on an ancestor.
|
|
804
|
+
*/
|
|
805
|
+
isLockedByAncestorForPath(pathOrFile) {
|
|
806
|
+
return getManager().isLockedByAncestor(this.app, pathOrFile);
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Checks whether the note at the given path is currently locked by any plugin.
|
|
810
|
+
*
|
|
811
|
+
* @param pathOrFile - The path or file of the note to check.
|
|
812
|
+
* @returns `true` if the note has at least one active lock, `false` otherwise.
|
|
813
|
+
*/
|
|
814
|
+
isLockedForPath(pathOrFile) {
|
|
815
|
+
return getManager().isLocked(this.app, pathOrFile);
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Checks whether a vault mutation of the path is currently blocked by a `shouldBlockMutations` lock
|
|
819
|
+
* on the path itself or on a `subtree`-locked ancestor folder.
|
|
820
|
+
*
|
|
821
|
+
* @param pathOrFile - The path or file to check.
|
|
822
|
+
* @returns `true` if editing/deleting/renaming/moving/creating the path would throw a
|
|
823
|
+
* {@link ResourceLockedError}.
|
|
824
|
+
*/
|
|
825
|
+
isMutationBlockedByAncestorForPath(pathOrFile) {
|
|
826
|
+
return getManager().isMutationBlockedByAncestor(this.app, pathOrFile);
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Locks the note at the given path on behalf of this plugin, making it read-only in every current
|
|
830
|
+
* and future {@link MarkdownView} until the lock is released. Reference-counted: balance each call
|
|
831
|
+
* with a dispose of the returned {@link Disposable} (ideally via `using`) or {@link unlockForPath}.
|
|
832
|
+
*
|
|
833
|
+
* @param pathOrFile - The path or file of the note to lock.
|
|
834
|
+
* @param options - Optional locking options. Pass an
|
|
835
|
+
* {@link ResourceLockComponentLockForPathOptions.abortController} to make the lock cancelable via the
|
|
836
|
+
* indicator's right-click "unlock" menu or {@link requestResourceUnlockForPath}.
|
|
837
|
+
* @returns A {@link Disposable} that releases this lock when disposed. Disposing more than once is a no-op.
|
|
838
|
+
*/
|
|
839
|
+
lockForPath(pathOrFile, options) {
|
|
840
|
+
return getManager().lock(this.app, pathOrFile, this.pluginId, { abortController: options?.abortController, blocksMutations: options?.shouldBlockMutations, mode: options?.mode });
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Releases every lock still held by this plugin when the component (and thus the plugin) unloads,
|
|
844
|
+
* so no note is left stuck read-only by an operation that never completed.
|
|
845
|
+
*/
|
|
846
|
+
onunload() {
|
|
847
|
+
getManager().unlockAllForPlugin(this.app, this.pluginId);
|
|
848
|
+
super.onunload();
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Releases one lock previously acquired for the note at the given path via {@link lockForPath}.
|
|
852
|
+
*
|
|
853
|
+
* @param pathOrFile - The path or file of the note to unlock.
|
|
854
|
+
*/
|
|
855
|
+
unlockForPath(pathOrFile) {
|
|
856
|
+
getManager().unlock(this.app, pathOrFile, this.pluginId);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
class ResourceLockedError extends Error {
|
|
860
|
+
/**
|
|
861
|
+
* The vault path whose mutation was blocked.
|
|
862
|
+
*/
|
|
863
|
+
path;
|
|
864
|
+
/**
|
|
865
|
+
* Creates a {@link ResourceLockedError}.
|
|
866
|
+
*
|
|
867
|
+
* @param path - The vault path whose mutation was blocked.
|
|
868
|
+
*/
|
|
869
|
+
constructor(path) {
|
|
870
|
+
super(`The resource "${path}" is locked and cannot be mutated.`);
|
|
871
|
+
this.name = "ResourceLockedError";
|
|
872
|
+
this.path = path;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function isResourceLockedForPath(app, pathOrFile) {
|
|
876
|
+
return getManager().isLocked(app, pathOrFile);
|
|
877
|
+
}
|
|
878
|
+
function lockResourceForPath(app, pathOrFile, pluginId) {
|
|
879
|
+
return getManager().lock(app, pathOrFile, pluginId);
|
|
880
|
+
}
|
|
881
|
+
function requestResourceUnlockForPath(app, pathOrFile) {
|
|
882
|
+
getManager().requestUnlock(app, pathOrFile);
|
|
883
|
+
}
|
|
884
|
+
function unlockResourceForPath(app, pathOrFile, pluginId) {
|
|
885
|
+
getManager().unlock(app, pathOrFile, pluginId);
|
|
886
|
+
}
|
|
887
|
+
function getManager() {
|
|
888
|
+
return (0, import_obsidian_dev_utils_state.getObsidianDevUtilsState)(RESOURCE_LOCK_STATE_KEY, new ResourceLockManager()).value;
|
|
889
|
+
}
|
|
890
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
891
|
+
0 && (module.exports = {
|
|
892
|
+
ResourceLockComponent,
|
|
893
|
+
ResourceLockedError,
|
|
894
|
+
isResourceLockedForPath,
|
|
895
|
+
lockResourceForPath,
|
|
896
|
+
requestResourceUnlockForPath,
|
|
897
|
+
unlockResourceForPath
|
|
898
|
+
});
|
|
899
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vc3JjL29ic2lkaWFuL3Jlc291cmNlLWxvY2sudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQGZpbGVcbiAqXG4gKiBQYXRoLXNjb3BlZCByZXNvdXJjZSBsb2NraW5nIGZvciB2YXVsdCBmaWxlcyBhbmQgZm9sZGVycywgd2l0aCB0aHJlZSBlbmZvcmNlbWVudCBsYXllcnMgb3ZlciBvbmVcbiAqIHNoYXJlZCBsb2NrZWQtcGF0aCBzZXQ6XG4gKlxuICogMS4gKipFZGl0b3IgcmVhZC1vbmx5KiogXHUyMDE0IHdoaWxlIGEgcGF0aCBpcyBsb2NrZWQsIGV2ZXJ5IGN1cnJlbnQgYW5kIGZ1dHVyZSB7QGxpbmsgTWFya2Rvd25WaWV3fSBvZlxuICogICAgdGhhdCBub3RlIChpbiBhbnkgd2luZG93LCBpbmNsdWRpbmcgcG9wb3V0cykgaXMgbWFkZSByZWFkLW9ubHkgYW5kIHNob3dzIGEgbG9jayBpbmRpY2F0b3IgaW4gaXRzXG4gKiAgICB0YWIgaGVhZGVyLCBpdHMgdmlldyBhY3Rpb24gYmFyLCBhbmQgdGhlIHN0YXR1cyBiYXI7IGEgbm90ZSBpbnNpZGUgYSBgc3VidHJlZWAtbG9ja2VkIGZvbGRlciBpc1xuICogICAgY292ZXJlZCB0b28uIExvY2tzIGFyZSByZWZlcmVuY2UtY291bnRlZCBwZXIgbG9ja2luZyBwbHVnaW4gKHRoZSBpbmRpY2F0b3JzJyB0b29sdGlwIGxpc3RzIHdoaWNoXG4gKiAgICBwbHVnaW5zIGhvbGQgYSBsb2NrKSwgc28gbmVzdGVkL2NvbmN1cnJlbnQgb3BlcmF0aW9ucyBhcmUgc2FmZSBcdTIwMTQgdGhlIHBhdGggaXMgdW5sb2NrZWQgb25seSB3aGVuXG4gKiAgICB0aGUgbGFzdCBsb2NrIGlzIHJlbGVhc2VkLlxuICogMi4gKipWYXVsdC1tdXRhdGlvbiBibG9ja2luZyoqIChvcHQtaW4gdmlhIGBzaG91bGRCbG9ja011dGF0aW9uc2ApIFx1MjAxNCBhbnkgZWRpdC9kZWxldGUvcmVuYW1lL21vdmUvXG4gKiAgICBjcmVhdGUgb2YgYSBsb2NrZWQgcGF0aCB0aHJvdWdoIHRoZSBgVmF1bHRgL2BGaWxlTWFuYWdlcmAgQVBJIHRocm93cyB7QGxpbmsgUmVzb3VyY2VMb2NrZWRFcnJvcn0sXG4gKiAgICB1bmxlc3MgdGhlIG93bmluZyBvcGVyYXRpb24gaGFzIG9wZW5lZCBhIHtAbGluayBSZXNvdXJjZUxvY2tDb21wb25lbnQuYnlwYXNzQmxvY2tlZE11dGF0aW9uc30gc2NvcGVcbiAqICAgIG92ZXIgdGhhdCBwYXRoLlxuICogMy4gKipFeHRlcm5hbC1jaGFuZ2UgZGV0ZWN0aW9uKiogXHUyMDE0IGEgdmF1bHQvbWV0YWRhdGEgZXZlbnQgb24gYSBsb2NrZWQgcGF0aCB0aGF0IG5vIGJ5cGFzcyBzY29wZVxuICogICAgY292ZXJzIGlzIHRyZWF0ZWQgYXMgYW4gaW50cnVkZXIgKFN5bmMgLyByYXcgZmlsZXN5c3RlbSAvIGFub3RoZXIgcGx1Z2luKSBhbmQgYWJvcnRzIHRoZSBvd25pbmdcbiAqICAgIG9wZXJhdGlvbidzIHtAbGluayBBYm9ydENvbnRyb2xsZXJ9LlxuICpcbiAqIFRoZSBhY3F1aXJlcnMgcmV0dXJuIGEge0BsaW5rIERpc3Bvc2FibGV9LCBzbyB0aGUgcHJlZmVycmVkIGNhbGwgc3R5bGUgaXMgYSBgdXNpbmdgIGRlY2xhcmF0aW9uIHRoYXRcbiAqIHJlbGVhc2VzIGF1dG9tYXRpY2FsbHkgYXQgc2NvcGUgZXhpdCAoaW5jbHVkaW5nIG9uIHRocm93KTpcbiAqXG4gKiBgYGB0c1xuICogdXNpbmcgX2xvY2sgPSBsb2NrUmVzb3VyY2VGb3JQYXRoKGFwcCwgcGF0aCwgdGhpcy5tYW5pZmVzdC5pZCk7XG4gKiAvLyAuLi4gbG9uZy1ydW5uaW5nIHdvcmsgKHByb2Nlc3MsIHByb2Nlc3NGcm9udE1hdHRlciwgbWVyZ2Uvc3BsaXQpIC4uLlxuICogLy8gYXV0by11bmxvY2tlZCBhdCBzY29wZSBleGl0XG4gKiBgYGBcbiAqXG4gKiBUaGUgZXhwbGljaXQge0BsaW5rIHVubG9ja1Jlc291cmNlRm9yUGF0aH0gcGFpciByZW1haW5zIGF2YWlsYWJsZSBmb3Igbm9uLWB1c2luZ2AgY2FsbCBzaXRlcy5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7XG4gIEFwcCxcbiAgVEFic3RyYWN0RmlsZVxufSBmcm9tICdvYnNpZGlhbic7XG5cbmltcG9ydCB7IFZpZXdUeXBlIH0gZnJvbSAnQG9ic2lkaWFuLXR5cGluZ3Mvb2JzaWRpYW4tcHVibGljLWxhdGVzdC9pbXBsZW1lbnRhdGlvbnMnO1xuaW1wb3J0IHtcbiAgRmlsZU1hbmFnZXIsXG4gIE1hcmtkb3duVmlldyxcbiAgTWVudSxcbiAgc2V0SWNvbixcbiAgc2V0VG9vbHRpcCxcbiAgVmF1bHRcbn0gZnJvbSAnb2JzaWRpYW4nO1xuXG5pbXBvcnQgdHlwZSB7IFBhdGhPckZpbGUgfSBmcm9tICcuL2ZpbGUtc3lzdGVtLnRzJztcblxuaW1wb3J0IHsgY29udmVydEFzeW5jVG9TeW5jIH0gZnJvbSAnLi4vYXN5bmMudHMnO1xuaW1wb3J0IHtcbiAgQ2FsbGJhY2tEaXNwb3NhYmxlLFxuICBNdWx0aXBsZURpc3Bvc2VCZWhhdmlvclxufSBmcm9tICcuLi9kaXNwb3NhYmxlLnRzJztcbmltcG9ydCB7IG5vb3AgfSBmcm9tICcuLi9mdW5jdGlvbi50cyc7XG5pbXBvcnQgeyBnZXRPYnNpZGlhbkRldlV0aWxzU3RhdGUgfSBmcm9tICcuLi9vYnNpZGlhbi1kZXYtdXRpbHMtc3RhdGUudHMnO1xuaW1wb3J0IHsgYXNzZXJ0Tm9uTnVsbGFibGUgfSBmcm9tICcuLi90eXBlLWd1YXJkcy50cyc7XG5pbXBvcnQgeyBDb21wb25lbnRFeCB9IGZyb20gJy4vY29tcG9uZW50cy9jb21wb25lbnQtZXgudHMnO1xuaW1wb3J0IHsgTW9ua2V5QXJvdW5kQ29tcG9uZW50IH0gZnJvbSAnLi9jb21wb25lbnRzL21vbmtleS1hcm91bmQtY29tcG9uZW50LnRzJztcbmltcG9ydCB7IHRvZ2dsZUVkaXRvclJlYWRPbmx5IH0gZnJvbSAnLi9lZGl0b3IudHMnO1xuaW1wb3J0IHsgZ2V0UGF0aCB9IGZyb20gJy4vZmlsZS1zeXN0ZW0udHMnO1xuaW1wb3J0IHsgYXBwZW5kQ29kZUJsb2NrIH0gZnJvbSAnLi9odG1sLWVsZW1lbnQudHMnO1xuaW1wb3J0IHsgdCB9IGZyb20gJy4vaTE4bi9pMThuLnRzJztcbmltcG9ydCB7IGNvbmZpcm0gfSBmcm9tICcuL21vZGFscy9jb25maXJtLnRzJztcbmltcG9ydCB7XG4gIGlzQ2hpbGQsXG4gIGlzQ2hpbGRPclNlbGZcbn0gZnJvbSAnLi92YXVsdC50cyc7XG5cbmNvbnN0IEJFRVBfRFVSQVRJT05fU0VDT05EUyA9IDAuMDg7XG5jb25zdCBCRUVQX0ZSRVFVRU5DWV9IWiA9IDY2MDtcbmNvbnN0IEJFRVBfR0FJTiA9IDAuMDU7XG5jb25zdCBCRUVQX1RIUk9UVExFX01JTExJU0VDT05EUyA9IDIwMDtcbmNvbnN0IFJFU09VUkNFX0xPQ0tfU1RBVEVfS0VZID0gJ3Jlc291cmNlTG9jayc7XG5jb25zdCBMT0NLX0lDT05fSUQgPSAnbG9jayc7XG5jb25zdCBMT0NLX0lORElDQVRPUl9DU1NfQ0xBU1MgPSAnb2JzaWRpYW4tZGV2LXV0aWxzLWxvY2staW5kaWNhdG9yJztcbmNvbnN0IExPQ0tfSU5ESUNBVE9SX0ZMQVNIX0NTU19DTEFTUyA9ICdvYnNpZGlhbi1kZXYtdXRpbHMtbG9jay1pbmRpY2F0b3ItZmxhc2gnO1xuY29uc3QgU1RBVFVTX0JBUl9JVEVNX0NTU19DTEFTUyA9ICdzdGF0dXMtYmFyLWl0ZW0nO1xuY29uc3QgU1RBVFVTX0JBUl9DU1NfU0VMRUNUT1IgPSAnLnN0YXR1cy1iYXInO1xuY29uc3QgVU5MT0NLX0lDT05fSUQgPSAndW5sb2NrJztcblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmsgUmVzb3VyY2VMb2NrQ29tcG9uZW50LmxvY2tGb3JQYXRofS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNvdXJjZUxvY2tDb21wb25lbnRMb2NrRm9yUGF0aE9wdGlvbnMge1xuICAvKipcbiAgICogQW4gb3B0aW9uYWwge0BsaW5rIEFib3J0Q29udHJvbGxlcn0gYXNzb2NpYXRlZCB3aXRoIHRoZSBsb2NrLiBXaGVuIHRoZSBsb2NrIGluZGljYXRvciBpc1xuICAgKiByaWdodC1jbGlja2VkIGFuZCB0aGUgdXNlciBjb25maXJtcyBhbiB1bmxvY2sgKG9yIHtAbGluayByZXF1ZXN0UmVzb3VyY2VVbmxvY2tGb3JQYXRofSBpcyBjYWxsZWQgZm9yXG4gICAqIHRoZSBwYXRoKSwgdGhpcyBjb250cm9sbGVyIGlzIGFib3J0ZWQgc28gdGhlIG9wZXJhdGlvbiBob2xkaW5nIHRoZSBsb2NrIGNhbiBjYW5jZWwgaXRzZWxmIGFuZFxuICAgKiByZWxlYXNlIHRoZSBsb2NrIGluIGl0cyBvd24gY2xlYW51cC5cbiAgICovXG4gIHJlYWRvbmx5IGFib3J0Q29udHJvbGxlcj86IEFib3J0Q29udHJvbGxlcjtcblxuICAvKipcbiAgICogVGhlIGxvY2sgc2NvcGUuIGAnc3VidHJlZSdgIGFsc28gbG9ja3MgZXZlcnkgZGVzY2VuZGFudCBvZiBhIGZvbGRlciBwYXRoOyBgJ2ZpbGUnYCBsb2NrcyBvbmx5IHRoZVxuICAgKiBleGFjdCBwYXRoLiBBIGBzdWJ0cmVlYCBsb2NrIG11c3QgYmUgcmVsZWFzZWQgdmlhIHRoZSByZXR1cm5lZCB7QGxpbmsgRGlzcG9zYWJsZX0gKG5vdCB0aGVcbiAgICogZXhwbGljaXQge0BsaW5rIFJlc291cmNlTG9ja0NvbXBvbmVudC51bmxvY2tGb3JQYXRofSkuXG4gICAqXG4gICAqIEBkZWZhdWx0IGAnZmlsZSdgXG4gICAqL1xuICByZWFkb25seSBtb2RlPzogUmVzb3VyY2VMb2NrTW9kZTtcblxuICAvKipcbiAgICogV2hlbiBgdHJ1ZWAsIHRoZSBsb2NrIGFsc28gKipibG9ja3MgdmF1bHQgbXV0YXRpb25zKiogb2YgdGhlIGNvdmVyZWQgcGF0aChzKTogYW55IGF0dGVtcHQgdG8gZWRpdCxcbiAgICogZGVsZXRlLCByZW5hbWUsIG1vdmUsIG9yIChyZSljcmVhdGUgdGhlIHJlc291cmNlIHRocm91Z2ggdGhlIGBWYXVsdGAvYEZpbGVNYW5hZ2VyYCBBUEkgdGhyb3dzIGFcbiAgICoge0BsaW5rIFJlc291cmNlTG9ja2VkRXJyb3J9LiBJbnN0YWxsZWQgbGF6aWx5IG9uIHRoZSBmaXJzdCBzdWNoIGxvY2sgYW5kIHJlbW92ZWQgd2hlbiB0aGUgbGFzdCBvbmVcbiAgICogaXMgcmVsZWFzZWQuIFdoZW4gYGZhbHNlYCAodGhlIGRlZmF1bHQpIHRoZSBsb2NrIG9ubHkgbWFrZXMgdGhlIGVkaXRvciByZWFkLW9ubHkgXHUyMDE0IGxlZ2FjeSBiZWhhdmlvcixcbiAgICogc28gZXhpc3RpbmcgY29uc3VtZXJzIHRoYXQgd3JpdGUgdG8gYSBub3RlIHRoZXkgaG9sZCBhbiBlZGl0b3IgbG9jayBvbiBhcmUgdW5hZmZlY3RlZC5cbiAgICpcbiAgICogQGRlZmF1bHQgYGZhbHNlYFxuICAgKi9cbiAgcmVhZG9ubHkgc2hvdWxkQmxvY2tNdXRhdGlvbnM/OiBib29sZWFuO1xufVxuXG4vKipcbiAqIFRoZSBzY29wZSBvZiBhIGxvY2suXG4gKlxuICogLSBgJ2ZpbGUnYCBsb2NrcyBvbmx5IHRoZSBleGFjdCBwYXRoLlxuICogLSBgJ3N1YnRyZWUnYCBsb2NrcyB0aGUgcGF0aCBhbmQgZXZlcnkgZGVzY2VuZGFudCB1bmRlciBpdCAoaXRzIHdob2xlIGZvbGRlciBzdWJ0cmVlKSwgc28gYSBmaWxlXG4gKiAgIGluc2lkZSBhIGBzdWJ0cmVlYC1sb2NrZWQgZm9sZGVyIGlzIHRyZWF0ZWQgYXMgbG9ja2VkIHRvby5cbiAqL1xuZXhwb3J0IHR5cGUgUmVzb3VyY2VMb2NrTW9kZSA9ICdmaWxlJyB8ICdzdWJ0cmVlJztcblxuaW50ZXJmYWNlIExvY2tFbnRyeSB7XG4gIHJlYWRvbmx5IGFib3J0Q29udHJvbGxlcjogQWJvcnRDb250cm9sbGVyIHwgbnVsbDtcbiAgcmVhZG9ubHkgYmxvY2tzTXV0YXRpb25zOiBib29sZWFuO1xuICByZWFkb25seSBtb2RlOiBSZXNvdXJjZUxvY2tNb2RlO1xuICByZWFkb25seSBwbHVnaW5JZDogc3RyaW5nO1xufVxuXG5pbnRlcmZhY2UgTG9ja0luZGljYXRvcnMge1xuICByZWFkb25seSBhY3Rpb25JY29uRWw6IEhUTUxFbGVtZW50O1xuICBkaXNwb3NlVHlwZUxpc3RlbmVyKCk6IHZvaWQ7XG4gIHJlYWRvbmx5IHRhYkljb25FbDogSFRNTEVsZW1lbnQgfCBudWxsO1xufVxuXG5pbnRlcmZhY2UgTWFuYWdlckxvY2tPcHRpb25zIHtcbiAgcmVhZG9ubHkgYWJvcnRDb250cm9sbGVyPzogQWJvcnRDb250cm9sbGVyIHwgdW5kZWZpbmVkO1xuICByZWFkb25seSBibG9ja3NNdXRhdGlvbnM/OiBib29sZWFuIHwgdW5kZWZpbmVkO1xuICByZWFkb25seSBtb2RlPzogUmVzb3VyY2VMb2NrTW9kZSB8IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBDb25zdHJ1Y3RvciBwYXJhbWV0ZXJzIGZvciB7QGxpbmsgUmVzb3VyY2VMb2NrRXZlbnRzQ29tcG9uZW50fS5cbiAqL1xuaW50ZXJmYWNlIFJlc291cmNlTG9ja0V2ZW50c0NvbXBvbmVudENvbnN0cnVjdG9yUGFyYW1zIHtcbiAgcmVhZG9ubHkgYXBwOiBBcHA7XG4gIG9uQ2hhbmdlKHRoaXM6IHZvaWQpOiB2b2lkO1xuICBvbkV4dGVybmFsTXV0YXRpb24odGhpczogdm9pZCwgcGF0aDogc3RyaW5nKTogdm9pZDtcbiAgb25GaWxlTWVudSh0aGlzOiB2b2lkLCBtZW51OiBNZW51LCBmaWxlOiBUQWJzdHJhY3RGaWxlKTogdm9pZDtcbn1cblxuaW50ZXJmYWNlIFJlc291cmNlTG9ja011dGF0aW9uQmxvY2tlckNvbXBvbmVudENvbnN0cnVjdG9yUGFyYW1zIHtcbiAgc2hvdWxkQmxvY2tNdXRhdGlvbih0aGlzOiB2b2lkLCBwYXRoOiBzdHJpbmcpOiBib29sZWFuO1xufVxuXG4vKipcbiAqIFN1YnNjcmliZXMgdG8gdGhlIHdvcmtzcGFjZSBldmVudHMgdGhhdCBzaG91bGQgdHJpZ2dlciBhIGxvY2sgcmVjb25jaWxlIChhbmQgdGhlIGZpbGUgY29udGV4dC1tZW51XG4gKiBldmVudCB0aGF0IG9mZmVycyBhbiBcIlVubG9ja1wiIGl0ZW0pLiBJbXBsZW1lbnRlZCBhcyBhIHtAbGluayBDb21wb25lbnRFeH0gc28gdGhlIHN1YnNjcmlwdGlvbnMgYXJlXG4gKiByZWdpc3RlcmVkIG9uIGBsb2FkKClgIGFuZCBhdXRvbWF0aWNhbGx5IHJlbW92ZWQgb24gYHVubG9hZCgpYCwgaW5zdGVhZCBvZiBiZWluZyB0cmFja2VkIGJ5IGhhbmQuXG4gKi9cbmNsYXNzIFJlc291cmNlTG9ja0V2ZW50c0NvbXBvbmVudCBleHRlbmRzIENvbXBvbmVudEV4IHtcbiAgcHJpdmF0ZSByZWFkb25seSBhcHA6IEFwcDtcbiAgcHJpdmF0ZSByZWFkb25seSBvbkNoYW5nZTogKCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSByZWFkb25seSBvbkV4dGVybmFsTXV0YXRpb246IChwYXRoOiBzdHJpbmcpID0+IHZvaWQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgb25GaWxlTWVudTogKG1lbnU6IE1lbnUsIGZpbGU6IFRBYnN0cmFjdEZpbGUpID0+IHZvaWQ7XG5cbiAgcHVibGljIGNvbnN0cnVjdG9yKHBhcmFtczogUmVzb3VyY2VMb2NrRXZlbnRzQ29tcG9uZW50Q29uc3RydWN0b3JQYXJhbXMpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMuYXBwID0gcGFyYW1zLmFwcDtcbiAgICB0aGlzLm9uQ2hhbmdlID0gcGFyYW1zLm9uQ2hhbmdlO1xuICAgIHRoaXMub25FeHRlcm5hbE11dGF0aW9uID0gcGFyYW1zLm9uRXh0ZXJuYWxNdXRhdGlvbjtcbiAgICB0aGlzLm9uRmlsZU1lbnUgPSBwYXJhbXMub25GaWxlTWVudTtcbiAgfVxuXG4gIHB1YmxpYyBvdmVycmlkZSBvbmxvYWQoKTogdm9pZCB7XG4gICAgc3VwZXIub25sb2FkKCk7XG4gICAgdGhpcy5yZWdpc3RlckV2ZW50KHRoaXMuYXBwLndvcmtzcGFjZS5vbignYWN0aXZlLWxlYWYtY2hhbmdlJywgdGhpcy5vbkNoYW5nZSkpO1xuICAgIHRoaXMucmVnaXN0ZXJFdmVudCh0aGlzLmFwcC53b3Jrc3BhY2Uub24oJ2xheW91dC1jaGFuZ2UnLCB0aGlzLm9uQ2hhbmdlKSk7XG4gICAgLy8gU2FtZS1sZWFmIG5hdmlnYXRpb24gdG8gYW5vdGhlciBub3RlIGZpcmVzIG5vIGxlYWYvbGF5b3V0IGNoYW5nZTsgd2l0aG91dCB0aGlzIGl0IHN0YXlzIHJlYWQtb25seS5cbiAgICB0aGlzLnJlZ2lzdGVyRXZlbnQodGhpcy5hcHAud29ya3NwYWNlLm9uKCdmaWxlLW9wZW4nLCB0aGlzLm9uQ2hhbmdlKSk7XG4gICAgLy8gQWRkcyBhbiBcIlVubG9ja1wiIGl0ZW0gdG8gYSBsb2NrZWQgbm90ZSdzIHRhYi9maWxlIGNvbnRleHQgbWVudSAoYWxvbmdzaWRlIHRoZSBpbmRpY2F0b3IgbWVudXMpLlxuICAgIHRoaXMucmVnaXN0ZXJFdmVudCh0aGlzLmFwcC53b3Jrc3BhY2Uub24oJ2ZpbGUtbWVudScsIHRoaXMub25GaWxlTWVudSkpO1xuICAgIC8vIEV4dGVybmFsLWNoYW5nZSBiYWNrc3RvcDogYSB2YXVsdC9tZXRhZGF0YSBldmVudCBvbiBhIG11dGF0aW9uLWJsb2NrZWQgcGF0aCBub3QgY292ZXJlZCBieSBhbiBhY3RpdmUgYnlwYXNzIHNjb3BlIGlzIGFuIGludHJ1ZGVyIChhIHN5bmMvRlMvYWRhcHRlciBjaGFuZ2UgdGhhdCBieXBhc3NlZCB0aGUgYmxvY2tlciBwYXRjaCkuXG4gICAgdGhpcy5yZWdpc3RlckV2ZW50KHRoaXMuYXBwLnZhdWx0Lm9uKCdjcmVhdGUnLCAoZmlsZSk6IHZvaWQgPT4ge1xuICAgICAgdGhpcy5vbkV4dGVybmFsTXV0YXRpb24oZmlsZS5wYXRoKTtcbiAgICB9KSk7XG4gICAgdGhpcy5yZWdpc3RlckV2ZW50KHRoaXMuYXBwLnZhdWx0Lm9uKCdkZWxldGUnLCAoZmlsZSk6IHZvaWQgPT4ge1xuICAgICAgdGhpcy5vbkV4dGVybmFsTXV0YXRpb24oZmlsZS5wYXRoKTtcbiAgICB9KSk7XG4gICAgdGhpcy5yZWdpc3RlckV2ZW50KHRoaXMuYXBwLnZhdWx0Lm9uKCdyZW5hbWUnLCAoZmlsZSwgb2xkUGF0aCk6IHZvaWQgPT4ge1xuICAgICAgdGhpcy5vbkV4dGVybmFsTXV0YXRpb24ob2xkUGF0aCk7XG4gICAgICB0aGlzLm9uRXh0ZXJuYWxNdXRhdGlvbihmaWxlLnBhdGgpO1xuICAgIH0pKTtcbiAgICB0aGlzLnJlZ2lzdGVyRXZlbnQodGhpcy5hcHAubWV0YWRhdGFDYWNoZS5vbignZGVsZXRlZCcsIChmaWxlKTogdm9pZCA9PiB7XG4gICAgICB0aGlzLm9uRXh0ZXJuYWxNdXRhdGlvbihmaWxlLnBhdGgpO1xuICAgIH0pKTtcbiAgfVxufVxuXG4vKipcbiAqIE1vbmtleS1wYXRjaGVzIHRoZSB2YXVsdC1tdXRhdGlvbiBjaG9rZSBwb2ludHMgb24gYFZhdWx0YC9gRmlsZU1hbmFnZXJgIHNvIHRoYXQgYW55IGVkaXQsIGRlbGV0ZSxcbiAqIHJlbmFtZSwgbW92ZSwgb3IgKHJlKWNyZWF0ZSBvZiBhIG11dGF0aW9uLWJsb2NrZWQgcGF0aCB0aHJvd3MgYSB7QGxpbmsgUmVzb3VyY2VMb2NrZWRFcnJvcn0uXG4gKiBJbnN0YWxsZWQgbGF6aWx5IGJ5IHtAbGluayBSZXNvdXJjZUxvY2tNYW5hZ2VyfSBvbiB0aGUgZmlyc3QgYHNob3VsZEJsb2NrTXV0YXRpb25zYCBsb2NrIGFuZCByZW1vdmVkXG4gKiB3aGVuIHRoZSBsYXN0IG9uZSBpcyByZWxlYXNlZDsgYmVpbmcgYSB7QGxpbmsgTW9ua2V5QXJvdW5kQ29tcG9uZW50fSwgZXZlcnkgcGF0Y2ggaXMgdG9ybiBkb3duIG9uXG4gKiBgdW5sb2FkYC4gUmVuYW1lL2NvcHkgY2hlY2sgYm90aCBzb3VyY2UgYW5kIGRlc3RpbmF0aW9uOyB0aGUgb3RoZXIgbWV0aG9kcyBjaGVjayB0aGVpciBzaW5nbGUgcGF0aC5cbiAqL1xuY2xhc3MgUmVzb3VyY2VMb2NrTXV0YXRpb25CbG9ja2VyQ29tcG9uZW50IGV4dGVuZHMgTW9ua2V5QXJvdW5kQ29tcG9uZW50IHtcbiAgcHJpdmF0ZSByZWFkb25seSBzaG91bGRCbG9ja011dGF0aW9uOiAocGF0aDogc3RyaW5nKSA9PiBib29sZWFuO1xuXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihwYXJhbXM6IFJlc291cmNlTG9ja011dGF0aW9uQmxvY2tlckNvbXBvbmVudENvbnN0cnVjdG9yUGFyYW1zKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLnNob3VsZEJsb2NrTXV0YXRpb24gPSBwYXJhbXMuc2hvdWxkQmxvY2tNdXRhdGlvbjtcbiAgfVxuXG4gIHB1YmxpYyBvdmVycmlkZSBvbmxvYWQoKTogdm9pZCB7XG4gICAgc3VwZXIub25sb2FkKCk7XG5cbiAgICB0aGlzLnJlZ2lzdGVyTWV0aG9kUGF0Y2g8VmF1bHQsICdhcHBlbmQnPih7XG4gICAgICBtZXRob2ROYW1lOiAnYXBwZW5kJyxcbiAgICAgIG9iajogVmF1bHQucHJvdG90eXBlLFxuICAgICAgcGF0Y2hIYW5kbGVyOiAoeyBmYWxsYmFjaywgb3JpZ2luYWxBcmdzOiBbZmlsZV0gfSkgPT4ge1xuICAgICAgICB0aGlzLmFzc2VydE5vdEJsb2NrZWQoW2ZpbGUucGF0aF0pO1xuICAgICAgICByZXR1cm4gZmFsbGJhY2soKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnJlZ2lzdGVyTWV0aG9kUGF0Y2g8VmF1bHQsICdjb3B5Jz4oe1xuICAgICAgbWV0aG9kTmFtZTogJ2NvcHknLFxuICAgICAgb2JqOiBWYXVsdC5wcm90b3R5cGUsXG4gICAgICBwYXRjaEhhbmRsZXI6ICh7IGZhbGxiYWNrLCBvcmlnaW5hbEFyZ3M6IFssIG5ld1BhdGhdIH0pID0+IHtcbiAgICAgICAgdGhpcy5hc3NlcnROb3RCbG9ja2VkKFtuZXdQYXRoXSk7XG4gICAgICAgIHJldHVybiBmYWxsYmFjaygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucmVnaXN0ZXJNZXRob2RQYXRjaDxWYXVsdCwgJ2NyZWF0ZSc+KHtcbiAgICAgIG1ldGhvZE5hbWU6ICdjcmVhdGUnLFxuICAgICAgb2JqOiBWYXVsdC5wcm90b3R5cGUsXG4gICAgICBwYXRjaEhhbmRsZXI6ICh7IGZhbGxiYWNrLCBvcmlnaW5hbEFyZ3M6IFtwYXRoXSB9KSA9PiB7XG4gICAgICAgIHRoaXMuYXNzZXJ0Tm90QmxvY2tlZChbcGF0aF0pO1xuICAgICAgICByZXR1cm4gZmFsbGJhY2soKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLnJlZ2lzdGVyTWV0aG9kUGF0Y2g8VmF1bHQsICdjcmVhdGVCaW5hcnknPih7XG4gICAgICBtZXRob2ROYW1lOiAnY3JlYXRlQmluYXJ5JyxcbiAgICAgIG9iajogVmF1bHQucHJvdG90eXBlLFxuICAgICAgcGF0Y2hIYW5kbGVyOiAoeyBmYWxsYmFjaywgb3JpZ2luYWxBcmdzOiBbcGF0aF0gfSkgPT4ge1xuICAgICAgICB0aGlzLmFzc2VydE5vdEJsb2NrZWQoW3BhdGhdKTtcbiAgICAgICAgcmV0dXJuIGZhbGxiYWNrKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5yZWdpc3Rlck1ldGhvZFBhdGNoPFZhdWx0LCAnY3JlYXRlRm9sZGVyJz4oe1xuICAgICAgbWV0aG9kTmFtZTogJ2NyZWF0ZUZvbGRlcicsXG4gICAgICBvYmo6IFZhdWx0LnByb3RvdHlwZSxcbiAgICAgIHBhdGNoSGFuZGxlcjogKHsgZmFsbGJhY2ssIG9yaWdpbmFsQXJnczogW3BhdGhdIH0pID0+IHtcbiAgICAgICAgdGhpcy5hc3NlcnROb3RCbG9ja2VkKFtwYXRoXSk7XG4gICAgICAgIHJldHVybiBmYWxsYmFjaygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucmVnaXN0ZXJNZXRob2RQYXRjaDxWYXVsdCwgJ2RlbGV0ZSc+KHtcbiAgICAgIG1ldGhvZE5hbWU6ICdkZWxldGUnLFxuICAgICAgb2JqOiBWYXVsdC5wcm90b3R5cGUsXG4gICAgICBwYXRjaEhhbmRsZXI6ICh7IGZhbGxiYWNrLCBvcmlnaW5hbEFyZ3M6IFtmaWxlXSB9KSA9PiB7XG4gICAgICAgIHRoaXMuYXNzZXJ0Tm90QmxvY2tlZChbZmlsZS5wYXRoXSk7XG4gICAgICAgIHJldHVybiBmYWxsYmFjaygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucmVnaXN0ZXJNZXRob2RQYXRjaDxWYXVsdCwgJ21vZGlmeSc+KHtcbiAgICAgIG1ldGhvZE5hbWU6ICdtb2RpZnknLFxuICAgICAgb2JqOiBWYXVsdC5wcm90b3R5cGUsXG4gICAgICBwYXRjaEhhbmRsZXI6ICh7IGZhbGxiYWNrLCBvcmlnaW5hbEFyZ3M6IFtmaWxlXSB9KSA9PiB7XG4gICAgICAgIHRoaXMuYXNzZXJ0Tm90QmxvY2tlZChbZmlsZS5wYXRoXSk7XG4gICAgICAgIHJldHVybiBmYWxsYmFjaygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucmVnaXN0ZXJNZXRob2RQYXRjaDxWYXVsdCwgJ21vZGlmeUJpbmFyeSc+KHtcbiAgICAgIG1ldGhvZE5hbWU6ICdtb2RpZnlCaW5hcnknLFxuICAgICAgb2JqOiBWYXVsdC5wcm90b3R5cGUsXG4gICAgICBwYXRjaEhhbmRsZXI6ICh7IGZhbGxiYWNrLCBvcmlnaW5hbEFyZ3M6IFtmaWxlXSB9KSA9PiB7XG4gICAgICAgIHRoaXMuYXNzZXJ0Tm90QmxvY2tlZChbZmlsZS5wYXRoXSk7XG4gICAgICAgIHJldHVybiBmYWxsYmFjaygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucmVnaXN0ZXJNZXRob2RQYXRjaDxWYXVsdCwgJ3Byb2Nlc3MnPih7XG4gICAgICBtZXRob2ROYW1lOiAncHJvY2VzcycsXG4gICAgICBvYmo6IFZhdWx0LnByb3RvdHlwZSxcbiAgICAgIHBhdGNoSGFuZGxlcjogKHsgZmFsbGJhY2ssIG9yaWdpbmFsQXJnczogW2ZpbGVdIH0pID0+IHtcbiAgICAgICAgdGhpcy5hc3NlcnROb3RCbG9ja2VkKFtmaWxlLnBhdGhdKTtcbiAgICAgICAgcmV0dXJuIGZhbGxiYWNrKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5yZWdpc3Rlck1ldGhvZFBhdGNoPFZhdWx0LCAncmVuYW1lJz4oe1xuICAgICAgbWV0aG9kTmFtZTogJ3JlbmFtZScsXG4gICAgICBvYmo6IFZhdWx0LnByb3RvdHlwZSxcbiAgICAgIHBhdGNoSGFuZGxlcjogKHsgZmFsbGJhY2ssIG9yaWdpbmFsQXJnczogW2ZpbGUsIG5ld1BhdGhdIH0pID0+IHtcbiAgICAgICAgdGhpcy5hc3NlcnROb3RCbG9ja2VkKFtmaWxlLnBhdGgsIG5ld1BhdGhdKTtcbiAgICAgICAgcmV0dXJuIGZhbGxiYWNrKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5yZWdpc3Rlck1ldGhvZFBhdGNoPFZhdWx0LCAndHJhc2gnPih7XG4gICAgICBtZXRob2ROYW1lOiAndHJhc2gnLFxuICAgICAgb2JqOiBWYXVsdC5wcm90b3R5cGUsXG4gICAgICBwYXRjaEhhbmRsZXI6ICh7IGZhbGxiYWNrLCBvcmlnaW5hbEFyZ3M6IFtmaWxlXSB9KSA9PiB7XG4gICAgICAgIHRoaXMuYXNzZXJ0Tm90QmxvY2tlZChbZmlsZS5wYXRoXSk7XG4gICAgICAgIHJldHVybiBmYWxsYmFjaygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMucmVnaXN0ZXJNZXRob2RQYXRjaDxGaWxlTWFuYWdlciwgJ3JlbmFtZUZpbGUnPih7XG4gICAgICBtZXRob2ROYW1lOiAncmVuYW1lRmlsZScsXG4gICAgICBvYmo6IEZpbGVNYW5hZ2VyLnByb3RvdHlwZSxcbiAgICAgIHBhdGNoSGFuZGxlcjogKHsgZmFsbGJhY2ssIG9yaWdpbmFsQXJnczogW2ZpbGUsIG5ld1BhdGhdIH0pID0+IHtcbiAgICAgICAgdGhpcy5hc3NlcnROb3RCbG9ja2VkKFtmaWxlLnBhdGgsIG5ld1BhdGhdKTtcbiAgICAgICAgcmV0dXJuIGZhbGxiYWNrKCk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy5yZWdpc3Rlck1ldGhvZFBhdGNoPEZpbGVNYW5hZ2VyLCAndHJhc2hGaWxlJz4oe1xuICAgICAgbWV0aG9kTmFtZTogJ3RyYXNoRmlsZScsXG4gICAgICBvYmo6IEZpbGVNYW5hZ2VyLnByb3RvdHlwZSxcbiAgICAgIHBhdGNoSGFuZGxlcjogKHsgZmFsbGJhY2ssIG9yaWdpbmFsQXJnczogW2ZpbGVdIH0pID0+IHtcbiAgICAgICAgdGhpcy5hc3NlcnROb3RCbG9ja2VkKFtmaWxlLnBhdGhdKTtcbiAgICAgICAgcmV0dXJuIGZhbGxiYWNrKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzc2VydE5vdEJsb2NrZWQocGF0aHM6IHN0cmluZ1tdKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBwYXRoIG9mIHBhdGhzKSB7XG4gICAgICBpZiAodGhpcy5zaG91bGRCbG9ja011dGF0aW9uKHBhdGgpKSB7XG4gICAgICAgIHRocm93IG5ldyBSZXNvdXJjZUxvY2tlZEVycm9yKHBhdGgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIFRyYWNrcyBwYXRoLXNjb3BlZCBlZGl0b3IgbG9ja3MgYW5kIGtlZXBzIGV2ZXJ5IG9wZW4ge0BsaW5rIE1hcmtkb3duVmlld30gaW4gc3luYyB3aXRoIHRoZSBsb2NrZWRcbiAqIHNldC4gQSBzaW5nbGUgaW5zdGFuY2UgbGl2ZXMgb24gdGhlIHNoYXJlZCBgb2JzaWRpYW4tZGV2LXV0aWxzYCBzdGF0ZS4gRWFjaCBsb2NrIGlzIG9uZVxuICoge0BsaW5rIExvY2tFbnRyeX0gaW4gYSBwZXItcGF0aCBsaXN0LCBzbyBhIHBhdGggY2FuIGJlIGhlbGQgYnkgc2V2ZXJhbCBwbHVnaW5zIChvciBzZXZlcmFsIHRpbWVzIGJ5XG4gKiBvbmUpIGNvbmN1cnJlbnRseSBhbmQgaXMgcmVsZWFzZWQgb25seSB3aGVuIGl0cyBsYXN0IGVudHJ5IGlzIHJlbW92ZWQ7IHRoZSBpbmRpY2F0b3JzIHJlcG9ydCB3aGljaFxuICogcGx1Z2lucyBjdXJyZW50bHkgaG9sZCBhIGxvY2suXG4gKi9cbmNsYXNzIFJlc291cmNlTG9ja01hbmFnZXIge1xuICBwcml2YXRlIGF1ZGlvQ29udGV4dDogQXVkaW9Db250ZXh0IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgcmVhZG9ubHkgYnlwYXNzUGF0aFNldHMgPSBuZXcgU2V0PFJlYWRvbmx5U2V0PHN0cmluZz4+KCk7XG4gIHByaXZhdGUgZXZlbnRzQ29tcG9uZW50OiBudWxsIHwgUmVzb3VyY2VMb2NrRXZlbnRzQ29tcG9uZW50ID0gbnVsbDtcbiAgcHJpdmF0ZSByZWFkb25seSBpbmRpY2F0b3JzQnlWaWV3ID0gbmV3IE1hcDxNYXJrZG93blZpZXcsIExvY2tJbmRpY2F0b3JzPigpO1xuICBwcml2YXRlIGxhc3RCZWVwTWlsbGlzZWNvbmRzID0gMDtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2NrRW50cmllc0J5UGF0aCA9IG5ldyBNYXA8c3RyaW5nLCBMb2NrRW50cnlbXT4oKTtcbiAgcHJpdmF0ZSBtdXRhdGlvbkJsb2NrZXJDb21wb25lbnQ6IG51bGwgfCBSZXNvdXJjZUxvY2tNdXRhdGlvbkJsb2NrZXJDb21wb25lbnQgPSBudWxsO1xuICBwcml2YXRlIHN0YXR1c0Jhckl0ZW1FbDogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcblxuICBwdWJsaWMgYnlwYXNzKGFwcDogQXBwLCBwYXRoc09yRmlsZXM6IHJlYWRvbmx5IFBhdGhPckZpbGVbXSk6IERpc3Bvc2FibGUge1xuICAgIGNvbnN0IGJ5cGFzc2VkUGF0aHM6IFJlYWRvbmx5U2V0PHN0cmluZz4gPSBuZXcgU2V0KHBhdGhzT3JGaWxlcy5tYXAoKHBhdGhPckZpbGUpID0+IGdldFBhdGgoYXBwLCBwYXRoT3JGaWxlKSkpO1xuICAgIHRoaXMuYnlwYXNzUGF0aFNldHMuYWRkKGJ5cGFzc2VkUGF0aHMpO1xuICAgIHJldHVybiBuZXcgQ2FsbGJhY2tEaXNwb3NhYmxlKHtcbiAgICAgIGNhbGxiYWNrOiAoKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMuYnlwYXNzUGF0aFNldHMuZGVsZXRlKGJ5cGFzc2VkUGF0aHMpO1xuICAgICAgfSxcbiAgICAgIG11bHRpcGxlRGlzcG9zZUJlaGF2aW9yOiBNdWx0aXBsZURpc3Bvc2VCZWhhdmlvci5JZ25vcmVcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBpc0xvY2tlZChhcHA6IEFwcCwgcGF0aE9yRmlsZTogUGF0aE9yRmlsZSk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmlzUGF0aExvY2tlZChnZXRQYXRoKGFwcCwgcGF0aE9yRmlsZSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyB3aGV0aGVyIHRoZSBnaXZlbiBwYXRoIGlzIGxvY2tlZCBkaXJlY3RseSBvciBpcyBjb3ZlcmVkIGJ5IGEgYHN1YnRyZWVgLWxvY2tlZCBhbmNlc3RvclxuICAgKiBmb2xkZXIuXG4gICAqXG4gICAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICAgKiBAcGFyYW0gcGF0aE9yRmlsZSAtIFRoZSBwYXRoIG9yIGZpbGUgdG8gY2hlY2suXG4gICAqIEByZXR1cm5zIGB0cnVlYCBpZiB0aGUgcGF0aCBpcyBpdHNlbGYgbG9ja2VkIG9yIGxpZXMgdW5kZXIgYSBgc3VidHJlZWAtbG9ja2VkIGZvbGRlci5cbiAgICovXG4gIHB1YmxpYyBpc0xvY2tlZEJ5QW5jZXN0b3IoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5yZXNvbHZlTG9ja093bmVyUGF0aChhcHAsIGdldFBhdGgoYXBwLCBwYXRoT3JGaWxlKSkgIT09IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIHdoZXRoZXIgdGhlIGdpdmVuIHBhdGggaXMgY292ZXJlZCBieSBhIG11dGF0aW9uLWJsb2NraW5nIGxvY2sgKG9uZSB0YWtlbiB3aXRoXG4gICAqIGBibG9ja3NNdXRhdGlvbnNgKSBkaXJlY3RseSBvciBvbiBhIGBzdWJ0cmVlYC1sb2NrZWQgYW5jZXN0b3IgZm9sZGVyLlxuICAgKlxuICAgKiBAcGFyYW0gYXBwIC0gVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZS5cbiAgICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIHRvIGNoZWNrLlxuICAgKiBAcmV0dXJucyBgdHJ1ZWAgaWYgYSBtdXRhdGlvbiBvZiB0aGUgcGF0aCBpcyBjdXJyZW50bHkgYmxvY2tlZC5cbiAgICovXG4gIHB1YmxpYyBpc011dGF0aW9uQmxvY2tlZEJ5QW5jZXN0b3IoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUpOiBib29sZWFuIHtcbiAgICBjb25zdCBwYXRoID0gZ2V0UGF0aChhcHAsIHBhdGhPckZpbGUpO1xuICAgIGNvbnN0IGV4YWN0RW50cmllcyA9IHRoaXMubG9ja0VudHJpZXNCeVBhdGguZ2V0KHBhdGgpO1xuICAgIGlmIChleGFjdEVudHJpZXM/LnNvbWUoKGVudHJ5KSA9PiBlbnRyeS5ibG9ja3NNdXRhdGlvbnMpKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgZm9yIChjb25zdCBbbG9ja2VkUGF0aCwgZW50cmllc10gb2YgdGhpcy5sb2NrRW50cmllc0J5UGF0aCkge1xuICAgICAgaWYgKGVudHJpZXMuc29tZSgoZW50cnkpID0+IGVudHJ5Lm1vZGUgPT09ICdzdWJ0cmVlJyAmJiBlbnRyeS5ibG9ja3NNdXRhdGlvbnMpICYmIGlzQ2hpbGQoeyBhcHAsIGNoaWxkUGF0aE9yRmlsZTogcGF0aCwgcGFyZW50UGF0aE9yRmlsZTogbG9ja2VkUGF0aCB9KSkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcHVibGljIGxvY2soYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUsIHBsdWdpbklkOiBzdHJpbmcsIG9wdGlvbnM6IE1hbmFnZXJMb2NrT3B0aW9ucyA9IHt9KTogRGlzcG9zYWJsZSB7XG4gICAgY29uc3QgcGF0aCA9IGdldFBhdGgoYXBwLCBwYXRoT3JGaWxlKTtcbiAgICBjb25zdCBlbnRyeTogTG9ja0VudHJ5ID0ge1xuICAgICAgYWJvcnRDb250cm9sbGVyOiBvcHRpb25zLmFib3J0Q29udHJvbGxlciA/PyBudWxsLFxuICAgICAgYmxvY2tzTXV0YXRpb25zOiBvcHRpb25zLmJsb2Nrc011dGF0aW9ucyA/PyBmYWxzZSxcbiAgICAgIG1vZGU6IG9wdGlvbnMubW9kZSA/PyAnZmlsZScsXG4gICAgICBwbHVnaW5JZFxuICAgIH07XG4gICAgbGV0IGVudHJpZXMgPSB0aGlzLmxvY2tFbnRyaWVzQnlQYXRoLmdldChwYXRoKTtcbiAgICBpZiAoIWVudHJpZXMpIHtcbiAgICAgIGVudHJpZXMgPSBbXTtcbiAgICAgIHRoaXMubG9ja0VudHJpZXNCeVBhdGguc2V0KHBhdGgsIGVudHJpZXMpO1xuICAgIH1cbiAgICBlbnRyaWVzLnB1c2goZW50cnkpO1xuICAgIHRoaXMuZW5zdXJlU3Vic2NyaWJlZChhcHApO1xuICAgIGlmIChlbnRyeS5ibG9ja3NNdXRhdGlvbnMpIHtcbiAgICAgIHRoaXMuZW5zdXJlQmxvY2tlckluc3RhbGxlZChhcHApO1xuICAgIH1cbiAgICB0aGlzLnJlY29uY2lsZShhcHApO1xuXG4gICAgcmV0dXJuIG5ldyBDYWxsYmFja0Rpc3Bvc2FibGUoe1xuICAgICAgY2FsbGJhY2s6ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5yZW1vdmVFbnRyeShhcHAsIHBhdGgsIGVudHJ5KTtcbiAgICAgIH0sXG4gICAgICBtdWx0aXBsZURpc3Bvc2VCZWhhdmlvcjogTXVsdGlwbGVEaXNwb3NlQmVoYXZpb3IuSWdub3JlXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQWJvcnRzIGV2ZXJ5IHtAbGluayBBYm9ydENvbnRyb2xsZXJ9IGN1cnJlbnRseSByZWdpc3RlcmVkIGZvciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCwgd2hpY2hcbiAgICogY2FuY2VscyB0aGUgb3BlcmF0aW9ucyBob2xkaW5nIHRoZSBsb2NrLiBFYWNoIG9wZXJhdGlvbiByZWxlYXNlcyBpdHMgb3duIGxvY2sgaW4gaXRzIGBmaW5hbGx5YFxuICAgKiB3aGVuIGl0IG9ic2VydmVzIHRoZSBhYm9ydCwgc28gdGhpcyBkb2VzIG5vdCB1bmxvY2sgdGhlIHBhdGggZGlyZWN0bHkuIEEgbm8tb3Agd2hlbiBubyBjb250cm9sbGVyXG4gICAqIGlzIHJlZ2lzdGVyZWQgZm9yIHRoZSBwYXRoLlxuICAgKlxuICAgKiBAcGFyYW0gYXBwIC0gVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZS5cbiAgICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIHJlcXVlc3QgYW4gdW5sb2NrIGZvci5cbiAgICovXG4gIHB1YmxpYyByZXF1ZXN0VW5sb2NrKGFwcDogQXBwLCBwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogdm9pZCB7XG4gICAgY29uc3QgZW50cmllcyA9IHRoaXMubG9ja0VudHJpZXNCeVBhdGguZ2V0KGdldFBhdGgoYXBwLCBwYXRoT3JGaWxlKSk7XG4gICAgaWYgKCFlbnRyaWVzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgZW50cnkuYWJvcnRDb250cm9sbGVyPy5hYm9ydCgpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyB1bmxvY2soYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUsIHBsdWdpbklkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBwYXRoID0gZ2V0UGF0aChhcHAsIHBhdGhPckZpbGUpO1xuICAgIGNvbnN0IGVudHJpZXMgPSB0aGlzLmxvY2tFbnRyaWVzQnlQYXRoLmdldChwYXRoKTtcbiAgICBpZiAoIWVudHJpZXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgaW5kZXggPSBlbnRyaWVzLmZpbmRJbmRleCgoZW50cnkpID0+IGVudHJ5LnBsdWdpbklkID09PSBwbHVnaW5JZCk7XG4gICAgaWYgKGluZGV4ID09PSAtMSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBlbnRyaWVzLnNwbGljZShpbmRleCwgMSk7XG4gICAgaWYgKGVudHJpZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLmxvY2tFbnRyaWVzQnlQYXRoLmRlbGV0ZShwYXRoKTtcbiAgICB9XG4gICAgdGhpcy5yZWNvbmNpbGVBbmRDbGVhbnVwKGFwcCk7XG4gIH1cblxuICBwdWJsaWMgdW5sb2NrQWxsRm9yUGx1Z2luKGFwcDogQXBwLCBwbHVnaW5JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBbcGF0aCwgZW50cmllc10gb2YgdGhpcy5sb2NrRW50cmllc0J5UGF0aCkge1xuICAgICAgY29uc3QgcmVtYWluaW5nID0gZW50cmllcy5maWx0ZXIoKGVudHJ5KSA9PiBlbnRyeS5wbHVnaW5JZCAhPT0gcGx1Z2luSWQpO1xuICAgICAgaWYgKHJlbWFpbmluZy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgdGhpcy5sb2NrRW50cmllc0J5UGF0aC5kZWxldGUocGF0aCk7XG4gICAgICB9IGVsc2UgaWYgKHJlbWFpbmluZy5sZW5ndGggIT09IGVudHJpZXMubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMubG9ja0VudHJpZXNCeVBhdGguc2V0KHBhdGgsIHJlbWFpbmluZyk7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMucmVjb25jaWxlQW5kQ2xlYW51cChhcHApO1xuICB9XG5cbiAgcHJpdmF0ZSBhZGRVbmxvY2tNZW51SXRlbShhcHA6IEFwcCwgbWVudTogTWVudSwgcGF0aDogc3RyaW5nKTogdm9pZCB7XG4gICAgbWVudS5hZGRJdGVtKChpdGVtKSA9PiB7XG4gICAgICBpdGVtXG4gICAgICAgIC5zZXRUaXRsZSh0KCgkKSA9PiAkLm9ic2lkaWFuRGV2VXRpbHMucmVzb3VyY2VMb2NrLnVubG9ja01lbnVJdGVtKSlcbiAgICAgICAgLnNldEljb24oVU5MT0NLX0lDT05fSUQpXG4gICAgICAgIC5vbkNsaWNrKGNvbnZlcnRBc3luY1RvU3luYyhhc3luYyAoKSA9PiB7XG4gICAgICAgICAgY29uc3QgaXNDb25maXJtZWQgPSBhd2FpdCBjb25maXJtKHtcbiAgICAgICAgICAgIGFwcCxcbiAgICAgICAgICAgIG1lc3NhZ2U6IHRoaXMudW5sb2NrQ29uZmlybU1lc3NhZ2UoYXBwLCBwYXRoKSxcbiAgICAgICAgICAgIHRpdGxlOiB0KCgkKSA9PiAkLm9ic2lkaWFuRGV2VXRpbHMucmVzb3VyY2VMb2NrLnVubG9ja0NvbmZpcm1UaXRsZSlcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpZiAoaXNDb25maXJtZWQpIHtcbiAgICAgICAgICAgIHRoaXMucmVxdWVzdFVubG9jayhhcHAsIHBhdGgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSkpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBiZWVwKCk6IHZvaWQge1xuICAgIC8vIFRocm90dGxlIHNvIGEgYnVyc3Qgb2Yga2V5c3Ryb2tlcyBkb2VzIG5vdCBzdGFjayBvdmVybGFwcGluZyB0b25lcyBpbnRvIGEgYnV6ei5cbiAgICBjb25zdCBub3dNaWxsaXNlY29uZHMgPSBEYXRlLm5vdygpO1xuICAgIGlmIChub3dNaWxsaXNlY29uZHMgLSB0aGlzLmxhc3RCZWVwTWlsbGlzZWNvbmRzIDwgQkVFUF9USFJPVFRMRV9NSUxMSVNFQ09ORFMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5sYXN0QmVlcE1pbGxpc2Vjb25kcyA9IG5vd01pbGxpc2Vjb25kcztcblxuICAgIC8vIGBsaWIuZG9tYCB0eXBlcyBgQXVkaW9Db250ZXh0YCBhcyBhbHdheXMgcHJlc2VudCwgYnV0IGl0IGlzIGFic2VudCBpbiBzb21lIHJ1bnRpbWVzXG4gICAgLy8gKGUuZy4gdGhlIGpzZG9tIHRlc3QgZW52aXJvbm1lbnQpLCBzbyB0aGlzIGd1YXJkIGlzIHJlYWwsIG5vdCByZWR1bmRhbnQuXG4gICAgY29uc3QgQXVkaW9Db250ZXh0Q2xhc3MgPSB3aW5kb3cuQXVkaW9Db250ZXh0O1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW5uZWNlc3NhcnktY29uZGl0aW9uIC0tIHNlZSB0aGUgY29tbWVudCBhYm92ZS5cbiAgICBpZiAoIUF1ZGlvQ29udGV4dENsYXNzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuYXVkaW9Db250ZXh0ID8/PSBuZXcgQXVkaW9Db250ZXh0Q2xhc3MoKTtcbiAgICBjb25zdCBhdWRpb0NvbnRleHQgPSB0aGlzLmF1ZGlvQ29udGV4dDtcbiAgICBjb25zdCBvc2NpbGxhdG9yID0gYXVkaW9Db250ZXh0LmNyZWF0ZU9zY2lsbGF0b3IoKTtcbiAgICBjb25zdCBnYWluTm9kZSA9IGF1ZGlvQ29udGV4dC5jcmVhdGVHYWluKCk7XG4gICAgb3NjaWxsYXRvci50eXBlID0gJ3NpbmUnO1xuICAgIG9zY2lsbGF0b3IuZnJlcXVlbmN5LnZhbHVlID0gQkVFUF9GUkVRVUVOQ1lfSFo7XG4gICAgZ2Fpbk5vZGUuZ2Fpbi52YWx1ZSA9IEJFRVBfR0FJTjtcbiAgICBvc2NpbGxhdG9yLmNvbm5lY3QoZ2Fpbk5vZGUpO1xuICAgIGdhaW5Ob2RlLmNvbm5lY3QoYXVkaW9Db250ZXh0LmRlc3RpbmF0aW9uKTtcbiAgICBvc2NpbGxhdG9yLnN0YXJ0KCk7XG4gICAgb3NjaWxsYXRvci5zdG9wKGF1ZGlvQ29udGV4dC5jdXJyZW50VGltZSArIEJFRVBfRFVSQVRJT05fU0VDT05EUyk7XG4gIH1cblxuICBwcml2YXRlIGNyZWF0ZUluZGljYXRvcnMoYXBwOiBBcHAsIHZpZXc6IE1hcmtkb3duVmlldywgcGF0aDogc3RyaW5nLCB0b29sdGlwOiBzdHJpbmcpOiBMb2NrSW5kaWNhdG9ycyB7XG4gICAgY29uc3QgYWN0aW9uSWNvbkVsID0gdmlldy5hZGRBY3Rpb24oTE9DS19JQ09OX0lELCB0b29sdGlwLCBub29wKTtcbiAgICB0aGlzLnJlZ2lzdGVyVW5sb2NrTWVudShhcHAsIGFjdGlvbkljb25FbCwgKCkgPT4gcGF0aCk7XG5cbiAgICBsZXQgdGFiSWNvbkVsOiBIVE1MRWxlbWVudCB8IG51bGwgPSBudWxsO1xuICAgIGNvbnN0IHRhYlN0YXR1c0NvbnRhaW5lckVsID0gdmlldy5sZWFmLnRhYkhlYWRlclN0YXR1c0NvbnRhaW5lckVsO1xuICAgIGlmICh0YWJTdGF0dXNDb250YWluZXJFbCkge1xuICAgICAgdGFiSWNvbkVsID0gdGFiU3RhdHVzQ29udGFpbmVyRWwuY3JlYXRlU3Bhbih7IGNsczogTE9DS19JTkRJQ0FUT1JfQ1NTX0NMQVNTIH0pO1xuICAgICAgc2V0SWNvbih0YWJJY29uRWwsIExPQ0tfSUNPTl9JRCk7XG4gICAgICBzZXRUb29sdGlwKHRhYkljb25FbCwgdG9vbHRpcCk7XG4gICAgICB0aGlzLnJlZ2lzdGVyVW5sb2NrTWVudShhcHAsIHRhYkljb25FbCwgKCkgPT4gcGF0aCk7XG4gICAgfVxuXG4gICAgLy8gQSBsb2NrZWQgdmlldyBpcyByZWFkLW9ubHkgYnV0IHN0aWxsIGVkaXRhYmxlLWZvY3VzYWJsZSwgc28gYSBrZXlzdHJva2UgZmlyZXMgYGJlZm9yZWlucHV0YC5cbiAgICAvLyBPbiB0aGF0IHJlamVjdGVkIGF0dGVtcHQsIGZsYXNoIHRoZSBpbmRpY2F0b3JzIGFuZCBiZWVwIHNvIGl0J3MgY2xlYXIgdGhlIG5vdGUgaXMgbG9ja2VkLlxuICAgIC8vIFRoZSBsaXN0ZW5lciBpcyByZW1vdmVkIG9uIHVubG9jay5cbiAgICBjb25zdCBvblR5cGVBdHRlbXB0ID0gKCk6IHZvaWQgPT4ge1xuICAgICAgdGhpcy5mbGFzaEluZGljYXRvcnModmlldyk7XG4gICAgICB0aGlzLmJlZXAoKTtcbiAgICB9O1xuICAgIHZpZXcuY29udGVudEVsLmFkZEV2ZW50TGlzdGVuZXIoJ2JlZm9yZWlucHV0Jywgb25UeXBlQXR0ZW1wdCk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgYWN0aW9uSWNvbkVsLFxuICAgICAgZGlzcG9zZVR5cGVMaXN0ZW5lcjogKCk6IHZvaWQgPT4ge1xuICAgICAgICB2aWV3LmNvbnRlbnRFbC5yZW1vdmVFdmVudExpc3RlbmVyKCdiZWZvcmVpbnB1dCcsIG9uVHlwZUF0dGVtcHQpO1xuICAgICAgfSxcbiAgICAgIHRhYkljb25FbFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGVuc3VyZUJsb2NrZXJJbnN0YWxsZWQoYXBwOiBBcHApOiB2b2lkIHtcbiAgICBpZiAodGhpcy5tdXRhdGlvbkJsb2NrZXJDb21wb25lbnQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5tdXRhdGlvbkJsb2NrZXJDb21wb25lbnQgPSBuZXcgUmVzb3VyY2VMb2NrTXV0YXRpb25CbG9ja2VyQ29tcG9uZW50KHtcbiAgICAgIHNob3VsZEJsb2NrTXV0YXRpb246IChwYXRoKTogYm9vbGVhbiA9PiB0aGlzLnNob3VsZEJsb2NrTXV0YXRpb24oYXBwLCBwYXRoKVxuICAgIH0pO1xuICAgIHRoaXMubXV0YXRpb25CbG9ja2VyQ29tcG9uZW50LmxvYWQoKTtcbiAgfVxuXG4gIHByaXZhdGUgZW5zdXJlU3Vic2NyaWJlZChhcHA6IEFwcCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmV2ZW50c0NvbXBvbmVudCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmV2ZW50c0NvbXBvbmVudCA9IG5ldyBSZXNvdXJjZUxvY2tFdmVudHNDb21wb25lbnQoe1xuICAgICAgYXBwLFxuICAgICAgb25DaGFuZ2U6ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5yZWNvbmNpbGUoYXBwKTtcbiAgICAgIH0sXG4gICAgICBvbkV4dGVybmFsTXV0YXRpb246IChwYXRoKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMuaGFuZGxlRXh0ZXJuYWxNdXRhdGlvbihhcHAsIHBhdGgpO1xuICAgICAgfSxcbiAgICAgIG9uRmlsZU1lbnU6IChtZW51LCBmaWxlKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMuaGFuZGxlRmlsZU1lbnUoYXBwLCBtZW51LCBmaWxlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLmV2ZW50c0NvbXBvbmVudC5sb2FkKCk7XG4gIH1cblxuICBwcml2YXRlIGZsYXNoRWxlbWVudChlbDogSFRNTEVsZW1lbnQpOiB2b2lkIHtcbiAgICBlbC5yZW1vdmVDbGFzcyhMT0NLX0lORElDQVRPUl9GTEFTSF9DU1NfQ0xBU1MpO1xuICAgIC8vIFJlYWQgbGF5b3V0IHRvIGZvcmNlIGEgcmVmbG93IHNvIHRoZSBmbGFzaCBhbmltYXRpb24gcmVzdGFydHMgb24gZXZlcnkgcmVqZWN0ZWQga2V5c3Ryb2tlLlxuICAgIGVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGVsLmFkZENsYXNzKExPQ0tfSU5ESUNBVE9SX0ZMQVNIX0NTU19DTEFTUyk7XG4gIH1cblxuICBwcml2YXRlIGZsYXNoSW5kaWNhdG9ycyh2aWV3OiBNYXJrZG93blZpZXcpOiB2b2lkIHtcbiAgICBjb25zdCBpbmRpY2F0b3JzID0gdGhpcy5pbmRpY2F0b3JzQnlWaWV3LmdldCh2aWV3KTtcbiAgICBmb3IgKGNvbnN0IGVsIG9mIFtpbmRpY2F0b3JzPy5hY3Rpb25JY29uRWwsIGluZGljYXRvcnM/LnRhYkljb25FbCwgdGhpcy5zdGF0dXNCYXJJdGVtRWxdKSB7XG4gICAgICBpZiAoZWwpIHtcbiAgICAgICAgdGhpcy5mbGFzaEVsZW1lbnQoZWwpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZWFjdHMgdG8gYSB2YXVsdC9tZXRhZGF0YSBldmVudCBvbiBhIHBhdGguIElmIHRoZSBwYXRoIGlzIG11dGF0aW9uLWJsb2NrZWQgYW5kIE5PVCBjb3ZlcmVkIGJ5IGFuXG4gICAqIGFjdGl2ZSBieXBhc3Mgc2NvcGUsIHRoZSBjaGFuZ2UgYnlwYXNzZWQgdGhlIGJsb2NrZXIgcGF0Y2ggKHN5bmMgLyBmaWxlc3lzdGVtIC8gYSByYXctYWRhcHRlclxuICAgKiB3cml0ZSkgXHUyMDE0IGFuIGludHJ1ZGVyLiBBYm9ydCB0aGUgYGFib3J0Q29udHJvbGxlcmAgb2YgZXZlcnkgbXV0YXRpb24tYmxvY2tpbmcgbG9jayBjb3ZlcmluZyB0aGUgcGF0aFxuICAgKiBzbyB0aGUgb3duaW5nIG9wZXJhdGlvbiBjYW5jZWxzIGFuZCByb2xscyBiYWNrLiBBIG5vLW9wIGZvciB0aGUgb3duZXIncyBvd24gKGJ5cGFzc2VkKSBtdXRhdGlvbnMsXG4gICAqIGZvciB1bmJsb2NrZWQgcGF0aHMsIGFuZCBmb3IgYmxvY2tpbmcgbG9ja3Mgd2l0aCBubyBgYWJvcnRDb250cm9sbGVyYC5cbiAgICpcbiAgICogQHBhcmFtIGFwcCAtIFRoZSBPYnNpZGlhbiBhcHAgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSBwYXRoIC0gVGhlIHBhdGggdGhlIGV2ZW50IGZpcmVkIG9uLlxuICAgKi9cbiAgcHJpdmF0ZSBoYW5kbGVFeHRlcm5hbE11dGF0aW9uKGFwcDogQXBwLCBwYXRoOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNNdXRhdGlvbkJsb2NrZWRCeUFuY2VzdG9yKGFwcCwgcGF0aCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMuaXNCeXBhc3NlZChhcHAsIHBhdGgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGZvciAoY29uc3QgW2xvY2tlZFBhdGgsIGVudHJpZXNdIG9mIHRoaXMubG9ja0VudHJpZXNCeVBhdGgpIHtcbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICBpZiAoIWVudHJ5LmJsb2Nrc011dGF0aW9ucyB8fCAhZW50cnkuYWJvcnRDb250cm9sbGVyKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY292ZXJzUGF0aCA9IGxvY2tlZFBhdGggPT09IHBhdGggfHwgKGVudHJ5Lm1vZGUgPT09ICdzdWJ0cmVlJyAmJiBpc0NoaWxkKHsgYXBwLCBjaGlsZFBhdGhPckZpbGU6IHBhdGgsIHBhcmVudFBhdGhPckZpbGU6IGxvY2tlZFBhdGggfSkpO1xuICAgICAgICBpZiAoY292ZXJzUGF0aCkge1xuICAgICAgICAgIGVudHJ5LmFib3J0Q29udHJvbGxlci5hYm9ydCgpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBoYW5kbGVGaWxlTWVudShhcHA6IEFwcCwgbWVudTogTWVudSwgZmlsZTogVEFic3RyYWN0RmlsZSk6IHZvaWQge1xuICAgIC8vIE9mZmVyIFwiVW5sb2NrXCIgZm9yIGEgZGlyZWN0bHktbG9ja2VkIGZpbGUvZm9sZGVyLCBvciBhbnl0aGluZyBjb3ZlcmVkIGJ5IGEgYHN1YnRyZWVgIGxvY2sgb24gYW4gYW5jZXN0b3IuIFRoZSBtZW51IHRhcmdldHMgdGhlIG93bmVyIGxvY2sncyBwYXRoLlxuICAgIGNvbnN0IG93bmVyUGF0aCA9IHRoaXMucmVzb2x2ZUxvY2tPd25lclBhdGgoYXBwLCBmaWxlLnBhdGgpO1xuICAgIGlmIChvd25lclBhdGggPT09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5hZGRVbmxvY2tNZW51SXRlbShhcHAsIG1lbnUsIG93bmVyUGF0aCk7XG4gIH1cblxuICBwcml2YXRlIGhhc0Jsb2NraW5nTG9jaygpOiBib29sZWFuIHtcbiAgICBmb3IgKGNvbnN0IGVudHJpZXMgb2YgdGhpcy5sb2NrRW50cmllc0J5UGF0aC52YWx1ZXMoKSkge1xuICAgICAgaWYgKGVudHJpZXMuc29tZSgoZW50cnkpID0+IGVudHJ5LmJsb2Nrc011dGF0aW9ucykpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3Mgd2hldGhlciB0aGUgcGF0aCBpcyBjb3ZlcmVkIGJ5IGFuIGFjdGl2ZSB7QGxpbmsgUmVzb3VyY2VMb2NrTWFuYWdlci5ieXBhc3N9IHNjb3BlIFx1MjAxNCB0aGVcbiAgICogb3duZXIgZGVjbGFyaW5nIHRoYXQgaXRzIG93biBtdXRhdGlvbnMgb2YgdGhlIHBhdGggKGFuZCwgZm9yIGEgZm9sZGVyLCBpdHMgc3VidHJlZSkgYXJlIHNhbmN0aW9uZWQuXG4gICAqXG4gICAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICAgKiBAcGFyYW0gcGF0aCAtIFRoZSBwYXRoIGJlaW5nIG11dGF0ZWQuXG4gICAqIEByZXR1cm5zIGB0cnVlYCBpZiBzb21lIGFjdGl2ZSBieXBhc3Mgc2NvcGUgY292ZXJzIHRoZSBwYXRoLlxuICAgKi9cbiAgcHJpdmF0ZSBpc0J5cGFzc2VkKGFwcDogQXBwLCBwYXRoOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBmb3IgKGNvbnN0IGJ5cGFzc2VkUGF0aHMgb2YgdGhpcy5ieXBhc3NQYXRoU2V0cykge1xuICAgICAgZm9yIChjb25zdCBieXBhc3NlZFBhdGggb2YgYnlwYXNzZWRQYXRocykge1xuICAgICAgICBpZiAoaXNDaGlsZE9yU2VsZih7IGFwcCwgY2hpbGRQYXRoT3JGaWxlOiBwYXRoLCBwYXJlbnRQYXRoT3JGaWxlOiBieXBhc3NlZFBhdGggfSkpIHtcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBwcml2YXRlIGlzUGF0aExvY2tlZChwYXRoOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKHRoaXMubG9ja0VudHJpZXNCeVBhdGguZ2V0KHBhdGgpPy5sZW5ndGggPz8gMCkgPiAwO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2NraW5nUGx1Z2luTmFtZXMoYXBwOiBBcHAsIHBhdGg6IHN0cmluZyk6IHN0cmluZ1tdIHtcbiAgICBjb25zdCBlbnRyaWVzID0gdGhpcy5sb2NrRW50cmllc0J5UGF0aC5nZXQocGF0aCk7XG4gICAgLy8gYGxvY2tpbmdQbHVnaW5OYW1lc2AgaXMgb25seSBldmVyIGNhbGxlZCBmb3IgYSBsb2NrZWQgcGF0aCwgc28gaXRzIGVudHJpZXMgYXJlIGFsd2F5cyBwcmVzZW50LlxuICAgIGFzc2VydE5vbk51bGxhYmxlKGVudHJpZXMpO1xuICAgIGNvbnN0IHBsdWdpbklkcyA9IFsuLi5uZXcgU2V0KGVudHJpZXMubWFwKChlbnRyeSkgPT4gZW50cnkucGx1Z2luSWQpKV07XG4gICAgcmV0dXJuIHBsdWdpbklkcy5tYXAoKHBsdWdpbklkKSA9PiBhcHAucGx1Z2lucy5tYW5pZmVzdHNbcGx1Z2luSWRdPy5uYW1lID8/IHBsdWdpbklkKTtcbiAgfVxuXG4gIHByaXZhdGUgbG9ja1Rvb2x0aXAoYXBwOiBBcHAsIHBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgaGVhZGVyID0gdCgoJCkgPT4gJC5vYnNpZGlhbkRldlV0aWxzLnJlc291cmNlTG9jay5sb2NrZWRCeVRvb2x0aXApO1xuICAgIC8vIEEgcnVudGltZS1sZW5ndGggbGlzdCBvZiBwbHVnaW4gbmFtZXMsIG9uZSBwZXIgbGluZSB1bmRlciB0aGUgaGVhZGVyLlxuICAgIHJldHVybiBbaGVhZGVyLCAuLi50aGlzLmxvY2tpbmdQbHVnaW5OYW1lcyhhcHAsIHBhdGgpXS5qb2luKCdcXG4nKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVjb25jaWxlKGFwcDogQXBwKTogdm9pZCB7XG4gICAgY29uc3Qgdmlld3NUb0xvY2sgPSBuZXcgU2V0PE1hcmtkb3duVmlldz4oKTtcbiAgICBmb3IgKGNvbnN0IGxlYWYgb2YgYXBwLndvcmtzcGFjZS5nZXRMZWF2ZXNPZlR5cGUoVmlld1R5cGUuTWFya2Rvd24pKSB7XG4gICAgICBjb25zdCB2aWV3ID0gbGVhZi52aWV3O1xuICAgICAgaWYgKCEodmlldyBpbnN0YW5jZW9mIE1hcmtkb3duVmlldykpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBhdGggPSB2aWV3LmZpbGU/LnBhdGg7XG4gICAgICBpZiAocGF0aCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXNvbHZlIHRoZSBsb2NrIGNvdmVyaW5nIHRoaXMgdmlldzogdGhlIGV4YWN0IHBhdGggd2hlbiBkaXJlY3RseSBsb2NrZWQsIG90aGVyd2lzZSB0aGUgZW5jbG9zaW5nIGBzdWJ0cmVlYC1sb2NrZWQgZm9sZGVyLiBJbmRpY2F0b3JzL3Rvb2x0aXAvdW5sb2NrIG1lbnUga2V5IG9mZiB0aGF0IG93bmVyIHBhdGguXG4gICAgICBjb25zdCBvd25lclBhdGggPSB0aGlzLnJlc29sdmVMb2NrT3duZXJQYXRoKGFwcCwgcGF0aCk7XG4gICAgICBpZiAob3duZXJQYXRoICE9PSBudWxsKSB7XG4gICAgICAgIHZpZXdzVG9Mb2NrLmFkZCh2aWV3KTtcbiAgICAgICAgY29uc3QgdG9vbHRpcCA9IHRoaXMubG9ja1Rvb2x0aXAoYXBwLCBvd25lclBhdGgpO1xuICAgICAgICAvLyBSZS1hcHBseSB0aGUgcmVhZC1vbmx5IHRvZ2dsZSBvbiBldmVyeSByZWNvbmNpbGUsIG5vdCBvbmx5IHRoZSBmaXJzdCB0aW1lIGEgdmlldyBpcyB0cmFja2VkLlxuICAgICAgICAvLyBUaGUgdG9nZ2xlIGlzIGlkZW1wb3RlbnQsIHNvIHJlLWFwcGx5aW5nIHRvIGFuIGFscmVhZHktbG9ja2VkIHZpZXcgaXMgY2hlYXAuXG4gICAgICAgIC8vIEEgdmlldyBvcGVuZWQgYWZ0ZXIgdGhlIGxvY2sgaXMgcmVjb25jaWxlZCBzeW5jaHJvbm91c2x5LCBiZWZvcmUgaXRzIENvZGVNaXJyb3IgaXMgcmVhZHkuXG4gICAgICAgIC8vIEl0cyBmaXJzdCB0b2dnbGUgaXMgdGhlcmVmb3JlIGEgbm8tb3A7IHJlLWFwcGx5aW5nIG1ha2VzIHRoZSBsb2NrIHRha2UgaG9sZCBvbmNlIGl0IHNldHRsZXMuXG4gICAgICAgIHRvZ2dsZUVkaXRvclJlYWRPbmx5KHZpZXcuZWRpdG9yLCB0cnVlKTtcbiAgICAgICAgY29uc3QgaW5kaWNhdG9ycyA9IHRoaXMuaW5kaWNhdG9yc0J5Vmlldy5nZXQodmlldyk7XG4gICAgICAgIGlmIChpbmRpY2F0b3JzKSB7XG4gICAgICAgICAgdGhpcy51cGRhdGVJbmRpY2F0b3JUb29sdGlwcyhpbmRpY2F0b3JzLCB0b29sdGlwKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLmluZGljYXRvcnNCeVZpZXcuc2V0KHZpZXcsIHRoaXMuY3JlYXRlSW5kaWNhdG9ycyhhcHAsIHZpZXcsIG93bmVyUGF0aCwgdG9vbHRpcCkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBbdmlldywgaW5kaWNhdG9yc10gb2YgdGhpcy5pbmRpY2F0b3JzQnlWaWV3KSB7XG4gICAgICBpZiAoIXZpZXdzVG9Mb2NrLmhhcyh2aWV3KSkge1xuICAgICAgICB0b2dnbGVFZGl0b3JSZWFkT25seSh2aWV3LmVkaXRvciwgZmFsc2UpO1xuICAgICAgICBpbmRpY2F0b3JzLmRpc3Bvc2VUeXBlTGlzdGVuZXIoKTtcbiAgICAgICAgaW5kaWNhdG9ycy5hY3Rpb25JY29uRWwucmVtb3ZlKCk7XG4gICAgICAgIGluZGljYXRvcnMudGFiSWNvbkVsPy5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy5pbmRpY2F0b3JzQnlWaWV3LmRlbGV0ZSh2aWV3KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLnVwZGF0ZVN0YXR1c0JhcihhcHApO1xuICB9XG5cbiAgcHJpdmF0ZSByZWNvbmNpbGVBbmRDbGVhbnVwKGFwcDogQXBwKTogdm9pZCB7XG4gICAgdGhpcy5yZWNvbmNpbGUoYXBwKTtcbiAgICBpZiAodGhpcy5sb2NrRW50cmllc0J5UGF0aC5zaXplID09PSAwKSB7XG4gICAgICB0aGlzLmV2ZW50c0NvbXBvbmVudD8udW5sb2FkKCk7XG4gICAgICB0aGlzLmV2ZW50c0NvbXBvbmVudCA9IG51bGw7XG4gICAgfVxuICAgIGlmICghdGhpcy5oYXNCbG9ja2luZ0xvY2soKSkge1xuICAgICAgdGhpcy5tdXRhdGlvbkJsb2NrZXJDb21wb25lbnQ/LnVubG9hZCgpO1xuICAgICAgdGhpcy5tdXRhdGlvbkJsb2NrZXJDb21wb25lbnQgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgcmVnaXN0ZXJVbmxvY2tNZW51KGFwcDogQXBwLCBlbDogSFRNTEVsZW1lbnQsIGdldENvbnRleHRQYXRoOiAoKSA9PiBzdHJpbmcgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICAvLyBBIHBsYWluIGxpc3RlbmVyIGRpZXMgd2l0aCB0aGUgZWxlbWVudCB3aGVuIGl0IGlzIGAucmVtb3ZlKClgZCBvbiB1bmxvY2ssIHNvIGl0IG5ldmVyIGxlYWtzLlxuICAgIGVsLmFkZEV2ZW50TGlzdGVuZXIoJ2NvbnRleHRtZW51JywgKGV2dCkgPT4ge1xuICAgICAgZXZ0LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBjb25zdCBwYXRoID0gZ2V0Q29udGV4dFBhdGgoKTtcbiAgICAgIGlmIChwYXRoID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgbWVudSA9IG5ldyBNZW51KCk7XG4gICAgICB0aGlzLmFkZFVubG9ja01lbnVJdGVtKGFwcCwgbWVudSwgcGF0aCk7XG4gICAgICBtZW51LnNob3dBdE1vdXNlRXZlbnQoZXZ0KTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgcmVtb3ZlRW50cnkoYXBwOiBBcHAsIHBhdGg6IHN0cmluZywgZW50cnk6IExvY2tFbnRyeSk6IHZvaWQge1xuICAgIGNvbnN0IGVudHJpZXMgPSB0aGlzLmxvY2tFbnRyaWVzQnlQYXRoLmdldChwYXRoKTtcbiAgICBpZiAoIWVudHJpZXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgaW5kZXggPSBlbnRyaWVzLmluZGV4T2YoZW50cnkpO1xuICAgIGlmIChpbmRleCA9PT0gLTEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZW50cmllcy5zcGxpY2UoaW5kZXgsIDEpO1xuICAgIGlmIChlbnRyaWVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5sb2NrRW50cmllc0J5UGF0aC5kZWxldGUocGF0aCk7XG4gICAgfVxuICAgIHRoaXMucmVjb25jaWxlQW5kQ2xlYW51cChhcHApO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmVzIHdoaWNoIGxvY2tlZCBwYXRoIChpZiBhbnkpIGNvdmVycyB0aGUgZ2l2ZW4gcGF0aDogdGhlIHBhdGggaXRzZWxmIHdoZW4gZGlyZWN0bHkgbG9ja2VkLFxuICAgKiBvdGhlcndpc2UgdGhlIGxvbmdlc3QgYHN1YnRyZWVgLWxvY2tlZCBhbmNlc3RvciBmb2xkZXIgdGhhdCBjb250YWlucyBpdCAobG9uZ2VzdCBzbyBhIG5lc3RlZFxuICAgKiBzdWJ0cmVlIGxvY2sgd2lucyBkZXRlcm1pbmlzdGljYWxseSBvdmVyIGFuIG91dGVyIG9uZSkuXG4gICAqXG4gICAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICAgKiBAcGFyYW0gcGF0aCAtIFRoZSBwYXRoIHRvIHJlc29sdmUgYW4gb3duZXIgbG9jayBmb3IuXG4gICAqIEByZXR1cm5zIFRoZSBjb3ZlcmluZyBsb2NrZWQgcGF0aCwgb3IgYG51bGxgIHdoZW4gbm90aGluZyBjb3ZlcnMgaXQuXG4gICAqL1xuICBwcml2YXRlIHJlc29sdmVMb2NrT3duZXJQYXRoKGFwcDogQXBwLCBwYXRoOiBzdHJpbmcpOiBudWxsIHwgc3RyaW5nIHtcbiAgICBpZiAodGhpcy5pc1BhdGhMb2NrZWQocGF0aCkpIHtcbiAgICAgIHJldHVybiBwYXRoO1xuICAgIH1cbiAgICBsZXQgYmVzdE93bmVyUGF0aDogbnVsbCB8IHN0cmluZyA9IG51bGw7XG4gICAgZm9yIChjb25zdCBbbG9ja2VkUGF0aCwgZW50cmllc10gb2YgdGhpcy5sb2NrRW50cmllc0J5UGF0aCkge1xuICAgICAgY29uc3QgaGFzU3VidHJlZUxvY2sgPSBlbnRyaWVzLnNvbWUoKGVudHJ5KSA9PiBlbnRyeS5tb2RlID09PSAnc3VidHJlZScpO1xuICAgICAgaWYgKGhhc1N1YnRyZWVMb2NrICYmIGlzQ2hpbGQoeyBhcHAsIGNoaWxkUGF0aE9yRmlsZTogcGF0aCwgcGFyZW50UGF0aE9yRmlsZTogbG9ja2VkUGF0aCB9KSAmJiAoYmVzdE93bmVyUGF0aCA9PT0gbnVsbCB8fCBsb2NrZWRQYXRoLmxlbmd0aCA+IGJlc3RPd25lclBhdGgubGVuZ3RoKSkge1xuICAgICAgICBiZXN0T3duZXJQYXRoID0gbG9ja2VkUGF0aDtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGJlc3RPd25lclBhdGg7XG4gIH1cblxuICAvKipcbiAgICogRGVjaWRlcyB3aGV0aGVyIGEgbXV0YXRpb24gb2YgdGhlIHBhdGggbXVzdCBiZSBibG9ja2VkOiBgdHJ1ZWAgd2hlbiB0aGUgcGF0aCBpcyBtdXRhdGlvbi1ibG9ja2VkXG4gICAqIGFuZCBub3QgY292ZXJlZCBieSBhbnkgYWN0aXZlIHtAbGluayBSZXNvdXJjZUxvY2tNYW5hZ2VyLmJ5cGFzc30gc2NvcGUuXG4gICAqXG4gICAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICAgKiBAcGFyYW0gcGF0aCAtIFRoZSBwYXRoIGJlaW5nIG11dGF0ZWQuXG4gICAqIEByZXR1cm5zIGB0cnVlYCB0byByZWplY3QgdGhlIG11dGF0aW9uLCBgZmFsc2VgIHRvIGFsbG93IGl0LlxuICAgKi9cbiAgcHJpdmF0ZSBzaG91bGRCbG9ja011dGF0aW9uKGFwcDogQXBwLCBwYXRoOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBpZiAoIXRoaXMuaXNNdXRhdGlvbkJsb2NrZWRCeUFuY2VzdG9yKGFwcCwgcGF0aCkpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuICF0aGlzLmlzQnlwYXNzZWQoYXBwLCBwYXRoKTtcbiAgfVxuXG4gIHByaXZhdGUgdW5sb2NrQ29uZmlybU1lc3NhZ2UoYXBwOiBBcHAsIHBhdGg6IHN0cmluZyk6IERvY3VtZW50RnJhZ21lbnQge1xuICAgIGNvbnN0IGZyYWdtZW50ID0gY3JlYXRlRnJhZ21lbnQoKTtcbiAgICBmcmFnbWVudC5hcHBlbmRUZXh0KHQoKCQpID0+ICQub2JzaWRpYW5EZXZVdGlscy5yZXNvdXJjZUxvY2sudW5sb2NrQ29uZmlybU1lc3NhZ2UpKTtcbiAgICAvLyBBIHJ1bnRpbWUtbGVuZ3RoIGxpc3Qgb2YgdGhlIHBsdWdpbnMgY3VycmVudGx5IGhvbGRpbmcgYSBsb2NrLCBlYWNoIGFzIGEgY29kZSBibG9jayBvbiBpdHMgb3duIGxpbmUuXG4gICAgZm9yIChjb25zdCBuYW1lIG9mIHRoaXMubG9ja2luZ1BsdWdpbk5hbWVzKGFwcCwgcGF0aCkpIHtcbiAgICAgIGZyYWdtZW50LmNyZWF0ZUVsKCdicicpO1xuICAgICAgYXBwZW5kQ29kZUJsb2NrKGZyYWdtZW50LCBuYW1lKTtcbiAgICB9XG4gICAgcmV0dXJuIGZyYWdtZW50O1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVJbmRpY2F0b3JUb29sdGlwcyhpbmRpY2F0b3JzOiBMb2NrSW5kaWNhdG9ycywgdG9vbHRpcDogc3RyaW5nKTogdm9pZCB7XG4gICAgc2V0VG9vbHRpcChpbmRpY2F0b3JzLmFjdGlvbkljb25FbCwgdG9vbHRpcCk7XG4gICAgaWYgKGluZGljYXRvcnMudGFiSWNvbkVsKSB7XG4gICAgICBzZXRUb29sdGlwKGluZGljYXRvcnMudGFiSWNvbkVsLCB0b29sdGlwKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZVN0YXR1c0JhcihhcHA6IEFwcCk6IHZvaWQge1xuICAgIGNvbnN0IGFjdGl2ZVZpZXcgPSBhcHAud29ya3NwYWNlLmdldEFjdGl2ZVZpZXdPZlR5cGUoTWFya2Rvd25WaWV3KTtcbiAgICBjb25zdCBhY3RpdmVQYXRoID0gYWN0aXZlVmlldz8uZmlsZT8ucGF0aDtcbiAgICBjb25zdCBhY3RpdmVPd25lclBhdGggPSBhY3RpdmVQYXRoID09PSB1bmRlZmluZWQgPyBudWxsIDogdGhpcy5yZXNvbHZlTG9ja093bmVyUGF0aChhcHAsIGFjdGl2ZVBhdGgpO1xuXG4gICAgaWYgKGFjdGl2ZU93bmVyUGF0aCA9PT0gbnVsbCB8fCAhYWN0aXZlVmlldykge1xuICAgICAgdGhpcy5zdGF0dXNCYXJJdGVtRWw/LnJlbW92ZSgpO1xuICAgICAgdGhpcy5zdGF0dXNCYXJJdGVtRWwgPSBudWxsO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5zdGF0dXNCYXJJdGVtRWwpIHtcbiAgICAgIGNvbnN0IHN0YXR1c0JhckVsID0gYWN0aXZlVmlldy5jb250YWluZXJFbC5vd25lckRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3RvcjxIVE1MRWxlbWVudD4oU1RBVFVTX0JBUl9DU1NfU0VMRUNUT1IpO1xuICAgICAgaWYgKCFzdGF0dXNCYXJFbCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLnN0YXR1c0Jhckl0ZW1FbCA9IHN0YXR1c0JhckVsLmNyZWF0ZURpdih7IGNsczogW1NUQVRVU19CQVJfSVRFTV9DU1NfQ0xBU1MsIExPQ0tfSU5ESUNBVE9SX0NTU19DTEFTU10gfSk7XG4gICAgICBzZXRJY29uKHRoaXMuc3RhdHVzQmFySXRlbUVsLCBMT0NLX0lDT05fSUQpO1xuICAgICAgLy8gVGhlIHNpbmdsZSBzdGF0dXMtYmFyIGl0ZW0gaXMgcmV1c2VkIGFjcm9zcyBhY3RpdmUtbm90ZSBzd2l0Y2hlcy5cbiAgICAgIC8vIEl0IG11c3QgcmVzb2x2ZSB0aGUgY3VycmVudGx5IGFjdGl2ZSBub3RlJ3Mgb3duZXIgbG9jayBhdCBjbGljayB0aW1lIHJhdGhlciB0aGFuIGNhcHR1cmluZyBvbmUgcGF0aC5cbiAgICAgIHRoaXMucmVnaXN0ZXJVbmxvY2tNZW51KGFwcCwgdGhpcy5zdGF0dXNCYXJJdGVtRWwsICgpID0+IHtcbiAgICAgICAgY29uc3QgcGF0aCA9IGFwcC53b3Jrc3BhY2UuZ2V0QWN0aXZlVmlld09mVHlwZShNYXJrZG93blZpZXcpPy5maWxlPy5wYXRoO1xuICAgICAgICByZXR1cm4gcGF0aCA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogdGhpcy5yZXNvbHZlTG9ja093bmVyUGF0aChhcHAsIHBhdGgpID8/IHVuZGVmaW5lZDtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHNldFRvb2x0aXAodGhpcy5zdGF0dXNCYXJJdGVtRWwsIHRoaXMubG9ja1Rvb2x0aXAoYXBwLCBhY3RpdmVPd25lclBhdGgpKTtcbiAgfVxufVxuXG4vKipcbiAqIEEgcGVyLXBsdWdpbiBoYW5kbGUgZm9yIHBhdGgtc2NvcGVkIHJlc291cmNlIGxvY2tpbmcgKGVkaXRvciByZWFkLW9ubHksIG9wdGlvbmFsIHZhdWx0LW11dGF0aW9uXG4gKiBibG9ja2luZywgYW5kIGV4dGVybmFsLWNoYW5nZSBkZXRlY3Rpb24gXHUyMDE0IHNlZSB0aGUgZmlsZSBvdmVydmlldykuIEFkZCBpdCBhcyBhIGNoaWxkIG9mIHlvdXIgcGx1Z2luXG4gKiAoYHRoaXMuYWRkQ2hpbGQobmV3IFJlc291cmNlTG9ja0NvbXBvbmVudCh0aGlzLmFwcCkpYCkgc28gdGhhdCBhbnkgbG9ja3MgaXQgc3RpbGwgaG9sZHMgYXJlIHJlbGVhc2VkXG4gKiBhdXRvbWF0aWNhbGx5IHdoZW4gdGhlIHBsdWdpbiB1bmxvYWRzIFx1MjAxNCBhIHJlc291cmNlIGNhbiBuZXZlciBiZSBsZWZ0IHN0dWNrIGxvY2tlZCBiZWNhdXNlIHRoZSBwbHVnaW5cbiAqIHRoYXQgbG9ja2VkIGl0IHdhcyBkaXNhYmxlZCBvciByZWxvYWRlZCBtaWQtb3BlcmF0aW9uLlxuICpcbiAqIExvY2tzIGFyZSByZWZlcmVuY2UtY291bnRlZCBhbmQgYXR0cmlidXRlZCB0byB0aGlzIHBsdWdpbiwgc28gdGhlIGxvY2sgaW5kaWNhdG9ycycgdG9vbHRpcCBuYW1lcyBpdFxuICogYW1vbmcgdGhlIHBsdWdpbnMgY3VycmVudGx5IGhvbGRpbmcgYSBsb2NrLlxuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VMb2NrQ29tcG9uZW50IGV4dGVuZHMgQ29tcG9uZW50RXgge1xuICBwcml2YXRlIHJlYWRvbmx5IGFwcDogQXBwO1xuICBwcml2YXRlIHJlYWRvbmx5IHBsdWdpbklkOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYW4gcmVzb3VyY2UtbG9jayBoYW5kbGUgb3duZWQgYnkgYSBwbHVnaW4uXG4gICAqXG4gICAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICAgKiBAcGFyYW0gcGx1Z2luSWQgLSBUaGUgaWQgb2YgdGhlIG93bmluZyBwbHVnaW4gKGUuZy4gaXRzIGBtYW5pZmVzdC5pZGApLiBMb2NrcyBhcmUgYXR0cmlidXRlZCB0b1xuICAgKiBpdCBmb3IgcmVmZXJlbmNlLWNvdW50aW5nIGFuZCB0aGUgaW5kaWNhdG9ycycgXCJsb2NrZWQgYnlcIiB0b29sdGlwLlxuICAgKi9cbiAgcHVibGljIGNvbnN0cnVjdG9yKGFwcDogQXBwLCBwbHVnaW5JZDogc3RyaW5nKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLmFwcCA9IGFwcDtcbiAgICB0aGlzLnBsdWdpbklkID0gcGx1Z2luSWQ7XG4gIH1cblxuICAvKipcbiAgICogT3BlbnMgYW4gKiphbWJpZW50IGJ5cGFzcyBzY29wZSoqOiB3aGlsZSB0aGUgcmV0dXJuZWQge0BsaW5rIERpc3Bvc2FibGV9IGlzIG5vdCBkaXNwb3NlZCwgdGhpc1xuICAgKiBwbHVnaW4ncyBvd24gbXV0YXRpb25zIG9mIHRoZSBnaXZlbiBwYXRocyAoYW5kLCBmb3IgYSBmb2xkZXIsIGl0cyB3aG9sZSBzdWJ0cmVlKSBwYXNzIHRocm91Z2ggdGhlXG4gICAqIG11dGF0aW9uIGJsb2NrZXIgaW5zdGVhZCBvZiB0aHJvd2luZyB7QGxpbmsgUmVzb3VyY2VMb2NrZWRFcnJvcn0uIEFueSBtdXRhdGlvbiBvZiBhIGJsb2NrZWQgcGF0aFxuICAgKiBOT1QgY292ZXJlZCBieSBhbiBhY3RpdmUgYnlwYXNzIHNjb3BlIGlzIHN0aWxsIHJlamVjdGVkLiBVc2UgaXQgd2l0aCBhIGB1c2luZ2AgZGVjbGFyYXRpb24gYXJvdW5kXG4gICAqIHRoZSBvcGVyYXRpb24gdGhhdCBsZWdpdGltYXRlbHkgbXV0YXRlcyByZXNvdXJjZXMgaXQgaGFzIGxvY2tlZCB3aXRoXG4gICAqIHtAbGluayBSZXNvdXJjZUxvY2tDb21wb25lbnRMb2NrRm9yUGF0aE9wdGlvbnMuc2hvdWxkQmxvY2tNdXRhdGlvbnN9OlxuICAgKlxuICAgKiBgYGB0c1xuICAgKiB1c2luZyBfYnlwYXNzID0gY29tcG9uZW50LmJ5cGFzc0Jsb2NrZWRNdXRhdGlvbnMoW3NvdXJjZUZpbGUsIHRhcmdldEZpbGVdKTtcbiAgICogLy8gLi4uIHRoZSBvcGVyYXRpb24ncyBvd24gdmF1bHQgbXV0YXRpb25zIG9mIHRob3NlIHBhdGhzIGFyZSBhbGxvd2VkIC4uLlxuICAgKiAvLyBzY29wZSBlbmQ6IHRoZSBwYXRocyBhcmUgZW5mb3JjZWQgYWdhaW5cbiAgICogYGBgXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoc09yRmlsZXMgLSBUaGUgcGF0aHMgKG9yIGZpbGVzL2ZvbGRlcnMpIHdob3NlIG11dGF0aW9uIGJ5IHRoaXMgb3BlcmF0aW9uIGlzIHNhbmN0aW9uZWQuXG4gICAqIEByZXR1cm5zIEEge0BsaW5rIERpc3Bvc2FibGV9IHRoYXQgZW5kcyB0aGUgYnlwYXNzIHNjb3BlIHdoZW4gZGlzcG9zZWQuXG4gICAqL1xuICBwdWJsaWMgYnlwYXNzQmxvY2tlZE11dGF0aW9ucyhwYXRoc09yRmlsZXM6IHJlYWRvbmx5IFBhdGhPckZpbGVbXSk6IERpc3Bvc2FibGUge1xuICAgIHJldHVybiBnZXRNYW5hZ2VyKCkuYnlwYXNzKHRoaXMuYXBwLCBwYXRoc09yRmlsZXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyB3aGV0aGVyIHRoZSBwYXRoIGlzIGxvY2tlZCBkaXJlY3RseSBvciBsaWVzIHVuZGVyIGEgYHN1YnRyZWVgLWxvY2tlZCBhbmNlc3RvciBmb2xkZXIuXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSB0byBjaGVjay5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBwYXRoIGlzIGl0c2VsZiBsb2NrZWQgb3IgaXMgY292ZXJlZCBieSBhIGBzdWJ0cmVlYCBsb2NrIG9uIGFuIGFuY2VzdG9yLlxuICAgKi9cbiAgcHVibGljIGlzTG9ja2VkQnlBbmNlc3RvckZvclBhdGgocGF0aE9yRmlsZTogUGF0aE9yRmlsZSk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBnZXRNYW5hZ2VyKCkuaXNMb2NrZWRCeUFuY2VzdG9yKHRoaXMuYXBwLCBwYXRoT3JGaWxlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3Mgd2hldGhlciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCBpcyBjdXJyZW50bHkgbG9ja2VkIGJ5IGFueSBwbHVnaW4uXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byBjaGVjay5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBub3RlIGhhcyBhdCBsZWFzdCBvbmUgYWN0aXZlIGxvY2ssIGBmYWxzZWAgb3RoZXJ3aXNlLlxuICAgKi9cbiAgcHVibGljIGlzTG9ja2VkRm9yUGF0aChwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGdldE1hbmFnZXIoKS5pc0xvY2tlZCh0aGlzLmFwcCwgcGF0aE9yRmlsZSk7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIHdoZXRoZXIgYSB2YXVsdCBtdXRhdGlvbiBvZiB0aGUgcGF0aCBpcyBjdXJyZW50bHkgYmxvY2tlZCBieSBhIGBzaG91bGRCbG9ja011dGF0aW9uc2AgbG9ja1xuICAgKiBvbiB0aGUgcGF0aCBpdHNlbGYgb3Igb24gYSBgc3VidHJlZWAtbG9ja2VkIGFuY2VzdG9yIGZvbGRlci5cbiAgICpcbiAgICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIHRvIGNoZWNrLlxuICAgKiBAcmV0dXJucyBgdHJ1ZWAgaWYgZWRpdGluZy9kZWxldGluZy9yZW5hbWluZy9tb3ZpbmcvY3JlYXRpbmcgdGhlIHBhdGggd291bGQgdGhyb3cgYVxuICAgKiB7QGxpbmsgUmVzb3VyY2VMb2NrZWRFcnJvcn0uXG4gICAqL1xuICBwdWJsaWMgaXNNdXRhdGlvbkJsb2NrZWRCeUFuY2VzdG9yRm9yUGF0aChwYXRoT3JGaWxlOiBQYXRoT3JGaWxlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGdldE1hbmFnZXIoKS5pc011dGF0aW9uQmxvY2tlZEJ5QW5jZXN0b3IodGhpcy5hcHAsIHBhdGhPckZpbGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIExvY2tzIHRoZSBub3RlIGF0IHRoZSBnaXZlbiBwYXRoIG9uIGJlaGFsZiBvZiB0aGlzIHBsdWdpbiwgbWFraW5nIGl0IHJlYWQtb25seSBpbiBldmVyeSBjdXJyZW50XG4gICAqIGFuZCBmdXR1cmUge0BsaW5rIE1hcmtkb3duVmlld30gdW50aWwgdGhlIGxvY2sgaXMgcmVsZWFzZWQuIFJlZmVyZW5jZS1jb3VudGVkOiBiYWxhbmNlIGVhY2ggY2FsbFxuICAgKiB3aXRoIGEgZGlzcG9zZSBvZiB0aGUgcmV0dXJuZWQge0BsaW5rIERpc3Bvc2FibGV9IChpZGVhbGx5IHZpYSBgdXNpbmdgKSBvciB7QGxpbmsgdW5sb2NrRm9yUGF0aH0uXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byBsb2NrLlxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIGxvY2tpbmcgb3B0aW9ucy4gUGFzcyBhblxuICAgKiB7QGxpbmsgUmVzb3VyY2VMb2NrQ29tcG9uZW50TG9ja0ZvclBhdGhPcHRpb25zLmFib3J0Q29udHJvbGxlcn0gdG8gbWFrZSB0aGUgbG9jayBjYW5jZWxhYmxlIHZpYSB0aGVcbiAgICogaW5kaWNhdG9yJ3MgcmlnaHQtY2xpY2sgXCJ1bmxvY2tcIiBtZW51IG9yIHtAbGluayByZXF1ZXN0UmVzb3VyY2VVbmxvY2tGb3JQYXRofS5cbiAgICogQHJldHVybnMgQSB7QGxpbmsgRGlzcG9zYWJsZX0gdGhhdCByZWxlYXNlcyB0aGlzIGxvY2sgd2hlbiBkaXNwb3NlZC4gRGlzcG9zaW5nIG1vcmUgdGhhbiBvbmNlIGlzIGEgbm8tb3AuXG4gICAqL1xuICBwdWJsaWMgbG9ja0ZvclBhdGgocGF0aE9yRmlsZTogUGF0aE9yRmlsZSwgb3B0aW9ucz86IFJlc291cmNlTG9ja0NvbXBvbmVudExvY2tGb3JQYXRoT3B0aW9ucyk6IERpc3Bvc2FibGUge1xuICAgIHJldHVybiBnZXRNYW5hZ2VyKCkubG9jayh0aGlzLmFwcCwgcGF0aE9yRmlsZSwgdGhpcy5wbHVnaW5JZCwgeyBhYm9ydENvbnRyb2xsZXI6IG9wdGlvbnM/LmFib3J0Q29udHJvbGxlciwgYmxvY2tzTXV0YXRpb25zOiBvcHRpb25zPy5zaG91bGRCbG9ja011dGF0aW9ucywgbW9kZTogb3B0aW9ucz8ubW9kZSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWxlYXNlcyBldmVyeSBsb2NrIHN0aWxsIGhlbGQgYnkgdGhpcyBwbHVnaW4gd2hlbiB0aGUgY29tcG9uZW50IChhbmQgdGh1cyB0aGUgcGx1Z2luKSB1bmxvYWRzLFxuICAgKiBzbyBubyBub3RlIGlzIGxlZnQgc3R1Y2sgcmVhZC1vbmx5IGJ5IGFuIG9wZXJhdGlvbiB0aGF0IG5ldmVyIGNvbXBsZXRlZC5cbiAgICovXG4gIHB1YmxpYyBvdmVycmlkZSBvbnVubG9hZCgpOiB2b2lkIHtcbiAgICBnZXRNYW5hZ2VyKCkudW5sb2NrQWxsRm9yUGx1Z2luKHRoaXMuYXBwLCB0aGlzLnBsdWdpbklkKTtcbiAgICBzdXBlci5vbnVubG9hZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2VzIG9uZSBsb2NrIHByZXZpb3VzbHkgYWNxdWlyZWQgZm9yIHRoZSBub3RlIGF0IHRoZSBnaXZlbiBwYXRoIHZpYSB7QGxpbmsgbG9ja0ZvclBhdGh9LlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aE9yRmlsZSAtIFRoZSBwYXRoIG9yIGZpbGUgb2YgdGhlIG5vdGUgdG8gdW5sb2NrLlxuICAgKi9cbiAgcHVibGljIHVubG9ja0ZvclBhdGgocGF0aE9yRmlsZTogUGF0aE9yRmlsZSk6IHZvaWQge1xuICAgIGdldE1hbmFnZXIoKS51bmxvY2sodGhpcy5hcHAsIHBhdGhPckZpbGUsIHRoaXMucGx1Z2luSWQpO1xuICB9XG59XG5cbi8qKlxuICogVGhyb3duIGJ5IHRoZSB2YXVsdC1tdXRhdGlvbiBibG9ja2VyIHdoZW4gYSBwbHVnaW4gdHJpZXMgdG8gZWRpdCwgZGVsZXRlLCByZW5hbWUsIG1vdmUsIG9yIGNyZWF0ZSBhXG4gKiByZXNvdXJjZSB3aG9zZSBwYXRoIGlzIGNvdmVyZWQgYnkgYSBsb2NrIHRha2VuIHdpdGhcbiAqIHtAbGluayBSZXNvdXJjZUxvY2tDb21wb25lbnRMb2NrRm9yUGF0aE9wdGlvbnMuc2hvdWxkQmxvY2tNdXRhdGlvbnN9LlxuICovXG5leHBvcnQgY2xhc3MgUmVzb3VyY2VMb2NrZWRFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgLyoqXG4gICAqIFRoZSB2YXVsdCBwYXRoIHdob3NlIG11dGF0aW9uIHdhcyBibG9ja2VkLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHBhdGg6IHN0cmluZztcblxuICAvKipcbiAgICogQ3JlYXRlcyBhIHtAbGluayBSZXNvdXJjZUxvY2tlZEVycm9yfS5cbiAgICpcbiAgICogQHBhcmFtIHBhdGggLSBUaGUgdmF1bHQgcGF0aCB3aG9zZSBtdXRhdGlvbiB3YXMgYmxvY2tlZC5cbiAgICovXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihwYXRoOiBzdHJpbmcpIHtcbiAgICBzdXBlcihgVGhlIHJlc291cmNlIFwiJHtwYXRofVwiIGlzIGxvY2tlZCBhbmQgY2Fubm90IGJlIG11dGF0ZWQuYCk7XG4gICAgdGhpcy5uYW1lID0gJ1Jlc291cmNlTG9ja2VkRXJyb3InO1xuICAgIHRoaXMucGF0aCA9IHBhdGg7XG4gIH1cbn1cblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCBpcyBjdXJyZW50bHkgbG9ja2VkLlxuICpcbiAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIGNoZWNrLlxuICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBub3RlIGhhcyBhdCBsZWFzdCBvbmUgYWN0aXZlIGxvY2ssIGBmYWxzZWAgb3RoZXJ3aXNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNSZXNvdXJjZUxvY2tlZEZvclBhdGgoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUpOiBib29sZWFuIHtcbiAgcmV0dXJuIGdldE1hbmFnZXIoKS5pc0xvY2tlZChhcHAsIHBhdGhPckZpbGUpO1xufVxuXG4vKipcbiAqIExvY2tzIHRoZSBub3RlIGF0IHRoZSBnaXZlbiBwYXRoLCBtYWtpbmcgaXQgcmVhZC1vbmx5IGluIGV2ZXJ5IGN1cnJlbnQgYW5kIGZ1dHVyZVxuICoge0BsaW5rIE1hcmtkb3duVmlld30gdW50aWwgdGhlIGxvY2sgaXMgcmVsZWFzZWQsIGFuZCBzaG93aW5nIGEgbG9jayBpbmRpY2F0b3IgaW4gdGhlIHRhYiBoZWFkZXIsXG4gKiB2aWV3IGFjdGlvbiBiYXIsIGFuZCAod2hpbGUgYWN0aXZlKSB0aGUgc3RhdHVzIGJhci4gVGhlIGluZGljYXRvcnMnIHRvb2x0aXAgbGlzdHMgdGhlIHBsdWdpbnNcbiAqIHRoYXQgY3VycmVudGx5IGhvbGQgYSBsb2NrIG9uIHRoZSBub3RlLlxuICpcbiAqIFRoZSBsb2NrIGlzIHJlZmVyZW5jZS1jb3VudGVkIHBlciBjYWxsaW5nIHBsdWdpbjogZWFjaCBjYWxsIG11c3QgYmUgYmFsYW5jZWQgYnkgZXhhY3RseSBvbmVcbiAqIHJlbGVhc2UgKGVpdGhlciBkaXNwb3NpbmcgdGhlIHJldHVybmVkIHtAbGluayBEaXNwb3NhYmxlfSBcdTIwMTQgaWRlYWxseSB2aWEgYSBgdXNpbmdgIGRlY2xhcmF0aW9uIFx1MjAxNCBvclxuICogYSBtYXRjaGluZyB7QGxpbmsgdW5sb2NrUmVzb3VyY2VGb3JQYXRofSBjYWxsKS5cbiAqXG4gKiBAcGFyYW0gYXBwIC0gVGhlIE9ic2lkaWFuIGFwcCBpbnN0YW5jZS5cbiAqIEBwYXJhbSBwYXRoT3JGaWxlIC0gVGhlIHBhdGggb3IgZmlsZSBvZiB0aGUgbm90ZSB0byBsb2NrLlxuICogQHBhcmFtIHBsdWdpbklkIC0gVGhlIGlkIG9mIHRoZSBsb2NraW5nIHBsdWdpbiAoZS5nLiBpdHMgYG1hbmlmZXN0LmlkYCkuIFRoZSBsb2NrIGlzIGF0dHJpYnV0ZWQgdG9cbiAqIGl0IGZvciByZWZlcmVuY2UtY291bnRpbmcgYW5kIHRoZSBpbmRpY2F0b3JzJyBcImxvY2tlZCBieVwiIHRvb2x0aXAuXG4gKiBAcmV0dXJucyBBIHtAbGluayBEaXNwb3NhYmxlfSB0aGF0IHJlbGVhc2VzIHRoaXMgbG9jayB3aGVuIGRpc3Bvc2VkLiBEaXNwb3NpbmcgbW9yZSB0aGFuIG9uY2UgaXMgYSBuby1vcC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxvY2tSZXNvdXJjZUZvclBhdGgoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUsIHBsdWdpbklkOiBzdHJpbmcpOiBEaXNwb3NhYmxlIHtcbiAgcmV0dXJuIGdldE1hbmFnZXIoKS5sb2NrKGFwcCwgcGF0aE9yRmlsZSwgcGx1Z2luSWQpO1xufVxuXG4vKipcbiAqIFJlcXVlc3RzIGFuIHVubG9jayBvZiB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCBieSBhYm9ydGluZyBldmVyeSB7QGxpbmsgQWJvcnRDb250cm9sbGVyfSB0aGF0IHdhc1xuICogYXNzb2NpYXRlZCB3aXRoIGEgbG9jayBvbiBpdCAodmlhIHtAbGluayBSZXNvdXJjZUxvY2tDb21wb25lbnQubG9ja0ZvclBhdGh9J3NcbiAqIHtAbGluayBSZXNvdXJjZUxvY2tDb21wb25lbnRMb2NrRm9yUGF0aE9wdGlvbnMuYWJvcnRDb250cm9sbGVyfSkuIFRoZSBvcGVyYXRpb25zIGhvbGRpbmcgdGhlIGxvY2tcbiAqIG9ic2VydmUgdGhlIGFib3J0IGFuZCByZWxlYXNlIHRoZWlyIG93biBsb2Nrcy4gQSBuby1vcCB3aGVuIG5vIGFib3J0YWJsZSBsb2NrIGlzIHJlZ2lzdGVyZWQgZm9yIHRoZVxuICogcGF0aC5cbiAqXG4gKiBUaGlzIGxldHMgYSBjb25zdW1pbmcgcGx1Z2luIHdpcmUgaXRzIG93biBcInVubG9jayBhY3RpdmUgbm90ZVwiIGNvbW1hbmQgd2l0aG91dCByZWFjaGluZyBpbnRvIHRoZVxuICogbG9jayBtYW5hZ2VyIGRpcmVjdGx5LlxuICpcbiAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIHJlcXVlc3QgYW4gdW5sb2NrIGZvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlcXVlc3RSZXNvdXJjZVVubG9ja0ZvclBhdGgoYXBwOiBBcHAsIHBhdGhPckZpbGU6IFBhdGhPckZpbGUpOiB2b2lkIHtcbiAgZ2V0TWFuYWdlcigpLnJlcXVlc3RVbmxvY2soYXBwLCBwYXRoT3JGaWxlKTtcbn1cblxuLyoqXG4gKiBSZWxlYXNlcyBvbmUgbG9jayBwcmV2aW91c2x5IGFjcXVpcmVkIGZvciB0aGUgbm90ZSBhdCB0aGUgZ2l2ZW4gcGF0aCB2aWEge0BsaW5rIGxvY2tSZXNvdXJjZUZvclBhdGh9LlxuICpcbiAqIFdoZW4gdGhlIGxhc3QgbG9jayBpcyByZWxlYXNlZCB0aGUgbm90ZSBiZWNvbWVzIGZ1bGx5IGVkaXRhYmxlIGFnYWluIGFuZCBpdHMgbG9jayBpbmRpY2F0b3JzIGFyZVxuICogcmVtb3ZlZC4gQ2FsbGluZyB0aGlzIHdoZW4gdGhlIG5vdGUgaXMgbm90IGxvY2tlZCBpcyBhIG5vLW9wLlxuICpcbiAqIEBwYXJhbSBhcHAgLSBUaGUgT2JzaWRpYW4gYXBwIGluc3RhbmNlLlxuICogQHBhcmFtIHBhdGhPckZpbGUgLSBUaGUgcGF0aCBvciBmaWxlIG9mIHRoZSBub3RlIHRvIHVubG9jay5cbiAqIEBwYXJhbSBwbHVnaW5JZCAtIFRoZSBpZCBvZiB0aGUgcGx1Z2luIHRoYXQgaG9sZHMgdGhlIGxvY2sgKGUuZy4gaXRzIGBtYW5pZmVzdC5pZGApOyBvbmUgb2YgaXRzXG4gKiBsb2NrcyBvbiB0aGUgbm90ZSBpcyByZWxlYXNlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVubG9ja1Jlc291cmNlRm9yUGF0aChhcHA6IEFwcCwgcGF0aE9yRmlsZTogUGF0aE9yRmlsZSwgcGx1Z2luSWQ6IHN0cmluZyk6IHZvaWQge1xuICBnZXRNYW5hZ2VyKCkudW5sb2NrKGFwcCwgcGF0aE9yRmlsZSwgcGx1Z2luSWQpO1xufVxuXG5mdW5jdGlvbiBnZXRNYW5hZ2VyKCk6IFJlc291cmNlTG9ja01hbmFnZXIge1xuICByZXR1cm4gZ2V0T2JzaWRpYW5EZXZVdGlsc1N0YXRlPFJlc291cmNlTG9ja01hbmFnZXI+KFJFU09VUkNFX0xPQ0tfU1RBVEVfS0VZLCBuZXcgUmVzb3VyY2VMb2NrTWFuYWdlcigpKS52YWx1ZTtcbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFxQ0EsNkJBQXlCO0FBQ3pCLHNCQU9PO0FBSVAsbUJBQW1DO0FBQ25DLHdCQUdPO0FBQ1Asc0JBQXFCO0FBQ3JCLHNDQUF5QztBQUN6Qyx5QkFBa0M7QUFDbEMsMEJBQTRCO0FBQzVCLHFDQUFzQztBQUN0QyxvQkFBcUM7QUFDckMseUJBQXdCO0FBQ3hCLDBCQUFnQztBQUNoQyxrQkFBa0I7QUFDbEIscUJBQXdCO0FBQ3hCLG1CQUdPO0FBRVAsTUFBTSx3QkFBd0I7QUFDOUIsTUFBTSxvQkFBb0I7QUFDMUIsTUFBTSxZQUFZO0FBQ2xCLE1BQU0sNkJBQTZCO0FBQ25DLE1B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
|