UNPKG

17.1 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 */
8import * as html from './ast';
9import { NGSP_UNICODE } from './entities';
10import { ParseTreeResult } from './parser';
11export const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
12const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
13// Equivalent to \s with \u00a0 (non-breaking space) excluded.
14// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
15const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
16const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);
17const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');
18function hasPreserveWhitespacesAttr(attrs) {
19 return attrs.some((attr) => attr.name === PRESERVE_WS_ATTR_NAME);
20}
21/**
22 * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
23 * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
24 * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
25 * and later on replaced by a space. We are re-implementing the same idea here.
26 */
27export function replaceNgsp(value) {
28 // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
29 return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
30}
31/**
32 * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:
33 * - consider spaces, tabs and new lines as whitespace characters;
34 * - drop text nodes consisting of whitespace characters only;
35 * - for all other text nodes replace consecutive whitespace characters with one space;
36 * - convert &ngsp; pseudo-entity to a single space;
37 *
38 * Removal and trimming of whitespaces have positive performance impact (less code to generate
39 * while compiling templates, faster view creation). At the same time it can be "destructive"
40 * in some cases (whitespaces can influence layout). Because of the potential of breaking layout
41 * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for
42 * whitespace removal. The default option for whitespace removal will be revisited in Angular 6
43 * and might be changed to "on" by default.
44 */
45export class WhitespaceVisitor {
46 visitElement(element, context) {
47 if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
48 // don't descent into elements where we need to preserve whitespaces
49 // but still visit all attributes to eliminate one used as a market to preserve WS
50 return new html.Element(element.name, html.visitAll(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
51 }
52 return new html.Element(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
53 }
54 visitAttribute(attribute, context) {
55 return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
56 }
57 visitText(text, context) {
58 const isNotBlank = text.value.match(NO_WS_REGEXP);
59 const hasExpansionSibling = context &&
60 (context.prev instanceof html.Expansion || context.next instanceof html.Expansion);
61 if (isNotBlank || hasExpansionSibling) {
62 // Process the whitespace in the tokens of this Text node
63 const tokens = text.tokens.map(token => token.type === 5 /* TEXT */ ? createWhitespaceProcessedTextToken(token) : token);
64 // Process the whitespace of the value of this Text node
65 const value = processWhitespace(text.value);
66 return new html.Text(value, text.sourceSpan, tokens, text.i18n);
67 }
68 return null;
69 }
70 visitComment(comment, context) {
71 return comment;
72 }
73 visitExpansion(expansion, context) {
74 return expansion;
75 }
76 visitExpansionCase(expansionCase, context) {
77 return expansionCase;
78 }
79}
80function createWhitespaceProcessedTextToken({ type, parts, sourceSpan }) {
81 return { type, parts: [processWhitespace(parts[0])], sourceSpan };
82}
83function processWhitespace(text) {
84 return replaceNgsp(text).replace(WS_REPLACE_REGEXP, ' ');
85}
86export function removeWhitespaces(htmlAstWithErrors) {
87 return new ParseTreeResult(html.visitAll(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), htmlAstWithErrors.errors);
88}
89function visitAllWithSiblings(visitor, nodes) {
90 const result = [];
91 nodes.forEach((ast, i) => {
92 const context = { prev: nodes[i - 1], next: nodes[i + 1] };
93 const astResult = ast.visit(visitor, context);
94 if (astResult) {
95 result.push(astResult);
96 }
97 });
98 return result;
99}
100//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHRtbF93aGl0ZXNwYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvbXBpbGVyL3NyYy9tbF9wYXJzZXIvaHRtbF93aGl0ZXNwYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEtBQUssSUFBSSxNQUFNLE9BQU8sQ0FBQztBQUM5QixPQUFPLEVBQUMsWUFBWSxFQUFDLE1BQU0sWUFBWSxDQUFDO0FBQ3hDLE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxVQUFVLENBQUM7QUFHekMsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsdUJBQXVCLENBQUM7QUFFN0QsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0FBRXRGLDhEQUE4RDtBQUM5RCxtR0FBbUc7QUFDbkcsTUFBTSxRQUFRLEdBQUcsMEVBQTBFLENBQUM7QUFDNUYsTUFBTSxZQUFZLEdBQUcsSUFBSSxNQUFNLENBQUMsS0FBSyxRQUFRLEdBQUcsQ0FBQyxDQUFDO0FBQ2xELE1BQU0saUJBQWlCLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSxRQUFRLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQztBQUUvRCxTQUFTLDBCQUEwQixDQUFDLEtBQXVCO0lBQ3pELE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQW9CLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUsscUJBQXFCLENBQUMsQ0FBQztBQUNuRixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUFDLEtBQWE7SUFDdkMsZ0VBQWdFO0lBQ2hFLE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLE1BQU0sQ0FBQyxZQUFZLEVBQUUsR0FBRyxDQUFDLEVB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RUQsU0FBUyxpQkFBaUIsQ0FBQyxJQUFZO0lBQ3JDLE9BQU8sV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUMzRCxDQUFDO0FBRUQsTUFBTSxVQUFVLGlCQUFpQixDQUFDLGlCQUFrQztJQUNsRSxPQUFPLElBQUksZUFBZSxDQUN0QixJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksaUJBQWlCLEVBQUUsRUFBRSxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsRUFDbkUsaUJBQWlCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDaEMsQ0FBQztBQU9ELFNBQVMsb0JBQW9CLENBQUMsT0FBMEIsRUFBRSxLQUFrQjtJQUMxRSxNQUFNLE1BQU0sR0FBVSxFQUFFLENBQUM7SUFFekIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUN2QixNQUFNLE9BQU8sR0FBMEIsRUFBQyxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBQyxDQUFDO1FBQ2hGLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzlDLElBQUksU0FBUyxFQUFFO1lBQ2IsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztTQUN4QjtJQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQgKiBhcyBodG1sIGZyb20gJy4vYXN0JztcbmltcG9ydCB7TkdTUF9VTklDT0RFfSBmcm9tICcuL2VudGl0aWVzJztcbmltcG9ydCB7UGFyc2VUcmVlUmVzdWx0fSBmcm9tICcuL3BhcnNlcic7XG5pbXBvcnQge1RleHRUb2tlbiwgVG9rZW5UeXBlfSBmcm9tICcuL3Rva2Vucyc7XG5cbmV4cG9ydCBjb25zdCBQUkVTRVJWRV9XU19BVFRSX05BTUUgPSAnbmdQcmVzZXJ2ZVdoaXRlc3BhY2VzJztcblxuY29uc3QgU0tJUF9XU19UUklNX1RBR1MgPSBuZXcgU2V0KFsncHJlJywgJ3RlbXBsYXRlJywgJ3RleHRhcmVhJywgJ3NjcmlwdCcsICdzdHlsZSddKTtcblxuLy8gRXF1aXZhbGVudCB0byBcXHMgd2l0aCBcXHUwMGEwIChub24tYnJlYWtpbmcgc3BhY2UpIGV4Y2x1ZGVkLlxuLy8gQmFzZWQgb24gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvUmVnRXhwXG5jb25zdCBXU19DSEFSUyA9ICcgXFxmXFxuXFxyXFx0XFx2XFx1MTY4MFxcdTE4MGVcXHUyMDAwLVxcdTIwMGFcXHUyMDI4XFx1MjAyOVxcdTIwMmZcXHUyMDVmXFx1MzAwMFxcdWZlZmYnO1xuY29uc3QgTk9fV1NfUkVHRVhQID0gbmV3IFJlZ0V4cChgW14ke1dTX0NIQVJTfV1gKTtcbmNvbnN0IFdTX1JFUExBQ0VfUkVHRVhQID0gbmV3IFJlZ0V4cChgWyR7V1NfQ0hBUlN9XXsyLH1gLCAnZycpO1xuXG5mdW5jdGlvbiBoYXNQcmVzZXJ2ZVdoaXRlc3BhY2VzQXR0cihhdHRyczogaHRtbC5BdHRyaWJ1dGVbXSk6IGJvb2xlYW4ge1xuICByZXR1cm4gYXR0cnMuc29tZSgoYXR0cjogaHRtbC5BdHRyaWJ1dGUpID0+IGF0dHIubmFtZSA9PT0gUFJFU0VSVkVfV1NfQVRUUl9OQU1FKTtcbn1cblxuLyoqXG4gKiBBbmd1bGFyIERhcnQgaW50cm9kdWNlZCAmbmdzcDsgYXMgYSBwbGFjZWhvbGRlciBmb3Igbm9uLXJlbW92YWJsZSBzcGFjZSwgc2VlOlxuICogaHR0cHM6Ly9naXRodWIuY29tL2RhcnQtbGFuZy9hbmd1bGFyL2Jsb2IvMGJiNjExMzg3ZDI5ZDY1YjVhZjdmOWQyNTE1YWI1NzFmZDNmYmVlNC9fdGVzdHMvdGVzdC9jb21waWxlci9wcmVzZXJ2ZV93aGl0ZXNwYWNlX3Rlc3QuZGFydCNMMjUtTDMyXG4gKiBJbiBBbmd1bGFyIERhcnQgJm5nc3A7IGlzIGNvbnZlcnRlZCB0byB0aGUgMHhFNTAwIFBVQSAoUHJpdmF0ZSBVc2UgQXJlYXMpIHVuaWNvZGUgY2hhcmFjdGVyXG4gKiBhbmQgbGF0ZXIgb24gcmVwbGFjZWQgYnkgYSBzcGFjZS4gV2UgYXJlIHJlLWltcGxlbWVudGluZyB0aGUgc2FtZSBpZGVhIGhlcmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXBsYWNlTmdzcCh2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gbGV4ZXIgaXMgcmVwbGFjaW5nIHRoZSAmbmdzcDsgcHNldWRvLWVudGl0eSB3aXRoIE5HU1BfVU5JQ09ERVxuICByZXR1cm4gdmFsdWUucmVwbGFjZShuZXcgUmVnRXhwKE5HU1BfVU5JQ09ERSwgJ2cnKSwgJyAnKTtcbn1cblxuLyoqXG4gKiBUaGlzIHZpc2l0b3IgY2FuIHdhbGsgSFRNTCBwYXJzZSB0cmVlIGFuZCByZW1vdmUgLyB0cmltIHRleHQgbm9kZXMgdXNpbmcgdGhlIGZvbGxvd2luZyBydWxlczpcbiAqIC0gY29uc2lkZXIgc3BhY2VzLCB0YWJzIGFuZCBuZXcgbGluZXMgYXMgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzO1xuICogLSBkcm9wIHRleHQgbm9kZXMgY29uc2lzdGluZyBvZiB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMgb25seTtcbiAqIC0gZm9yIGFsbCBvdGhlciB0ZXh0IG5vZGVzIHJlcGxhY2UgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzIHdpdGggb25lIHNwYWNlO1xuICogLSBjb252ZXJ0ICZuZ3NwOyBwc2V1ZG8tZW50aXR5IHRvIGEgc2luZ2xlIHNwYWNlO1xuICpcbiAqIFJlbW92YWwgYW5kIHRyaW1taW5nIG9mIHdoaXRlc3BhY2VzIGhhdmUgcG9zaXRpdmUgcGVyZm9ybWFuY2UgaW1wYWN0IChsZXNzIGNvZGUgdG8gZ2VuZXJhdGVcbiAqIHdoaWxlIGNvbXBpbGluZyB0ZW1wbGF0ZXMsIGZhc3RlciB2aWV3IGNyZWF0aW9uKS4gQXQgdGhlIHNhbWUgdGltZSBpdCBjYW4gYmUgXCJkZXN0cnVjdGl2ZVwiXG4gKiBpbiBzb21lIGNhc2VzICh3aGl0ZXNwYWNlcyBjYW4gaW5mbHVlbmNlIGxheW91dCkuIEJlY2F1c2Ugb2YgdGhlIHBvdGVudGlhbCBvZiBicmVha2luZyBsYXlvdXRcbiAqIHRoaXMgdmlzaXRvciBpcyBub3QgYWN0aXZhdGVkIGJ5IGRlZmF1bHQgaW4gQW5ndWxhciA1IGFuZCBwZW9wbGUgbmVlZCB0byBleHBsaWNpdGx5IG9wdC1pbiBmb3JcbiAqIHdoaXRlc3BhY2UgcmVtb3ZhbC4gVGhlIGRlZmF1bHQgb3B0aW9uIGZvciB3aGl0ZXNwYWNlIHJlbW92YWwgd2lsbCBiZSByZXZpc2l0ZWQgaW4gQW5ndWxhciA2XG4gKiBhbmQgbWlnaHQgYmUgY2hhbmdlZCB0byBcIm9uXCIgYnkgZGVmYXVsdC5cbiAqL1xuZXhwb3J0IGNsYXNzIFdoaXRlc3BhY2VWaXNpdG9yIGltcGxlbWVudHMgaHRtbC5WaXNpdG9yIHtcbiAgdmlzaXRFbGVtZW50KGVsZW1lbnQ6IGh0bWwuRWxlbWVudCwgY29udGV4dDogYW55KTogYW55IHtcbiAgICBpZiAoU0tJUF9XU19UUklNX1RBR1MuaGFzKGVsZW1lbnQubmFtZSkgfHwgaGFzUHJlc2VydmVXaGl0ZXNwYWNlc0F0dHIoZWxlbWVudC5hdHRycykpIHtcbiAgICAgIC8vIGRvbid0IGRlc2NlbnQgaW50byBlbGVtZW50cyB3aGVyZSB3ZSBuZWVkIHRvIHByZXNlcnZlIHdoaXRlc3BhY2VzXG4gICAgICAvLyBidXQgc3RpbGwgdmlzaXQgYWxsIGF0dHJpYnV0ZXMgdG8gZWxpbWluYXRlIG9uZSB1c2VkIGFzIGEgbWFya2V0IHRvIHByZXNlcnZlIFdTXG4gICAgICByZXR1cm4gbmV3IGh0bWwuRWxlbWVudChcbiAgICAgICAgICBlbGVtZW50Lm5hbWUsIGh0bWwudmlzaXRBbGwodGhpcywgZWxlbWVudC5hdHRycyksIGVsZW1lbnQuY2hpbGRyZW4sIGVsZW1lbnQuc291cmNlU3BhbixcbiAgICAgICAgICBlbGVtZW50LnN0YXJ0U291cmNlU3BhbiwgZWxlbWVudC5lbmRTb3VyY2VTcGFuLCBlbGVtZW50LmkxOG4pO1xuICAgIH1cblxuICAgIHJldHVybiBuZXcgaHRtbC5FbGVtZW50KFxuICAgICAgICBlbGVtZW50Lm5hbWUsIGVsZW1lbnQuYXR0cnMsIHZpc2l0QWxsV2l0aFNpYmxpbmdzKHRoaXMsIGVsZW1lbnQuY2hpbGRyZW4pLFxuICAgICAgICBlbGVtZW50LnNvdXJjZVNwYW4sIGVsZW1lbnQuc3RhcnRTb3VyY2VTcGFuLCBlbGVtZW50LmVuZFNvdXJjZVNwYW4sIGVsZW1lbnQuaTE4bik7XG4gIH1cblxuICB2aXNpdEF0dHJpYnV0ZShhdHRyaWJ1dGU6IGh0bWwuQXR0cmlidXRlLCBjb250ZXh0OiBhbnkpOiBhbnkge1xuICAgIHJldHVybiBhdHRyaWJ1dGUubmFtZSAhPT0gUFJFU0VSVkVfV1NfQVRUUl9OQU1FID8gYXR0cmlidXRlIDogbnVsbDtcbiAgfVxuXG4gIHZpc2l0VGV4dCh0ZXh0OiBodG1sLlRleHQsIGNvbnRleHQ6IFNpYmxpbmdWaXNpdG9yQ29udGV4dHxudWxsKTogYW55IHtcbiAgICBjb25zdCBpc05vdEJsYW5rID0gdGV4dC52YWx1ZS5tYXRjaChOT19XU19SRUdFWFApO1xuICAgIGNvbnN0IGhhc0V4cGFuc2lvblNpYmxpbmcgPSBjb250ZXh0ICYmXG4gICAgICAgIChjb250ZXh0LnByZXYgaW5zdGFuY2VvZiBodG1sLkV4cGFuc2lvbiB8fCBjb250ZXh0Lm5leHQgaW5zdGFuY2VvZiBodG1sLkV4cGFuc2lvbik7XG5cbiAgICBpZiAoaXNOb3RCbGFuayB8fCBoYXNFeHBhbnNpb25TaWJsaW5nKSB7XG4gICAgICAvLyBQcm9jZXNzIHRoZSB3aGl0ZXNwYWNlIGluIHRoZSB0b2tlbnMgb2YgdGhpcyBUZXh0IG5vZGVcbiAgICAgIGNvbnN0IHRva2VucyA9IHRleHQudG9rZW5zLm1hcChcbiAgICAgICAgICB0b2tlbiA9PlxuICAgICAgICAgICAgICB0b2tlbi50eXBlID09PSBUb2tlblR5cGUuVEVYVCA/IGNyZWF0ZVdoaXRlc3BhY2VQcm9jZXNzZWRUZXh0VG9rZW4odG9rZW4pIDogdG9rZW4pO1xuICAgICAgLy8gUHJvY2VzcyB0aGUgd2hpdGVzcGFjZSBvZiB0aGUgdmFsdWUgb2YgdGhpcyBUZXh0IG5vZGVcbiAgICAgIGNvbnN0IHZhbHVlID0gcHJvY2Vzc1doaXRlc3BhY2UodGV4dC52YWx1ZSk7XG4gICAgICByZXR1cm4gbmV3IGh0bWwuVGV4dCh2YWx1ZSwgdGV4dC5zb3VyY2VTcGFuLCB0b2tlbnMsIHRleHQuaTE4bik7XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICB2aXNpdENvbW1lbnQoY29tbWVudDogaHRtbC5Db21tZW50LCBjb250ZXh0OiBhbnkpOiBhbnkge1xuICAgIHJldHVybiBjb21tZW50O1xuICB9XG5cbiAgdmlzaXRFeHBhbnNpb24oZXhwYW5zaW9uOiBodG1sLkV4cGFuc2lvbiwgY29udGV4dDogYW55KTogYW55IHtcbiAgICByZXR1cm4gZXhwYW5zaW9uO1xuICB9XG5cbiAgdmlzaXRFeHBhbnNpb25DYXNlKGV4cGFuc2lvbkNhc2U6IGh0bWwuRXhwYW5zaW9uQ2FzZSwgY29udGV4dDogYW55KTogYW55IHtcbiAgICByZXR1cm4gZXhwYW5zaW9uQ2FzZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBjcmVhdGVXaGl0ZXNwYWNlUHJvY2Vzc2VkVGV4dFRva2VuKHt0eXBlLCBwYXJ0cywgc291cmNlU3Bhbn06IFRleHRUb2tlbik6IFRleHRUb2tlbiB7XG4gIHJldHVybiB7dHlwZSwgcGFydHM6IFtwcm9jZXNzV2hpdGVzcGFjZShwYXJ0c1swXSldLCBzb3VyY2VTcGFufTtcbn1cblxuZnVuY3Rpb24gcHJvY2Vzc1doaXRlc3BhY2UodGV4dDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHJlcGxhY2VOZ3NwKHRleHQpLnJlcGxhY2UoV1NfUkVQTEFDRV9SRUdFWFAsICcgJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW1vdmVXaGl0ZXNwYWNlcyhodG1sQXN0V2l0aEVycm9yczogUGFyc2VUcmVlUmVzdWx0KTogUGFyc2VUcmVlUmVzdWx0IHtcbiAgcmV0dXJuIG5ldyBQYXJzZVRyZWVSZXN1bHQoXG4gICAgICBodG1sLnZpc2l0QWxsKG5ldyBXaGl0ZXNwYWNlVmlzaXRvcigpLCBodG1sQXN0V2l0aEVycm9ycy5yb290Tm9kZXMpLFxuICAgICAgaHRtbEFzdFdpdGhFcnJvcnMuZXJyb3JzKTtcbn1cblxuaW50ZXJmYWNlIFNpYmxpbmdWaXNpdG9yQ29udGV4dCB7XG4gIHByZXY6IGh0bWwuTm9kZXx1bmRlZmluZWQ7XG4gIG5leHQ6IGh0bWwuTm9kZXx1bmRlZmluZWQ7XG59XG5cbmZ1bmN0aW9uIHZpc2l0QWxsV2l0aFNpYmxpbmdzKHZpc2l0b3I6IFdoaXRlc3BhY2VWaXNpdG9yLCBub2RlczogaHRtbC5Ob2RlW10pOiBhbnlbXSB7XG4gIGNvbnN0IHJlc3VsdDogYW55W10gPSBbXTtcblxuICBub2Rlcy5mb3JFYWNoKChhc3QsIGkpID0+IHtcbiAgICBjb25zdCBjb250ZXh0OiBTaWJsaW5nVmlzaXRvckNvbnRleHQgPSB7cHJldjogbm9kZXNbaSAtIDFdLCBuZXh0OiBub2Rlc1tpICsgMV19O1xuICAgIGNvbnN0IGFzdFJlc3VsdCA9IGFzdC52aXNpdCh2aXNpdG9yLCBjb250ZXh0KTtcbiAgICBpZiAoYXN0UmVzdWx0KSB7XG4gICAgICByZXN1bHQucHVzaChhc3RSZXN1bHQpO1xuICAgIH1cbiAgfSk7XG4gIHJldHVybiByZXN1bHQ7XG59XG4iXX0=
\No newline at end of file