ngx-preboot
Version:
Record server view events and play back to Angular client view
271 lines • 35.9 kB
JavaScript
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