UNPKG

28.8 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright Google LLC All Rights Reserved.
4 *
5 * Use of this source code is governed by an MIT-style license that can be
6 * found in the LICENSE file at https://angular.io/license
7 */
8(function (factory) {
9 if (typeof module === "object" && typeof module.exports === "object") {
10 var v = factory(require, exports);
11 if (v !== undefined) module.exports = v;
12 }
13 else if (typeof define === "function" && define.amd) {
14 define("@angular/language-service/ivy/hybrid_visitor", ["require", "exports", "tslib", "@angular/compiler", "@angular/compiler/src/expression_parser/ast", "@angular/compiler/src/render3/r3_ast", "@angular/language-service/ivy/utils"], factory);
15 }
16})(function (require, exports) {
17 "use strict";
18 Object.defineProperty(exports, "__esModule", { value: true });
19 exports.findNodeAtPosition = exports.getPathToNodeAtPosition = void 0;
20 var tslib_1 = require("tslib");
21 var compiler_1 = require("@angular/compiler");
22 var e = require("@angular/compiler/src/expression_parser/ast"); // e for expression AST
23 var t = require("@angular/compiler/src/render3/r3_ast"); // t for template AST
24 var utils_1 = require("@angular/language-service/ivy/utils");
25 /**
26 * Return the path to the template AST node or expression AST node that most accurately
27 * represents the node at the specified cursor `position`.
28 *
29 * @param ast AST tree
30 * @param position cursor position
31 */
32 function getPathToNodeAtPosition(ast, position) {
33 var visitor = new R3Visitor(position);
34 visitor.visitAll(ast);
35 var candidate = visitor.path[visitor.path.length - 1];
36 if (!candidate) {
37 return;
38 }
39 if (utils_1.isTemplateNodeWithKeyAndValue(candidate)) {
40 var keySpan = candidate.keySpan, valueSpan = candidate.valueSpan;
41 var isWithinKeyValue = isWithin(position, keySpan) || (valueSpan && isWithin(position, valueSpan));
42 if (!isWithinKeyValue) {
43 // If cursor is within source span but not within key span or value span,
44 // do not return the node.
45 return;
46 }
47 }
48 return visitor.path;
49 }
50 exports.getPathToNodeAtPosition = getPathToNodeAtPosition;
51 /**
52 * Return the template AST node or expression AST node that most accurately
53 * represents the node at the specified cursor `position`.
54 *
55 * @param ast AST tree
56 * @param position cursor position
57 */
58 function findNodeAtPosition(ast, position) {
59 var path = getPathToNodeAtPosition(ast, position);
60 if (!path) {
61 return;
62 }
63 return path[path.length - 1];
64 }
65 exports.findNodeAtPosition = findNodeAtPosition;
66 var R3Visitor = /** @class */ (function () {
67 // Position must be absolute in the source file.
68 function R3Visitor(position) {
69 this.position = position;
70 // We need to keep a path instead of the last node because we might need more
71 // context for the last node, for example what is the parent node?
72 this.path = [];
73 }
74 R3Visitor.prototype.visit = function (node) {
75 var _a = getSpanIncludingEndTag(node), start = _a.start, end = _a.end;
76 if (isWithin(this.position, { start: start, end: end })) {
77 var length_1 = end - start;
78 var last = this.path[this.path.length - 1];
79 if (last) {
80 var _b = utils_1.isTemplateNode(last) ? getSpanIncludingEndTag(last) : last.sourceSpan, start_1 = _b.start, end_1 = _b.end;
81 var lastLength = end_1 - start_1;
82 if (length_1 > lastLength) {
83 // The current node has a span that is larger than the last node found
84 // so we do not descend into it. This typically means we have found
85 // a candidate in one of the root nodes so we do not need to visit
86 // other root nodes.
87 return;
88 }
89 }
90 this.path.push(node);
91 node.visit(this);
92 }
93 };
94 R3Visitor.prototype.visitElement = function (element) {
95 this.visitAll(element.attributes);
96 this.visitAll(element.inputs);
97 this.visitAll(element.outputs);
98 this.visitAll(element.references);
99 this.visitAll(element.children);
100 };
101 R3Visitor.prototype.visitTemplate = function (template) {
102 this.visitAll(template.attributes);
103 this.visitAll(template.inputs);
104 this.visitAll(template.outputs);
105 this.visitAll(template.templateAttrs);
106 this.visitAll(template.references);
107 this.visitAll(template.variables);
108 this.visitAll(template.children);
109 };
110 R3Visitor.prototype.visitContent = function (content) {
111 t.visitAll(this, content.attributes);
112 };
113 R3Visitor.prototype.visitVariable = function (variable) {
114 // Variable has no template nodes or expression nodes.
115 };
116 R3Visitor.prototype.visitReference = function (reference) {
117 // Reference has no template nodes or expression nodes.
118 };
119 R3Visitor.prototype.visitTextAttribute = function (attribute) {
120 // Text attribute has no template nodes or expression nodes.
121 };
122 R3Visitor.prototype.visitBoundAttribute = function (attribute) {
123 var visitor = new ExpressionVisitor(this.position);
124 visitor.visit(attribute.value, this.path);
125 };
126 R3Visitor.prototype.visitBoundEvent = function (event) {
127 var isTwoWayBinding = this.path.some(function (n) { return n instanceof t.BoundAttribute && event.name === n.name + 'Change'; });
128 if (isTwoWayBinding) {
129 // For two-way binding aka banana-in-a-box, there are two matches:
130 // BoundAttribute and BoundEvent. Both have the same spans. We choose to
131 // return BoundAttribute because it matches the identifier name verbatim.
132 // TODO: For operations like go to definition, ideally we want to return
133 // both.
134 this.path.pop(); // remove bound event from the AST path
135 return;
136 }
137 var visitor = new ExpressionVisitor(this.position);
138 visitor.visit(event.handler, this.path);
139 };
140 R3Visitor.prototype.visitText = function (text) {
141 // Text has no template nodes or expression nodes.
142 };
143 R3Visitor.prototype.visitBoundText = function (text) {
144 var visitor = new ExpressionVisitor(this.position);
145 visitor.visit(text.value, this.path);
146 };
147 R3Visitor.prototype.visitIcu = function (icu) {
148 var e_1, _a, e_2, _b;
149 try {
150 for (var _c = tslib_1.__values(Object.values(icu.vars)), _d = _c.next(); !_d.done; _d = _c.next()) {
151 var boundText = _d.value;
152 this.visit(boundText);
153 }
154 }
155 catch (e_1_1) { e_1 = { error: e_1_1 }; }
156 finally {
157 try {
158 if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
159 }
160 finally { if (e_1) throw e_1.error; }
161 }
162 try {
163 for (var _e = tslib_1.__values(Object.values(icu.placeholders)), _f = _e.next(); !_f.done; _f = _e.next()) {
164 var boundTextOrText = _f.value;
165 this.visit(boundTextOrText);
166 }
167 }
168 catch (e_2_1) { e_2 = { error: e_2_1 }; }
169 finally {
170 try {
171 if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
172 }
173 finally { if (e_2) throw e_2.error; }
174 }
175 };
176 R3Visitor.prototype.visitAll = function (nodes) {
177 var e_3, _a;
178 try {
179 for (var nodes_1 = tslib_1.__values(nodes), nodes_1_1 = nodes_1.next(); !nodes_1_1.done; nodes_1_1 = nodes_1.next()) {
180 var node = nodes_1_1.value;
181 this.visit(node);
182 }
183 }
184 catch (e_3_1) { e_3 = { error: e_3_1 }; }
185 finally {
186 try {
187 if (nodes_1_1 && !nodes_1_1.done && (_a = nodes_1.return)) _a.call(nodes_1);
188 }
189 finally { if (e_3) throw e_3.error; }
190 }
191 };
192 return R3Visitor;
193 }());
194 var ExpressionVisitor = /** @class */ (function (_super) {
195 tslib_1.__extends(ExpressionVisitor, _super);
196 // Position must be absolute in the source file.
197 function ExpressionVisitor(position) {
198 var _this = _super.call(this) || this;
199 _this.position = position;
200 return _this;
201 }
202 ExpressionVisitor.prototype.visit = function (node, path) {
203 if (node instanceof e.ASTWithSource) {
204 // In order to reduce noise, do not include `ASTWithSource` in the path.
205 // For the purpose of source spans, there is no difference between
206 // `ASTWithSource` and and underlying node that it wraps.
207 node = node.ast;
208 }
209 // The third condition is to account for the implicit receiver, which should
210 // not be visited.
211 if (isWithin(this.position, node.sourceSpan) && !(node instanceof e.ImplicitReceiver)) {
212 path.push(node);
213 node.visit(this, path);
214 }
215 };
216 return ExpressionVisitor;
217 }(e.RecursiveAstVisitor));
218 function getSpanIncludingEndTag(ast) {
219 var result = {
220 start: ast.sourceSpan.start.offset,
221 end: ast.sourceSpan.end.offset,
222 };
223 // For Element and Template node, sourceSpan.end is the end of the opening
224 // tag. For the purpose of language service, we need to actually recognize
225 // the end of the closing tag. Otherwise, for situation like
226 // <my-component></my-comp¦onent> where the cursor is in the closing tag
227 // we will not be able to return any information.
228 if ((ast instanceof t.Element || ast instanceof t.Template) && ast.endSourceSpan) {
229 result.end = ast.endSourceSpan.end.offset;
230 }
231 return result;
232 }
233 function isWithin(position, span) {
234 var start, end;
235 if (span instanceof compiler_1.ParseSourceSpan) {
236 start = span.start.offset;
237 end = span.end.offset;
238 }
239 else {
240 start = span.start;
241 end = span.end;
242 }
243 // Note both start and end are inclusive because we want to match conditions
244 // like ¦start and end¦ where ¦ is the cursor.
245 return start <= position && position <= end;
246 }
247});
248//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHlicmlkX3Zpc2l0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9sYW5ndWFnZS1zZXJ2aWNlL2l2eS9oeWJyaWRfdmlzaXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7Ozs7O0lBRUgsOENBQXNFO0lBQ3RFLCtEQUFpRSxDQUFFLHVCQUF1QjtJQUMxRix3REFBMEQsQ0FBUyxxQkFBcUI7SUFFeEYsNkRBQXNFO0lBRXRFOzs7Ozs7T0FNRztJQUNILFNBQWdCLHVCQUF1QixDQUFDLEdBQWEsRUFBRSxRQUFnQjtRQUVyRSxJQUFNLE9BQU8sR0FBRyxJQUFJLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUN4QyxPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3RCLElBQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDeEQsSUFBSSxDQUFDLFNBQVMsRUFBRTtZQUNkLE9BQU87U0FDUjtRQUNELElBQUkscUNBQTZCLENBQUMsU0FBUyxDQUFDLEVBQUU7WUFDckMsSUFBQSxPQUFPLEdBQWUsU0FBUyxRQUF4QixFQUFFLFNBQVMsR0FBSSxTQUFTLFVBQWIsQ0FBYztZQUN2QyxJQUFNLGdCQUFnQixHQUNsQixRQUFRLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLFFBQVEsQ0FBQyxRQUFRLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQztZQUNoRixJQUFJLENBQUMsZ0JBQWdCLEVBQUU7Z0JBQ3JCLHlFQUF5RTtnQkFDekUsMEJBQTBCO2dCQUMxQixPQUFPO2FBQ1I7U0FDRjtRQUNELE9BQU8sT0FBTyxDQUFDLElBQUksQ0FBQztJQUN0QixDQUFDO0lBbkJELDBEQW1CQztJQUVEOzs7Ozs7T0FNRztJQUNILFNBQWdCLGtCQUFrQixDQUFDLEdBQWEsRUFBRSxRQUFnQjtRQUNoRSxJQUFNLElBQUksR0FBRyx1QkFBdUIsQ0FBQyxHQUFHLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDcEQsSUFBSSxDQUFDLElBQUksRUFBRTtZQUNULE9BQU87U0FDUjtRQUNELE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQU5ELGdEQU1DO0lBRUQ7UUFLRSxnREFBZ0Q7UUFDaEQsbUJBQTZCLFFBQWdCO1lBQWhCLGFBQVEsR0FBUixRQUFRLENBQVE7WUFMN0MsNkVBQTZFO1lBQzdFLGtFQUFrRTtZQUN6RCxTQUFJLEdBQXdCLEVBQUUsQ0FBQztRQUdRLENBQUM7UUFFakQseUJBQUssR0FBTCxVQUFNLElBQVk7WUFDVixJQUFBLEtBQWUsc0JBQXNCLENBQUMsSUFBSSxDQUFDLEVBQTFDLEtBQUssV0FBQSxFQUFFLEdBQUcsU0FBZ0MsQ0FBQztZQUNsRCxJQUFJLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLEVBQUMsS0FBSyxPQUFBLEVBQUUsR0FBRyxLQUFBLEVBQUMsQ0FBQyxFQUFFO2dCQUN6QyxJQUFNLFFBQU0sR0FBRyxHQUFHLEdBQUcsS0FBSyxDQUFDO2dCQUMzQixJQUFNLElBQUksR0FBMkIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDckUsSUFBSSxJQUFJLEVBQUU7b0JBQ0YsSUFBQSxLQUFlLHNCQUFjLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLHNCQUFzQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFuRixPQUFLLFdBQUEsRUFBRSxLQUFHLFNBQXlFLENBQUM7b0JBQzNGLElBQU0sVUFBVSxHQUFHLEtBQUcsR0FBRyxPQUFLLENBQUM7b0JBQy9CLElBQUksUUFBTSxHQUFHLFVBQVUsRUFBRTt3QkFDdkIsc0VBQXNFO3dCQUN0RSxtRUFBbUU7d0JBQ25FLGtFQUFrRTt3QkFDbEUsb0JBQW9CO3dCQUNwQixPQUFPO3FCQUNSO2lCQUNGO2dCQUNELElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNyQixJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO2FBQ2xCO1FBQ0gsQ0FBQztRQUVELGdDQUFZLEdBQVosVUFBYSxPQUFrQjtZQUM3QixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUNsQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUM5QixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUMvQixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUNsQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNsQyxDQUFDO1FBRUQsaUNBQWEsR0FBYixVQUFjLFFBQW9CO1lBQ2hDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ25DLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQy9CLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2hDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQ3RDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ25DLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ2xDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ25DLENBQUM7UUFFRCxnQ0FBWSxHQUFaLFVBQWEsT0FBa0I7WUFDN0IsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3ZDLENBQUM7UUFFRCxpQ0FBYSxHQUFiLFVBQWMsUUFBb0I7WUFDaEMsc0RBQXNEO1FBQ3hELENBQUM7UUFFRCxrQ0FBYyxHQUFkLFVBQWUsU0FBc0I7WUFDbkMsdURBQXVEO1FBQ3pELENBQUM7UUFFRCxzQ0FBa0IsR0FBbEIsVUFBbUIsU0FBMEI7WUFDM0MsNERBQTREO1FBQzlELENBQUM7UUFFRCx1Q0FBbUIsR0FBbkIsVUFBb0IsU0FBMkI7WUFDN0MsSUFBTSxPQUFPLEdBQUcsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDckQsT0FBTyxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM1QyxDQUFDO1FBRUQsbUNBQWUsR0FBZixVQUFnQixLQUFtQjtZQUNqQyxJQUFNLGVBQWUsR0FDakIsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBQSxDQUFDLElBQUksT0FBQSxDQUFDLFlBQVksQ0FBQyxDQUFDLGNBQWMsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLENBQUMsQ0FBQyxJQUFJLEdBQUcsUUFBUSxFQUFqRSxDQUFpRSxDQUFDLENBQUM7WUFDM0YsSUFBSSxlQUFlLEVBQUU7Z0JBQ25CLGtFQUFrRTtnQkFDbEUsd0VBQXdFO2dCQUN4RSx5RUFBeUU7Z0JBQ3pFLHdFQUF3RTtnQkFDeEUsUUFBUTtnQkFDUixJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUUsdUNBQXVDO2dCQUN6RCxPQUFPO2FBQ1I7WUFDRCxJQUFNLE9BQU8sR0FBRyxJQUFJLGlCQUFpQixDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNyRCxPQUFPLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzFDLENBQUM7UUFFRCw2QkFBUyxHQUFULFVBQVUsSUFBWTtZQUNwQixrREFBa0Q7UUFDcEQsQ0FBQztRQUVELGtDQUFjLEdBQWQsVUFBZSxJQUFpQjtZQUM5QixJQUFNLE9BQU8sR0FBRyxJQUFJLGlCQUFpQixDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNyRCxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZDLENBQUM7UUFFRCw0QkFBUSxHQUFSLFVBQVMsR0FBVTs7O2dCQUNqQixLQUF3QixJQUFBLEtBQUEsaUJBQUEsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUEsZ0JBQUEsNEJBQUU7b0JBQTVDLElBQU0sU0FBUyxXQUFBO29CQUNsQixJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDO2lCQUN2Qjs7Ozs7Ozs7OztnQkFDRCxLQUE4QixJQUFBLEtBQUEsaUJBQUEsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUEsZ0JBQUEsNEJBQUU7b0JBQTFELElBQU0sZUFBZSxXQUFBO29CQUN4QixJQUFJLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxDQUFDO2lCQUM3Qjs7Ozs7Ozs7O1FBQ0gsQ0FBQztRQUVELDRCQUFRLEdBQVIsVUFBUyxLQUFlOzs7Z0JBQ3RCLEtBQW1CLElBQUEsVUFBQSxpQkFBQSxLQUFLLENBQUEsNEJBQUEsK0NBQUU7b0JBQXJCLElBQU0sSUFBSSxrQkFBQTtvQkFDYixJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO2lCQUNsQjs7Ozs7Ozs7O1FBQ0gsQ0FBQztRQUNILGdCQUFDO0lBQUQsQ0FBQyxBQTNHRCxJQTJHQztJQUVEO1FBQWdDLDZDQUFxQjtRQUNuRCxnREFBZ0Q7UUFDaEQsMkJBQTZCLFFBQWdCO1lBQTdDLFlBQ0UsaUJBQU8sU0FDUjtZQUY0QixjQUFRLEdBQVIsUUFBUSxDQUFROztRQUU3QyxDQUFDO1FBRUQsaUNBQUssR0FBTCxVQUFNLElBQVcsRUFBRSxJQUF5QjtZQUMxQyxJQUFJLElBQUksWUFBWSxDQUFDLENBQUMsYUFBYSxFQUFFO2dCQUNuQyx3RUFBd0U7Z0JBQ3hFLGtFQUFrRTtnQkFDbEUseURBQXlEO2dCQUN6RCxJQUFJLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQzthQUNqQjtZQUNELDRFQUE0RTtZQUM1RSxrQkFBa0I7WUFDbEIsSUFBSSxRQUFRLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksWUFBWSxDQUFDLENBQUMsZ0JBQWdCLENBQUMsRUFBRTtnQkFDckYsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDaEIsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7YUFDeEI7UUFDSCxDQUFDO1FBQ0gsd0JBQUM7SUFBRCxDQUFDLEFBcEJELENBQWdDLENBQUMsQ0FBQyxtQkFBbUIsR0FvQnBEO0lBRUQsU0FBUyxzQkFBc0IsQ0FBQyxHQUFXO1FBQ3pDLElBQU0sTUFBTSxHQUFHO1lBQ2IsS0FBSyxFQUFFLEdBQUcsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLE1BQU07WUFDbEMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLE1BQU07U0FDL0IsQ0FBQztRQUNGLDBFQUEwRTtRQUMxRSwwRUFBMEU7UUFDMUUsNERBQTREO1FBQzVELHdFQUF3RTtRQUN4RSxpREFBaUQ7UUFDakQsSUFBSSxDQUFDLEdBQUcsWUFBWSxDQUFDLENBQUMsT0FBTyxJQUFJLEdBQUcsWUFBWSxDQUFDLENBQUMsUUFBUSxDQUFDLElBQUksR0FBRyxDQUFDLGFBQWEsRUFBRTtZQUNoRixNQUFNLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQztTQUMzQztRQUNELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFRCxTQUFTLFFBQVEsQ0FBQyxRQUFnQixFQUFFLElBQXdDO1FBQzFFLElBQUksS0FBYSxFQUFFLEdBQVcsQ0FBQztRQUMvQixJQUFJLElBQUksWUFBWSwwQkFBZSxFQUFFO1lBQ25DLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQztZQUMxQixHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUM7U0FDdkI7YUFBTTtZQUNMLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDO1lBQ25CLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO1NBQ2hCO1FBQ0QsNEVBQTRFO1FBQzVFLDhDQUE4QztRQUM5QyxPQUFPLEtBQUssSUFBSSxRQUFRLElBQUksUUFBUSxJQUFJLEdBQUcsQ0FBQztJQUM5QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBMTEMgQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5cbmltcG9ydCB7QWJzb2x1dGVTb3VyY2VTcGFuLCBQYXJzZVNvdXJjZVNwYW59IGZyb20gJ0Bhbmd1bGFyL2NvbXBpbGVyJztcbmltcG9ydCAqIGFzIGUgZnJvbSAnQGFuZ3VsYXIvY29tcGlsZXIvc3JjL2V4cHJlc3Npb25fcGFyc2VyL2FzdCc7ICAvLyBlIGZvciBleHByZXNzaW9uIEFTVFxuaW1wb3J0ICogYXMgdCBmcm9tICdAYW5ndWxhci9jb21waWxlci9zcmMvcmVuZGVyMy9yM19hc3QnOyAgICAgICAgIC8vIHQgZm9yIHRlbXBsYXRlIEFTVFxuXG5pbXBvcnQge2lzVGVtcGxhdGVOb2RlLCBpc1RlbXBsYXRlTm9kZVdpdGhLZXlBbmRWYWx1ZX0gZnJvbSAnLi91dGlscyc7XG5cbi8qKlxuICogUmV0dXJuIHRoZSBwYXRoIHRvIHRoZSB0ZW1wbGF0ZSBBU1Qgbm9kZSBvciBleHByZXNzaW9uIEFTVCBub2RlIHRoYXQgbW9zdCBhY2N1cmF0ZWx5XG4gKiByZXByZXNlbnRzIHRoZSBub2RlIGF0IHRoZSBzcGVjaWZpZWQgY3Vyc29yIGBwb3NpdGlvbmAuXG4gKlxuICogQHBhcmFtIGFzdCBBU1QgdHJlZVxuICogQHBhcmFtIHBvc2l0aW9uIGN1cnNvciBwb3NpdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0UGF0aFRvTm9kZUF0UG9zaXRpb24oYXN0OiB0Lk5vZGVbXSwgcG9zaXRpb246IG51bWJlcik6IEFycmF5PHQuTm9kZXxlLkFTVD58XG4gICAgdW5kZWZpbmVkIHtcbiAgY29uc3QgdmlzaXRvciA9IG5ldyBSM1Zpc2l0b3IocG9zaXRpb24pO1xuICB2aXNpdG9yLnZpc2l0QWxsKGFzdCk7XG4gIGNvbnN0IGNhbmRpZGF0ZSA9IHZpc2l0b3IucGF0aFt2aXNpdG9yLnBhdGgubGVuZ3RoIC0gMV07XG4gIGlmICghY2FuZGlkYXRlKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmIChpc1RlbXBsYXRlTm9kZVdpdGhLZXlBbmRWYWx1ZShjYW5kaWRhdGUpKSB7XG4gICAgY29uc3Qge2tleVNwYW4sIHZhbHVlU3Bhbn0gPSBjYW5kaWRhdGU7XG4gICAgY29uc3QgaXNXaXRoaW5LZXlWYWx1ZSA9XG4gICAgICAgIGlzV2l0aGluKHBvc2l0aW9uLCBrZXlTcGFuKSB8fCAodmFsdWVTcGFuICYmIGlzV2l0aGluKHBvc2l0aW9uLCB2YWx1ZVNwYW4pKTtcbiAgICBpZiAoIWlzV2l0aGluS2V5VmFsdWUpIHtcbiAgICAgIC8vIElmIGN1cnNvciBpcyB3aXRoaW4gc291cmNlIHNwYW4gYnV0IG5vdCB3aXRoaW4ga2V5IHNwYW4gb3IgdmFsdWUgc3BhbixcbiAgICAgIC8vIGRvIG5vdCByZXR1cm4gdGhlIG5vZGUuXG4gICAgICByZXR1cm47XG4gICAgfVxuICB9XG4gIHJldHVybiB2aXNpdG9yLnBhdGg7XG59XG5cbi8qKlxuICogUmV0dXJuIHRoZSB0ZW1wbGF0ZSBBU1Qgbm9kZSBvciBleHByZXNzaW9uIEFTVCBub2RlIHRoYXQgbW9zdCBhY2N1cmF0ZWx5XG4gKiByZXByZXNlbnRzIHRoZSBub2RlIGF0IHRoZSBzcGVjaWZpZWQgY3Vyc29yIGBwb3NpdGlvbmAuXG4gKlxuICogQHBhcmFtIGFzdCBBU1QgdHJlZVxuICogQHBhcmFtIHBvc2l0aW9uIGN1cnNvciBwb3NpdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZE5vZGVBdFBvc2l0aW9uKGFzdDogdC5Ob2RlW10sIHBvc2l0aW9uOiBudW1iZXIpOiB0Lk5vZGV8ZS5BU1R8dW5kZWZpbmVkIHtcbiAgY29uc3QgcGF0aCA9IGdldFBhdGhUb05vZGVBdFBvc2l0aW9uKGFzdCwgcG9zaXRpb24pO1xuICBpZiAoIXBhdGgpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgcmV0dXJuIHBhdGhbcGF0aC5sZW5ndGggLSAxXTtcbn1cblxuY2xhc3MgUjNWaXNpdG9yIGltcGxlbWVudHMgdC5WaXNpdG9yIHtcbiAgLy8gV2UgbmVlZCB0byBrZWVwIGEgcGF0aCBpbnN0ZWFkIG9mIHRoZSBsYXN0IG5vZGUgYmVjYXVzZSB3ZSBtaWdodCBuZWVkIG1vcmVcbiAgLy8gY29udGV4dCBmb3IgdGhlIGxhc3Qgbm9kZSwgZm9yIGV4YW1wbGUgd2hhdCBpcyB0aGUgcGFyZW50IG5vZGU/XG4gIHJlYWRvbmx5IHBhdGg6IEFycmF5PHQuTm9kZXxlLkFTVD4gPSBbXTtcblxuICAvLyBQb3NpdGlvbiBtdXN0IGJlIGFic29sdXRlIGluIHRoZSBzb3VyY2UgZmlsZS5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBwb3NpdGlvbjogbnVtYmVyKSB7fVxuXG4gIHZpc2l0KG5vZGU6IHQuTm9kZSkge1xuICAgIGNvbnN0IHtzdGFydCwgZW5kfSA9IGdldFNwYW5JbmNsdWRpbmdFbmRUYWcobm9kZSk7XG4gICAgaWYgKGlzV2l0aGluKHRoaXMucG9zaXRpb24sIHtzdGFydCwgZW5kfSkpIHtcbiAgICAgIGNvbnN0IGxlbmd0aCA9IGVuZCAtIHN0YXJ0O1xuICAgICAgY29uc3QgbGFzdDogdC5Ob2RlfGUuQVNUfHVuZGVmaW5lZCA9IHRoaXMucGF0aFt0aGlzLnBhdGgubGVuZ3RoIC0gMV07XG4gICAgICBpZiAobGFzdCkge1xuICAgICAgICBjb25zdCB7c3RhcnQsIGVuZH0gPSBpc1RlbXBsYXRlTm9kZShsYXN0KSA/IGdldFNwYW5JbmNsdWRpbmdFbmRUYWcobGFzdCkgOiBsYXN0LnNvdXJjZVNwYW47XG4gICAgICAgIGNvbnN0IGxhc3RMZW5ndGggPSBlbmQgLSBzdGFydDtcbiAgICAgICAgaWYgKGxlbmd0aCA+IGxhc3RMZW5ndGgpIHtcbiAgICAgICAgICAvLyBUaGUgY3VycmVudCBub2RlIGhhcyBhIHNwYW4gdGhhdCBpcyBsYXJnZXIgdGhhbiB0aGUgbGFzdCBub2RlIGZvdW5kXG4gICAgICAgICAgLy8gc28gd2UgZG8gbm90IGRlc2NlbmQgaW50byBpdC4gVGhpcyB0eXBpY2FsbHkgbWVhbnMgd2UgaGF2ZSBmb3VuZFxuICAgICAgICAgIC8vIGEgY2FuZGlkYXRlIGluIG9uZSBvZiB0aGUgcm9vdCBub2RlcyBzbyB3ZSBkbyBub3QgbmVlZCB0byB2aXNpdFxuICAgICAgICAgIC8vIG90aGVyIHJvb3Qgbm9kZXMuXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLnBhdGgucHVzaChub2RlKTtcbiAgICAgIG5vZGUudmlzaXQodGhpcyk7XG4gICAgfVxuICB9XG5cbiAgdmlzaXRFbGVtZW50KGVsZW1lbnQ6IHQuRWxlbWVudCkge1xuICAgIHRoaXMudmlzaXRBbGwoZWxlbWVudC5hdHRyaWJ1dGVzKTtcbiAgICB0aGlzLnZpc2l0QWxsKGVsZW1lbnQuaW5wdXRzKTtcbiAgICB0aGlzLnZpc2l0QWxsKGVsZW1lbnQub3V0cHV0cyk7XG4gICAgdGhpcy52aXNpdEFsbChlbGVtZW50LnJlZmVyZW5jZXMpO1xuICAgIHRoaXMudmlzaXRBbGwoZWxlbWVudC5jaGlsZHJlbik7XG4gIH1cblxuICB2aXNpdFRlbXBsYXRlKHRlbXBsYXRlOiB0LlRlbXBsYXRlKSB7XG4gICAgdGhpcy52aXNpdEFsbCh0ZW1wbGF0ZS5hdHRyaWJ1dGVzKTtcbiAgICB0aGlzLnZpc2l0QWxsKHRlbXBsYXRlLmlucHV0cyk7XG4gICAgdGhpcy52aXNpdEFsbCh0ZW1wbGF0ZS5vdXRwdXRzKTtcbiAgICB0aGlzLnZpc2l0QWxsKHRlbXBsYXRlLnRlbXBsYXRlQXR0cnMpO1xuICAgIHRoaXMudmlzaXRBbGwodGVtcGxhdGUucmVmZXJlbmNlcyk7XG4gICAgdGhpcy52aXNpdEFsbCh0ZW1wbGF0ZS52YXJpYWJsZXMpO1xuICAgIHRoaXMudmlzaXRBbGwodGVtcGxhdGUuY2hpbGRyZW4pO1xuICB9XG5cbiAgdmlzaXRDb250ZW50KGNvbnRlbnQ6IHQuQ29udGVudCkge1xuICAgIHQudmlzaXRBbGwodGhpcywgY29udGVudC5hdHRyaWJ1dGVzKTtcbiAgfVxuXG4gIHZpc2l0VmFyaWFibGUodmFyaWFibGU6IHQuVmFyaWFibGUpIHtcbiAgICAvLyBWYXJpYWJsZSBoYXMgbm8gdGVtcGxhdGUgbm9kZXMgb3IgZXhwcmVzc2lvbiBub2Rlcy5cbiAgfVxuXG4gIHZpc2l0UmVmZXJlbmNlKHJlZmVyZW5jZTogdC5SZWZlcmVuY2UpIHtcbiAgICAvLyBSZWZlcmVuY2UgaGFzIG5vIHRlbXBsYXRlIG5vZGVzIG9yIGV4cHJlc3Npb24gbm9kZXMuXG4gIH1cblxuICB2aXNpdFRleHRBdHRyaWJ1dGUoYXR0cmlidXRlOiB0LlRleHRBdHRyaWJ1dGUpIHtcbiAgICAvLyBUZXh0IGF0dHJpYnV0ZSBoYXMgbm8gdGVtcGxhdGUgbm9kZXMgb3IgZXhwcmVzc2lvbiBub2Rlcy5cbiAgfVxuXG4gIHZpc2l0Qm91bmRBdHRyaWJ1dGUoYXR0cmlidXRlOiB0LkJvdW5kQXR0cmlidXRlKSB7XG4gICAgY29uc3QgdmlzaXRvciA9IG5ldyBFeHByZXNzaW9uVmlzaXRvcih0aGlzLnBvc2l0aW9uKTtcbiAgICB2aXNpdG9yLnZpc2l0KGF0dHJpYnV0ZS52YWx1ZSwgdGhpcy5wYXRoKTtcbiAgfVxuXG4gIHZpc2l0Qm91bmRFdmVudChldmVudDogdC5Cb3VuZEV2ZW50KSB7XG4gICAgY29uc3QgaXNUd29XYXlCaW5kaW5nID1cbiAgICAgICAgdGhpcy5wYXRoLnNvbWUobiA9PiBuIGluc3RhbmNlb2YgdC5Cb3VuZEF0dHJpYnV0ZSAmJiBldmVudC5uYW1lID09PSBuLm5hbWUgKyAnQ2hhbmdlJyk7XG4gICAgaWYgKGlzVHdvV2F5QmluZGluZykge1xuICAgICAgLy8gRm9yIHR3by13YXkgYmluZGluZyBha2EgYmFuYW5hLWluLWEtYm94LCB0aGVyZSBhcmUgdHdvIG1hdGNoZXM6XG4gICAgICAvLyBCb3VuZEF0dHJpYnV0ZSBhbmQgQm91bmRFdmVudC4gQm90aCBoYXZlIHRoZSBzYW1lIHNwYW5zLiBXZSBjaG9vc2UgdG9cbiAgICAgIC8vIHJldHVybiBCb3VuZEF0dHJpYnV0ZSBiZWNhdXNlIGl0IG1hdGNoZXMgdGhlIGlkZW50aWZpZXIgbmFtZSB2ZXJiYXRpbS5cbiAgICAgIC8vIFRPRE86IEZvciBvcGVyYXRpb25zIGxpa2UgZ28gdG8gZGVmaW5pdGlvbiwgaWRlYWxseSB3ZSB3YW50IHRvIHJldHVyblxuICAgICAgLy8gYm90aC5cbiAgICAgIHRoaXMucGF0aC5wb3AoKTsgIC8vIHJlbW92ZSBib3VuZCBldmVudCBmcm9tIHRoZSBBU1QgcGF0aFxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB2aXNpdG9yID0gbmV3IEV4cHJlc3Npb25WaXNpdG9yKHRoaXMucG9zaXRpb24pO1xuICAgIHZpc2l0b3IudmlzaXQoZXZlbnQuaGFuZGxlciwgdGhpcy5wYXRoKTtcbiAgfVxuXG4gIHZpc2l0VGV4dCh0ZXh0OiB0LlRleHQpIHtcbiAgICAvLyBUZXh0IGhhcyBubyB0ZW1wbGF0ZSBub2RlcyBvciBleHByZXNzaW9uIG5vZGVzLlxuICB9XG5cbiAgdmlzaXRCb3VuZFRleHQodGV4dDogdC5Cb3VuZFRleHQpIHtcbiAgICBjb25zdCB2aXNpdG9yID0gbmV3IEV4cHJlc3Npb25WaXNpdG9yKHRoaXMucG9zaXRpb24pO1xuICAgIHZpc2l0b3IudmlzaXQodGV4dC52YWx1ZSwgdGhpcy5wYXRoKTtcbiAgfVxuXG4gIHZpc2l0SWN1KGljdTogdC5JY3UpIHtcbiAgICBmb3IgKGNvbnN0IGJvdW5kVGV4dCBvZiBPYmplY3QudmFsdWVzKGljdS52YXJzKSkge1xuICAgICAgdGhpcy52aXNpdChib3VuZFRleHQpO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IGJvdW5kVGV4dE9yVGV4dCBvZiBPYmplY3QudmFsdWVzKGljdS5wbGFjZWhvbGRlcnMpKSB7XG4gICAgICB0aGlzLnZpc2l0KGJvdW5kVGV4dE9yVGV4dCk7XG4gICAgfVxuICB9XG5cbiAgdmlzaXRBbGwobm9kZXM6IHQuTm9kZVtdKSB7XG4gICAgZm9yIChjb25zdCBub2RlIG9mIG5vZGVzKSB7XG4gICAgICB0aGlzLnZpc2l0KG5vZGUpO1xuICAgIH1cbiAgfVxufVxuXG5jbGFzcyBFeHByZXNzaW9uVmlzaXRvciBleHRlbmRzIGUuUmVjdXJzaXZlQXN0VmlzaXRvciB7XG4gIC8vIFBvc2l0aW9uIG11c3QgYmUgYWJzb2x1dGUgaW4gdGhlIHNvdXJjZSBmaWxlLlxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHBvc2l0aW9uOiBudW1iZXIpIHtcbiAgICBzdXBlcigpO1xuICB9XG5cbiAgdmlzaXQobm9kZTogZS5BU1QsIHBhdGg6IEFycmF5PHQuTm9kZXxlLkFTVD4pIHtcbiAgICBpZiAobm9kZSBpbnN0YW5jZW9mIGUuQVNUV2l0aFNvdXJjZSkge1xuICAgICAgLy8gSW4gb3JkZXIgdG8gcmVkdWNlIG5vaXNlLCBkbyBub3QgaW5jbHVkZSBgQVNUV2l0aFNvdXJjZWAgaW4gdGhlIHBhdGguXG4gICAgICAvLyBGb3IgdGhlIHB1cnBvc2Ugb2Ygc291cmNlIHNwYW5zLCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW5cbiAgICAgIC8vIGBBU1RXaXRoU291cmNlYCBhbmQgYW5kIHVuZGVybHlpbmcgbm9kZSB0aGF0IGl0IHdyYXBzLlxuICAgICAgbm9kZSA9IG5vZGUuYXN0O1xuICAgIH1cbiAgICAvLyBUaGUgdGhpcmQgY29uZGl0aW9uIGlzIHRvIGFjY291bnQgZm9yIHRoZSBpbXBsaWNpdCByZWNlaXZlciwgd2hpY2ggc2hvdWxkXG4gICAgLy8gbm90IGJlIHZpc2l0ZWQuXG4gICAgaWYgKGlzV2l0aGluKHRoaXMucG9zaXRpb24sIG5vZGUuc291cmNlU3BhbikgJiYgIShub2RlIGluc3RhbmNlb2YgZS5JbXBsaWNpdFJlY2VpdmVyKSkge1xuICAgICAgcGF0aC5wdXNoKG5vZGUpO1xuICAgICAgbm9kZS52aXNpdCh0aGlzLCBwYXRoKTtcbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0U3BhbkluY2x1ZGluZ0VuZFRhZyhhc3Q6IHQuTm9kZSkge1xuICBjb25zdCByZXN1bHQgPSB7XG4gICAgc3RhcnQ6IGFzdC5zb3VyY2VTcGFuLnN0YXJ0Lm9mZnNldCxcbiAgICBlbmQ6IGFzdC5zb3VyY2VTcGFuLmVuZC5vZmZzZXQsXG4gIH07XG4gIC8vIEZvciBFbGVtZW50IGFuZCBUZW1wbGF0ZSBub2RlLCBzb3VyY2VTcGFuLmVuZCBpcyB0aGUgZW5kIG9mIHRoZSBvcGVuaW5nXG4gIC8vIHRhZy4gRm9yIHRoZSBwdXJwb3NlIG9mIGxhbmd1YWdlIHNlcnZpY2UsIHdlIG5lZWQgdG8gYWN0dWFsbHkgcmVjb2duaXplXG4gIC8vIHRoZSBlbmQgb2YgdGhlIGNsb3NpbmcgdGFnLiBPdGhlcndpc2UsIGZvciBzaXR1YXRpb24gbGlrZVxuICAvLyA8bXktY29tcG9uZW50PjwvbXktY29tcMKmb25lbnQ+IHdoZXJlIHRoZSBjdXJzb3IgaXMgaW4gdGhlIGNsb3NpbmcgdGFnXG4gIC8vIHdlIHdpbGwgbm90IGJlIGFibGUgdG8gcmV0dXJuIGFueSBpbmZvcm1hdGlvbi5cbiAgaWYgKChhc3QgaW5zdGFuY2VvZiB0LkVsZW1lbnQgfHwgYXN0IGluc3RhbmNlb2YgdC5UZW1wbGF0ZSkgJiYgYXN0LmVuZFNvdXJjZVNwYW4pIHtcbiAgICByZXN1bHQuZW5kID0gYXN0LmVuZFNvdXJjZVNwYW4uZW5kLm9mZnNldDtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5mdW5jdGlvbiBpc1dpdGhpbihwb3NpdGlvbjogbnVtYmVyLCBzcGFuOiBBYnNvbHV0ZVNvdXJjZVNwYW58UGFyc2VTb3VyY2VTcGFuKTogYm9vbGVhbiB7XG4gIGxldCBzdGFydDogbnVtYmVyLCBlbmQ6IG51bWJlcjtcbiAgaWYgKHNwYW4gaW5zdGFuY2VvZiBQYXJzZVNvdXJjZVNwYW4pIHtcbiAgICBzdGFydCA9IHNwYW4uc3RhcnQub2Zmc2V0O1xuICAgIGVuZCA9IHNwYW4uZW5kLm9mZnNldDtcbiAgfSBlbHNlIHtcbiAgICBzdGFydCA9IHNwYW4uc3RhcnQ7XG4gICAgZW5kID0gc3Bhbi5lbmQ7XG4gIH1cbiAgLy8gTm90ZSBib3RoIHN0YXJ0IGFuZCBlbmQgYXJlIGluY2x1c2l2ZSBiZWNhdXNlIHdlIHdhbnQgdG8gbWF0Y2ggY29uZGl0aW9uc1xuICAvLyBsaWtlIMKmc3RhcnQgYW5kIGVuZMKmIHdoZXJlIMKmIGlzIHRoZSBjdXJzb3IuXG4gIHJldHVybiBzdGFydCA8PSBwb3NpdGlvbiAmJiBwb3NpdGlvbiA8PSBlbmQ7XG59XG4iXX0=
\No newline at end of file