UNPKG

ngx-preboot

Version:

Record server view events and play back to Angular client view

267 lines 34.6 kB
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