ngx-preboot
Version:
Record server view events and play back to Angular client view
267 lines • 34.6 kB
JavaScript
import { getNodeKeyForPreboot } from '../common/get-node-key';
export function _window() {
return {
prebootData: window['prebootData'],
getComputedStyle: window.getComputedStyle.bind(window),
document: document,
};
}
export class EventReplayer {
constructor() {
this.clientNodeCache = {};
this.replayStarted = false;
}
/**
* Window setting and getter to facilitate testing of window
* in non-browser environments
*/
setWindow(win) {
this.win = win;
}
/**
* Window setting and getter to facilitate testing of window
* in non-browser environments
*/
getWindow() {
if (!this.win) {
this.win = _window();
}
return this.win;
}
/**
* Replay all events for all apps. this can only be run once.
* if called multiple times, will only do something once
*/
replayAll() {
if (this.replayStarted) {
return;
}
else {
this.replayStarted = true;
}
// loop through each of the preboot apps
const prebootData = this.getWindow().prebootData || {};
const apps = prebootData.apps || [];
apps.forEach((appData) => this.replayForApp(appData));
// once all events have been replayed and buffers switched, then we cleanup preboot
this.cleanup(prebootData);
}
/**
* Replay all events for one app (most of the time there is just one app)
* @param appData
*/
replayForApp(appData) {
appData = (appData || {});
// try catch around events b/c even if error occurs, we still move forward
try {
const events = appData.events || [];
// replay all the events from the server view onto the client view
events.forEach((event) => this.replayEvent(appData, event));
}
catch (ex) {
console.error(ex);
}
// if we are buffering, switch the buffers
this.switchBuffer(appData);
}
/**
* Replay one particular event
* @param appData
* @param prebootEvent
*/
replayEvent(appData, prebootEvent) {
appData = (appData || {});
prebootEvent = (prebootEvent || {});
const event = prebootEvent.event;
const serverNode = prebootEvent.node || {};
const nodeKey = prebootEvent.nodeKey;
const clientNode = this.findClientNode({
root: appData.root,
node: serverNode,
nodeKey: nodeKey,
});
// if client node can't be found, log a warning
if (!clientNode) {
console.warn(`Trying to dispatch event ${event.type} to node ${nodeKey}
but could not find client node. Server node is: ${serverNode}`);
return;
}
// now dispatch events and whatnot to the client node
clientNode.checked = serverNode.checked;
clientNode.selected = serverNode.selected;
clientNode.value = serverNode.value;
clientNode.dispatchEvent(event);
}
/**
* Switch the buffer for one particular app (i.e. display the client
* view and destroy the server view)
* @param appData
*/
switchBuffer(appData) {
appData = (appData || {});
const root = (appData.root || {});
const serverView = root.serverNode;
const clientView = root.clientNode;
// if no client view or the server view is the body or client
// and server view are the same, then don't do anything and return
if (!clientView || !serverView || serverView === clientView || serverView.nodeName === 'BODY') {
return;
}
// do a try-catch just in case something messed up
try {
// get the server view display mode
const gcs = this.getWindow().getComputedStyle;
const display = gcs(serverView).getPropertyValue('display') || 'block';
// first remove the server view
// serverView.remove ? serverView.remove() : (serverView.style.display = 'none');
// // now add the client view
// clientView.style.display = display;
clientView.style.cssText = `display: ${display}; visibility:hidden; z-index: 0;position: absolute;top: 0;left: 0;right: 0;bottom: 0; transition: visibility .3s ease-in-out`;
setTimeout(() => {
serverView.remove ? serverView.remove() : (serverView.style.display = 'none');
clientView.style.visibility = 'visible';
}, 300);
}
catch (ex) {
console.error(ex);
}
}
/**
* Finally, set focus, remove all the event listeners and remove
* any freeze screen that may be there
* @param prebootData
*/
cleanup(prebootData) {
prebootData = prebootData || {};
const listeners = prebootData.listeners || [];
// set focus on the active node AFTER a small delay to ensure buffer
// switched
const activeNode = prebootData.activeNode;
if (activeNode != null) {
setTimeout(() => this.setFocus(activeNode), 1);
}
// remove all event listeners
for (const listener of listeners) {
listener.node.removeEventListener(listener.eventName, listener.handler);
}
// remove the freeze overlay if it exists
const doc = this.getWindow().document;
// clear out the data stored for each app
prebootData.apps = [];
this.clientNodeCache = {};
// send event to document that signals preboot complete
// constructor is not supported by older browsers ( i.e. IE9-11 )
// in these browsers, the type of CustomEvent will be "object"
if (typeof CustomEvent === 'function') {
const completeEvent = new CustomEvent('PrebootComplete');
doc.dispatchEvent(completeEvent);
const prebootOverlay = doc.getElementById('prebootOverlay');
if (prebootOverlay) {
prebootOverlay.remove
? prebootOverlay.remove()
: prebootOverlay.parentNode !== null
? prebootOverlay.parentNode.removeChild(prebootOverlay)
: (prebootOverlay.style.display = 'none');
}
}
else {
console.warn(`Could not dispatch PrebootComplete event.
You can fix this by including a polyfill for CustomEvent.`);
}
}
setFocus(activeNode) {
// only do something if there is an active node
if (!activeNode || !activeNode.node || !activeNode.nodeKey) {
return;
}
// find the client node in the new client view
const clientNode = this.findClientNode(activeNode);
if (clientNode) {
// set focus on the client node
clientNode.focus();
// set selection if a modern browser (i.e. IE9+, etc.)
const selection = activeNode.selection;
if (clientNode.setSelectionRange && selection) {
try {
clientNode.setSelectionRange(selection.start, selection.end, selection.direction);
}
catch (ex) { }
}
}
}
/**
* Given a node from the server rendered view, find the equivalent
* node in the client rendered view. We do this by the following approach:
* 1. take the name of the server node tag (ex. div or h1 or input)
* 2. add either id (ex. div#myid) or class names (ex. div.class1.class2)
* 3. use that value as a selector to get all the matching client nodes
* 4. loop through all client nodes found and for each generate a key value
* 5. compare the client key to the server key; once there is a match,
* we have our client node
*
* NOTE: this only works when the client view is almost exactly the same as
* the server view. we will need an improvement here in the future to account
* for situations where the client view is different in structure from the
* server view
*/
findClientNode(serverNodeContext) {
serverNodeContext = (serverNodeContext || {});
const serverNode = serverNodeContext.node;
const root = serverNodeContext.root;
// if no server or client root, don't do anything
if (!root || !root.serverNode || !root.clientNode) {
return null;
}
// we use the string of the node to compare to the client node & as key in
// cache
const serverNodeKey = serverNodeContext.nodeKey || getNodeKeyForPreboot(serverNodeContext);
// if client node already in cache, return it
if (this.clientNodeCache[serverNodeKey]) {
return this.clientNodeCache[serverNodeKey];
}
// get the selector for client nodes
const className = (serverNode.className || '').replace('ng-binding', '').trim();
let selector = serverNode.tagName;
if (serverNode.id) {
selector += `#${serverNode.id}`;
}
else if (className) {
selector += `.${className.replace(/ /g, '.')}`;
}
// select all possible client nodes and look through them to try and find a
// match
const rootClientNode = root.clientNode;
let clientNodes = rootClientNode.querySelectorAll(selector);
// if nothing found, then just try the tag name as a final option
if (!clientNodes.length) {
console.log(`nothing found for ${selector} so using ${serverNode.tagName}`);
clientNodes = rootClientNode.querySelectorAll(serverNode.tagName);
}
const length = clientNodes.length;
for (let i = 0; i < length; i++) {
const clientNode = clientNodes.item(i);
// get the key for the client node
const clientNodeKey = getNodeKeyForPreboot({
root: root,
node: clientNode,
});
// if the client node key is exact match for the server node key, then we
// found the client node
if (clientNodeKey === serverNodeKey) {
this.clientNodeCache[serverNodeKey] = clientNode;
return clientNode;
}
}
// if we get here and there is one clientNode, use it as a fallback
if (clientNodes.length === 1) {
this.clientNodeCache[serverNodeKey] = clientNodes[0];
return clientNodes[0];
}
// if we get here it means we couldn't find the client node so give the user
// a warning
console.warn(`No matching client node found for ${serverNodeKey}.
You can fix this by assigning this element a unique id attribute.`);
return null;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnQucmVwbGF5ZXIuanMiLCJzb3VyY2VSb290IjoiLi4vLi4vc3JjL2xpYi8iLCJzb3VyY2VzIjpbImFwaS9ldmVudC5yZXBsYXllci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFRQSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUU5RCxNQUFNLFVBQVUsT0FBTztJQUNyQixPQUFPO1FBQ0wsV0FBVyxFQUFHLE1BQWMsQ0FBQyxhQUFhLENBQUM7UUFDM0MsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDdEQsUUFBUSxFQUFFLFFBQVE7S0FDbkIsQ0FBQztBQUNKLENBQUM7QUFFRCxNQUFNLE9BQU8sYUFBYTtJQUExQjtRQUNFLG9CQUFlLEdBQStCLEVBQUUsQ0FBQztRQUNqRCxrQkFBYSxHQUFHLEtBQUssQ0FBQztJQXlTeEIsQ0FBQztJQXRTQzs7O09BR0c7SUFDSCxTQUFTLENBQUMsR0FBa0I7UUFDMUIsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7SUFDakIsQ0FBQztJQUVEOzs7T0FHRztJQUNILFNBQVM7UUFDUCxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNiLElBQUksQ0FBQyxHQUFHLEdBQUcsT0FBTyxFQUFFLENBQUM7U0FDdEI7UUFDRCxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUM7SUFDbEIsQ0FBQztJQUVEOzs7T0FHRztJQUNILFNBQVM7UUFDUCxJQUFJLElBQUksQ0FBQyxhQUFhLEVBQUU7WUFDdEIsT0FBTztTQUNSO2FBQU07WUFDTCxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQztTQUMzQjtRQUVELHdDQUF3QztRQUN4QyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsV0FBVyxJQUFJLEVBQUUsQ0FBQztRQUN2RCxNQUFNLElBQUksR0FBRyxXQUFXLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FB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QkFDbkMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsR0FBRyxVQUFVLENBQUM7Z0JBQ2pELE9BQU8sVUFBeUIsQ0FBQzthQUNsQztTQUNGO1FBRUQsbUVBQW1FO1FBQ25FLElBQUksV0FBVyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUU7WUFDNUIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxhQUFhLENBQUMsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDckQsT0FBTyxXQUFXLENBQUMsQ0FBQyxDQUFnQixDQUFDO1NBQ3RDO1FBRUQsNEVBQTRFO1FBQzVFLFlBQVk7UUFDWixPQUFPLENBQUMsSUFBSSxDQUNWLHFDQUFxQyxhQUFhO3lFQUNpQixDQUNwRSxDQUFDO1FBQ0YsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgR29vZ2xlIExMQyBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cbmltcG9ydCB7IE5vZGVDb250ZXh0LCBQcmVib290QXBwRGF0YSwgUHJlYm9vdERhdGEsIFByZWJvb3RFdmVudCwgUHJlYm9vdFdpbmRvdywgU2VydmVyQ2xpZW50Um9vdCB9IGZyb20gJy4uL2NvbW1vbi9wcmVib290LmludGVyZmFjZXMnO1xuaW1wb3J0IHsgZ2V0Tm9kZUtleUZvclByZWJvb3QgfSBmcm9tICcuLi9jb21tb24vZ2V0LW5vZGUta2V5JztcblxuZXhwb3J0IGZ1bmN0aW9uIF93aW5kb3coKTogUHJlYm9vdFdpbmRvdyB7XG4gIHJldHVybiB7XG4gICAgcHJlYm9vdERhdGE6ICh3aW5kb3cgYXMgYW55KVsncHJlYm9vdERhdGEnXSxcbiAgICBnZXRDb21wdXRlZFN0eWxlOiB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZS5iaW5kKHdpbmRvdyksXG4gICAgZG9jdW1lbnQ6IGRvY3VtZW50LFxuICB9O1xufVxuXG5leHBvcnQgY2xhc3MgRXZlbnRSZXBsYXllciB7XG4gIGNsaWVudE5vZGVDYWNoZTogeyBba2V5OiBzdHJpbmddOiBFbGVtZW50IH0gPSB7fTtcbiAgcmVwbGF5U3RhcnRlZCA9IGZhbHNlO1xuICB3aW46IFByZWJvb3RXaW5kb3c7XG5cbiAgLyoqXG4gICAqIFdpbmRvdyBzZXR0aW5nIGFuZCBnZXR0ZXIgdG8gZmFjaWxpdGF0ZSB0ZXN0aW5nIG9mIHdpbmRvd1xuICAgKiBpbiBub24tYnJvd3NlciBlbnZpcm9ubWVudHNcbiAgICovXG4gIHNldFdpbmRvdyh3aW46IFByZWJvb3RXaW5kb3cpIHtcbiAgICB0aGlzLndpbiA9IHdpbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaW5kb3cgc2V0dGluZyBhbmQgZ2V0dGVyIHRvIGZhY2lsaXRhdGUgdGVzdGluZyBvZiB3aW5kb3dcbiAgICogaW4gbm9uLWJyb3dzZXIgZW52aXJvbm1lbnRzXG4gICAqL1xuICBnZXRXaW5kb3coKSB7XG4gICAgaWYgKCF0aGlzLndpbikge1xuICAgICAgdGhpcy53aW4gPSBfd2luZG93KCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLndpbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYXkgYWxsIGV2ZW50cyBmb3IgYWxsIGFwcHMuIHRoaXMgY2FuIG9ubHkgYmUgcnVuIG9uY2UuXG4gICAqIGlmIGNhbGxlZCBtdWx0aXBsZSB0aW1lcywgd2lsbCBvbmx5IGRvIHNvbWV0aGluZyBvbmNlXG4gICAqL1xuICByZXBsYXlBbGwoKSB7XG4gICAgaWYgKHRoaXMucmVwbGF5U3RhcnRlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlcGxheVN0YXJ0ZWQgPSB0cnVlO1xuICAgIH1cblxuICAgIC8vIGxvb3AgdGhyb3VnaCBlYWNoIG9mIHRoZSBwcmVib290IGFwcHNcbiAgICBjb25zdCBwcmVib290RGF0YSA9IHRoaXMuZ2V0V2luZG93KCkucHJlYm9vdERhdGEgfHwge307XG4gICAgY29uc3QgYXBwcyA9IHByZWJvb3REYXRhLmFwcHMgfHwgW107XG4gICAgYXBwcy5mb3JFYWNoKChhcHBEYXRhKSA9PiB0aGlzLnJlcGxheUZvckFwcChhcHBEYXRhKSk7XG5cbiAgICAvLyBvbmNlIGFsbCBldmVudHMgaGF2ZSBiZWVuIHJlcGxheWVkIGFuZCBidWZmZXJzIHN3aXRjaGVkLCB0aGVuIHdlIGNsZWFudXAgcHJlYm9vdFxuICAgIHRoaXMuY2xlYW51cChwcmVib290RGF0YSk7XG4gIH1cblxuICAvKipcbiAgICogUmVwbGF5IGFsbCBldmVudHMgZm9yIG9uZSBhcHAgKG1vc3Qgb2YgdGhlIHRpbWUgdGhlcmUgaXMganVzdCBvbmUgYXBwKVxuICAgKiBAcGFyYW0gYXBwRGF0YVxuICAgKi9cbiAgcmVwbGF5Rm9yQXBwKGFwcERhdGE6IFByZWJvb3RBcHBEYXRhKSB7XG4gICAgYXBwRGF0YSA9IDxQcmVib290QXBwRGF0YT4oYXBwRGF0YSB8fCB7fSk7XG5cbiAgICAvLyB0cnkgY2F0Y2ggYXJvdW5kIGV2ZW50cyBiL2MgZXZlbiBpZiBlcnJvciBvY2N1cnMsIHdlIHN0aWxsIG1vdmUgZm9yd2FyZFxuICAgIHRyeSB7XG4gICAgICBjb25zdCBldmVudHMgPSBhcHBEYXRhLmV2ZW50cyB8fCBbXTtcblxuICAgICAgLy8gcmVwbGF5IGFsbCB0aGUgZXZlbnRzIGZyb20gdGhlIHNlcnZlciB2aWV3IG9udG8gdGhlIGNsaWVudCB2aWV3XG4gICAgICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHRoaXMucmVwbGF5RXZlbnQoYXBwRGF0YSwgZXZlbnQpKTtcbiAgICB9IGNhdGNoIChleCkge1xuICAgICAgY29uc29sZS5lcnJvcihleCk7XG4gICAgfVxuXG4gICAgLy8gaWYgd2UgYXJlIGJ1ZmZlcmluZywgc3dpdGNoIHRoZSBidWZmZXJzXG4gICAgdGhpcy5zd2l0Y2hCdWZmZXIoYXBwRGF0YSk7XG4gIH1cblxuICAvKipcbiAgICogUmVwbGF5IG9uZSBwYXJ0aWN1bGFyIGV2ZW50XG4gICAqIEBwYXJhbSBhcHBEYXRhXG4gICAqIEBwYXJhbSBwcmVib290RXZlbnRcbiAgICovXG4gIHJlcGxheUV2ZW50KGFwcERhdGE6IFByZWJvb3RBcHBEYXRhLCBwcmVib290RXZlbnQ6IFByZWJvb3RFdmVudCkge1xuICAgIGFwcERhdGEgPSA8UHJlYm9vdEFwcERhdGE+KGFwcERhdGEgfHwge30pO1xuICAgIHByZWJvb3RFdmVudCA9IDxQcmVib290RXZlbnQ+KHByZWJvb3RFdmVudCB8fCB7fSk7XG5cbiAgICBjb25zdCBldmVudCA9IHByZWJvb3RFdmVudC5ldmVudCBhcyBFdmVudDtcbiAgICBjb25zdCBzZXJ2ZXJOb2RlID0gcHJlYm9vdEV2ZW50Lm5vZGUgfHwge307XG4gICAgY29uc3Qgbm9kZUtleSA9IHByZWJvb3RFdmVudC5ub2RlS2V5O1xuICAgIGNvbnN0IGNsaWVudE5vZGUgPSB0aGlzLmZpbmRDbGllbnROb2RlKHtcbiAgICAgIHJvb3Q6IGFwcERhdGEucm9vdCxcbiAgICAgIG5vZGU6IHNlcnZlck5vZGUsXG4gICAgICBub2RlS2V5OiBub2RlS2V5LFxuICAgIH0pO1xuXG4gICAgLy8gaWYgY2xpZW50IG5vZGUgY2FuJ3QgYmUgZm91bmQsIGxvZyBhIHdhcm5pbmdcbiAgICBpZiAoIWNsaWVudE5vZGUpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYFRyeWluZyB0byBkaXNwYXRjaCBldmVudCAke2V2ZW50LnR5cGV9IHRvIG5vZGUgJHtub2RlS2V5fVxuICAgICAgICBidXQgY291bGQgbm90IGZpbmQgY2xpZW50IG5vZGUuIFNlcnZlciBub2RlIGlzOiAke3NlcnZlck5vZGV9YFxuICAgICAgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBub3cgZGlzcGF0Y2ggZXZlbnRzIGFuZCB3aGF0bm90IHRvIHRoZSBjbGllbnQgbm9kZVxuICAgIChjbGllbnROb2RlIGFzIEhUTUxJbnB1dEVsZW1lbnQpLmNoZWNrZWQgPSBzZXJ2ZXJOb2RlLmNoZWNrZWQ7XG4gICAgKGNsaWVudE5vZGUgYXMgSFRNTE9wdGlvbkVsZW1lbnQpLnNlbGVjdGVkID0gc2VydmVyTm9kZS5zZWxlY3RlZDtcbiAgICAoY2xpZW50Tm9kZSBhcyBIVE1MT3B0aW9uRWxlbWVudCkudmFsdWUgPSBzZXJ2ZXJOb2RlLnZhbHVlO1xuICAgIGNsaWVudE5vZGUuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gIH1cblxuICAvKipcbiAgICogU3dpdGNoIHRoZSBidWZmZXIgZm9yIG9uZSBwYXJ0aWN1bGFyIGFwcCAoaS5lLiBkaXNwbGF5IHRoZSBjbGllbnRcbiAgICogdmlldyBhbmQgZGVzdHJveSB0aGUgc2VydmVyIHZpZXcpXG4gICAqIEBwYXJhbSBhcHBEYXRhXG4gICAqL1xuICBzd2l0Y2hCdWZmZXIoYXBwRGF0YTogUHJlYm9vdEFwcERhdGEpIHtcbiAgICBhcHBEYXRhID0gPFByZWJvb3RBcHBEYXRhPihhcHBEYXRhIHx8IHt9KTtcblxuICAgIGNvbnN0IHJvb3QgPSA8U2VydmVyQ2xpZW50Um9vdD4oYXBwRGF0YS5yb290IHx8IHt9KTtcbiAgICBjb25zdCBzZXJ2ZXJWaWV3ID0gcm9vdC5zZXJ2ZXJOb2RlO1xuICAgIGNvbnN0IGNsaWVudFZpZXcgPSByb290LmNsaWVudE5vZGU7XG5cbiAgICAvLyBpZiBubyBjbGllbnQgdmlldyBvciB0aGUgc2VydmVyIHZpZXcgaXMgdGhlIGJvZHkgb3IgY2xpZW50XG4gICAgLy8gYW5kIHNlcnZlciB2aWV3IGFyZSB0aGUgc2FtZSwgdGhlbiBkb24ndCBkbyBhbnl0aGluZyBhbmQgcmV0dXJuXG4gICAgaWYgKCFjbGllbnRWaWV3IHx8ICFzZXJ2ZXJWaWV3IHx8IHNlcnZlclZpZXcgPT09IGNsaWVudFZpZXcgfHwgc2VydmVyVmlldy5ub2RlTmFtZSA9PT0gJ0JPRFknKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gZG8gYSB0cnktY2F0Y2gganVzdCBpbiBjYXNlIHNvbWV0aGluZyBtZXNzZWQgdXBcbiAgICB0cnkge1xuICAgICAgLy8gZ2V0IHRoZSBzZXJ2ZXIgdmlldyBkaXNwbGF5IG1vZGVcbiAgICAgIGNvbnN0IGdjcyA9IHRoaXMuZ2V0V2luZG93KCkuZ2V0Q29tcHV0ZWRTdHlsZTtcbiAgICAgIGNvbnN0IGRpc3BsYXkgPSBnY3Moc2VydmVyVmlldykuZ2V0UHJvcGVydHlWYWx1ZSgnZGlzcGxheScpIHx8ICdibG9jayc7XG5cbiAgICAgIC8vIGZpcnN0IHJlbW92ZSB0aGUgc2VydmVyIHZpZXdcbiAgICAgIC8vIHNlcnZlclZpZXcucmVtb3ZlID8gc2VydmVyVmlldy5yZW1vdmUoKSA6IChzZXJ2ZXJWaWV3LnN0eWxlLmRpc3BsYXkgPSAnbm9uZScpO1xuXG4gICAgICAvLyAvLyBub3cgYWRkIHRoZSBjbGllbnQgdmlld1xuICAgICAgLy8gY2xpZW50Vmlldy5zdHlsZS5kaXNwbGF5ID0gZGlzcGxheTtcblxuICAgICAgY2xpZW50Vmlldy5zdHlsZS5jc3NUZXh0ID0gYGRpc3BsYXk6ICR7ZGlzcGxheX07IHZpc2liaWxpdHk6aGlkZGVuOyB6LWluZGV4OiAwO3Bvc2l0aW9uOiBhYnNvbHV0ZTt0b3A6IDA7bGVmdDogMDtyaWdodDogMDtib3R0b206IDA7IHRyYW5zaXRpb246IHZpc2liaWxpdHkgLjNzIGVhc2UtaW4tb3V0YDtcblxuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIHNlcnZlclZpZXcucmVtb3ZlID8gc2VydmVyVmlldy5yZW1vdmUoKSA6IChzZXJ2ZXJWaWV3LnN0eWxlLmRpc3BsYXkgPSAnbm9uZScpO1xuICAgICAgICBjbGllbnRWaWV3LnN0eWxlLnZpc2liaWxpdHkgPSAndmlzaWJsZSc7XG4gICAgICB9LCAzMDApO1xuICAgIH0gY2F0Y2ggKGV4KSB7XG4gICAgICBjb25zb2xlLmVycm9yKGV4KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRmluYWxseSwgc2V0IGZvY3VzLCByZW1vdmUgYWxsIHRoZSBldmVudCBsaXN0ZW5lcnMgYW5kIHJlbW92ZVxuICAgKiBhbnkgZnJlZXplIHNjcmVlbiB0aGF0IG1heSBiZSB0aGVyZVxuICAgKiBAcGFyYW0gcHJlYm9vdERhdGFcbiAgICovXG4gIGNsZWFudXAocHJlYm9vdERhdGE6IFByZWJvb3REYXRhKSB7XG4gICAgcHJlYm9vdERhdGEgPSBwcmVib290RGF0YSB8fCB7fTtcblxuICAgIGNvbnN0IGxpc3RlbmVycyA9IHByZWJvb3REYXRhLmxpc3RlbmVycyB8fCBbXTtcblxuICAgIC8vIHNldCBmb2N1cyBvbiB0aGUgYWN0aXZlIG5vZGUgQUZURVIgYSBzbWFsbCBkZWxheSB0byBlbnN1cmUgYnVmZmVyXG4gICAgLy8gc3dpdGNoZWRcbiAgICBjb25zdCBhY3RpdmVOb2RlID0gcHJlYm9vdERhdGEuYWN0aXZlTm9kZTtcbiAgICBpZiAoYWN0aXZlTm9kZSAhPSBudWxsKSB7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuc2V0Rm9jdXMoYWN0aXZlTm9kZSksIDEpO1xuICAgIH1cblxuICAgIC8vIHJlbW92ZSBhbGwgZXZlbnQgbGlzdGVuZXJzXG4gICAgZm9yIChjb25zdCBsaXN0ZW5lciBvZiBsaXN0ZW5lcnMpIHtcbiAgICAgIGxpc3RlbmVyLm5vZGUucmVtb3ZlRXZlbnRMaXN0ZW5lcihsaXN0ZW5lci5ldmVudE5hbWUsIGxpc3RlbmVyLmhhbmRsZXIpO1xuICAgIH1cblxuICAgIC8vIHJlbW92ZSB0aGUgZnJlZXplIG92ZXJsYXkgaWYgaXQgZXhpc3RzXG4gICAgY29uc3QgZG9jID0gdGhpcy5nZXRXaW5kb3coKS5kb2N1bWVudDtcblxuICAgIC8vIGNsZWFyIG91dCB0aGUgZGF0YSBzdG9yZWQgZm9yIGVhY2ggYXBwXG4gICAgcHJlYm9vdERhdGEuYXBwcyA9IFtdO1xuICAgIHRoaXMuY2xpZW50Tm9kZUNhY2hlID0ge307XG5cbiAgICAvLyBzZW5kIGV2ZW50IHRvIGRvY3VtZW50IHRoYXQgc2lnbmFscyBwcmVib290IGNvbXBsZXRlXG4gICAgLy8gY29uc3RydWN0b3IgaXMgbm90IHN1cHBvcnRlZCBieSBvbGRlciBicm93c2VycyAoIGkuZS4gSUU5LTExIClcbiAgICAvLyBpbiB0aGVzZSBicm93c2VycywgdGhlIHR5cGUgb2YgQ3VzdG9tRXZlbnQgd2lsbCBiZSBcIm9iamVjdFwiXG4gICAgaWYgKHR5cGVvZiBDdXN0b21FdmVudCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgY29uc3QgY29tcGxldGVFdmVudCA9IG5ldyBDdXN0b21FdmVudCgnUHJlYm9vdENvbXBsZXRlJyk7XG4gICAgICBkb2MuZGlzcGF0Y2hFdmVudChjb21wbGV0ZUV2ZW50KTtcbiAgICAgIGNvbnN0IHByZWJvb3RPdmVybGF5ID0gZG9jLmdldEVsZW1lbnRCeUlkKCdwcmVib290T3ZlcmxheScpO1xuICAgICAgaWYgKHByZWJvb3RPdmVybGF5KSB7XG4gICAgICAgIHByZWJvb3RPdmVybGF5LnJlbW92ZVxuICAgICAgICAgID8gcHJlYm9vdE92ZXJsYXkucmVtb3ZlKClcbiAgICAgICAgICA6IHByZWJvb3RPdmVybGF5LnBhcmVudE5vZGUgIT09IG51bGxcbiAgICAgICAgICA/IHByZWJvb3RPdmVybGF5LnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQocHJlYm9vdE92ZXJsYXkpXG4gICAgICAgICAgOiAocHJlYm9vdE92ZXJsYXkuc3R5bGUuZGlzcGxheSA9ICdub25lJyk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnNvbGUud2FybihgQ291bGQgbm90IGRpc3BhdGNoIFByZWJvb3RDb21wbGV0ZSBldmVudC5cbiAgICAgICBZb3UgY2FuIGZpeCB0aGlzIGJ5IGluY2x1ZGluZyBhIHBvbHlmaWxsIGZvciBDdXN0b21FdmVudC5gKTtcbiAgICB9XG4gIH1cblxuICBzZXRGb2N1cyhhY3RpdmVOb2RlOiBOb2RlQ29udGV4dCkge1xuICAgIC8vIG9ubHkgZG8gc29tZXRoaW5nIGlmIHRoZXJlIGlzIGFuIGFjdGl2ZSBub2RlXG4gICAgaWYgKCFhY3RpdmVOb2RlIHx8ICFhY3RpdmVOb2RlLm5vZGUgfHwgIWFjdGl2ZU5vZGUubm9kZUtleSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIGZpbmQgdGhlIGNsaWVudCBub2RlIGluIHRoZSBuZXcgY2xpZW50IHZpZXdcbiAgICBjb25zdCBjbGllbnROb2RlID0gdGhpcy5maW5kQ2xpZW50Tm9kZShhY3RpdmVOb2RlKTtcbiAgICBpZiAoY2xpZW50Tm9kZSkge1xuICAgICAgLy8gc2V0IGZvY3VzIG9uIHRoZSBjbGllbnQgbm9kZVxuICAgICAgY2xpZW50Tm9kZS5mb2N1cygpO1xuXG4gICAgICAvLyBzZXQgc2VsZWN0aW9uIGlmIGEgbW9kZXJuIGJyb3dzZXIgKGkuZS4gSUU5KywgZXRjLilcbiAgICAgIGNvbnN0IHNlbGVjdGlvbiA9IGFjdGl2ZU5vZGUuc2VsZWN0aW9uO1xuICAgICAgaWYgKChjbGllbnROb2RlIGFzIEhUTUxJbnB1dEVsZW1lbnQpLnNldFNlbGVjdGlvblJhbmdlICYmIHNlbGVjdGlvbikge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIChjbGllbnROb2RlIGFzIEhUTUxJbnB1dEVsZW1lbnQpLnNldFNlbGVjdGlvblJhbmdlKHNlbGVjdGlvbi5zdGFydCwgc2VsZWN0aW9uLmVuZCwgc2VsZWN0aW9uLmRpcmVjdGlvbik7XG4gICAgICAgIH0gY2F0Y2ggKGV4KSB7fVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHaXZlbiBhIG5vZGUgZnJvbSB0aGUgc2VydmVyIHJlbmRlcmVkIHZpZXcsIGZpbmQgdGhlIGVxdWl2YWxlbnRcbiAgICogbm9kZSBpbiB0aGUgY2xpZW50IHJlbmRlcmVkIHZpZXcuIFdlIGRvIHRoaXMgYnkgdGhlIGZvbGxvd2luZyBhcHByb2FjaDpcbiAgICogICAgICAxLiB0YWtlIHRoZSBuYW1lIG9mIHRoZSBzZXJ2ZXIgbm9kZSB0YWcgKGV4LiBkaXYgb3IgaDEgb3IgaW5wdXQpXG4gICAqICAgICAgMi4gYWRkIGVpdGhlciBpZCAoZXguIGRpdiNteWlkKSBvciBjbGFzcyBuYW1lcyAoZXguIGRpdi5jbGFzczEuY2xhc3MyKVxuICAgKiAgICAgIDMuIHVzZSB0aGF0IHZhbHVlIGFzIGEgc2VsZWN0b3IgdG8gZ2V0IGFsbCB0aGUgbWF0Y2hpbmcgY2xpZW50IG5vZGVzXG4gICAqICAgICAgNC4gbG9vcCB0aHJvdWdoIGFsbCBjbGllbnQgbm9kZXMgZm91bmQgYW5kIGZvciBlYWNoIGdlbmVyYXRlIGEga2V5IHZhbHVlXG4gICAqICAgICAgNS4gY29tcGFyZSB0aGUgY2xpZW50IGtleSB0byB0aGUgc2VydmVyIGtleTsgb25jZSB0aGVyZSBpcyBhIG1hdGNoLFxuICAgKiAgICAgICAgICB3ZSBoYXZlIG91ciBjbGllbnQgbm9kZVxuICAgKlxuICAgKiBOT1RFOiB0aGlzIG9ubHkgd29ya3Mgd2hlbiB0aGUgY2xpZW50IHZpZXcgaXMgYWxtb3N0IGV4YWN0bHkgdGhlIHNhbWUgYXNcbiAgICogdGhlIHNlcnZlciB2aWV3LiB3ZSB3aWxsIG5lZWQgYW4gaW1wcm92ZW1lbnQgaGVyZSBpbiB0aGUgZnV0dXJlIHRvIGFjY291bnRcbiAgICogZm9yIHNpdHVhdGlvbnMgd2hlcmUgdGhlIGNsaWVudCB2aWV3IGlzIGRpZmZlcmVudCBpbiBzdHJ1Y3R1cmUgZnJvbSB0aGVcbiAgICogc2VydmVyIHZpZXdcbiAgICovXG4gIGZpbmRDbGllbnROb2RlKHNlcnZlck5vZGVDb250ZXh0OiBOb2RlQ29udGV4dCk6IEhUTUxFbGVtZW50IHwgbnVsbCB7XG4gICAgc2VydmVyTm9kZUNvbnRleHQgPSA8Tm9kZUNvbnRleHQ+KHNlcnZlck5vZGVDb250ZXh0IHx8IHt9KTtcblxuICAgIGNvbnN0IHNlcnZlck5vZGUgPSBzZXJ2ZXJOb2RlQ29udGV4dC5ub2RlO1xuICAgIGNvbnN0IHJvb3QgPSBzZXJ2ZXJOb2RlQ29udGV4dC5yb290O1xuXG4gICAgLy8gaWYgbm8gc2VydmVyIG9yIGNsaWVudCByb290LCBkb24ndCBkbyBhbnl0aGluZ1xuICAgIGlmICghcm9vdCB8fCAhcm9vdC5zZXJ2ZXJOb2RlIHx8ICFyb290LmNsaWVudE5vZGUpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIC8vIHdlIHVzZSB0aGUgc3RyaW5nIG9mIHRoZSBub2RlIHRvIGNvbXBhcmUgdG8gdGhlIGNsaWVudCBub2RlICYgYXMga2V5IGluXG4gICAgLy8gY2FjaGVcbiAgICBjb25zdCBzZXJ2ZXJOb2RlS2V5ID0gc2VydmVyTm9kZUNvbnRleHQubm9kZUtleSB8fCBnZXROb2RlS2V5Rm9yUHJlYm9vdChzZXJ2ZXJOb2RlQ29udGV4dCk7XG5cbiAgICAvLyBpZiBjbGllbnQgbm9kZSBhbHJlYWR5IGluIGNhY2hlLCByZXR1cm4gaXRcbiAgICBpZiAodGhpcy5jbGllbnROb2RlQ2FjaGVbc2VydmVyTm9kZUtleV0pIHtcbiAgICAgIHJldHVybiB0aGlzLmNsaWVudE5vZGVDYWNoZVtzZXJ2ZXJOb2RlS2V5XSBhcyBIVE1MRWxlbWVudDtcbiAgICB9XG5cbiAgICAvLyBnZXQgdGhlIHNlbGVjdG9yIGZvciBjbGllbnQgbm9kZXNcbiAgICBjb25zdCBjbGFzc05hbWUgPSAoc2VydmVyTm9kZS5jbGFzc05hbWUgfHwgJycpLnJlcGxhY2UoJ25nLWJpbmRpbmcnLCAnJykudHJpbSgpO1xuICAgIGxldCBzZWxlY3RvciA9IHNlcnZlck5vZGUudGFnTmFtZTtcblxuICAgIGlmIChzZXJ2ZXJOb2RlLmlkKSB7XG4gICAgICBzZWxlY3RvciArPSBgIyR7c2VydmVyTm9kZS5pZH1gO1xuICAgIH0gZWxzZSBpZiAoY2xhc3NOYW1lKSB7XG4gICAgICBzZWxlY3RvciArPSBgLiR7Y2xhc3NOYW1lLnJlcGxhY2UoLyAvZywgJy4nKX1gO1xuICAgIH1cblxuICAgIC8vIHNlbGVjdCBhbGwgcG9zc2libGUgY2xpZW50IG5vZGVzIGFuZCBsb29rIHRocm91Z2ggdGhlbSB0byB0cnkgYW5kIGZpbmQgYVxuICAgIC8vIG1hdGNoXG4gICAgY29uc3Qgcm9vdENsaWVudE5vZGUgPSByb290LmNsaWVudE5vZGU7XG4gICAgbGV0IGNsaWVudE5vZGVzID0gcm9vdENsaWVudE5vZGUucXVlcnlTZWxlY3RvckFsbChzZWxlY3Rvcik7XG5cbiAgICAvLyBpZiBub3RoaW5nIGZvdW5kLCB0aGVuIGp1c3QgdHJ5IHRoZSB0YWcgbmFtZSBhcyBhIGZpbmFsIG9wdGlvblxuICAgIGlmICghY2xpZW50Tm9kZXMubGVuZ3RoKSB7XG4gICAgICBjb25zb2xlLmxvZyhgbm90aGluZyBmb3VuZCBmb3IgJHtzZWxlY3Rvcn0gc28gdXNpbmcgJHtzZXJ2ZXJOb2RlLnRhZ05hbWV9YCk7XG4gICAgICBjbGllbnROb2RlcyA9IHJvb3RDbGllbnROb2RlLnF1ZXJ5U2VsZWN0b3JBbGwoc2VydmVyTm9kZS50YWdOYW1lKTtcbiAgICB9XG5cbiAgICBjb25zdCBsZW5ndGggPSBjbGllbnROb2Rlcy5sZW5ndGg7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgY2xpZW50Tm9kZSA9IGNsaWVudE5vZGVzLml0ZW0oaSk7XG5cbiAgICAgIC8vIGdldCB0aGUga2V5IGZvciB0aGUgY2xpZW50IG5vZGVcbiAgICAgIGNvbnN0IGNsaWVudE5vZGVLZXkgPSBnZXROb2RlS2V5Rm9yUHJlYm9vdCh7XG4gICAgICAgIHJvb3Q6IHJvb3QsXG4gICAgICAgIG5vZGU6IGNsaWVudE5vZGUsXG4gICAgICB9KTtcblxuICAgICAgLy8gaWYgdGhlIGNsaWVudCBub2RlIGtleSBpcyBleGFjdCBtYXRjaCBmb3IgdGhlIHNlcnZlciBub2RlIGtleSwgdGhlbiB3ZVxuICAgICAgLy8gZm91bmQgdGhlIGNsaWVudCBub2RlXG4gICAgICBpZiAoY2xpZW50Tm9kZUtleSA9PT0gc2VydmVyTm9kZUtleSkge1xuICAgICAgICB0aGlzLmNsaWVudE5vZGVDYWNoZVtzZXJ2ZXJOb2RlS2V5XSA9IGNsaWVudE5vZGU7XG4gICAgICAgIHJldHVybiBjbGllbnROb2RlIGFzIEhUTUxFbGVtZW50O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGlmIHdlIGdldCBoZXJlIGFuZCB0aGVyZSBpcyBvbmUgY2xpZW50Tm9kZSwgdXNlIGl0IGFzIGEgZmFsbGJhY2tcbiAgICBpZiAoY2xpZW50Tm9kZXMubGVuZ3RoID09PSAxKSB7XG4gICAgICB0aGlzLmNsaWVudE5vZGVDYWNoZVtzZXJ2ZXJOb2RlS2V5XSA9IGNsaWVudE5vZGVzWzBdO1xuICAgICAgcmV0dXJuIGNsaWVudE5vZGVzWzBdIGFzIEhUTUxFbGVtZW50O1xuICAgIH1cblxuICAgIC8vIGlmIHdlIGdldCBoZXJlIGl0IG1lYW5zIHdlIGNvdWxkbid0IGZpbmQgdGhlIGNsaWVudCBub2RlIHNvIGdpdmUgdGhlIHVzZXJcbiAgICAvLyBhIHdhcm5pbmdcbiAgICBjb25zb2xlLndhcm4oXG4gICAgICBgTm8gbWF0Y2hpbmcgY2xpZW50IG5vZGUgZm91bmQgZm9yICR7c2VydmVyTm9kZUtleX0uXG4gICAgICAgWW91IGNhbiBmaXggdGhpcyBieSBhc3NpZ25pbmcgdGhpcyBlbGVtZW50IGEgdW5pcXVlIGlkIGF0dHJpYnV0ZS5gXG4gICAgKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufVxuIl19