UNPKG

33.3 kBJavaScriptView Raw
1import { getNodeKeyForPreboot } from '../common/get-node-key';
2export function _window() {
3 return {
4 prebootData: window['prebootData'],
5 getComputedStyle: window.getComputedStyle,
6 document: document
7 };
8}
9export class EventReplayer {
10 constructor() {
11 this.clientNodeCache = {};
12 this.replayStarted = false;
13 }
14 /**
15 * Window setting and getter to facilitate testing of window
16 * in non-browser environments
17 */
18 setWindow(win) {
19 this.win = win;
20 }
21 /**
22 * Window setting and getter to facilitate testing of window
23 * in non-browser environments
24 */
25 getWindow() {
26 if (!this.win) {
27 this.win = _window();
28 }
29 return this.win;
30 }
31 /**
32 * Replay all events for all apps. this can only be run once.
33 * if called multiple times, will only do something once
34 */
35 replayAll() {
36 if (this.replayStarted) {
37 return;
38 }
39 else {
40 this.replayStarted = true;
41 }
42 // loop through each of the preboot apps
43 const prebootData = this.getWindow().prebootData || {};
44 const apps = prebootData.apps || [];
45 apps.forEach(appData => this.replayForApp(appData));
46 // once all events have been replayed and buffers switched, then we cleanup preboot
47 this.cleanup(prebootData);
48 }
49 /**
50 * Replay all events for one app (most of the time there is just one app)
51 * @param appData
52 */
53 replayForApp(appData) {
54 appData = (appData || {});
55 // try catch around events b/c even if error occurs, we still move forward
56 try {
57 const events = appData.events || [];
58 // replay all the events from the server view onto the client view
59 events.forEach(event => this.replayEvent(appData, event));
60 }
61 catch (ex) {
62 console.error(ex);
63 }
64 // if we are buffering, switch the buffers
65 this.switchBuffer(appData);
66 }
67 /**
68 * Replay one particular event
69 * @param appData
70 * @param prebootEvent
71 */
72 replayEvent(appData, prebootEvent) {
73 appData = (appData || {});
74 prebootEvent = (prebootEvent || {});
75 const event = prebootEvent.event;
76 const serverNode = prebootEvent.node || {};
77 const nodeKey = prebootEvent.nodeKey;
78 const clientNode = this.findClientNode({
79 root: appData.root,
80 node: serverNode,
81 nodeKey: nodeKey
82 });
83 // if client node can't be found, log a warning
84 if (!clientNode) {
85 console.warn(`Trying to dispatch event ${event.type} to node ${nodeKey}
86 but could not find client node. Server node is: ${serverNode}`);
87 return;
88 }
89 // now dispatch events and whatnot to the client node
90 clientNode.checked = serverNode.checked;
91 clientNode.selected = serverNode.selected;
92 clientNode.value = serverNode.value;
93 clientNode.dispatchEvent(event);
94 }
95 /**
96 * Switch the buffer for one particular app (i.e. display the client
97 * view and destroy the server view)
98 * @param appData
99 */
100 switchBuffer(appData) {
101 appData = (appData || {});
102 const root = (appData.root || {});
103 const serverView = root.serverNode;
104 const clientView = root.clientNode;
105 // if no client view or the server view is the body or client
106 // and server view are the same, then don't do anything and return
107 if (!clientView || !serverView || serverView === clientView || serverView.nodeName === 'BODY') {
108 return;
109 }
110 // do a try-catch just in case something messed up
111 try {
112 // get the server view display mode
113 const gcs = this.getWindow().getComputedStyle;
114 const display = gcs(serverView).getPropertyValue('display') || 'block';
115 // first remove the server view
116 serverView.remove ? serverView.remove() : (serverView.style.display = 'none');
117 // now add the client view
118 clientView.style.display = display;
119 }
120 catch (ex) {
121 console.error(ex);
122 }
123 }
124 /**
125 * Finally, set focus, remove all the event listeners and remove
126 * any freeze screen that may be there
127 * @param prebootData
128 */
129 cleanup(prebootData) {
130 prebootData = prebootData || {};
131 const listeners = prebootData.listeners || [];
132 // set focus on the active node AFTER a small delay to ensure buffer
133 // switched
134 const activeNode = prebootData.activeNode;
135 if (activeNode != null) {
136 setTimeout(() => this.setFocus(activeNode), 1);
137 }
138 // remove all event listeners
139 for (const listener of listeners) {
140 listener.node.removeEventListener(listener.eventName, listener.handler);
141 }
142 // remove the freeze overlay if it exists
143 const doc = this.getWindow().document;
144 const prebootOverlay = doc.getElementById('prebootOverlay');
145 if (prebootOverlay) {
146 prebootOverlay.remove ?
147 prebootOverlay.remove() : prebootOverlay.parentNode !== null ?
148 prebootOverlay.parentNode.removeChild(prebootOverlay) :
149 prebootOverlay.style.display = 'none';
150 }
151 // clear out the data stored for each app
152 prebootData.apps = [];
153 this.clientNodeCache = {};
154 // send event to document that signals preboot complete
155 // constructor is not supported by older browsers ( i.e. IE9-11 )
156 // in these browsers, the type of CustomEvent will be "object"
157 if (typeof CustomEvent === 'function') {
158 const completeEvent = new CustomEvent('PrebootComplete');
159 doc.dispatchEvent(completeEvent);
160 }
161 else {
162 console.warn(`Could not dispatch PrebootComplete event.
163 You can fix this by including a polyfill for CustomEvent.`);
164 }
165 }
166 setFocus(activeNode) {
167 // only do something if there is an active node
168 if (!activeNode || !activeNode.node || !activeNode.nodeKey) {
169 return;
170 }
171 // find the client node in the new client view
172 const clientNode = this.findClientNode(activeNode);
173 if (clientNode) {
174 // set focus on the client node
175 clientNode.focus();
176 // set selection if a modern browser (i.e. IE9+, etc.)
177 const selection = activeNode.selection;
178 if (clientNode.setSelectionRange && selection) {
179 try {
180 clientNode
181 .setSelectionRange(selection.start, selection.end, selection.direction);
182 }
183 catch (ex) { }
184 }
185 }
186 }
187 /**
188 * Given a node from the server rendered view, find the equivalent
189 * node in the client rendered view. We do this by the following approach:
190 * 1. take the name of the server node tag (ex. div or h1 or input)
191 * 2. add either id (ex. div#myid) or class names (ex. div.class1.class2)
192 * 3. use that value as a selector to get all the matching client nodes
193 * 4. loop through all client nodes found and for each generate a key value
194 * 5. compare the client key to the server key; once there is a match,
195 * we have our client node
196 *
197 * NOTE: this only works when the client view is almost exactly the same as
198 * the server view. we will need an improvement here in the future to account
199 * for situations where the client view is different in structure from the
200 * server view
201 */
202 findClientNode(serverNodeContext) {
203 serverNodeContext = (serverNodeContext || {});
204 const serverNode = serverNodeContext.node;
205 const root = serverNodeContext.root;
206 // if no server or client root, don't do anything
207 if (!root || !root.serverNode || !root.clientNode) {
208 return null;
209 }
210 // we use the string of the node to compare to the client node & as key in
211 // cache
212 const serverNodeKey = serverNodeContext.nodeKey || getNodeKeyForPreboot(serverNodeContext);
213 // if client node already in cache, return it
214 if (this.clientNodeCache[serverNodeKey]) {
215 return this.clientNodeCache[serverNodeKey];
216 }
217 // get the selector for client nodes
218 const className = (serverNode.className || '').replace('ng-binding', '').trim();
219 let selector = serverNode.tagName;
220 if (serverNode.id) {
221 selector += `#${serverNode.id}`;
222 }
223 else if (className) {
224 selector += `.${className.replace(/ /g, '.')}`;
225 }
226 // select all possible client nodes and look through them to try and find a
227 // match
228 const rootClientNode = root.clientNode;
229 let clientNodes = rootClientNode.querySelectorAll(selector);
230 // if nothing found, then just try the tag name as a final option
231 if (!clientNodes.length) {
232 console.log(`nothing found for ${selector} so using ${serverNode.tagName}`);
233 clientNodes = rootClientNode.querySelectorAll(serverNode.tagName);
234 }
235 const length = clientNodes.length;
236 for (let i = 0; i < length; i++) {
237 const clientNode = clientNodes.item(i);
238 // get the key for the client node
239 const clientNodeKey = getNodeKeyForPreboot({
240 root: root,
241 node: clientNode
242 });
243 // if the client node key is exact match for the server node key, then we
244 // found the client node
245 if (clientNodeKey === serverNodeKey) {
246 this.clientNodeCache[serverNodeKey] = clientNode;
247 return clientNode;
248 }
249 }
250 // if we get here and there is one clientNode, use it as a fallback
251 if (clientNodes.length === 1) {
252 this.clientNodeCache[serverNodeKey] = clientNodes[0];
253 return clientNodes[0];
254 }
255 // if we get here it means we couldn't find the client node so give the user
256 // a warning
257 console.warn(`No matching client node found for ${serverNodeKey}.
258 You can fix this by assigning this element a unique id attribute.`);
259 return null;
260 }
261}
262//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnQucmVwbGF5ZXIuanMiLCJzb3VyY2VSb290IjoiLi4vLi4vc3JjL2xpYi8iLCJzb3VyY2VzIjpbImFwaS9ldmVudC5yZXBsYXllci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFlQSxPQUFPLEVBQUMsb0JBQW9CLEVBQUMsTUFBTSx3QkFBd0IsQ0FBQztBQUU1RCxNQUFNLFVBQVUsT0FBTztJQUNyQixPQUFPO1FBQ0wsV0FBVyxFQUFHLE1BQWMsQ0FBQyxhQUFhLENBQUM7UUFDM0MsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGdCQUFnQjtRQUN6QyxRQUFRLEVBQUUsUUFBUTtLQUNuQixDQUFDO0FBQ0osQ0FBQztBQUVELE1BQU0sT0FBTyxhQUFhO0lBQTFCO1FBQ0Usb0JBQWUsR0FBK0IsRUFBRSxDQUFDO1FBQ2pELGtCQUFhLEdBQUcsS0FBSyxDQUFDO0lBa1N4QixDQUFDO0lBL1JDOzs7T0FHRztJQUNILFNBQVMsQ0FBQyxHQUFrQjtRQUMxQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztJQUNqQixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsU0FBUztRQUNQLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ2IsSUFBSSxDQUFDLEdBQUcsR0FBRyxPQUFPLEVBQUUsQ0FBQztTQUN0QjtRQUNELE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQztJQUNsQixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsU0FBUztRQUNQLElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUN0QixPQUFPO1NBQ1I7YUFBTTtZQUNMLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDO1NBQzNCO1FBRUQsd0NBQXdDO1FBQ3hDLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxXQUFXLElBQUksRUFBRSxDQUFDO1FBQ3ZELE1BQU0sSUFBSSxHQUFHLFdBQVcsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxPQUFPLENB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QyxDQUFDLENBQUMsQ0FBQztZQUNyRCxPQUFPLFdBQVcsQ0FBQyxDQUFDLENBQWdCLENBQUM7U0FDdEM7UUFFRCw0RUFBNEU7UUFDNUUsWUFBWTtRQUNaLE9BQU8sQ0FBQyxJQUFJLENBQ1YscUNBQXFDLGFBQWE7eUVBQ2lCLENBQ3BFLENBQUM7UUFDRixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuaW1wb3J0IHtcbiAgTm9kZUNvbnRleHQsXG4gIFByZWJvb3RBcHBEYXRhLFxuICBQcmVib290RGF0YSxcbiAgUHJlYm9vdEV2ZW50LFxuICBQcmVib290V2luZG93LFxuICBTZXJ2ZXJDbGllbnRSb290LFxufSBmcm9tICcuLi9jb21tb24vcHJlYm9vdC5pbnRlcmZhY2VzJztcbmltcG9ydCB7Z2V0Tm9kZUtleUZvclByZWJvb3R9IGZyb20gJy4uL2NvbW1vbi9nZXQtbm9kZS1rZXknO1xuXG5leHBvcnQgZnVuY3Rpb24gX3dpbmRvdygpOiBQcmVib290V2luZG93IHtcbiAgcmV0dXJuIHtcbiAgICBwcmVib290RGF0YTogKHdpbmRvdyBhcyBhbnkpWydwcmVib290RGF0YSddLFxuICAgIGdldENvbXB1dGVkU3R5bGU6IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlLFxuICAgIGRvY3VtZW50OiBkb2N1bWVudFxuICB9O1xufVxuXG5leHBvcnQgY2xhc3MgRXZlbnRSZXBsYXllciB7XG4gIGNsaWVudE5vZGVDYWNoZTogeyBba2V5OiBzdHJpbmddOiBFbGVtZW50IH0gPSB7fTtcbiAgcmVwbGF5U3RhcnRlZCA9IGZhbHNlO1xuICB3aW46IFByZWJvb3RXaW5kb3c7XG5cbiAgLyoqXG4gICAqIFdpbmRvdyBzZXR0aW5nIGFuZCBnZXR0ZXIgdG8gZmFjaWxpdGF0ZSB0ZXN0aW5nIG9mIHdpbmRvd1xuICAgKiBpbiBub24tYnJvd3NlciBlbnZpcm9ubWVudHNcbiAgICovXG4gIHNldFdpbmRvdyh3aW46IFByZWJvb3RXaW5kb3cpIHtcbiAgICB0aGlzLndpbiA9IHdpbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaW5kb3cgc2V0dGluZyBhbmQgZ2V0dGVyIHRvIGZhY2lsaXRhdGUgdGVzdGluZyBvZiB3aW5kb3dcbiAgICogaW4gbm9uLWJyb3dzZXIgZW52aXJvbm1lbnRzXG4gICAqL1xuICBnZXRXaW5kb3coKSB7XG4gICAgaWYgKCF0aGlzLndpbikge1xuICAgICAgdGhpcy53aW4gPSBfd2luZG93KCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLndpbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYXkgYWxsIGV2ZW50cyBmb3IgYWxsIGFwcHMuIHRoaXMgY2FuIG9ubHkgYmUgcnVuIG9uY2UuXG4gICAqIGlmIGNhbGxlZCBtdWx0aXBsZSB0aW1lcywgd2lsbCBvbmx5IGRvIHNvbWV0aGluZyBvbmNlXG4gICAqL1xuICByZXBsYXlBbGwoKSB7XG4gICAgaWYgKHRoaXMucmVwbGF5U3RhcnRlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlcGxheVN0YXJ0ZWQgPSB0cnVlO1xuICAgIH1cblxuICAgIC8vIGxvb3AgdGhyb3VnaCBlYWNoIG9mIHRoZSBwcmVib290IGFwcHNcbiAgICBjb25zdCBwcmVib290RGF0YSA9IHRoaXMuZ2V0V2luZG93KCkucHJlYm9vdERhdGEgfHwge307XG4gICAgY29uc3QgYXBwcyA9IHByZWJvb3REYXRhLmFwcHMgfHwgW107XG4gICAgYXBwcy5mb3JFYWNoKGFwcERhdGEgPT4gdGhpcy5yZXBsYXlGb3JBcHAoYXBwRGF0YSkpO1xuXG4gICAgLy8gb25jZSBhbGwgZXZlbnRzIGhhdmUgYmVlbiByZXBsYXllZCBhbmQgYnVmZmVycyBzd2l0Y2hlZCwgdGhlbiB3ZSBjbGVhbnVwIHByZWJvb3RcbiAgICB0aGlzLmNsZWFudXAocHJlYm9vdERhdGEpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcGxheSBhbGwgZXZlbnRzIGZvciBvbmUgYXBwIChtb3N0IG9mIHRoZSB0aW1lIHRoZXJlIGlzIGp1c3Qgb25lIGFwcClcbiAgICogQHBhcmFtIGFwcERhdGFcbiAgICovXG4gIHJlcGxheUZvckFwcChhcHBEYXRhOiBQcmVib290QXBwRGF0YSkge1xuICAgIGFwcERhdGEgPSA8UHJlYm9vdEFwcERhdGE+KGFwcERhdGEgfHwge30pO1xuXG4gICAgLy8gdHJ5IGNhdGNoIGFyb3VuZCBldmVudHMgYi9jIGV2ZW4gaWYgZXJyb3Igb2NjdXJzLCB3ZSBzdGlsbCBtb3ZlIGZvcndhcmRcbiAgICB0cnkge1xuICAgICAgY29uc3QgZXZlbnRzID0gYXBwRGF0YS5ldmVudHMgfHwgW107XG5cbiAgICAgIC8vIHJlcGxheSBhbGwgdGhlIGV2ZW50cyBmcm9tIHRoZSBzZXJ2ZXIgdmlldyBvbnRvIHRoZSBjbGllbnQgdmlld1xuICAgICAgZXZlbnRzLmZvckVhY2goZXZlbnQgPT4gdGhpcy5yZXBsYXlFdmVudChhcHBEYXRhLCBldmVudCkpO1xuICAgIH0gY2F0Y2ggKGV4KSB7XG4gICAgICBjb25zb2xlLmVycm9yKGV4KTtcbiAgICB9XG5cbiAgICAvLyBpZiB3ZSBhcmUgYnVmZmVyaW5nLCBzd2l0Y2ggdGhlIGJ1ZmZlcnNcbiAgICB0aGlzLnN3aXRjaEJ1ZmZlcihhcHBEYXRhKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYXkgb25lIHBhcnRpY3VsYXIgZXZlbnRcbiAgICogQHBhcmFtIGFwcERhdGFcbiAgICogQHBhcmFtIHByZWJvb3RFdmVudFxuICAgKi9cbiAgcmVwbGF5RXZlbnQoYXBwRGF0YTogUHJlYm9vdEFwcERhdGEsIHByZWJvb3RFdmVudDogUHJlYm9vdEV2ZW50KSB7XG4gICAgYXBwRGF0YSA9IDxQcmVib290QXBwRGF0YT4oYXBwRGF0YSB8fCB7fSk7XG4gICAgcHJlYm9vdEV2ZW50ID0gPFByZWJvb3RFdmVudD4ocHJlYm9vdEV2ZW50IHx8IHt9KTtcblxuICAgIGNvbnN0IGV2ZW50ID0gcHJlYm9vdEV2ZW50LmV2ZW50IGFzIEV2ZW50O1xuICAgIGNvbnN0IHNlcnZlck5vZGUgPSBwcmVib290RXZlbnQubm9kZSB8fCB7fTtcbiAgICBjb25zdCBub2RlS2V5ID0gcHJlYm9vdEV2ZW50Lm5vZGVLZXk7XG4gICAgY29uc3QgY2xpZW50Tm9kZSA9IHRoaXMuZmluZENsaWVudE5vZGUoe1xuICAgICAgcm9vdDogYXBwRGF0YS5yb290LFxuICAgICAgbm9kZTogc2VydmVyTm9kZSxcbiAgICAgIG5vZGVLZXk6IG5vZGVLZXlcbiAgICB9KTtcblxuICAgIC8vIGlmIGNsaWVudCBub2RlIGNhbid0IGJlIGZvdW5kLCBsb2cgYSB3YXJuaW5nXG4gICAgaWYgKCFjbGllbnROb2RlKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgIGBUcnlpbmcgdG8gZGlzcGF0Y2ggZXZlbnQgJHtldmVudC50eXBlfSB0byBub2RlICR7bm9kZUtleX1cbiAgICAgICAgYnV0IGNvdWxkIG5vdCBmaW5kIGNsaWVudCBub2RlLiBTZXJ2ZXIgbm9kZSBpczogJHtzZXJ2ZXJOb2RlfWBcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gbm93IGRpc3BhdGNoIGV2ZW50cyBhbmQgd2hhdG5vdCB0byB0aGUgY2xpZW50IG5vZGVcbiAgICAoY2xpZW50Tm9kZSBhcyBIVE1MSW5wdXRFbGVtZW50KS5jaGVja2VkID0gc2VydmVyTm9kZS5jaGVja2VkO1xuICAgIChjbGllbnROb2RlIGFzIEhUTUxPcHRpb25FbGVtZW50KS5zZWxlY3RlZCA9IHNlcnZlck5vZGUuc2VsZWN0ZWQ7XG4gICAgKGNsaWVudE5vZGUgYXMgSFRNTE9wdGlvbkVsZW1lbnQpLnZhbHVlID0gc2VydmVyTm9kZS52YWx1ZTtcbiAgICBjbGllbnROb2RlLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFN3aXRjaCB0aGUgYnVmZmVyIGZvciBvbmUgcGFydGljdWxhciBhcHAgKGkuZS4gZGlzcGxheSB0aGUgY2xpZW50XG4gICAqIHZpZXcgYW5kIGRlc3Ryb3kgdGhlIHNlcnZlciB2aWV3KVxuICAgKiBAcGFyYW0gYXBwRGF0YVxuICAgKi9cbiAgc3dpdGNoQnVmZmVyKGFwcERhdGE6IFByZWJvb3RBcHBEYXRhKSB7XG4gICAgYXBwRGF0YSA9IDxQcmVib290QXBwRGF0YT4oYXBwRGF0YSB8fCB7fSk7XG5cbiAgICBjb25zdCByb290ID0gPFNlcnZlckNsaWVudFJvb3Q+KGFwcERhdGEucm9vdCB8fCB7fSk7XG4gICAgY29uc3Qgc2VydmVyVmlldyA9IHJvb3Quc2VydmVyTm9kZTtcbiAgICBjb25zdCBjbGllbnRWaWV3ID0gcm9vdC5jbGllbnROb2RlO1xuXG4gICAgLy8gaWYgbm8gY2xpZW50IHZpZXcgb3IgdGhlIHNlcnZlciB2aWV3IGlzIHRoZSBib2R5IG9yIGNsaWVudFxuICAgIC8vIGFuZCBzZXJ2ZXIgdmlldyBhcmUgdGhlIHNhbWUsIHRoZW4gZG9uJ3QgZG8gYW55dGhpbmcgYW5kIHJldHVyblxuICAgIGlmICghY2xpZW50VmlldyB8fCAhc2VydmVyVmlldyB8fCBzZXJ2ZXJWaWV3ID09PSBjbGllbnRWaWV3IHx8IHNlcnZlclZpZXcubm9kZU5hbWUgPT09ICdCT0RZJykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIGRvIGEgdHJ5LWNhdGNoIGp1c3QgaW4gY2FzZSBzb21ldGhpbmcgbWVzc2VkIHVwXG4gICAgdHJ5IHtcbiAgICAgIC8vIGdldCB0aGUgc2VydmVyIHZpZXcgZGlzcGxheSBtb2RlXG4gICAgICBjb25zdCBnY3MgPSB0aGlzLmdldFdpbmRvdygpLmdldENvbXB1dGVkU3R5bGU7XG4gICAgICBjb25zdCBkaXNwbGF5ID0gZ2NzKHNlcnZlclZpZXcpLmdldFByb3BlcnR5VmFsdWUoJ2Rpc3BsYXknKSB8fCAnYmxvY2snO1xuXG4gICAgICAvLyBmaXJzdCByZW1vdmUgdGhlIHNlcnZlciB2aWV3XG4gICAgICBzZXJ2ZXJWaWV3LnJlbW92ZSA/IHNlcnZlclZpZXcucmVtb3ZlKCkgOiAoc2VydmVyVmlldy5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnKTtcblxuICAgICAgLy8gbm93IGFkZCB0aGUgY2xpZW50IHZpZXdcbiAgICAgIGNsaWVudFZpZXcuc3R5bGUuZGlzcGxheSA9IGRpc3BsYXk7XG4gICAgfSBjYXRjaCAoZXgpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoZXgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5hbGx5LCBzZXQgZm9jdXMsIHJlbW92ZSBhbGwgdGhlIGV2ZW50IGxpc3RlbmVycyBhbmQgcmVtb3ZlXG4gICAqIGFueSBmcmVlemUgc2NyZWVuIHRoYXQgbWF5IGJlIHRoZXJlXG4gICAqIEBwYXJhbSBwcmVib290RGF0YVxuICAgKi9cbiAgY2xlYW51cChwcmVib290RGF0YTogUHJlYm9vdERhdGEpIHtcbiAgICBwcmVib290RGF0YSA9IHByZWJvb3REYXRhIHx8IHt9O1xuXG4gICAgY29uc3QgbGlzdGVuZXJzID0gcHJlYm9vdERhdGEubGlzdGVuZXJzIHx8IFtdO1xuXG4gICAgLy8gc2V0IGZvY3VzIG9uIHRoZSBhY3RpdmUgbm9kZSBBRlRFUiBhIHNtYWxsIGRlbGF5IHRvIGVuc3VyZSBidWZmZXJcbiAgICAvLyBzd2l0Y2hlZFxuICAgIGNvbnN0IGFjdGl2ZU5vZGUgPSBwcmVib290RGF0YS5hY3RpdmVOb2RlO1xuICAgIGlmIChhY3RpdmVOb2RlICE9IG51bGwpIHtcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy5zZXRGb2N1cyhhY3RpdmVOb2RlKSwgMSk7XG4gICAgfVxuXG4gICAgLy8gcmVtb3ZlIGFsbCBldmVudCBsaXN0ZW5lcnNcbiAgICBmb3IgKGNvbnN0IGxpc3RlbmVyIG9mIGxpc3RlbmVycykge1xuICAgICAgbGlzdGVuZXIubm9kZS5yZW1vdmVFdmVudExpc3RlbmVyKGxpc3RlbmVyLmV2ZW50TmFtZSwgbGlzdGVuZXIuaGFuZGxlcik7XG4gICAgfVxuXG4gICAgLy8gcmVtb3ZlIHRoZSBmcmVlemUgb3ZlcmxheSBpZiBpdCBleGlzdHNcbiAgICBjb25zdCBkb2MgPSB0aGlzLmdldFdpbmRvdygpLmRvY3VtZW50O1xuICAgIGNvbnN0IHByZWJvb3RPdmVybGF5ID0gZG9jLmdldEVsZW1lbnRCeUlkKCdwcmVib290T3ZlcmxheScpO1xuICAgIGlmIChwcmVib290T3ZlcmxheSkge1xuICAgICAgcHJlYm9vdE92ZXJsYXkucmVtb3ZlID9cbiAgICAgICAgcHJlYm9vdE92ZXJsYXkucmVtb3ZlKCkgOiBwcmVib290T3ZlcmxheS5wYXJlbnROb2RlICE9PSBudWxsID9cbiAgICAgICAgcHJlYm9vdE92ZXJsYXkucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChwcmVib290T3ZlcmxheSkgOlxuICAgICAgICBwcmVib290T3ZlcmxheS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgIH1cblxuICAgIC8vIGNsZWFyIG91dCB0aGUgZGF0YSBzdG9yZWQgZm9yIGVhY2ggYXBwXG4gICAgcHJlYm9vdERhdGEuYXBwcyA9IFtdO1xuICAgIHRoaXMuY2xpZW50Tm9kZUNhY2hlID0ge307XG5cbiAgICAvLyBzZW5kIGV2ZW50IHRvIGRvY3VtZW50IHRoYXQgc2lnbmFscyBwcmVib290IGNvbXBsZXRlXG4gICAgLy8gY29uc3RydWN0b3IgaXMgbm90IHN1cHBvcnRlZCBieSBvbGRlciBicm93c2VycyAoIGkuZS4gSUU5LTExIClcbiAgICAvLyBpbiB0aGVzZSBicm93c2VycywgdGhlIHR5cGUgb2YgQ3VzdG9tRXZlbnQgd2lsbCBiZSBcIm9iamVjdFwiXG4gICAgaWYgKHR5cGVvZiBDdXN0b21FdmVudCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgY29uc3QgY29tcGxldGVFdmVudCA9IG5ldyBDdXN0b21FdmVudCgnUHJlYm9vdENvbXBsZXRlJyk7XG4gICAgICBkb2MuZGlzcGF0Y2hFdmVudChjb21wbGV0ZUV2ZW50KTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc29sZS53YXJuKGBDb3VsZCBub3QgZGlzcGF0Y2ggUHJlYm9vdENvbXBsZXRlIGV2ZW50LlxuICAgICAgIFlvdSBjYW4gZml4IHRoaXMgYnkgaW5jbHVkaW5nIGEgcG9seWZpbGwgZm9yIEN1c3RvbUV2ZW50LmApO1xuICAgIH1cbiAgfVxuXG4gIHNldEZvY3VzKGFjdGl2ZU5vZGU6IE5vZGVDb250ZXh0KSB7XG4gICAgLy8gb25seSBkbyBzb21ldGhpbmcgaWYgdGhlcmUgaXMgYW4gYWN0aXZlIG5vZGVcbiAgICBpZiAoIWFjdGl2ZU5vZGUgfHwgIWFjdGl2ZU5vZGUubm9kZSB8fCAhYWN0aXZlTm9kZS5ub2RlS2V5KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gZmluZCB0aGUgY2xpZW50IG5vZGUgaW4gdGhlIG5ldyBjbGllbnQgdmlld1xuICAgIGNvbnN0IGNsaWVudE5vZGUgPSB0aGlzLmZpbmRDbGllbnROb2RlKGFjdGl2ZU5vZGUpO1xuICAgIGlmIChjbGllbnROb2RlKSB7XG4gICAgICAvLyBzZXQgZm9jdXMgb24gdGhlIGNsaWVudCBub2RlXG4gICAgICBjbGllbnROb2RlLmZvY3VzKCk7XG5cbiAgICAgIC8vIHNldCBzZWxlY3Rpb24gaWYgYSBtb2Rlcm4gYnJvd3NlciAoaS5lLiBJRTkrLCBldGMuKVxuICAgICAgY29uc3Qgc2VsZWN0aW9uID0gYWN0aXZlTm9kZS5zZWxlY3Rpb247XG4gICAgICBpZiAoKGNsaWVudE5vZGUgYXMgSFRNTElucHV0RWxlbWVudCkuc2V0U2VsZWN0aW9uUmFuZ2UgJiYgc2VsZWN0aW9uKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgKGNsaWVudE5vZGUgYXMgSFRNTElucHV0RWxlbWVudClcbiAgICAgICAgICAgIC5zZXRTZWxlY3Rpb25SYW5nZShzZWxlY3Rpb24uc3RhcnQsIHNlbGVjdGlvbi5lbmQsIHNlbGVjdGlvbi5kaXJlY3Rpb24pO1xuICAgICAgICB9IGNhdGNoIChleCkge31cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2l2ZW4gYSBub2RlIGZyb20gdGhlIHNlcnZlciByZW5kZXJlZCB2aWV3LCBmaW5kIHRoZSBlcXVpdmFsZW50XG4gICAqIG5vZGUgaW4gdGhlIGNsaWVudCByZW5kZXJlZCB2aWV3LiBXZSBkbyB0aGlzIGJ5IHRoZSBmb2xsb3dpbmcgYXBwcm9hY2g6XG4gICAqICAgICAgMS4gdGFrZSB0aGUgbmFtZSBvZiB0aGUgc2VydmVyIG5vZGUgdGFnIChleC4gZGl2IG9yIGgxIG9yIGlucHV0KVxuICAgKiAgICAgIDIuIGFkZCBlaXRoZXIgaWQgKGV4LiBkaXYjbXlpZCkgb3IgY2xhc3MgbmFtZXMgKGV4LiBkaXYuY2xhc3MxLmNsYXNzMilcbiAgICogICAgICAzLiB1c2UgdGhhdCB2YWx1ZSBhcyBhIHNlbGVjdG9yIHRvIGdldCBhbGwgdGhlIG1hdGNoaW5nIGNsaWVudCBub2Rlc1xuICAgKiAgICAgIDQuIGxvb3AgdGhyb3VnaCBhbGwgY2xpZW50IG5vZGVzIGZvdW5kIGFuZCBmb3IgZWFjaCBnZW5lcmF0ZSBhIGtleSB2YWx1ZVxuICAgKiAgICAgIDUuIGNvbXBhcmUgdGhlIGNsaWVudCBrZXkgdG8gdGhlIHNlcnZlciBrZXk7IG9uY2UgdGhlcmUgaXMgYSBtYXRjaCxcbiAgICogICAgICAgICAgd2UgaGF2ZSBvdXIgY2xpZW50IG5vZGVcbiAgICpcbiAgICogTk9URTogdGhpcyBvbmx5IHdvcmtzIHdoZW4gdGhlIGNsaWVudCB2aWV3IGlzIGFsbW9zdCBleGFjdGx5IHRoZSBzYW1lIGFzXG4gICAqIHRoZSBzZXJ2ZXIgdmlldy4gd2Ugd2lsbCBuZWVkIGFuIGltcHJvdmVtZW50IGhlcmUgaW4gdGhlIGZ1dHVyZSB0byBhY2NvdW50XG4gICAqIGZvciBzaXR1YXRpb25zIHdoZXJlIHRoZSBjbGllbnQgdmlldyBpcyBkaWZmZXJlbnQgaW4gc3RydWN0dXJlIGZyb20gdGhlXG4gICAqIHNlcnZlciB2aWV3XG4gICAqL1xuICBmaW5kQ2xpZW50Tm9kZShzZXJ2ZXJOb2RlQ29udGV4dDogTm9kZUNvbnRleHQpOiBIVE1MRWxlbWVudCB8IG51bGwge1xuICAgIHNlcnZlck5vZGVDb250ZXh0ID0gPE5vZGVDb250ZXh0PihzZXJ2ZXJOb2RlQ29udGV4dCB8fCB7fSk7XG5cbiAgICBjb25zdCBzZXJ2ZXJOb2RlID0gc2VydmVyTm9kZUNvbnRleHQubm9kZTtcbiAgICBjb25zdCByb290ID0gc2VydmVyTm9kZUNvbnRleHQucm9vdDtcblxuICAgIC8vIGlmIG5vIHNlcnZlciBvciBjbGllbnQgcm9vdCwgZG9uJ3QgZG8gYW55dGhpbmdcbiAgICBpZiAoIXJvb3QgfHwgIXJvb3Quc2VydmVyTm9kZSB8fCAhcm9vdC5jbGllbnROb2RlKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvLyB3ZSB1c2UgdGhlIHN0cmluZyBvZiB0aGUgbm9kZSB0byBjb21wYXJlIHRvIHRoZSBjbGllbnQgbm9kZSAmIGFzIGtleSBpblxuICAgIC8vIGNhY2hlXG4gICAgY29uc3Qgc2VydmVyTm9kZUtleSA9IHNlcnZlck5vZGVDb250ZXh0Lm5vZGVLZXkgfHwgZ2V0Tm9kZUtleUZvclByZWJvb3Qoc2VydmVyTm9kZUNvbnRleHQpO1xuXG4gICAgLy8gaWYgY2xpZW50IG5vZGUgYWxyZWFkeSBpbiBjYWNoZSwgcmV0dXJuIGl0XG4gICAgaWYgKHRoaXMuY2xpZW50Tm9kZUNhY2hlW3NlcnZlck5vZGVLZXldKSB7XG4gICAgICByZXR1cm4gdGhpcy5jbGllbnROb2RlQ2FjaGVbc2VydmVyTm9kZUtleV0gYXMgSFRNTEVsZW1lbnQ7XG4gICAgfVxuXG4gICAgLy8gZ2V0IHRoZSBzZWxlY3RvciBmb3IgY2xpZW50IG5vZGVzXG4gICAgY29uc3QgY2xhc3NOYW1lID0gKHNlcnZlck5vZGUuY2xhc3NOYW1lIHx8ICcnKS5yZXBsYWNlKCduZy1iaW5kaW5nJywgJycpLnRyaW0oKTtcbiAgICBsZXQgc2VsZWN0b3IgPSBzZXJ2ZXJOb2RlLnRhZ05hbWU7XG5cbiAgICBpZiAoc2VydmVyTm9kZS5pZCkge1xuICAgICAgc2VsZWN0b3IgKz0gYCMke3NlcnZlck5vZGUuaWR9YDtcbiAgICB9IGVsc2UgaWYgKGNsYXNzTmFtZSkge1xuICAgICAgc2VsZWN0b3IgKz0gYC4ke2NsYXNzTmFtZS5yZXBsYWNlKC8gL2csICcuJyl9YDtcbiAgICB9XG5cbiAgICAvLyBzZWxlY3QgYWxsIHBvc3NpYmxlIGNsaWVudCBub2RlcyBhbmQgbG9vayB0aHJvdWdoIHRoZW0gdG8gdHJ5IGFuZCBmaW5kIGFcbiAgICAvLyBtYXRjaFxuICAgIGNvbnN0IHJvb3RDbGllbnROb2RlID0gcm9vdC5jbGllbnROb2RlO1xuICAgIGxldCBjbGllbnROb2RlcyA9IHJvb3RDbGllbnROb2RlLnF1ZXJ5U2VsZWN0b3JBbGwoc2VsZWN0b3IpO1xuXG4gICAgLy8gaWYgbm90aGluZyBmb3VuZCwgdGhlbiBqdXN0IHRyeSB0aGUgdGFnIG5hbWUgYXMgYSBmaW5hbCBvcHRpb25cbiAgICBpZiAoIWNsaWVudE5vZGVzLmxlbmd0aCkge1xuICAgICAgY29uc29sZS5sb2coYG5vdGhpbmcgZm91bmQgZm9yICR7c2VsZWN0b3J9IHNvIHVzaW5nICR7c2VydmVyTm9kZS50YWdOYW1lfWApO1xuICAgICAgY2xpZW50Tm9kZXMgPSByb290Q2xpZW50Tm9kZS5xdWVyeVNlbGVjdG9yQWxsKHNlcnZlck5vZGUudGFnTmFtZSk7XG4gICAgfVxuXG4gICAgY29uc3QgbGVuZ3RoID0gY2xpZW50Tm9kZXMubGVuZ3RoO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGNsaWVudE5vZGUgPSBjbGllbnROb2Rlcy5pdGVtKGkpO1xuXG4gICAgICAvLyBnZXQgdGhlIGtleSBmb3IgdGhlIGNsaWVudCBub2RlXG4gICAgICBjb25zdCBjbGllbnROb2RlS2V5ID0gZ2V0Tm9kZUtleUZvclByZWJvb3Qoe1xuICAgICAgICByb290OiByb290LFxuICAgICAgICBub2RlOiBjbGllbnROb2RlXG4gICAgICB9KTtcblxuICAgICAgLy8gaWYgdGhlIGNsaWVudCBub2RlIGtleSBpcyBleGFjdCBtYXRjaCBmb3IgdGhlIHNlcnZlciBub2RlIGtleSwgdGhlbiB3ZVxuICAgICAgLy8gZm91bmQgdGhlIGNsaWVudCBub2RlXG4gICAgICBpZiAoY2xpZW50Tm9kZUtleSA9PT0gc2VydmVyTm9kZUtleSkge1xuICAgICAgICB0aGlzLmNsaWVudE5vZGVDYWNoZVtzZXJ2ZXJOb2RlS2V5XSA9IGNsaWVudE5vZGU7XG4gICAgICAgIHJldHVybiBjbGllbnROb2RlIGFzIEhUTUxFbGVtZW50O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGlmIHdlIGdldCBoZXJlIGFuZCB0aGVyZSBpcyBvbmUgY2xpZW50Tm9kZSwgdXNlIGl0IGFzIGEgZmFsbGJhY2tcbiAgICBpZiAoY2xpZW50Tm9kZXMubGVuZ3RoID09PSAxKSB7XG4gICAgICB0aGlzLmNsaWVudE5vZGVDYWNoZVtzZXJ2ZXJOb2RlS2V5XSA9IGNsaWVudE5vZGVzWzBdO1xuICAgICAgcmV0dXJuIGNsaWVudE5vZGVzWzBdIGFzIEhUTUxFbGVtZW50O1xuICAgIH1cblxuICAgIC8vIGlmIHdlIGdldCBoZXJlIGl0IG1lYW5zIHdlIGNvdWxkbid0IGZpbmQgdGhlIGNsaWVudCBub2RlIHNvIGdpdmUgdGhlIHVzZXJcbiAgICAvLyBhIHdhcm5pbmdcbiAgICBjb25zb2xlLndhcm4oXG4gICAgICBgTm8gbWF0Y2hpbmcgY2xpZW50IG5vZGUgZm91bmQgZm9yICR7c2VydmVyTm9kZUtleX0uXG4gICAgICAgWW91IGNhbiBmaXggdGhpcyBieSBhc3NpZ25pbmcgdGhpcyBlbGVtZW50IGEgdW5pcXVlIGlkIGF0dHJpYnV0ZS5gXG4gICAgKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufVxuIl19
\No newline at end of file