@angular/compiler
Version:
Angular - the compiler library
75 lines • 13.2 kB
JavaScript
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as html from './ast';
import { ParseTreeResult } from './parser';
import { NGSP_UNICODE } from './tags';
export var PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
var SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
// Equivalent to \s with \u00a0 (non-breaking space) excluded.
// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
var WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
var NO_WS_REGEXP = new RegExp("[^" + WS_CHARS + "]");
var WS_REPLACE_REGEXP = new RegExp("[" + WS_CHARS + "]{2,}", 'g');
function hasPreserveWhitespacesAttr(attrs) {
return attrs.some(function (attr) { return attr.name === PRESERVE_WS_ATTR_NAME; });
}
/**
* Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
* https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
* In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
* and later on replaced by a space. We are re-implementing the same idea here.
*/
export function replaceNgsp(value) {
// lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
}
/**
* This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:
* - consider spaces, tabs and new lines as whitespace characters;
* - drop text nodes consisting of whitespace characters only;
* - for all other text nodes replace consecutive whitespace characters with one space;
* - convert &ngsp; pseudo-entity to a single space;
*
* Removal and trimming of whitespaces have positive performance impact (less code to generate
* while compiling templates, faster view creation). At the same time it can be "destructive"
* in some cases (whitespaces can influence layout). Because of the potential of breaking layout
* this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for
* whitespace removal. The default option for whitespace removal will be revisited in Angular 6
* and might be changed to "on" by default.
*/
var WhitespaceVisitor = /** @class */ (function () {
function WhitespaceVisitor() {
}
WhitespaceVisitor.prototype.visitElement = function (element, context) {
if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
// don't descent into elements where we need to preserve whitespaces
// but still visit all attributes to eliminate one used as a market to preserve WS
return new html.Element(element.name, html.visitAll(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan);
}
return new html.Element(element.name, element.attrs, html.visitAll(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan);
};
WhitespaceVisitor.prototype.visitAttribute = function (attribute, context) {
return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
};
WhitespaceVisitor.prototype.visitText = function (text, context) {
var isNotBlank = text.value.match(NO_WS_REGEXP);
if (isNotBlank) {
return new html.Text(replaceNgsp(text.value).replace(WS_REPLACE_REGEXP, ' '), text.sourceSpan);
}
return null;
};
WhitespaceVisitor.prototype.visitComment = function (comment, context) { return comment; };
WhitespaceVisitor.prototype.visitExpansion = function (expansion, context) { return expansion; };
WhitespaceVisitor.prototype.visitExpansionCase = function (expansionCase, context) { return expansionCase; };
return WhitespaceVisitor;
}());
export { WhitespaceVisitor };
export function removeWhitespaces(htmlAstWithErrors) {
return new ParseTreeResult(html.visitAll(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), htmlAstWithErrors.errors);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHRtbF93aGl0ZXNwYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvbXBpbGVyL3NyYy9tbF9wYXJzZXIvaHRtbF93aGl0ZXNwYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEtBQUssSUFBSSxNQUFNLE9BQU8sQ0FBQztBQUM5QixPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sVUFBVSxDQUFDO0FBQ3pDLE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxRQUFRLENBQUM7QUFFcEMsTUFBTSxDQUFDLElBQU0scUJBQXFCLEdBQUcsdUJBQXVCLENBQUM7QUFFN0QsSUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO0FBRXRGLDhEQUE4RDtBQUM5RCxtR0FBbUc7QUFDbkcsSUFBTSxRQUFRLEdBQUcsMEVBQTBFLENBQUM7QUFDNUYsSUFBTSxZQUFZLEdBQUcsSUFBSSxNQUFNLENBQUMsT0FBSyxRQUFRLE1BQUcsQ0FBQyxDQUFDO0FBQ2xELElBQU0saUJBQWlCLEdBQUcsSUFBSSxNQUFNLENBQUMsTUFBSSxRQUFRLFVBQU8sRUFBRSxHQUFHLENBQUMsQ0FBQztBQUUvRCxvQ0FBb0MsS0FBdUI7SUFDekQsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLFVBQUMsSUFBb0IsSUFBSyxPQUFBLElBQUksQ0FBQyxJQUFJLEtBQUsscUJBQXFCLEVBQW5DLENBQW1DLENBQUMsQ0FBQztBQUNuRixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLHNCQUFzQixLQUFhO0lBQ3ZDLGdFQUFnRTtJQUNoRSxPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxNQUFNLENBQUMsWUFBWSxFQUFFLEdBQUcsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBQzNELENBQUM7QUFFRDs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0g7SUFBQTtJQW1DQSxDQUFDO0lBbENDLHdDQUFZLEdBQVosVUFBYSxPQUFxQixFQUFFLE9BQVk7UUFDOUMsSUFBSSxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLDBCQUEwQixDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtZQUNwRixvRUFBb0U7WUFDcEUsa0ZBQWtGO1lBQ2xGLE9BQU8sSUFBSSxJQUFJLENBQUMsT0FBTyxDQUNuQixPQUFPLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxVQUFVLEVBQ3RGLE9BQU8sQ0FBQyxlQUFlLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1NBQ3JEO1FBRUQsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLENBQ25CLE9BQU8sQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsT0FBTyxDQUFDLFVBQVUsRUFDdEYsT0FBTyxDQUFDLGVBQWUsRUFBRSxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVELDBDQUFjLEdBQWQsVUFBZSxTQUF5QixFQUFFLE9BQVk7UUFDcEQsT0FBTyxTQUFTLENBQUMsSUFBSSxLQUFLLHFCQUFxQixDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUNyRSxDQUFDO0lBRUQscUNBQVMsR0FBVCxVQUFVLElBQWUsRUFBRSxPQUFZO1FBQ3JDLElBQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRWxELElBQUksVUFBVSxFQUFFO1lBQ2QsT0FBTyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQ2hCLFdBQVcsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsT0FBTyxDQUFDLGlCQUFpQixFQUFFLEdBQUcsQ0FBQyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztTQUMvRTtRQUVELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELHdDQUFZLEdBQVosVUFBYSxPQUFxQixFQUFFLE9BQVksSUFBUyxPQUFPLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFFMUUsMENBQWMsR0FBZCxVQUFlLFNBQXlCLEVBQUUsT0FBWSxJQUFTLE9BQU8sU0FBUyxDQUFDLENBQUMsQ0FBQztJQUVsRiw4Q0FBa0IsR0FBbEIsVUFBbUIsYUFBaUMsRUFBRSxPQUFZLElBQVMsT0FBTyxhQUFhLENBQUMsQ0FBQyxDQUFDO0lBQ3BHLHdCQUFDO0FBQUQsQ0FBQyxBQW5DRCxJQW1DQzs7QUFFRCxNQUFNLDRCQUE0QixpQkFBa0M7SUFDbEUsT0FBTyxJQUFJLGVBQWUsQ0FDdEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLGlCQUFpQixFQUFFLEVBQUUsaUJBQWlCLENBQUMsU0FBUyxDQUFDLEVBQ25FLGlCQUFpQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBQ2hDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgR29vZ2xlIEluYy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5cbmltcG9ydCAqIGFzIGh0bWwgZnJvbSAnLi9hc3QnO1xuaW1wb3J0IHtQYXJzZVRyZWVSZXN1bHR9IGZyb20gJy4vcGFyc2VyJztcbmltcG9ydCB7TkdTUF9VTklDT0RFfSBmcm9tICcuL3RhZ3MnO1xuXG5leHBvcnQgY29uc3QgUFJFU0VSVkVfV1NfQVRUUl9OQU1FID0gJ25nUHJlc2VydmVXaGl0ZXNwYWNlcyc7XG5cbmNvbnN0IFNLSVBfV1NfVFJJTV9UQUdTID0gbmV3IFNldChbJ3ByZScsICd0ZW1wbGF0ZScsICd0ZXh0YXJlYScsICdzY3JpcHQnLCAnc3R5bGUnXSk7XG5cbi8vIEVxdWl2YWxlbnQgdG8gXFxzIHdpdGggXFx1MDBhMCAobm9uLWJyZWFraW5nIHNwYWNlKSBleGNsdWRlZC5cbi8vIEJhc2VkIG9uIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL1JlZ0V4cFxuY29uc3QgV1NfQ0hBUlMgPSAnIFxcZlxcblxcclxcdFxcdlxcdTE2ODBcXHUxODBlXFx1MjAwMC1cXHUyMDBhXFx1MjAyOFxcdTIwMjlcXHUyMDJmXFx1MjA1ZlxcdTMwMDBcXHVmZWZmJztcbmNvbnN0IE5PX1dTX1JFR0VYUCA9IG5ldyBSZWdFeHAoYFteJHtXU19DSEFSU31dYCk7XG5jb25zdCBXU19SRVBMQUNFX1JFR0VYUCA9IG5ldyBSZWdFeHAoYFske1dTX0NIQVJTfV17Mix9YCwgJ2cnKTtcblxuZnVuY3Rpb24gaGFzUHJlc2VydmVXaGl0ZXNwYWNlc0F0dHIoYXR0cnM6IGh0bWwuQXR0cmlidXRlW10pOiBib29sZWFuIHtcbiAgcmV0dXJuIGF0dHJzLnNvbWUoKGF0dHI6IGh0bWwuQXR0cmlidXRlKSA9PiBhdHRyLm5hbWUgPT09IFBSRVNFUlZFX1dTX0FUVFJfTkFNRSk7XG59XG5cbi8qKlxuICogQW5ndWxhciBEYXJ0IGludHJvZHVjZWQgJm5nc3A7IGFzIGEgcGxhY2Vob2xkZXIgZm9yIG5vbi1yZW1vdmFibGUgc3BhY2UsIHNlZTpcbiAqIGh0dHBzOi8vZ2l0aHViLmNvbS9kYXJ0LWxhbmcvYW5ndWxhci9ibG9iLzBiYjYxMTM4N2QyOWQ2NWI1YWY3ZjlkMjUxNWFiNTcxZmQzZmJlZTQvX3Rlc3RzL3Rlc3QvY29tcGlsZXIvcHJlc2VydmVfd2hpdGVzcGFjZV90ZXN0LmRhcnQjTDI1LUwzMlxuICogSW4gQW5ndWxhciBEYXJ0ICZuZ3NwOyBpcyBjb252ZXJ0ZWQgdG8gdGhlIDB4RTUwMCBQVUEgKFByaXZhdGUgVXNlIEFyZWFzKSB1bmljb2RlIGNoYXJhY3RlclxuICogYW5kIGxhdGVyIG9uIHJlcGxhY2VkIGJ5IGEgc3BhY2UuIFdlIGFyZSByZS1pbXBsZW1lbnRpbmcgdGhlIHNhbWUgaWRlYSBoZXJlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVwbGFjZU5nc3AodmFsdWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIC8vIGxleGVyIGlzIHJlcGxhY2luZyB0aGUgJm5nc3A7IHBzZXVkby1lbnRpdHkgd2l0aCBOR1NQX1VOSUNPREVcbiAgcmV0dXJuIHZhbHVlLnJlcGxhY2UobmV3IFJlZ0V4cChOR1NQX1VOSUNPREUsICdnJyksICcgJyk7XG59XG5cbi8qKlxuICogVGhpcyB2aXNpdG9yIGNhbiB3YWxrIEhUTUwgcGFyc2UgdHJlZSBhbmQgcmVtb3ZlIC8gdHJpbSB0ZXh0IG5vZGVzIHVzaW5nIHRoZSBmb2xsb3dpbmcgcnVsZXM6XG4gKiAtIGNvbnNpZGVyIHNwYWNlcywgdGFicyBhbmQgbmV3IGxpbmVzIGFzIHdoaXRlc3BhY2UgY2hhcmFjdGVycztcbiAqIC0gZHJvcCB0ZXh0IG5vZGVzIGNvbnNpc3Rpbmcgb2Ygd2hpdGVzcGFjZSBjaGFyYWN0ZXJzIG9ubHk7XG4gKiAtIGZvciBhbGwgb3RoZXIgdGV4dCBub2RlcyByZXBsYWNlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycyB3aXRoIG9uZSBzcGFjZTtcbiAqIC0gY29udmVydCAmbmdzcDsgcHNldWRvLWVudGl0eSB0byBhIHNpbmdsZSBzcGFjZTtcbiAqXG4gKiBSZW1vdmFsIGFuZCB0cmltbWluZyBvZiB3aGl0ZXNwYWNlcyBoYXZlIHBvc2l0aXZlIHBlcmZvcm1hbmNlIGltcGFjdCAobGVzcyBjb2RlIHRvIGdlbmVyYXRlXG4gKiB3aGlsZSBjb21waWxpbmcgdGVtcGxhdGVzLCBmYXN0ZXIgdmlldyBjcmVhdGlvbikuIEF0IHRoZSBzYW1lIHRpbWUgaXQgY2FuIGJlIFwiZGVzdHJ1Y3RpdmVcIlxuICogaW4gc29tZSBjYXNlcyAod2hpdGVzcGFjZXMgY2FuIGluZmx1ZW5jZSBsYXlvdXQpLiBCZWNhdXNlIG9mIHRoZSBwb3RlbnRpYWwgb2YgYnJlYWtpbmcgbGF5b3V0XG4gKiB0aGlzIHZpc2l0b3IgaXMgbm90IGFjdGl2YXRlZCBieSBkZWZhdWx0IGluIEFuZ3VsYXIgNSBhbmQgcGVvcGxlIG5lZWQgdG8gZXhwbGljaXRseSBvcHQtaW4gZm9yXG4gKiB3aGl0ZXNwYWNlIHJlbW92YWwuIFRoZSBkZWZhdWx0IG9wdGlvbiBmb3Igd2hpdGVzcGFjZSByZW1vdmFsIHdpbGwgYmUgcmV2aXNpdGVkIGluIEFuZ3VsYXIgNlxuICogYW5kIG1pZ2h0IGJlIGNoYW5nZWQgdG8gXCJvblwiIGJ5IGRlZmF1bHQuXG4gKi9cbmV4cG9ydCBjbGFzcyBXaGl0ZXNwYWNlVmlzaXRvciBpbXBsZW1lbnRzIGh0bWwuVmlzaXRvciB7XG4gIHZpc2l0RWxlbWVudChlbGVtZW50OiBodG1sLkVsZW1lbnQsIGNvbnRleHQ6IGFueSk6IGFueSB7XG4gICAgaWYgKFNLSVBfV1NfVFJJTV9UQUdTLmhhcyhlbGVtZW50Lm5hbWUpIHx8IGhhc1ByZXNlcnZlV2hpdGVzcGFjZXNBdHRyKGVsZW1lbnQuYXR0cnMpKSB7XG4gICAgICAvLyBkb24ndCBkZXNjZW50IGludG8gZWxlbWVudHMgd2hlcmUgd2UgbmVlZCB0byBwcmVzZXJ2ZSB3aGl0ZXNwYWNlc1xuICAgICAgLy8gYnV0IHN0aWxsIHZpc2l0IGFsbCBhdHRyaWJ1dGVzIHRvIGVsaW1pbmF0ZSBvbmUgdXNlZCBhcyBhIG1hcmtldCB0byBwcmVzZXJ2ZSBXU1xuICAgICAgcmV0dXJuIG5ldyBodG1sLkVsZW1lbnQoXG4gICAgICAgICAgZWxlbWVudC5uYW1lLCBodG1sLnZpc2l0QWxsKHRoaXMsIGVsZW1lbnQuYXR0cnMpLCBlbGVtZW50LmNoaWxkcmVuLCBlbGVtZW50LnNvdXJjZVNwYW4sXG4gICAgICAgICAgZWxlbWVudC5zdGFydFNvdXJjZVNwYW4sIGVsZW1lbnQuZW5kU291cmNlU3Bhbik7XG4gICAgfVxuXG4gICAgcmV0dXJuIG5ldyBodG1sLkVsZW1lbnQoXG4gICAgICAgIGVsZW1lbnQubmFtZSwgZWxlbWVudC5hdHRycywgaHRtbC52aXNpdEFsbCh0aGlzLCBlbGVtZW50LmNoaWxkcmVuKSwgZWxlbWVudC5zb3VyY2VTcGFuLFxuICAgICAgICBlbGVtZW50LnN0YXJ0U291cmNlU3BhbiwgZWxlbWVudC5lbmRTb3VyY2VTcGFuKTtcbiAgfVxuXG4gIHZpc2l0QXR0cmlidXRlKGF0dHJpYnV0ZTogaHRtbC5BdHRyaWJ1dGUsIGNvbnRleHQ6IGFueSk6IGFueSB7XG4gICAgcmV0dXJuIGF0dHJpYnV0ZS5uYW1lICE9PSBQUkVTRVJWRV9XU19BVFRSX05BTUUgPyBhdHRyaWJ1dGUgOiBudWxsO1xuICB9XG5cbiAgdmlzaXRUZXh0KHRleHQ6IGh0bWwuVGV4dCwgY29udGV4dDogYW55KTogYW55IHtcbiAgICBjb25zdCBpc05vdEJsYW5rID0gdGV4dC52YWx1ZS5tYXRjaChOT19XU19SRUdFWFApO1xuXG4gICAgaWYgKGlzTm90QmxhbmspIHtcbiAgICAgIHJldHVybiBuZXcgaHRtbC5UZXh0KFxuICAgICAgICAgIHJlcGxhY2VOZ3NwKHRleHQudmFsdWUpLnJlcGxhY2UoV1NfUkVQTEFDRV9SRUdFWFAsICcgJyksIHRleHQuc291cmNlU3Bhbik7XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICB2aXNpdENvbW1lbnQoY29tbWVudDogaHRtbC5Db21tZW50LCBjb250ZXh0OiBhbnkpOiBhbnkgeyByZXR1cm4gY29tbWVudDsgfVxuXG4gIHZpc2l0RXhwYW5zaW9uKGV4cGFuc2lvbjogaHRtbC5FeHBhbnNpb24sIGNvbnRleHQ6IGFueSk6IGFueSB7IHJldHVybiBleHBhbnNpb247IH1cblxuICB2aXNpdEV4cGFuc2lvbkNhc2UoZXhwYW5zaW9uQ2FzZTogaHRtbC5FeHBhbnNpb25DYXNlLCBjb250ZXh0OiBhbnkpOiBhbnkgeyByZXR1cm4gZXhwYW5zaW9uQ2FzZTsgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVtb3ZlV2hpdGVzcGFjZXMoaHRtbEFzdFdpdGhFcnJvcnM6IFBhcnNlVHJlZVJlc3VsdCk6IFBhcnNlVHJlZVJlc3VsdCB7XG4gIHJldHVybiBuZXcgUGFyc2VUcmVlUmVzdWx0KFxuICAgICAgaHRtbC52aXNpdEFsbChuZXcgV2hpdGVzcGFjZVZpc2l0b3IoKSwgaHRtbEFzdFdpdGhFcnJvcnMucm9vdE5vZGVzKSxcbiAgICAgIGh0bWxBc3RXaXRoRXJyb3JzLmVycm9ycyk7XG59XG4iXX0=