UNPKG

ngx-preboot

Version:

Record server view events and play back to Angular client view

271 lines 35.9 kB
import { getNodeKeyForPreboot } from '../common/get-node-key'; /** * Called right away to initialize preboot * * @param opts All the preboot options * @param win */ export function initAll(opts, win) { const theWindow = (win || window); // Add the preboot options to the preboot data and then add the data to // the window so it can be used later by the client. // Only set new options if they're not already set - we may have multiple app roots // and each of them invokes the init function separately. const data = (theWindow.prebootData = { opts: opts, apps: [], listeners: [], }); return () => start(data, theWindow); } /** * Start up preboot by going through each app and assigning the appropriate * handlers. Normally this wouldn't be called directly, but we have set it up so * that it can for older versions of Universal. * * @param prebootData Global preboot data object that contains options and will * have events * @param win Optional param to pass in mock window for testing purposes */ export function start(prebootData, win) { const theWindow = (win || window); const _document = (theWindow.document || {}); // Remove the current script from the DOM so that child indexes match // between the client & the server. The script is already running so it // doesn't affect it. const currentScript = _document.currentScript || // Support: IE 9-11 only // IE doesn't support document.currentScript. Since the script is invoked // synchronously, though, the current running script is just the last one // currently in the document. [].slice.call(_document.getElementsByTagName('script'), -1)[0]; if (!currentScript) { console.error('Preboot initialization failed, no currentScript has been detected.'); return; } let serverNode = currentScript.parentNode; if (!serverNode) { console.error('Preboot initialization failed, the script is detached'); return; } serverNode.removeChild(currentScript); const opts = prebootData.opts || {}; let eventSelectors = opts.eventSelectors || []; // get the root info const appRoot = prebootData.opts ? getAppRoot(_document, prebootData.opts, serverNode) : null; // we track all events for each app in the prebootData object which is on // the global scope; each `start` invocation adds data for one app only. const appData = { root: appRoot, events: [] }; if (prebootData.apps) { prebootData.apps.push(appData); } eventSelectors = eventSelectors.map((eventSelector) => { if (!eventSelector.hasOwnProperty('replay')) { eventSelector.replay = true; } return eventSelector; }); // loop through all the eventSelectors and create event handlers eventSelectors.forEach((eventSelector) => handleEvents(_document, prebootData, appData, eventSelector)); } /** * Create an overlay div and add it to the DOM so it can be used * if a freeze event occurs * * @param _document The global document object (passed in for testing purposes) * @returns Element The overlay node is returned */ export function createOverlay(_document) { let overlay = _document.createElement('div'); overlay.setAttribute('id', 'prebootOverlay'); overlay.setAttribute('style', 'display:none;position:absolute;left:0;' + 'top:0;width:100%;height:100%;z-index:999999;background:black;opacity:.3'); _document.documentElement.appendChild(overlay); return overlay; } /** * Get references to the current app root node based on input options. Users can * initialize preboot either by specifying appRoot which is just one or more * selectors for apps. This section option is useful for people that are doing their own * buffering (i.e. they have their own client and server view) * * @param _document The global document object used to attach the overlay * @param opts Options passed in by the user to init() * @param serverNode The server node serving as application root * @returns ServerClientRoot An array of root info for the current app */ export function getAppRoot(_document, opts, serverNode) { const root = { serverNode }; // if we are doing buffering, we need to create the buffer for the client // else the client root is the same as the server root.clientNode = opts.buffer ? createBuffer(root) : root.serverNode; // create an overlay if not disabled ,that can be used later if a freeze event occurs if (!opts.disableOverlay) { root.overlay = createOverlay(_document); } return root; } /** * Under given server root, for given selector, record events * * @param _document * @param prebootData * @param appData * @param eventSelector */ export function handleEvents(_document, prebootData, appData, eventSelector) { const serverRoot = appData.root.serverNode; // don't do anything if no server root if (!serverRoot) { return; } // Attach delegated event listeners for each event selector. // We need to use delegated events as only the top level server node // exists at this point. eventSelector.events.forEach((eventName) => { // get the appropriate handler and add it as an event listener const handler = createListenHandler(_document, prebootData, eventSelector, appData); // attach the handler in the capture phase so that it fires even if // one of the handlers below calls stopPropagation() serverRoot.addEventListener(eventName, handler, true); // need to keep track of listeners so we can do node.removeEventListener() // when preboot done if (prebootData.listeners) { prebootData.listeners.push({ node: serverRoot, eventName, handler, }); } }); } /** * Create handler for events that we will record */ export function createListenHandler(_document, prebootData, eventSelector, appData) { const CARET_EVENTS = ['keyup', 'keydown', 'focusin', 'mouseup', 'mousedown']; const CARET_NODES = ['INPUT', 'TEXTAREA']; // Support: IE 9-11 only // IE uses a prefixed `matches` version const matches = _document.documentElement.matches || _document.documentElement.msMatchesSelector; const opts = prebootData.opts; return function (event) { const node = event.target; // a delegated handlers on document is used so we need to check if // event target matches a desired selector if (!matches.call(node, eventSelector.selector)) { return; } const root = appData.root; const eventName = event.type; // if no node or no event name, just return if (!node || !eventName) { return; } // if key codes set for eventSelector, then don't do anything if event // doesn't include key const keyCodes = eventSelector.keyCodes; if (keyCodes && keyCodes.length) { const matchingKeyCodes = keyCodes.filter((keyCode) => event.which === keyCode); // if there are not matches (i.e. key entered NOT one of the key codes) // then don't do anything if (!matchingKeyCodes.length) { return; } } // if for a given set of events we are preventing default, do that if (eventSelector.preventDefault) { event.preventDefault(); } // if an action handler passed in, use that if (eventSelector.action) { eventSelector.action(node, event); } // get the node key for a given node const nodeKey = getNodeKeyForPreboot({ root: root, node: node }); // record active node if (CARET_EVENTS.indexOf(eventName) >= 0) { // if it's an caret node, get the selection for the active node const isCaretNode = CARET_NODES.indexOf(node.tagName ? node.tagName : '') >= 0; prebootData.activeNode = { root: root, node: node, nodeKey: nodeKey, selection: isCaretNode ? getSelection(node) : undefined, }; } else if (eventName !== 'change' && eventName !== 'focusout') { prebootData.activeNode = undefined; } // if overlay is not disabled and we are freezing the UI if (opts && !opts.disableOverlay && eventSelector.freeze) { const overlay = root.overlay; // show the overlay overlay.style.display = 'block'; overlay.classList.add('is-loading'); // hide the overlay after 10 seconds just in case preboot.complete() never // called setTimeout(() => { overlay.style.display = 'none'; }, 10000); } // we will record events for later replay unless explicitly marked as // doNotReplay if (eventSelector.replay) { appData.events.push({ node, nodeKey, event, name: eventName, }); } }; } /** * Get the selection data that is later used to set the cursor after client view * is active */ export function getSelection(node) { node = node || {}; const nodeValue = node.value || ''; const selection = { start: nodeValue.length, end: nodeValue.length, direction: 'forward', }; // if browser support selectionStart on node (Chrome, FireFox, IE9+) try { if (node.selectionStart || node.selectionStart === 0) { selection.start = node.selectionStart; selection.end = node.selectionEnd ? node.selectionEnd : 0; selection.direction = node.selectionDirection ? node.selectionDirection : 'none'; } } catch (ex) { } return selection; } /** * Create buffer for a given node * * @param root All the data related to a particular app * @returns Returns the root client node. */ export function createBuffer(root) { const serverNode = root.serverNode; // if no rootServerNode OR the selector is on the entire html doc or the body // OR no parentNode, don't buffer if (!serverNode || !serverNode.parentNode || serverNode === document.documentElement || serverNode === document.body) { return serverNode; } // create shallow clone of server root const rootClientNode = serverNode.cloneNode(false); // we want the client to write to a hidden div until the time for switching // the buffers rootClientNode.style.display = 'none'; // insert the client node before the server and return it serverNode.parentNode.insertBefore(rootClientNode, serverNode); // mark server node as not to be touched by AngularJS - needed for ngUpgrade serverNode.setAttribute('ng-non-bindable', ''); // return the rootClientNode return rootClientNode; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnQucmVjb3JkZXIuanMiLCJzb3VyY2VSb290IjoiLi4vLi4vc3JjL2xpYi8iLCJzb3VyY2VzIjpbImFwaS9ldmVudC5yZWNvcmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFrQkEsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFOUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsT0FBTyxDQUFDLElBQW9CLEVBQUUsR0FBbUI7SUFDL0QsTUFBTSxTQUFTLEdBQWtCLENBQUMsR0FBRyxJQUFJLE1BQU0sQ0FBQyxDQUFDO0lBRWpELHVFQUF1RTtJQUN2RSxvREFBb0Q7SUFDcEQsbUZBQW1GO0lBQ25GLHlEQUF5RDtJQUN6RCxNQUFNLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxXQUFXLEdBQWdCO1FBQ2pELElBQUksRUFBRSxJQUFJO1FBQ1YsSUFBSSxFQUFFLEVBQUU7UUFDUixTQUFTLEVBQUUsRUFBRTtLQUNkLENBQUMsQ0FBQztJQUVILE9BQU8sR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQztBQUN0QyxDQUFDO0FBRUQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsS0FBSyxDQUFDLFdBQXdCLEVBQUUsR0FBbUI7SUFDakUsTUFBTSxTQUFTLEdBQWtCLENBQUMsR0FBRyxJQUFJLE1BQU0sQ0FBQyxDQUFDO0lBQ2pELE1BQU0sU0FBUyxHQUFhLENBQUMsU0FBUyxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUV2RCxxRUFBcUU7SUFDckUsdUVBQXVFO0lBQ3ZFLHFCQUFxQjtJQUNyQixNQUFNLGFBQWEsR0FDakIsU0FBUyxDQUFDLGFBQWE7UUFDdkIsd0JBQXdCO1FBQ3hCLHlFQUF5RTtRQUN6RSx5RUFBeUU7UUFDekUsNkJBQTZCO1FBQzdCLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsRUFB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QztJQUVuQyw2RUFBNkU7SUFDN0UsaUNBQWlDO0lBQ2pDLElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxVQUFVLENBQUMsVUFBVSxJQUFJLFVBQVUsS0FBSyxRQUFRLENBQUMsZUFBZSxJQUFJLFVBQVUsS0FBSyxRQUFRLENBQUMsSUFBSSxFQUFFO1FBQ3BILE9BQU8sVUFBeUIsQ0FBQztLQUNsQztJQUVELHNDQUFzQztJQUN0QyxNQUFNLGNBQWMsR0FBRyxVQUFVLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBZ0IsQ0FBQztJQUNsRSwyRUFBMkU7SUFDM0UsY0FBYztJQUNkLGNBQWMsQ0FBQyxLQUFLLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztJQUV0Qyx5REFBeUQ7SUFDekQsVUFBVSxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsY0FBYyxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBRS9ELDRFQUE0RTtJQUM1RSxVQUFVLENBQUMsWUFBWSxDQUFDLGlCQUFpQixFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBRS9DLDRCQUE0QjtJQUM1QixPQUFPLGNBQWMsQ0FBQztBQUN4QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBMTEMgQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5pbXBvcnQge1xuICBFdmVudFNlbGVjdG9yLFxuICBQcmVib290T3B0aW9ucyxcbiAgUHJlYm9vdEFwcERhdGEsXG4gIFByZWJvb3REYXRhLFxuICBEb21FdmVudCxcbiAgUHJlYm9vdFdpbmRvdyxcbiAgU2VydmVyQ2xpZW50Um9vdCxcbiAgUHJlYm9vdFNlbGVjdGlvbixcbiAgUHJlYm9vdFNlbGVjdGlvbkRpcmVjdGlvbixcbn0gZnJvbSAnLi4vY29tbW9uL3ByZWJvb3QuaW50ZXJmYWNlcyc7XG5pbXBvcnQgeyBnZXROb2RlS2V5Rm9yUHJlYm9vdCB9IGZyb20gJy4uL2NvbW1vbi9nZXQtbm9kZS1rZXknO1xuXG4vKipcbiAqIENhbGxlZCByaWdodCBhd2F5IHRvIGluaXRpYWxpemUgcHJlYm9vdFxuICpcbiAqIEBwYXJhbSBvcHRzIEFsbCB0aGUgcHJlYm9vdCBvcHRpb25zXG4gKiBAcGFyYW0gd2luXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0QWxsKG9wdHM6IFByZWJvb3RPcHRpb25zLCB3aW4/OiBQcmVib290V2luZG93KSB7XG4gIGNvbnN0IHRoZVdpbmRvdyA9IDxQcmVib290V2luZG93Pih3aW4gfHwgd2luZG93KTtcblxuICAvLyBBZGQgdGhlIHByZWJvb3Qgb3B0aW9ucyB0byB0aGUgcHJlYm9vdCBkYXRhIGFuZCB0aGVuIGFkZCB0aGUgZGF0YSB0b1xuICAvLyB0aGUgd2luZG93IHNvIGl0IGNhbiBiZSB1c2VkIGxhdGVyIGJ5IHRoZSBjbGllbnQuXG4gIC8vIE9ubHkgc2V0IG5ldyBvcHRpb25zIGlmIHRoZXkncmUgbm90IGFscmVhZHkgc2V0IC0gd2UgbWF5IGhhdmUgbXVsdGlwbGUgYXBwIHJvb3RzXG4gIC8vIGFuZCBlYWNoIG9mIHRoZW0gaW52b2tlcyB0aGUgaW5pdCBmdW5jdGlvbiBzZXBhcmF0ZWx5LlxuICBjb25zdCBkYXRhID0gKHRoZVdpbmRvdy5wcmVib290RGF0YSA9IDxQcmVib290RGF0YT57XG4gICAgb3B0czogb3B0cyxcbiAgICBhcHBzOiBbXSxcbiAgICBsaXN0ZW5lcnM6IFtdLFxuICB9KTtcblxuICByZXR1cm4gKCkgPT4gc3RhcnQoZGF0YSwgdGhlV2luZG93KTtcbn1cblxuLyoqXG4gKiBTdGFydCB1cCBwcmVib290IGJ5IGdvaW5nIHRocm91Z2ggZWFjaCBhcHAgYW5kIGFzc2lnbmluZyB0aGUgYXBwcm9wcmlhdGVcbiAqIGhhbmRsZXJzLiBOb3JtYWxseSB0aGlzIHdvdWxkbid0IGJlIGNhbGxlZCBkaXJlY3RseSwgYnV0IHdlIGhhdmUgc2V0IGl0IHVwIHNvXG4gKiB0aGF0IGl0IGNhbiBmb3Igb2xkZXIgdmVyc2lvbnMgb2YgVW5pdmVyc2FsLlxuICpcbiAqIEBwYXJhbSBwcmVib290RGF0YSBHbG9iYWwgcHJlYm9vdCBkYXRhIG9iamVjdCB0aGF0IGNvbnRhaW5zIG9wdGlvbnMgYW5kIHdpbGxcbiAqIGhhdmUgZXZlbnRzXG4gKiBAcGFyYW0gd2luIE9wdGlvbmFsIHBhcmFtIHRvIHBhc3MgaW4gbW9jayB3aW5kb3cgZm9yIHRlc3RpbmcgcHVycG9zZXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0YXJ0KHByZWJvb3REYXRhOiBQcmVib290RGF0YSwgd2luPzogUHJlYm9vdFdpbmRvdykge1xuICBjb25zdCB0aGVXaW5kb3cgPSA8UHJlYm9vdFdpbmRvdz4od2luIHx8IHdpbmRvdyk7XG4gIGNvbnN0IF9kb2N1bWVudCA9IDxEb2N1bWVudD4odGhlV2luZG93LmRvY3VtZW50IHx8IHt9KTtcblxuICAvLyBSZW1vdmUgdGhlIGN1cnJlbnQgc2NyaXB0IGZyb20gdGhlIERPTSBzbyB0aGF0IGNoaWxkIGluZGV4ZXMgbWF0Y2hcbiAgLy8gYmV0d2VlbiB0aGUgY2xpZW50ICYgdGhlIHNlcnZlci4gVGhlIHNjcmlwdCBpcyBhbHJlYWR5IHJ1bm5pbmcgc28gaXRcbiAgLy8gZG9lc24ndCBhZmZlY3QgaXQuXG4gIGNvbnN0IGN1cnJlbnRTY3JpcHQgPVxuICAgIF9kb2N1bWVudC5jdXJyZW50U2NyaXB0IHx8XG4gICAgLy8gU3VwcG9ydDogSUUgOS0xMSBvbmx5XG4gICAgLy8gSUUgZG9lc24ndCBzdXBwb3J0IGRvY3VtZW50LmN1cnJlbnRTY3JpcHQuIFNpbmNlIHRoZSBzY3JpcHQgaXMgaW52b2tlZFxuICAgIC8vIHN5bmNocm9ub3VzbHksIHRob3VnaCwgdGhlIGN1cnJlbnQgcnVubmluZyBzY3JpcHQgaXMganVzdCB0aGUgbGFzdCBvbmVcbiAgICAvLyBjdXJyZW50bHkgaW4gdGhlIGRvY3VtZW50LlxuICAgIFtdLnNsaWNlLmNhbGwoX2RvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKSwgLTEpWzBdO1xuXG4gIGlmICghY3VycmVudFNjcmlwdCkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ1ByZWJvb3QgaW5pdGlhbGl6YXRpb24gZmFpbGVkLCBubyBjdXJyZW50U2NyaXB0IGhhcyBiZWVuIGRldGVjdGVkLicpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGxldCBzZXJ2ZXJOb2RlID0gY3VycmVudFNjcmlwdC5wYXJlbnROb2RlO1xuICBpZiAoIXNlcnZlck5vZGUpIHtcbiAgICBjb25zb2xlLmVycm9yKCdQcmVib290IGluaXRpYWxpemF0aW9uIGZhaWxlZCwgdGhlIHNjcmlwdCBpcyBkZXRhY2hlZCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHNlcnZlck5vZGUucmVtb3ZlQ2hpbGQoY3VycmVudFNjcmlwdCk7XG5cbiAgY29uc3Qgb3B0cyA9IHByZWJvb3REYXRhLm9wdHMgfHwgKHt9IGFzIFByZWJvb3RPcHRpb25zKTtcbiAgbGV0IGV2ZW50U2VsZWN0b3JzID0gb3B0cy5ldmVudFNlbGVjdG9ycyB8fCBbXTtcblxuICAvLyBnZXQgdGhlIHJvb3QgaW5mb1xuICBjb25zdCBhcHBSb290ID0gcHJlYm9vdERhdGEub3B0cyA/IGdldEFwcFJvb3QoX2RvY3VtZW50LCBwcmVib290RGF0YS5vcHRzLCBzZXJ2ZXJOb2RlKSA6IG51bGw7XG5cbiAgLy8gd2UgdHJhY2sgYWxsIGV2ZW50cyBmb3IgZWFjaCBhcHAgaW4gdGhlIHByZWJvb3REYXRhIG9iamVjdCB3aGljaCBpcyBvblxuICAvLyB0aGUgZ2xvYmFsIHNjb3BlOyBlYWNoIGBzdGFydGAgaW52b2NhdGlvbiBhZGRzIGRhdGEgZm9yIG9uZSBhcHAgb25seS5cbiAgY29uc3QgYXBwRGF0YSA9IDxQcmVib290QXBwRGF0YT57IHJvb3Q6IGFwcFJvb3QsIGV2ZW50czogW10gfTtcbiAgaWYgKHByZWJvb3REYXRhLmFwcHMpIHtcbiAgICBwcmVib290RGF0YS5hcHBzLnB1c2goYXBwRGF0YSk7XG4gIH1cblxuICBldmVudFNlbGVjdG9ycyA9IGV2ZW50U2VsZWN0b3JzLm1hcCgoZXZlbnRTZWxlY3RvcikgPT4ge1xuICAgIGlmICghZXZlbnRTZWxlY3Rvci5oYXNPd25Qcm9wZXJ0eSgncmVwbGF5JykpIHtcbiAgICAgIGV2ZW50U2VsZWN0b3IucmVwbGF5ID0gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGV2ZW50U2VsZWN0b3I7XG4gIH0pO1xuXG4gIC8vIGxvb3AgdGhyb3VnaCBhbGwgdGhlIGV2ZW50U2VsZWN0b3JzIGFuZCBjcmVhdGUgZXZlbnQgaGFuZGxlcnNcbiAgZXZlbnRTZWxlY3RvcnMuZm9yRWFjaCgoZXZlbnRTZWxlY3RvcikgPT4gaGFuZGxlRXZlbnRzKF9kb2N1bWVudCwgcHJlYm9vdERhdGEsIGFwcERhdGEsIGV2ZW50U2VsZWN0b3IpKTtcbn1cblxuLyoqXG4gKiBDcmVhdGUgYW4gb3ZlcmxheSBkaXYgYW5kIGFkZCBpdCB0byB0aGUgRE9NIHNvIGl0IGNhbiBiZSB1c2VkXG4gKiBpZiBhIGZyZWV6ZSBldmVudCBvY2N1cnNcbiAqXG4gKiBAcGFyYW0gX2RvY3VtZW50IFRoZSBnbG9iYWwgZG9jdW1lbnQgb2JqZWN0IChwYXNzZWQgaW4gZm9yIHRlc3RpbmcgcHVycG9zZXMpXG4gKiBAcmV0dXJucyBFbGVtZW50IFRoZSBvdmVybGF5IG5vZGUgaXMgcmV0dXJuZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU92ZXJsYXkoX2RvY3VtZW50OiBEb2N1bWVudCk6IEhUTUxFbGVtZW50IHwgdW5kZWZpbmVkIHtcbiAgbGV0IG92ZXJsYXkgPSBfZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gIG92ZXJsYXkuc2V0QXR0cmlidXRlKCdpZCcsICdwcmVib290T3ZlcmxheScpO1xuICBvdmVybGF5LnNldEF0dHJpYnV0ZShcbiAgICAnc3R5bGUnLFxuICAgICdkaXNwbGF5Om5vbmU7cG9zaXRpb246YWJzb2x1dGU7bGVmdDowOycgKyAndG9wOjA7d2lkdGg6MTAwJTtoZWlnaHQ6MTAwJTt6LWluZGV4Ojk5OTk5OTtiYWNrZ3JvdW5kOmJsYWNrO29wYWNpdHk6LjMnXG4gICk7XG4gIF9kb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuYXBwZW5kQ2hpbGQob3ZlcmxheSk7XG5cbiAgcmV0dXJuIG92ZXJsYXk7XG59XG5cbi8qKlxuICogR2V0IHJlZmVyZW5jZXMgdG8gdGhlIGN1cnJlbnQgYXBwIHJvb3Qgbm9kZSBiYXNlZCBvbiBpbnB1dCBvcHRpb25zLiBVc2VycyBjYW5cbiAqIGluaXRpYWxpemUgcHJlYm9vdCBlaXRoZXIgYnkgc3BlY2lmeWluZyBhcHBSb290IHdoaWNoIGlzIGp1c3Qgb25lIG9yIG1vcmVcbiAqIHNlbGVjdG9ycyBmb3IgYXBwcy4gVGhpcyBzZWN0aW9uIG9wdGlvbiBpcyB1c2VmdWwgZm9yIHBlb3BsZSB0aGF0IGFyZSBkb2luZyB0aGVpciBvd25cbiAqIGJ1ZmZlcmluZyAoaS5lLiB0aGV5IGhhdmUgdGhlaXIgb3duIGNsaWVudCBhbmQgc2VydmVyIHZpZXcpXG4gKlxuICogQHBhcmFtIF9kb2N1bWVudCBUaGUgZ2xvYmFsIGRvY3VtZW50IG9iamVjdCB1c2VkIHRvIGF0dGFjaCB0aGUgb3ZlcmxheVxuICogQHBhcmFtIG9wdHMgT3B0aW9ucyBwYXNzZWQgaW4gYnkgdGhlIHVzZXIgdG8gaW5pdCgpXG4gKiBAcGFyYW0gc2VydmVyTm9kZSBUaGUgc2VydmVyIG5vZGUgc2VydmluZyBhcyBhcHBsaWNhdGlvbiByb290XG4gKiBAcmV0dXJucyBTZXJ2ZXJDbGllbnRSb290IEFuIGFycmF5IG9mIHJvb3QgaW5mbyBmb3IgdGhlIGN1cnJlbnQgYXBwXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRBcHBSb290KF9kb2N1bWVudDogRG9jdW1lbnQsIG9wdHM6IFByZWJvb3RPcHRpb25zLCBzZXJ2ZXJOb2RlOiBIVE1MRWxlbWVudCk6IFNlcnZlckNsaWVudFJvb3Qge1xuICBjb25zdCByb290OiBTZXJ2ZXJDbGllbnRSb290ID0geyBzZXJ2ZXJOb2RlIH07XG5cbiAgLy8gaWYgd2UgYXJlIGRvaW5nIGJ1ZmZlcmluZywgd2UgbmVlZCB0byBjcmVhdGUgdGhlIGJ1ZmZlciBmb3IgdGhlIGNsaWVudFxuICAvLyBlbHNlIHRoZSBjbGllbnQgcm9vdCBpcyB0aGUgc2FtZSBhcyB0aGUgc2VydmVyXG4gIHJvb3QuY2xpZW50Tm9kZSA9IG9wdHMuYnVmZmVyID8gY3JlYXRlQnVmZmVyKHJvb3QpIDogcm9vdC5zZXJ2ZXJOb2RlO1xuXG4gIC8vIGNyZWF0ZSBhbiBvdmVybGF5IGlmIG5vdCBkaXNhYmxlZCAsdGhhdCBjYW4gYmUgdXNlZCBsYXRlciBpZiBhIGZyZWV6ZSBldmVudCBvY2N1cnNcbiAgaWYgKCFvcHRzLmRpc2FibGVPdmVybGF5KSB7XG4gICAgcm9vdC5vdmVybGF5ID0gY3JlYXRlT3ZlcmxheShfZG9jdW1lbnQpO1xuICB9XG5cbiAgcmV0dXJuIHJvb3Q7XG59XG5cbi8qKlxuICogVW5kZXIgZ2l2ZW4gc2VydmVyIHJvb3QsIGZvciBnaXZlbiBzZWxlY3RvciwgcmVjb3JkIGV2ZW50c1xuICpcbiAqIEBwYXJhbSBfZG9jdW1lbnRcbiAqIEBwYXJhbSBwcmVib290RGF0YVxuICogQHBhcmFtIGFwcERhdGFcbiAqIEBwYXJhbSBldmVudFNlbGVjdG9yXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBoYW5kbGVFdmVudHMoX2RvY3VtZW50OiBEb2N1bWVudCwgcHJlYm9vdERhdGE6IFByZWJvb3REYXRhLCBhcHBEYXRhOiBQcmVib290QXBwRGF0YSwgZXZlbnRTZWxlY3RvcjogRXZlbnRTZWxlY3Rvcikge1xuICBjb25zdCBzZXJ2ZXJSb290ID0gYXBwRGF0YS5yb290LnNlcnZlck5vZGU7XG5cbiAgLy8gZG9uJ3QgZG8gYW55dGhpbmcgaWYgbm8gc2VydmVyIHJvb3RcbiAgaWYgKCFzZXJ2ZXJSb290KSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gQXR0YWNoIGRlbGVnYXRlZCBldmVudCBsaXN0ZW5lcnMgZm9yIGVhY2ggZXZlbnQgc2VsZWN0b3IuXG4gIC8vIFdlIG5lZWQgdG8gdXNlIGRlbGVnYXRlZCBldmVudHMgYXMgb25seSB0aGUgdG9wIGxldmVsIHNlcnZlciBub2RlXG4gIC8vIGV4aXN0cyBhdCB0aGlzIHBvaW50LlxuICBldmVudFNlbGVjdG9yLmV2ZW50cy5mb3JFYWNoKChldmVudE5hbWU6IHN0cmluZykgPT4ge1xuICAgIC8vIGdldCB0aGUgYXBwcm9wcmlhdGUgaGFuZGxlciBhbmQgYWRkIGl0IGFzIGFuIGV2ZW50IGxpc3RlbmVyXG4gICAgY29uc3QgaGFuZGxlciA9IGNyZWF0ZUxpc3RlbkhhbmRsZXIoX2RvY3VtZW50LCBwcmVib290RGF0YSwgZXZlbnRTZWxlY3RvciwgYXBwRGF0YSk7XG4gICAgLy8gYXR0YWNoIHRoZSBoYW5kbGVyIGluIHRoZSBjYXB0dXJlIHBoYXNlIHNvIHRoYXQgaXQgZmlyZXMgZXZlbiBpZlxuICAgIC8vIG9uZSBvZiB0aGUgaGFuZGxlcnMgYmVsb3cgY2FsbHMgc3RvcFByb3BhZ2F0aW9uKClcbiAgICBzZXJ2ZXJSb290LmFkZEV2ZW50TGlzdGVuZXIoZXZlbnROYW1lLCBoYW5kbGVyLCB0cnVlKTtcblxuICAgIC8vIG5lZWQgdG8ga2VlcCB0cmFjayBvZiBsaXN0ZW5lcnMgc28gd2UgY2FuIGRvIG5vZGUucmVtb3ZlRXZlbnRMaXN0ZW5lcigpXG4gICAgLy8gd2hlbiBwcmVib290IGRvbmVcbiAgICBpZiAocHJlYm9vdERhdGEubGlzdGVuZXJzKSB7XG4gICAgICBwcmVib290RGF0YS5saXN0ZW5lcnMucHVzaCh7XG4gICAgICAgIG5vZGU6IHNlcnZlclJvb3QsXG4gICAgICAgIGV2ZW50TmFtZSxcbiAgICAgICAgaGFuZGxlcixcbiAgICAgIH0pO1xuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogQ3JlYXRlIGhhbmRsZXIgZm9yIGV2ZW50cyB0aGF0IHdlIHdpbGwgcmVjb3JkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVMaXN0ZW5IYW5kbGVyKFxuICBfZG9jdW1lbnQ6IERvY3VtZW50LFxuICBwcmVib290RGF0YTogUHJlYm9vdERhdGEsXG4gIGV2ZW50U2VsZWN0b3I6IEV2ZW50U2VsZWN0b3IsXG4gIGFwcERhdGE6IFByZWJvb3RBcHBEYXRhXG4pOiBFdmVudExpc3RlbmVyIHtcbiAgY29uc3QgQ0FSRVRfRVZFTlRTID0gWydrZXl1cCcsICdrZXlkb3duJywgJ2ZvY3VzaW4nLCAnbW91c2V1cCcsICdtb3VzZWRvd24nXTtcbiAgY29uc3QgQ0FSRVRfTk9ERVMgPSBbJ0lOUFVUJywgJ1RFWFRBUkVBJ107XG5cbiAgLy8gU3VwcG9ydDogSUUgOS0xMSBvbmx5XG4gIC8vIElFIHVzZXMgYSBwcmVmaXhlZCBgbWF0Y2hlc2AgdmVyc2lvblxuICBjb25zdCBtYXRjaGVzID0gX2RvY3VtZW50LmRvY3VtZW50RWxlbWVudC5tYXRjaGVzIHx8IChfZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50IGFzIGFueSkubXNNYXRjaGVzU2VsZWN0b3I7XG4gIGNvbnN0IG9wdHMgPSBwcmVib290RGF0YS5vcHRzO1xuXG4gIHJldHVybiBmdW5jdGlvbiAoZXZlbnQ6IERvbUV2ZW50KSB7XG4gICAgY29uc3Qgbm9kZTogRWxlbWVudCA9IGV2ZW50LnRhcmdldDtcblxuICAgIC8vIGEgZGVsZWdhdGVkIGhhbmRsZXJzIG9uIGRvY3VtZW50IGlzIHVzZWQgc28gd2UgbmVlZCB0byBjaGVjayBpZlxuICAgIC8vIGV2ZW50IHRhcmdldCBtYXRjaGVzIGEgZGVzaXJlZCBzZWxlY3RvclxuICAgIGlmICghbWF0Y2hlcy5jYWxsKG5vZGUsIGV2ZW50U2VsZWN0b3Iuc2VsZWN0b3IpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qgcm9vdCA9IGFwcERhdGEucm9vdDtcbiAgICBjb25zdCBldmVudE5hbWUgPSBldmVudC50eXBlO1xuXG4gICAgLy8gaWYgbm8gbm9kZSBvciBubyBldmVudCBuYW1lLCBqdXN0IHJldHVyblxuICAgIGlmICghbm9kZSB8fCAhZXZlbnROYW1lKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gaWYga2V5IGNvZGVzIHNldCBmb3IgZXZlbnRTZWxlY3RvciwgdGhlbiBkb24ndCBkbyBhbnl0aGluZyBpZiBldmVudFxuICAgIC8vIGRvZXNuJ3QgaW5jbHVkZSBrZXlcbiAgICBjb25zdCBrZXlDb2RlcyA9IGV2ZW50U2VsZWN0b3Iua2V5Q29kZXM7XG4gICAgaWYgKGtleUNvZGVzICYmIGtleUNvZGVzLmxlbmd0aCkge1xuICAgICAgY29uc3QgbWF0Y2hpbmdLZXlDb2RlcyA9IGtleUNvZGVzLmZpbHRlcigoa2V5Q29kZSkgPT4gZXZlbnQud2hpY2ggPT09IGtleUNvZGUpO1xuXG4gICAgICAvLyBpZiB0aGVyZSBhcmUgbm90IG1hdGNoZXMgKGkuZS4ga2V5IGVudGVyZWQgTk9UIG9uZSBvZiB0aGUga2V5IGNvZGVzKVxuICAgICAgLy8gdGhlbiBkb24ndCBkbyBhbnl0aGluZ1xuICAgICAgaWYgKCFtYXRjaGluZ0tleUNvZGVzLmxlbmd0aCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gaWYgZm9yIGEgZ2l2ZW4gc2V0IG9mIGV2ZW50cyB3ZSBhcmUgcHJldmVudGluZyBkZWZhdWx0LCBkbyB0aGF0XG4gICAgaWYgKGV2ZW50U2VsZWN0b3IucHJldmVudERlZmF1bHQpIHtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgfVxuXG4gICAgLy8gaWYgYW4gYWN0aW9uIGhhbmRsZXIgcGFzc2VkIGluLCB1c2UgdGhhdFxuICAgIGlmIChldmVudFNlbGVjdG9yLmFjdGlvbikge1xuICAgICAgZXZlbnRTZWxlY3Rvci5hY3Rpb24obm9kZSwgZXZlbnQpO1xuICAgIH1cblxuICAgIC8vIGdldCB0aGUgbm9kZSBrZXkgZm9yIGEgZ2l2ZW4gbm9kZVxuICAgIGNvbnN0IG5vZGVLZXkgPSBnZXROb2RlS2V5Rm9yUHJlYm9vdCh7IHJvb3Q6IHJvb3QsIG5vZGU6IG5vZGUgfSk7XG5cbiAgICAvLyByZWNvcmQgYWN0aXZlIG5vZGVcbiAgICBpZiAoQ0FSRVRfRVZFTlRTLmluZGV4T2YoZXZlbnROYW1lKSA+PSAwKSB7XG4gICAgICAvLyBpZiBpdCdzIGFuIGNhcmV0IG5vZGUsIGdldCB0aGUgc2VsZWN0aW9uIGZvciB0aGUgYWN0aXZlIG5vZGVcbiAgICAgIGNvbnN0IGlzQ2FyZXROb2RlID0gQ0FSRVRfTk9ERVMuaW5kZXhPZihub2RlLnRhZ05hbWUgPyBub2RlLnRhZ05hbWUgOiAnJykgPj0gMDtcblxuICAgICAgcHJlYm9vdERhdGEuYWN0aXZlTm9kZSA9IHtcbiAgICAgICAgcm9vdDogcm9vdCxcbiAgICAgICAgbm9kZTogbm9kZSxcbiAgICAgICAgbm9kZUtleTogbm9kZUtleSxcbiAgICAgICAgc2VsZWN0aW9uOiBpc0NhcmV0Tm9kZSA/IGdldFNlbGVjdGlvbihub2RlIGFzIEhUTUxJbnB1dEVsZW1lbnQpIDogdW5kZWZpbmVkLFxuICAgICAgfTtcbiAgICB9IGVsc2UgaWYgKGV2ZW50TmFtZSAhPT0gJ2NoYW5nZScgJiYgZXZlbnROYW1lICE9PSAnZm9jdXNvdXQnKSB7XG4gICAgICBwcmVib290RGF0YS5hY3RpdmVOb2RlID0gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIC8vIGlmIG92ZXJsYXkgaXMgbm90IGRpc2FibGVkIGFuZCB3ZSBhcmUgZnJlZXppbmcgdGhlIFVJXG4gICAgaWYgKG9wdHMgJiYgIW9wdHMuZGlzYWJsZU92ZXJsYXkgJiYgZXZlbnRTZWxlY3Rvci5mcmVlemUpIHtcbiAgICAgIGNvbnN0IG92ZXJsYXkgPSByb290Lm92ZXJsYXkgYXMgSFRNTEVsZW1lbnQ7XG5cbiAgICAgIC8vIHNob3cgdGhlIG92ZXJsYXlcbiAgICAgIG92ZXJsYXkuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7XG4gICAgICBvdmVybGF5LmNsYXNzTGlzdC5hZGQoJ2lzLWxvYWRpbmcnKTtcblxuICAgICAgLy8gaGlkZSB0aGUgb3ZlcmxheSBhZnRlciAxMCBzZWNvbmRzIGp1c3QgaW4gY2FzZSBwcmVib290LmNvbXBsZXRlKCkgbmV2ZXJcbiAgICAgIC8vIGNhbGxlZFxuXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgb3ZlcmxheS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgfSwgMTAwMDApO1xuICAgIH1cblxuICAgIC8vIHdlIHdpbGwgcmVjb3JkIGV2ZW50cyBmb3IgbGF0ZXIgcmVwbGF5IHVubGVzcyBleHBsaWNpdGx5IG1hcmtlZCBhc1xuICAgIC8vIGRvTm90UmVwbGF5XG4gICAgaWYgKGV2ZW50U2VsZWN0b3IucmVwbGF5KSB7XG4gICAgICBhcHBEYXRhLmV2ZW50cy5wdXNoKHtcbiAgICAgICAgbm9kZSxcbiAgICAgICAgbm9kZUtleSxcbiAgICAgICAgZXZlbnQsXG4gICAgICAgIG5hbWU6IGV2ZW50TmFtZSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfTtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIHNlbGVjdGlvbiBkYXRhIHRoYXQgaXMgbGF0ZXIgdXNlZCB0byBzZXQgdGhlIGN1cnNvciBhZnRlciBjbGllbnQgdmlld1xuICogaXMgYWN0aXZlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRTZWxlY3Rpb24obm9kZTogSFRNTElucHV0RWxlbWVudCk6IFByZWJvb3RTZWxlY3Rpb24ge1xuICBub2RlID0gbm9kZSB8fCAoe30gYXMgSFRNTElucHV0RWxlbWVudCk7XG5cbiAgY29uc3Qgbm9kZVZhbHVlID0gbm9kZS52YWx1ZSB8fCAnJztcbiAgY29uc3Qgc2VsZWN0aW9uOiBQcmVib290U2VsZWN0aW9uID0ge1xuICAgIHN0YXJ0OiBub2RlVmFsdWUubGVuZ3RoLFxuICAgIGVuZDogbm9kZVZhbHVlLmxlbmd0aCxcbiAgICBkaXJlY3Rpb246ICdmb3J3YXJkJyxcbiAgfTtcblxuICAvLyBpZiBicm93c2VyIHN1cHBvcnQgc2VsZWN0aW9uU3RhcnQgb24gbm9kZSAoQ2hyb21lLCBGaXJlRm94LCBJRTkrKVxuICB0cnkge1xuICAgIGlmIChub2RlLnNlbGVjdGlvblN0YXJ0IHx8IG5vZGUuc2VsZWN0aW9uU3RhcnQgPT09IDApIHtcbiAgICAgIHNlbGVjdGlvbi5zdGFydCA9IG5vZGUuc2VsZWN0aW9uU3RhcnQ7XG4gICAgICBzZWxlY3Rpb24uZW5kID0gbm9kZS5zZWxlY3Rpb25FbmQgPyBub2RlLnNlbGVjdGlvbkVuZCA6IDA7XG4gICAgICBzZWxlY3Rpb24uZGlyZWN0aW9uID0gbm9kZS5zZWxlY3Rpb25EaXJlY3Rpb24gPyAobm9kZS5zZWxlY3Rpb25EaXJlY3Rpb24gYXMgUHJlYm9vdFNlbGVjdGlvbkRpcmVjdGlvbikgOiAnbm9uZSc7XG4gICAgfVxuICB9IGNhdGNoIChleCkge31cblxuICByZXR1cm4gc2VsZWN0aW9uO1xufVxuXG4vKipcbiAqIENyZWF0ZSBidWZmZXIgZm9yIGEgZ2l2ZW4gbm9kZVxuICpcbiAqIEBwYXJhbSByb290IEFsbCB0aGUgZGF0YSByZWxhdGVkIHRvIGEgcGFydGljdWxhciBhcHBcbiAqIEByZXR1cm5zIFJldHVybnMgdGhlIHJvb3QgY2xpZW50IG5vZGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVCdWZmZXIocm9vdDogU2VydmVyQ2xpZW50Um9vdCk6IEhUTUxFbGVtZW50IHtcbiAgY29uc3Qgc2VydmVyTm9kZSA9IHJvb3Quc2VydmVyTm9kZTtcblxuICAvLyBpZiBubyByb290U2VydmVyTm9kZSBPUiB0aGUgc2VsZWN0b3IgaXMgb24gdGhlIGVudGlyZSBodG1sIGRvYyBvciB0aGUgYm9keVxuICAvLyBPUiBubyBwYXJlbnROb2RlLCBkb24ndCBidWZmZXJcbiAgaWYgKCFzZXJ2ZXJOb2RlIHx8ICFzZXJ2ZXJOb2RlLnBhcmVudE5vZGUgfHwgc2VydmVyTm9kZSA9PT0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50IHx8IHNlcnZlck5vZGUgPT09IGRvY3VtZW50LmJvZHkpIHtcbiAgICByZXR1cm4gc2VydmVyTm9kZSBhcyBIVE1MRWxlbWVudDtcbiAgfVxuXG4gIC8vIGNyZWF0ZSBzaGFsbG93IGNsb25lIG9mIHNlcnZlciByb290XG4gIGNvbnN0IHJvb3RDbGllbnROb2RlID0gc2VydmVyTm9kZS5jbG9uZU5vZGUoZmFsc2UpIGFzIEhUTUxFbGVtZW50O1xuICAvLyB3ZSB3YW50IHRoZSBjbGllbnQgdG8gd3JpdGUgdG8gYSBoaWRkZW4gZGl2IHVudGlsIHRoZSB0aW1lIGZvciBzd2l0Y2hpbmdcbiAgLy8gdGhlIGJ1ZmZlcnNcbiAgcm9vdENsaWVudE5vZGUuc3R5bGUuZGlzcGxheSA9ICdub25lJztcblxuICAvLyBpbnNlcnQgdGhlIGNsaWVudCBub2RlIGJlZm9yZSB0aGUgc2VydmVyIGFuZCByZXR1cm4gaXRcbiAgc2VydmVyTm9kZS5wYXJlbnROb2RlLmluc2VydEJlZm9yZShyb290Q2xpZW50Tm9kZSwgc2VydmVyTm9kZSk7XG5cbiAgLy8gbWFyayBzZXJ2ZXIgbm9kZSBhcyBub3QgdG8gYmUgdG91Y2hlZCBieSBBbmd1bGFySlMgLSBuZWVkZWQgZm9yIG5nVXBncmFkZVxuICBzZXJ2ZXJOb2RlLnNldEF0dHJpYnV0ZSgnbmctbm9uLWJpbmRhYmxlJywgJycpO1xuXG4gIC8vIHJldHVybiB0aGUgcm9vdENsaWVudE5vZGVcbiAgcmV0dXJuIHJvb3RDbGllbnROb2RlO1xufVxuIl19