UNPKG

@angular/core

Version:

Angular - the core framework

263 lines • 38.6 kB
/** * @license * Copyright Google LLC 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 { getDocument } from '../render3/interfaces/document'; import { isRootView } from '../render3/interfaces/type_checks'; import { HEADER_OFFSET, TVIEW } from '../render3/interfaces/view'; import { makeStateKey, TransferState } from '../transfer_state'; import { assertDefined } from '../util/assert'; import { CONTAINERS, DISCONNECTED_NODES, ELEMENT_CONTAINERS, MULTIPLIER, NUM_ROOT_NODES } from './interfaces'; /** * The name of the key used in the TransferState collection, * where hydration information is located. */ const TRANSFER_STATE_TOKEN_ID = '__nghData__'; /** * Lookup key used to reference DOM hydration data (ngh) in `TransferState`. */ export const NGH_DATA_KEY = makeStateKey(TRANSFER_STATE_TOKEN_ID); /** * The name of the attribute that would be added to host component * nodes and contain a reference to a particular slot in transferred * state that contains the necessary hydration info for this component. */ export const NGH_ATTR_NAME = 'ngh'; /** * Marker used in a comment node to ensure hydration content integrity */ export const SSR_CONTENT_INTEGRITY_MARKER = 'nghm'; /** * Reference to a function that reads `ngh` attribute value from a given RNode * and retrieves hydration information from the TransferState using that value * as an index. Returns `null` by default, when hydration is not enabled. * * @param rNode Component's host element. * @param injector Injector that this component has access to. * @param isRootView Specifies whether we trying to read hydration info for the root view. */ let _retrieveHydrationInfoImpl = () => null; export function retrieveHydrationInfoImpl(rNode, injector, isRootView = false) { let nghAttrValue = rNode.getAttribute(NGH_ATTR_NAME); if (nghAttrValue == null) return null; // For cases when a root component also acts as an anchor node for a ViewContainerRef // (for example, when ViewContainerRef is injected in a root component), there is a need // to serialize information about the component itself, as well as an LContainer that // represents this ViewContainerRef. Effectively, we need to serialize 2 pieces of info: // (1) hydration info for the root component itself and (2) hydration info for the // ViewContainerRef instance (an LContainer). Each piece of information is included into // the hydration data (in the TransferState object) separately, thus we end up with 2 ids. // Since we only have 1 root element, we encode both bits of info into a single string: // ids are separated by the `|` char (e.g. `10|25`, where `10` is the ngh for a component view // and 25 is the `ngh` for a root view which holds LContainer). const [componentViewNgh, rootViewNgh] = nghAttrValue.split('|'); nghAttrValue = isRootView ? rootViewNgh : componentViewNgh; if (!nghAttrValue) return null; // We've read one of the ngh ids, keep the remaining one, so that // we can set it back on the DOM element. const remainingNgh = isRootView ? componentViewNgh : (rootViewNgh ? `|${rootViewNgh}` : ''); let data = {}; // An element might have an empty `ngh` attribute value (e.g. `<comp ngh="" />`), // which means that no special annotations are required. Do not attempt to read // from the TransferState in this case. if (nghAttrValue !== '') { const transferState = injector.get(TransferState, null, { optional: true }); if (transferState !== null) { const nghData = transferState.get(NGH_DATA_KEY, []); // The nghAttrValue is always a number referencing an index // in the hydration TransferState data. data = nghData[Number(nghAttrValue)]; // If the `ngh` attribute exists and has a non-empty value, // the hydration info *must* be present in the TransferState. // If there is no data for some reasons, this is an error. ngDevMode && assertDefined(data, 'Unable to retrieve hydration info from the TransferState.'); } } const dehydratedView = { data, firstChild: rNode.firstChild ?? null, }; if (isRootView) { // If there is hydration info present for the root view, it means that there was // a ViewContainerRef injected in the root component. The root component host element // acted as an anchor node in this scenario. As a result, the DOM nodes that represent // embedded views in this ViewContainerRef are located as siblings to the host node, // i.e. `<app-root /><#VIEW1><#VIEW2>...<!--container-->`. In this case, the current // node becomes the first child of this root view and the next sibling is the first // element in the DOM segment. dehydratedView.firstChild = rNode; // We use `0` here, since this is the slot (right after the HEADER_OFFSET) // where a component LView or an LContainer is located in a root LView. setSegmentHead(dehydratedView, 0, rNode.nextSibling); } if (remainingNgh) { // If we have only used one of the ngh ids, store the remaining one // back on this RNode. rNode.setAttribute(NGH_ATTR_NAME, remainingNgh); } else { // The `ngh` attribute is cleared from the DOM node now // that the data has been retrieved for all indices. rNode.removeAttribute(NGH_ATTR_NAME); } // Note: don't check whether this node was claimed for hydration, // because this node might've been previously claimed while processing // template instructions. ngDevMode && markRNodeAsClaimedByHydration(rNode, /* checkIfAlreadyClaimed */ false); ngDevMode && ngDevMode.hydratedComponents++; return dehydratedView; } /** * Sets the implementation for the `retrieveHydrationInfo` function. */ export function enableRetrieveHydrationInfoImpl() { _retrieveHydrationInfoImpl = retrieveHydrationInfoImpl; } /** * Retrieves hydration info by reading the value from the `ngh` attribute * and accessing a corresponding slot in TransferState storage. */ export function retrieveHydrationInfo(rNode, injector, isRootView = false) { return _retrieveHydrationInfoImpl(rNode, injector, isRootView); } /** * Retrieves the necessary object from a given ViewRef to serialize: * - an LView for component views * - an LContainer for cases when component acts as a ViewContainerRef anchor * - `null` in case of an embedded view */ export function getLNodeForHydration(viewRef) { // Reading an internal field from `ViewRef` instance. let lView = viewRef._lView; const tView = lView[TVIEW]; // A registered ViewRef might represent an instance of an // embedded view, in which case we do not need to annotate it. if (tView.type === 2 /* TViewType.Embedded */) { return null; } // Check if it's a root view and if so, retrieve component's // LView from the first slot after the header. if (isRootView(lView)) { lView = lView[HEADER_OFFSET]; } return lView; } function getTextNodeContent(node) { return node.textContent?.replace(/\s/gm, ''); } /** * Restores text nodes and separators into the DOM that were lost during SSR * serialization. The hydration process replaces empty text nodes and text * nodes that are immediately adjacent to other text nodes with comment nodes * that this method filters on to restore those missing nodes that the * hydration process is expecting to be present. * * @param node The app's root HTML Element */ export function processTextNodeMarkersBeforeHydration(node) { const doc = getDocument(); const commentNodesIterator = doc.createNodeIterator(node, NodeFilter.SHOW_COMMENT, { acceptNode(node) { const content = getTextNodeContent(node); const isTextNodeMarker = content === "ngetn" /* TextNodeMarker.EmptyNode */ || content === "ngtns" /* TextNodeMarker.Separator */; return isTextNodeMarker ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; } }); let currentNode; // We cannot modify the DOM while using the commentIterator, // because it throws off the iterator state. // So we collect all marker nodes first and then follow up with // applying the changes to the DOM: either inserting an empty node // or just removing the marker if it was used as a separator. const nodes = []; while (currentNode = commentNodesIterator.nextNode()) { nodes.push(currentNode); } for (const node of nodes) { if (node.textContent === "ngetn" /* TextNodeMarker.EmptyNode */) { node.replaceWith(doc.createTextNode('')); } else { node.remove(); } } } /** * Marks a node as "claimed" by hydration process. * This is needed to make assessments in tests whether * the hydration process handled all nodes. */ export function markRNodeAsClaimedByHydration(node, checkIfAlreadyClaimed = true) { if (!ngDevMode) { throw new Error('Calling `markRNodeAsClaimedByHydration` in prod mode ' + 'is not supported and likely a mistake.'); } if (checkIfAlreadyClaimed && isRNodeClaimedForHydration(node)) { throw new Error('Trying to claim a node, which was claimed already.'); } node.__claimed = true; ngDevMode.hydratedNodes++; } export function isRNodeClaimedForHydration(node) { return !!node.__claimed; } export function setSegmentHead(hydrationInfo, index, node) { hydrationInfo.segmentHeads ??= {}; hydrationInfo.segmentHeads[index] = node; } export function getSegmentHead(hydrationInfo, index) { return hydrationInfo.segmentHeads?.[index] ?? null; } /** * Returns the size of an <ng-container>, using either the information * serialized in `ELEMENT_CONTAINERS` (element container size) or by * computing the sum of root nodes in all dehydrated views in a given * container (in case this `<ng-container>` was also used as a view * container host node, e.g. <ng-container *ngIf>). */ export function getNgContainerSize(hydrationInfo, index) { const data = hydrationInfo.data; let size = data[ELEMENT_CONTAINERS]?.[index] ?? null; // If there is no serialized information available in the `ELEMENT_CONTAINERS` slot, // check if we have info about view containers at this location (e.g. // `<ng-container *ngIf>`) and use container size as a number of root nodes in this // element container. if (size === null && data[CONTAINERS]?.[index]) { size = calcSerializedContainerSize(hydrationInfo, index); } return size; } export function getSerializedContainerViews(hydrationInfo, index) { return hydrationInfo.data[CONTAINERS]?.[index] ?? null; } /** * Computes the size of a serialized container (the number of root nodes) * by calculating the sum of root nodes in all dehydrated views in this container. */ export function calcSerializedContainerSize(hydrationInfo, index) { const views = getSerializedContainerViews(hydrationInfo, index) ?? []; let numNodes = 0; for (let view of views) { numNodes += view[NUM_ROOT_NODES] * (view[MULTIPLIER] ?? 1); } return numNodes; } /** * Checks whether a node is annotated as "disconnected", i.e. not present * in the DOM at serialization time. We should not attempt hydration for * such nodes and instead, use a regular "creation mode". */ export function isDisconnectedNode(hydrationInfo, index) { // Check if we are processing disconnected info for the first time. if (typeof hydrationInfo.disconnectedNodes === 'undefined') { const nodeIds = hydrationInfo.data[DISCONNECTED_NODES]; hydrationInfo.disconnectedNodes = nodeIds ? (new Set(nodeIds)) : null; } return !!hydrationInfo.disconnectedNodes?.has(index); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb3JlL3NyYy9oeWRyYXRpb24vdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0E7Ozs7OztHQU1HO0FBS0gsT0FBTyxFQUFDLFdBQVcsRUFBQyxNQUFNLGdDQUFnQyxDQUFDO0FBRTNELE9BQU8sRUFBQyxVQUFVLEVBQUMsTUFBTSxtQ0FBbUMsQ0FBQztBQUM3RCxPQUFPLEVBQUMsYUFBYSxFQUFTLEtBQUssRUFBWSxNQUFNLDRCQUE0QixDQUFDO0FBQ2xGLE9BQU8sRUFBQyxZQUFZLEVBQUUsYUFBYSxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDOUQsT0FBTyxFQUFDLGFBQWEsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBRTdDLE9BQU8sRUFBQyxVQUFVLEVBQWtCLGtCQUFrQixFQUFFLGtCQUFrQixFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQTBDLE1BQU0sY0FBYyxDQUFDO0FBRXJLOzs7R0FHRztBQUNILE1BQU0sdUJBQXVCLEdBQUcsYUFBYSxDQUFDO0FBRTlDOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFHLFlBQVksQ0FBd0IsdUJBQXVCLENBQUMsQ0FBQztBQUV6Rjs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQztBQUVuQzs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLDRCQUE0QixHQUFHLE1BQU0sQ0FBQztBQXVCbkQ7Ozs7Ozs7O0dBUUc7QUFDSCxJQUFJLDBCQUEwQixHQUFxQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUM7QUFFOUUsTUFBTSxVQUFVLHlCQUF5QixDQUNyQyxLQUFlLEVBQUUsUUFBa0IsRUFBRSxVQUFVLEdBQUcsS0FBSztJQUN6RCxJQUFJLFlBQVksR0FBRyxLQUFLLENBQUMsWUFBWSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3JELElBQUks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LElBQUksQ0FBQztBQUN6RCxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLDJCQUEyQixDQUFDLGFBQTZCLEVBQUUsS0FBYTtJQUN0RixNQUFNLEtBQUssR0FBRywyQkFBMkIsQ0FBQyxhQUFhLEVBQUUsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3RFLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztJQUNqQixLQUFLLElBQUksSUFBSSxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQ3ZCLFFBQVEsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDN0QsQ0FBQztJQUNELE9BQU8sUUFBUSxDQUFDO0FBQ2xCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUFDLGFBQTZCLEVBQUUsS0FBYTtJQUM3RSxtRUFBbUU7SUFDbkUsSUFBSSxPQUFPLGFBQWEsQ0FBQyxpQkFBaUIsS0FBSyxXQUFXLEVBQUUsQ0FBQztRQUMzRCxNQUFNLE9BQU8sR0FBRyxhQUFhLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFDdkQsYUFBYSxDQUFDLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDeEUsQ0FBQztJQUNELE9BQU8sQ0FBQyxDQUFDLGFBQWEsQ0FBQyxpQkFBaUIsRUFBRSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDdkQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIlxuLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBMTEMgQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5cbmltcG9ydCB7SW5qZWN0b3J9IGZyb20gJy4uL2RpL2luamVjdG9yJztcbmltcG9ydCB7Vmlld1JlZn0gZnJvbSAnLi4vbGlua2VyL3ZpZXdfcmVmJztcbmltcG9ydCB7TENvbnRhaW5lcn0gZnJvbSAnLi4vcmVuZGVyMy9pbnRlcmZhY2VzL2NvbnRhaW5lcic7XG5pbXBvcnQge2dldERvY3VtZW50fSBmcm9tICcuLi9yZW5kZXIzL2ludGVyZmFjZXMvZG9jdW1lbnQnO1xuaW1wb3J0IHtSRWxlbWVudCwgUk5vZGV9IGZyb20gJy4uL3JlbmRlcjMvaW50ZXJmYWNlcy9yZW5kZXJlcl9kb20nO1xuaW1wb3J0IHtpc1Jvb3RWaWV3fSBmcm9tICcuLi9yZW5kZXIzL2ludGVyZmFjZXMvdHlwZV9jaGVja3MnO1xuaW1wb3J0IHtIRUFERVJfT0ZGU0VULCBMVmlldywgVFZJRVcsIFRWaWV3VHlwZX0gZnJvbSAnLi4vcmVuZGVyMy9pbnRlcmZhY2VzL3ZpZXcnO1xuaW1wb3J0IHttYWtlU3RhdGVLZXksIFRyYW5zZmVyU3RhdGV9IGZyb20gJy4uL3RyYW5zZmVyX3N0YXRlJztcbmltcG9ydCB7YXNzZXJ0RGVmaW5lZH0gZnJvbSAnLi4vdXRpbC9hc3NlcnQnO1xuXG5pbXBvcnQge0NPTlRBSU5FUlMsIERlaHlkcmF0ZWRWaWV3LCBESVNDT05ORUNURURfTk9ERVMsIEVMRU1FTlRfQ09OVEFJTkVSUywgTVVMVElQTElFUiwgTlVNX1JPT1RfTk9ERVMsIFNlcmlhbGl6ZWRDb250YWluZXJWaWV3LCBTZXJpYWxpemVkVmlld30gZnJvbSAnLi9pbnRlcmZhY2VzJztcblxuLyoqXG4gKiBUaGUgbmFtZSBvZiB0aGUga2V5IHVzZWQgaW4gdGhlIFRyYW5zZmVyU3RhdGUgY29sbGVjdGlvbixcbiAqIHdoZXJlIGh5ZHJhdGlvbiBpbmZvcm1hdGlvbiBpcyBsb2NhdGVkLlxuICovXG5jb25zdCBUUkFOU0ZFUl9TVEFURV9UT0tFTl9JRCA9ICdfX25naERhdGFfXyc7XG5cbi8qKlxuICogTG9va3VwIGtleSB1c2VkIHRvIHJlZmVyZW5jZSBET00gaHlkcmF0aW9uIGRhdGEgKG5naCkgaW4gYFRyYW5zZmVyU3RhdGVgLlxuICovXG5leHBvcnQgY29uc3QgTkdIX0RBVEFfS0VZID0gbWFrZVN0YXRlS2V5PEFycmF5PFNlcmlhbGl6ZWRWaWV3Pj4oVFJBTlNGRVJfU1RBVEVfVE9LRU5fSUQpO1xuXG4vKipcbiAqIFRoZSBuYW1lIG9mIHRoZSBhdHRyaWJ1dGUgdGhhdCB3b3VsZCBiZSBhZGRlZCB0byBob3N0IGNvbXBvbmVudFxuICogbm9kZXMgYW5kIGNvbnRhaW4gYSByZWZlcmVuY2UgdG8gYSBwYXJ0aWN1bGFyIHNsb3QgaW4gdHJhbnNmZXJyZWRcbiAqIHN0YXRlIHRoYXQgY29udGFpbnMgdGhlIG5lY2Vzc2FyeSBoeWRyYXRpb24gaW5mbyBmb3IgdGhpcyBjb21wb25lbnQuXG4gKi9cbmV4cG9ydCBjb25zdCBOR0hfQVRUUl9OQU1FID0gJ25naCc7XG5cbi8qKlxuICogTWFya2VyIHVzZWQgaW4gYSBjb21tZW50IG5vZGUgdG8gZW5zdXJlIGh5ZHJhdGlvbiBjb250ZW50IGludGVncml0eVxuICovXG5leHBvcnQgY29uc3QgU1NSX0NPTlRFTlRfSU5URUdSSVRZX01BUktFUiA9ICduZ2htJztcblxuZXhwb3J0IGNvbnN0IGVudW0gVGV4dE5vZGVNYXJrZXIge1xuXG4gIC8qKlxuICAgKiBUaGUgY29udGVudHMgb2YgdGhlIHRleHQgY29tbWVudCBhZGRlZCB0byBub2RlcyB0aGF0IHdvdWxkIG90aGVyd2lzZSBiZVxuICAgKiBlbXB0eSB3aGVuIHNlcmlhbGl6ZWQgYnkgdGhlIHNlcnZlciBhbmQgcGFzc2VkIHRvIHRoZSBjbGllbnQuIFRoZSBlbXB0eVxuICAgKiBub2RlIGlzIGxvc3Qgd2hlbiB0aGUgYnJvd3NlciBwYXJzZXMgaXQgb3RoZXJ3aXNlLiBUaGlzIGNvbW1lbnQgbm9kZSB3aWxsXG4gICAqIGJlIHJlcGxhY2VkIGR1cmluZyBoeWRyYXRpb24gaW4gdGhlIGNsaWVudCB0byByZXN0b3JlIHRoZSBsb3N0IGVtcHR5IHRleHRcbiAgICogbm9kZS5cbiAgICovXG4gIEVtcHR5Tm9kZSA9ICduZ2V0bicsXG5cbiAgLyoqXG4gICAqIFRoZSBjb250ZW50cyBvZiB0aGUgdGV4dCBjb21tZW50IGFkZGVkIGluIHRoZSBjYXNlIG9mIGFkamFjZW50IHRleHQgbm9kZXMuXG4gICAqIFdoZW4gYWRqYWNlbnQgdGV4dCBub2RlcyBhcmUgc2VyaWFsaXplZCBieSB0aGUgc2VydmVyIGFuZCBzZW50IHRvIHRoZVxuICAgKiBjbGllbnQsIHRoZSBicm93c2VyIGxvc2VzIHJlZmVyZW5jZSB0byB0aGUgYW1vdW50IG9mIG5vZGVzIGFuZCBhc3N1bWVzXG4gICAqIGp1c3Qgb25lIHRleHQgbm9kZS4gVGhpcyBzZXBhcmF0b3IgaXMgcmVwbGFjZWQgZHVyaW5nIGh5ZHJhdGlvbiB0byByZXN0b3JlXG4gICAqIHRoZSBwcm9wZXIgc2VwYXJhdGlvbiBhbmQgYW1vdW50IG9mIHRleHQgbm9kZXMgdGhhdCBzaG91bGQgYmUgcHJlc2VudC5cbiAgICovXG4gIFNlcGFyYXRvciA9ICduZ3RucycsXG59XG5cbi8qKlxuICogUmVmZXJlbmNlIHRvIGEgZnVuY3Rpb24gdGhhdCByZWFkcyBgbmdoYCBhdHRyaWJ1dGUgdmFsdWUgZnJvbSBhIGdpdmVuIFJOb2RlXG4gKiBhbmQgcmV0cmlldmVzIGh5ZHJhdGlvbiBpbmZvcm1hdGlvbiBmcm9tIHRoZSBUcmFuc2ZlclN0YXRlIHVzaW5nIHRoYXQgdmFsdWVcbiAqIGFzIGFuIGluZGV4LiBSZXR1cm5zIGBudWxsYCBieSBkZWZhdWx0LCB3aGVuIGh5ZHJhdGlvbiBpcyBub3QgZW5hYmxlZC5cbiAqXG4gKiBAcGFyYW0gck5vZGUgQ29tcG9uZW50J3MgaG9zdCBlbGVtZW50LlxuICogQHBhcmFtIGluamVjdG9yIEluamVjdG9yIHRoYXQgdGhpcyBjb21wb25lbnQgaGFzIGFjY2VzcyB0by5cbiAqIEBwYXJhbSBpc1Jvb3RWaWV3IFNwZWNpZmllcyB3aGV0aGVyIHdlIHRyeWluZyB0byByZWFkIGh5ZHJhdGlvbiBpbmZvIGZvciB0aGUgcm9vdCB2aWV3LlxuICovXG5sZXQgX3JldHJpZXZlSHlkcmF0aW9uSW5mb0ltcGw6IHR5cGVvZiByZXRyaWV2ZUh5ZHJhdGlvbkluZm9JbXBsID0gKCkgPT4gbnVsbDtcblxuZXhwb3J0IGZ1bmN0aW9uIHJldHJpZXZlSHlkcmF0aW9uSW5mb0ltcGwoXG4gICAgck5vZGU6IFJFbGVtZW50LCBpbmplY3RvcjogSW5qZWN0b3IsIGlzUm9vdFZpZXcgPSBmYWxzZSk6IERlaHlkcmF0ZWRWaWV3fG51bGwge1xuICBsZXQgbmdoQXR0clZhbHVlID0gck5vZGUuZ2V0QXR0cmlidXRlKE5HSF9BVFRSX05BTUUpO1xuICBpZiAobmdoQXR0clZhbHVlID09IG51bGwpIHJldHVybiBudWxsO1xuXG4gIC8vIEZvciBjYXNlcyB3aGVuIGEgcm9vdCBjb21wb25lbnQgYWxzbyBhY3RzIGFzIGFuIGFuY2hvciBub2RlIGZvciBhIFZpZXdDb250YWluZXJSZWZcbiAgLy8gKGZvciBleGFtcGxlLCB3aGVuIFZpZXdDb250YWluZXJSZWYgaXMgaW5qZWN0ZWQgaW4gYSByb290IGNvbXBvbmVudCksIHRoZXJlIGlzIGEgbmVlZFxuICAvLyB0byBzZXJpYWxpemUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGNvbXBvbmVudCBpdHNlbGYsIGFzIHdlbGwgYXMgYW4gTENvbnRhaW5lciB0aGF0XG4gIC8vIHJlcHJlc2VudHMgdGhpcyBWaWV3Q29udGFpbmVyUmVmLiBFZmZlY3RpdmVseSwgd2UgbmVlZCB0byBzZXJpYWxpemUgMiBwaWVjZXMgb2YgaW5mbzpcbiAgLy8gKDEpIGh5ZHJhdGlvbiBpbmZvIGZvciB0aGUgcm9vdCBjb21wb25lbnQgaXRzZWxmIGFuZCAoMikgaHlkcmF0aW9uIGluZm8gZm9yIHRoZVxuICAvLyBWaWV3Q29udGFpbmVyUmVmIGluc3RhbmNlIChhbiBMQ29udGFpbmVyKS4gRWFjaCBwaWVjZSBvZiBpbmZvcm1hdGlvbiBpcyBpbmNsdWRlZCBpbnRvXG4gIC8vIHRoZSBoeWRyYXRpb24gZGF0YSAoaW4gdGhlIFRyYW5zZmVyU3RhdGUgb2JqZWN0KSBzZXBhcmF0ZWx5LCB0aHVzIHdlIGVuZCB1cCB3aXRoIDIgaWRzLlxuICAvLyBTaW5jZSB3ZSBvbmx5IGhhdmUgMSByb290IGVsZW1lbnQsIHdlIGVuY29kZSBib3RoIGJpdHMgb2YgaW5mbyBpbnRvIGEgc2luZ2xlIHN0cmluZzpcbiAgLy8gaWRzIGFyZSBzZXBhcmF0ZWQgYnkgdGhlIGB8YCBjaGFyIChlLmcuIGAxMHwyNWAsIHdoZXJlIGAxMGAgaXMgdGhlIG5naCBmb3IgYSBjb21wb25lbnQgdmlld1xuICAvLyBhbmQgMjUgaXMgdGhlIGBuZ2hgIGZvciBhIHJvb3QgdmlldyB3aGljaCBob2xkcyBMQ29udGFpbmVyKS5cbiAgY29uc3QgW2NvbXBvbmVudFZpZXdOZ2gsIHJvb3RWaWV3TmdoXSA9IG5naEF0dHJWYWx1ZS5zcGxpdCgnfCcpO1xuICBuZ2hBdHRyVmFsdWUgPSBpc1Jvb3RWaWV3ID8gcm9vdFZpZXdOZ2ggOiBjb21wb25lbnRWaWV3TmdoO1xuICBpZiAoIW5naEF0dHJWYWx1ZSkgcmV0dXJuIG51bGw7XG5cbiAgLy8gV2UndmUgcmVhZCBvbmUgb2YgdGhlIG5naCBpZHMsIGtlZXAgdGhlIHJlbWFpbmluZyBvbmUsIHNvIHRoYXRcbiAgLy8gd2UgY2FuIHNldCBpdCBiYWNrIG9uIHRoZSBET00gZWxlbWVudC5cbiAgY29uc3QgcmVtYWluaW5nTmdoID0gaXNSb290VmlldyA/IGNvbXBvbmVudFZpZXdOZ2ggOiAocm9vdFZpZXdOZ2ggPyBgfCR7cm9vdFZpZXdOZ2h9YCA6ICcnKTtcblxuICBsZXQgZGF0YTogU2VyaWFsaXplZFZpZXcgPSB7fTtcbiAgLy8gQW4gZWxlbWVudCBtaWdodCBoYXZlIGFuIGVtcHR5IGBuZ2hgIGF0dHJpYnV0ZSB2YWx1ZSAoZS5nLiBgPGNvbXAgbmdoPVwiXCIgLz5gKSxcbiAgLy8gd2hpY2ggbWVhbnMgdGhhdCBubyBzcGVjaWFsIGFubm90YXRpb25zIGFyZSByZXF1aXJlZC4gRG8gbm90IGF0dGVtcHQgdG8gcmVhZFxuICAvLyBmcm9tIHRoZSBUcmFuc2ZlclN0YXRlIGluIHRoaXMgY2FzZS5cbiAgaWYgKG5naEF0dHJWYWx1ZSAhPT0gJycpIHtcbiAgICBjb25zdCB0cmFuc2ZlclN0YXRlID0gaW5qZWN0b3IuZ2V0KFRyYW5zZmVyU3RhdGUsIG51bGwsIHtvcHRpb25hbDogdHJ1ZX0pO1xuICAgIGlmICh0cmFuc2ZlclN0YXRlICE9PSBudWxsKSB7XG4gICAgICBjb25zdCBuZ2hEYXRhID0gdHJhbnNmZXJTdGF0ZS5nZXQoTkdIX0RBVEFfS0VZLCBbXSk7XG5cbiAgICAgIC8vIFRoZSBuZ2hBdHRyVmFsdWUgaXMgYWx3YXlzIGEgbnVtYmVyIHJlZmVyZW5jaW5nIGFuIGluZGV4XG4gICAgICAvLyBpbiB0aGUgaHlkcmF0aW9uIFRyYW5zZmVyU3RhdGUgZGF0YS5cbiAgICAgIGRhdGEgPSBuZ2hEYXRhW051bWJlcihuZ2hBdHRyVmFsdWUpXTtcblxuICAgICAgLy8gSWYgdGhlIGBuZ2hgIGF0dHJpYnV0ZSBleGlzdHMgYW5kIGhhcyBhIG5vbi1lbXB0eSB2YWx1ZSxcbiAgICAgIC8vIHRoZSBoeWRyYXRpb24gaW5mbyAqbXVzdCogYmUgcHJlc2VudCBpbiB0aGUgVHJhbnNmZXJTdGF0ZS5cbiAgICAgIC8vIElmIHRoZXJlIGlzIG5vIGRhdGEgZm9yIHNvbWUgcmVhc29ucywgdGhpcyBpcyBhbiBlcnJvci5cbiAgICAgIG5nRGV2TW9kZSAmJiBhc3NlcnREZWZpbmVkKGRhdGEsICdVbmFibGUgdG8gcmV0cmlldmUgaHlkcmF0aW9uIGluZm8gZnJvbSB0aGUgVHJhbnNmZXJTdGF0ZS4nKTtcbiAgICB9XG4gIH1cbiAgY29uc3QgZGVoeWRyYXRlZFZpZXc6IERlaHlkcmF0ZWRWaWV3ID0ge1xuICAgIGRhdGEsXG4gICAgZmlyc3RDaGlsZDogck5vZGUuZmlyc3RDaGlsZCA/PyBudWxsLFxuICB9O1xuXG4gIGlmIChpc1Jvb3RWaWV3KSB7XG4gICAgLy8gSWYgdGhlcmUgaXMgaHlkcmF0aW9uIGluZm8gcHJlc2VudCBmb3IgdGhlIHJvb3QgdmlldywgaXQgbWVhbnMgdGhhdCB0aGVyZSB3YXNcbiAgICAvLyBhIFZpZXdDb250YWluZXJSZWYgaW5qZWN0ZWQgaW4gdGhlIHJvb3QgY29tcG9uZW50LiBUaGUgcm9vdCBjb21wb25lbnQgaG9zdCBlbGVtZW50XG4gICAgLy8gYWN0ZWQgYXMgYW4gYW5jaG9yIG5vZGUgaW4gdGhpcyBzY2VuYXJpby4gQXMgYSByZXN1bHQsIHRoZSBET00gbm9kZXMgdGhhdCByZXByZXNlbnRcbiAgICAvLyBlbWJlZGRlZCB2aWV3cyBpbiB0aGlzIFZpZXdDb250YWluZXJSZWYgYXJlIGxvY2F0ZWQgYXMgc2libGluZ3MgdG8gdGhlIGhvc3Qgbm9kZSxcbiAgICAvLyBpLmUuIGA8YXBwLXJvb3QgLz48I1ZJRVcxPjwjVklFVzI+Li4uPCEtLWNvbnRhaW5lci0tPmAuIEluIHRoaXMgY2FzZSwgdGhlIGN1cnJlbnRcbiAgICAvLyBub2RlIGJlY29tZXMgdGhlIGZpcnN0IGNoaWxkIG9mIHRoaXMgcm9vdCB2aWV3IGFuZCB0aGUgbmV4dCBzaWJsaW5nIGlzIHRoZSBmaXJzdFxuICAgIC8vIGVsZW1lbnQgaW4gdGhlIERPTSBzZWdtZW50LlxuICAgIGRlaHlkcmF0ZWRWaWV3LmZpcnN0Q2hpbGQgPSByTm9kZTtcblxuICAgIC8vIFdlIHVzZSBgMGAgaGVyZSwgc2luY2UgdGhpcyBpcyB0aGUgc2xvdCAocmlnaHQgYWZ0ZXIgdGhlIEhFQURFUl9PRkZTRVQpXG4gICAgLy8gd2hlcmUgYSBjb21wb25lbnQgTFZpZXcgb3IgYW4gTENvbnRhaW5lciBpcyBsb2NhdGVkIGluIGEgcm9vdCBMVmlldy5cbiAgICBzZXRTZWdtZW50SGVhZChkZWh5ZHJhdGVkVmlldywgMCwgck5vZGUubmV4dFNpYmxpbmcpO1xuICB9XG5cbiAgaWYgKHJlbWFpbmluZ05naCkge1xuICAgIC8vIElmIHdlIGhhdmUgb25seSB1c2VkIG9uZSBvZiB0aGUgbmdoIGlkcywgc3RvcmUgdGhlIHJlbWFpbmluZyBvbmVcbiAgICAvLyBiYWNrIG9uIHRoaXMgUk5vZGUuXG4gICAgck5vZGUuc2V0QXR0cmlidXRlKE5HSF9BVFRSX05BTUUsIHJlbWFpbmluZ05naCk7XG4gIH0gZWxzZSB7XG4gICAgLy8gVGhlIGBuZ2hgIGF0dHJpYnV0ZSBpcyBjbGVhcmVkIGZyb20gdGhlIERPTSBub2RlIG5vd1xuICAgIC8vIHRoYXQgdGhlIGRhdGEgaGFzIGJlZW4gcmV0cmlldmVkIGZvciBhbGwgaW5kaWNlcy5cbiAgICByTm9kZS5yZW1vdmVBdHRyaWJ1dGUoTkdIX0FUVFJfTkFNRSk7XG4gIH1cblxuICAvLyBOb3RlOiBkb24ndCBjaGVjayB3aGV0aGVyIHRoaXMgbm9kZSB3YXMgY2xhaW1lZCBmb3IgaHlkcmF0aW9uLFxuICAvLyBiZWNhdXNlIHRoaXMgbm9kZSBtaWdodCd2ZSBiZWVuIHByZXZpb3VzbHkgY2xhaW1lZCB3aGlsZSBwcm9jZXNzaW5nXG4gIC8vIHRlbXBsYXRlIGluc3RydWN0aW9ucy5cbiAgbmdEZXZNb2RlICYmIG1hcmtSTm9kZUFzQ2xhaW1lZEJ5SHlkcmF0aW9uKHJOb2RlLCAvKiBjaGVja0lmQWxyZWFkeUNsYWltZWQgKi8gZmFsc2UpO1xuICBuZ0Rldk1vZGUgJiYgbmdEZXZNb2RlLmh5ZHJhdGVkQ29tcG9uZW50cysrO1xuXG4gIHJldHVybiBkZWh5ZHJhdGVkVmlldztcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSBpbXBsZW1lbnRhdGlvbiBmb3IgdGhlIGByZXRyaWV2ZUh5ZHJhdGlvbkluZm9gIGZ1bmN0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmV0cmlldmVIeWRyYXRpb25JbmZvSW1wbCgpIHtcbiAgX3JldHJpZXZlSHlkcmF0aW9uSW5mb0ltcGwgPSByZXRyaWV2ZUh5ZHJhdGlvbkluZm9JbXBsO1xufVxuXG4vKipcbiAqIFJldHJpZXZlcyBoeWRyYXRpb24gaW5mbyBieSByZWFkaW5nIHRoZSB2YWx1ZSBmcm9tIHRoZSBgbmdoYCBhdHRyaWJ1dGVcbiAqIGFuZCBhY2Nlc3NpbmcgYSBjb3JyZXNwb25kaW5nIHNsb3QgaW4gVHJhbnNmZXJTdGF0ZSBzdG9yYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmV0cmlldmVIeWRyYXRpb25JbmZvKFxuICAgIHJOb2RlOiBSRWxlbWVudCwgaW5qZWN0b3I6IEluamVjdG9yLCBpc1Jvb3RWaWV3ID0gZmFsc2UpOiBEZWh5ZHJhdGVkVmlld3xudWxsIHtcbiAgcmV0dXJuIF9yZXRyaWV2ZUh5ZHJhdGlvbkluZm9JbXBsKHJOb2RlLCBpbmplY3RvciwgaXNSb290Vmlldyk7XG59XG5cbi8qKlxuICogUmV0cmlldmVzIHRoZSBuZWNlc3Nhcnkgb2JqZWN0IGZyb20gYSBnaXZlbiBWaWV3UmVmIHRvIHNlcmlhbGl6ZTpcbiAqICAtIGFuIExWaWV3IGZvciBjb21wb25lbnQgdmlld3NcbiAqICAtIGFuIExDb250YWluZXIgZm9yIGNhc2VzIHdoZW4gY29tcG9uZW50IGFjdHMgYXMgYSBWaWV3Q29udGFpbmVyUmVmIGFuY2hvclxuICogIC0gYG51bGxgIGluIGNhc2Ugb2YgYW4gZW1iZWRkZWQgdmlld1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TE5vZGVGb3JIeWRyYXRpb24odmlld1JlZjogVmlld1JlZik6IExWaWV3fExDb250YWluZXJ8bnVsbCB7XG4gIC8vIFJlYWRpbmcgYW4gaW50ZXJuYWwgZmllbGQgZnJvbSBgVmlld1JlZmAgaW5zdGFuY2UuXG4gIGxldCBsVmlldyA9ICh2aWV3UmVmIGFzIGFueSkuX2xWaWV3IGFzIExWaWV3O1xuICBjb25zdCB0VmlldyA9IGxWaWV3W1RWSUVXXTtcbiAgLy8gQSByZWdpc3RlcmVkIFZpZXdSZWYgbWlnaHQgcmVwcmVzZW50IGFuIGluc3RhbmNlIG9mIGFuXG4gIC8vIGVtYmVkZGVkIHZpZXcsIGluIHdoaWNoIGNhc2Ugd2UgZG8gbm90IG5lZWQgdG8gYW5ub3RhdGUgaXQuXG4gIGlmICh0Vmlldy50eXBlID09PSBUVmlld1R5cGUuRW1iZWRkZWQpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICAvLyBDaGVjayBpZiBpdCdzIGEgcm9vdCB2aWV3IGFuZCBpZiBzbywgcmV0cmlldmUgY29tcG9uZW50J3NcbiAgLy8gTFZpZXcgZnJvbSB0aGUgZmlyc3Qgc2xvdCBhZnRlciB0aGUgaGVhZGVyLlxuICBpZiAoaXNSb290VmlldyhsVmlldykpIHtcbiAgICBsVmlldyA9IGxWaWV3W0hFQURFUl9PRkZTRVRdO1xuICB9XG5cbiAgcmV0dXJuIGxWaWV3O1xufVxuXG5mdW5jdGlvbiBnZXRUZXh0Tm9kZUNvbnRlbnQobm9kZTogTm9kZSk6IHN0cmluZ3x1bmRlZmluZWQge1xuICByZXR1cm4gbm9kZS50ZXh0Q29udGVudD8ucmVwbGFjZSgvXFxzL2dtLCAnJyk7XG59XG5cbi8qKlxuICogUmVzdG9yZXMgdGV4dCBub2RlcyBhbmQgc2VwYXJhdG9ycyBpbnRvIHRoZSBET00gdGhhdCB3ZXJlIGxvc3QgZHVyaW5nIFNTUlxuICogc2VyaWFsaXphdGlvbi4gVGhlIGh5ZHJhdGlvbiBwcm9jZXNzIHJlcGxhY2VzIGVtcHR5IHRleHQgbm9kZXMgYW5kIHRleHRcbiAqIG5vZGVzIHRoYXQgYXJlIGltbWVkaWF0ZWx5IGFkamFjZW50IHRvIG90aGVyIHRleHQgbm9kZXMgd2l0aCBjb21tZW50IG5vZGVzXG4gKiB0aGF0IHRoaXMgbWV0aG9kIGZpbHRlcnMgb24gdG8gcmVzdG9yZSB0aG9zZSBtaXNzaW5nIG5vZGVzIHRoYXQgdGhlXG4gKiBoeWRyYXRpb24gcHJvY2VzcyBpcyBleHBlY3RpbmcgdG8gYmUgcHJlc2VudC5cbiAqXG4gKiBAcGFyYW0gbm9kZSBUaGUgYXBwJ3Mgcm9vdCBIVE1MIEVsZW1lbnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByb2Nlc3NUZXh0Tm9kZU1hcmtlcnNCZWZvcmVIeWRyYXRpb24obm9kZTogSFRNTEVsZW1lbnQpIHtcbiAgY29uc3QgZG9jID0gZ2V0RG9jdW1lbnQoKTtcbiAgY29uc3QgY29tbWVudE5vZGVzSXRlcmF0b3IgPSBkb2MuY3JlYXRlTm9kZUl0ZXJhdG9yKG5vZGUsIE5vZGVGaWx0ZXIuU0hPV19DT01NRU5ULCB7XG4gICAgYWNjZXB0Tm9kZShub2RlKSB7XG4gICAgICBjb25zdCBjb250ZW50ID0gZ2V0VGV4dE5vZGVDb250ZW50KG5vZGUpO1xuICAgICAgY29uc3QgaXNUZXh0Tm9kZU1hcmtlciA9XG4gICAgICAgICAgY29udGVudCA9PT0gVGV4dE5vZGVNYXJrZXIuRW1wdHlOb2RlIHx8IGNvbnRlbnQgPT09IFRleHROb2RlTWFya2VyLlNlcGFyYXRvcjtcbiAgICAgIHJldHVybiBpc1RleHROb2RlTWFya2VyID8gTm9kZUZpbHRlci5GSUxURVJfQUNDRVBUIDogTm9kZUZpbHRlci5GSUxURVJfUkVKRUNUO1xuICAgIH1cbiAgfSk7XG4gIGxldCBjdXJyZW50Tm9kZTogQ29tbWVudDtcbiAgLy8gV2UgY2Fubm90IG1vZGlmeSB0aGUgRE9NIHdoaWxlIHVzaW5nIHRoZSBjb21tZW50SXRlcmF0b3IsXG4gIC8vIGJlY2F1c2UgaXQgdGhyb3dzIG9mZiB0aGUgaXRlcmF0b3Igc3RhdGUuXG4gIC8vIFNvIHdlIGNvbGxlY3QgYWxsIG1hcmtlciBub2RlcyBmaXJzdCBhbmQgdGhlbiBmb2xsb3cgdXAgd2l0aFxuICAvLyBhcHBseWluZyB0aGUgY2hhbmdlcyB0byB0aGUgRE9NOiBlaXRoZXIgaW5zZXJ0aW5nIGFuIGVtcHR5IG5vZGVcbiAgLy8gb3IganVzdCByZW1vdmluZyB0aGUgbWFya2VyIGlmIGl0IHdhcyB1c2VkIGFzIGEgc2VwYXJhdG9yLlxuICBjb25zdCBub2RlcyA9IFtdO1xuICB3aGlsZSAoY3VycmVudE5vZGUgPSBjb21tZW50Tm9kZXNJdGVyYXRvci5uZXh0Tm9kZSgpIGFzIENvbW1lbnQpIHtcbiAgICBub2Rlcy5wdXNoKGN1cnJlbnROb2RlKTtcbiAgfVxuICBmb3IgKGNvbnN0IG5vZGUgb2Ygbm9kZXMpIHtcbiAgICBpZiAobm9kZS50ZXh0Q29udGVudCA9PT0gVGV4dE5vZGVNYXJrZXIuRW1wdHlOb2RlKSB7XG4gICAgICBub2RlLnJlcGxhY2VXaXRoKGRvYy5jcmVhdGVUZXh0Tm9kZSgnJykpO1xuICAgIH0gZWxzZSB7XG4gICAgICBub2RlLnJlbW92ZSgpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEludGVybmFsIHR5cGUgdGhhdCByZXByZXNlbnRzIGEgY2xhaW1lZCBub2RlLlxuICogT25seSB1c2VkIGluIGRldiBtb2RlLlxuICovXG50eXBlIENsYWltZWROb2RlID0ge1xuICBfX2NsYWltZWQ/OiBib29sZWFuO1xufTtcblxuLyoqXG4gKiBNYXJrcyBhIG5vZGUgYXMgXCJjbGFpbWVkXCIgYnkgaHlkcmF0aW9uIHByb2Nlc3MuXG4gKiBUaGlzIGlzIG5lZWRlZCB0byBtYWtlIGFzc2Vzc21lbnRzIGluIHRlc3RzIHdoZXRoZXJcbiAqIHRoZSBoeWRyYXRpb24gcHJvY2VzcyBoYW5kbGVkIGFsbCBub2Rlcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1hcmtSTm9kZUFzQ2xhaW1lZEJ5SHlkcmF0aW9uKG5vZGU6IFJOb2RlLCBjaGVja0lmQWxyZWFkeUNsYWltZWQgPSB0cnVlKSB7XG4gIGlmICghbmdEZXZNb2RlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnQ2FsbGluZyBgbWFya1JOb2RlQXNDbGFpbWVkQnlIeWRyYXRpb25gIGluIHByb2QgbW9kZSAnICtcbiAgICAgICAgJ2lzIG5vdCBzdXBwb3J0ZWQgYW5kIGxpa2VseSBhIG1pc3Rha2UuJyk7XG4gIH1cbiAgaWYgKGNoZWNrSWZBbHJlYWR5Q2xhaW1lZCAmJiBpc1JOb2RlQ2xhaW1lZEZvckh5ZHJhdGlvbihub2RlKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignVHJ5aW5nIHRvIGNsYWltIGEgbm9kZSwgd2hpY2ggd2FzIGNsYWltZWQgYWxyZWFkeS4nKTtcbiAgfVxuICAobm9kZSBhcyBDbGFpbWVkTm9kZSkuX19jbGFpbWVkID0gdHJ1ZTtcbiAgbmdEZXZNb2RlLmh5ZHJhdGVkTm9kZXMrKztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzUk5vZGVDbGFpbWVkRm9ySHlkcmF0aW9uKG5vZGU6IFJOb2RlKTogYm9vbGVhbiB7XG4gIHJldHVybiAhIShub2RlIGFzIENsYWltZWROb2RlKS5fX2NsYWltZWQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzZXRTZWdtZW50SGVhZChcbiAgICBoeWRyYXRpb25JbmZvOiBEZWh5ZHJhdGVkVmlldywgaW5kZXg6IG51bWJlciwgbm9kZTogUk5vZGV8bnVsbCk6IHZvaWQge1xuICBoeWRyYXRpb25JbmZvLnNlZ21lbnRIZWFkcyA/Pz0ge307XG4gIGh5ZHJhdGlvbkluZm8uc2VnbWVudEhlYWRzW2luZGV4XSA9IG5vZGU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTZWdtZW50SGVhZChoeWRyYXRpb25JbmZvOiBEZWh5ZHJhdGVkVmlldywgaW5kZXg6IG51bWJlcik6IFJOb2RlfG51bGwge1xuICByZXR1cm4gaHlkcmF0aW9uSW5mby5zZWdtZW50SGVhZHM/LltpbmRleF0gPz8gbnVsbDtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBzaXplIG9mIGFuIDxuZy1jb250YWluZXI+LCB1c2luZyBlaXRoZXIgdGhlIGluZm9ybWF0aW9uXG4gKiBzZXJpYWxpemVkIGluIGBFTEVNRU5UX0NPTlRBSU5FUlNgIChlbGVtZW50IGNvbnRhaW5lciBzaXplKSBvciBieVxuICogY29tcHV0aW5nIHRoZSBzdW0gb2Ygcm9vdCBub2RlcyBpbiBhbGwgZGVoeWRyYXRlZCB2aWV3cyBpbiBhIGdpdmVuXG4gKiBjb250YWluZXIgKGluIGNhc2UgdGhpcyBgPG5nLWNvbnRhaW5lcj5gIHdhcyBhbHNvIHVzZWQgYXMgYSB2aWV3XG4gKiBjb250YWluZXIgaG9zdCBub2RlLCBlLmcuIDxuZy1jb250YWluZXIgKm5nSWY+KS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE5nQ29udGFpbmVyU2l6ZShoeWRyYXRpb25JbmZvOiBEZWh5ZHJhdGVkVmlldywgaW5kZXg6IG51bWJlcik6IG51bWJlcnxudWxsIHtcbiAgY29uc3QgZGF0YSA9IGh5ZHJhdGlvbkluZm8uZGF0YTtcbiAgbGV0IHNpemUgPSBkYXRhW0VMRU1FTlRfQ09OVEFJTkVSU10/LltpbmRleF0gPz8gbnVsbDtcbiAgLy8gSWYgdGhlcmUgaXMgbm8gc2VyaWFsaXplZCBpbmZvcm1hdGlvbiBhdmFpbGFibGUgaW4gdGhlIGBFTEVNRU5UX0NPTlRBSU5FUlNgIHNsb3QsXG4gIC8vIGNoZWNrIGlmIHdlIGhhdmUgaW5mbyBhYm91dCB2aWV3IGNvbnRhaW5lcnMgYXQgdGhpcyBsb2NhdGlvbiAoZS5nLlxuICAvLyBgPG5nLWNvbnRhaW5lciAqbmdJZj5gKSBhbmQgdXNlIGNvbnRhaW5lciBzaXplIGFzIGEgbnVtYmVyIG9mIHJvb3Qgbm9kZXMgaW4gdGhpc1xuICAvLyBlbGVtZW50IGNvbnRhaW5lci5cbiAgaWYgKHNpemUgPT09IG51bGwgJiYgZGF0YVtDT05UQUlORVJTXT8uW2luZGV4XSkge1xuICAgIHNpemUgPSBjYWxjU2VyaWFsaXplZENvbnRhaW5lclNpemUoaHlkcmF0aW9uSW5mbywgaW5kZXgpO1xuICB9XG4gIHJldHVybiBzaXplO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VyaWFsaXplZENvbnRhaW5lclZpZXdzKFxuICAgIGh5ZHJhdGlvbkluZm86IERlaHlkcmF0ZWRWaWV3LCBpbmRleDogbnVtYmVyKTogU2VyaWFsaXplZENvbnRhaW5lclZpZXdbXXxudWxsIHtcbiAgcmV0dXJuIGh5ZHJhdGlvbkluZm8uZGF0YVtDT05UQUlORVJTXT8uW2luZGV4XSA/PyBudWxsO1xufVxuXG4vKipcbiAqIENvbXB1dGVzIHRoZSBzaXplIG9mIGEgc2VyaWFsaXplZCBjb250YWluZXIgKHRoZSBudW1iZXIgb2Ygcm9vdCBub2RlcylcbiAqIGJ5IGNhbGN1bGF0aW5nIHRoZSBzdW0gb2Ygcm9vdCBub2RlcyBpbiBhbGwgZGVoeWRyYXRlZCB2aWV3cyBpbiB0aGlzIGNvbnRhaW5lci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGNTZXJpYWxpemVkQ29udGFpbmVyU2l6ZShoeWRyYXRpb25JbmZvOiBEZWh5ZHJhdGVkVmlldywgaW5kZXg6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IHZpZXdzID0gZ2V0U2VyaWFsaXplZENvbnRhaW5lclZpZXdzKGh5ZHJhdGlvbkluZm8sIGluZGV4KSA/PyBbXTtcbiAgbGV0IG51bU5vZGVzID0gMDtcbiAgZm9yIChsZXQgdmlldyBvZiB2aWV3cykge1xuICAgIG51bU5vZGVzICs9IHZpZXdbTlVNX1JPT1RfTk9ERVNdICogKHZpZXdbTVVMVElQTElFUl0gPz8gMSk7XG4gIH1cbiAgcmV0dXJuIG51bU5vZGVzO1xufVxuXG4vKipcbiAqIENoZWNrcyB3aGV0aGVyIGEgbm9kZSBpcyBhbm5vdGF0ZWQgYXMgXCJkaXNjb25uZWN0ZWRcIiwgaS5lLiBub3QgcHJlc2VudFxuICogaW4gdGhlIERPTSBhdCBzZXJpYWxpemF0aW9uIHRpbWUuIFdlIHNob3VsZCBub3QgYXR0ZW1wdCBoeWRyYXRpb24gZm9yXG4gKiBzdWNoIG5vZGVzIGFuZCBpbnN0ZWFkLCB1c2UgYSByZWd1bGFyIFwiY3JlYXRpb24gbW9kZVwiLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNEaXNjb25uZWN0ZWROb2RlKGh5ZHJhdGlvbkluZm86IERlaHlkcmF0ZWRWaWV3LCBpbmRleDogbnVtYmVyKTogYm9vbGVhbiB7XG4gIC8vIENoZWNrIGlmIHdlIGFyZSBwcm9jZXNzaW5nIGRpc2Nvbm5lY3RlZCBpbmZvIGZvciB0aGUgZmlyc3QgdGltZS5cbiAgaWYgKHR5cGVvZiBoeWRyYXRpb25JbmZvLmRpc2Nvbm5lY3RlZE5vZGVzID09PSAndW5kZWZpbmVkJykge1xuICAgIGNvbnN0IG5vZGVJZHMgPSBoeWRyYXRpb25JbmZvLmRhdGFbRElTQ09OTkVDVEVEX05PREVTXTtcbiAgICBoeWRyYXRpb25JbmZvLmRpc2Nvbm5lY3RlZE5vZGVzID0gbm9kZUlkcyA/IChuZXcgU2V0KG5vZGVJZHMpKSA6IG51bGw7XG4gIH1cbiAgcmV0dXJuICEhaHlkcmF0aW9uSW5mby5kaXNjb25uZWN0ZWROb2Rlcz8uaGFzKGluZGV4KTtcbn1cbiJdfQ==