Skip to content

Instantly share code, notes, and snippets.

@spiralx
Created July 4, 2018 16:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spiralx/1326b73f02cc9c732a3153812694c749 to your computer and use it in GitHub Desktop.
Save spiralx/1326b73f02cc9c732a3153812694c749 to your computer and use it in GitHub Desktop.
A small module I've written to let you subscribe to DOM changes that match specified criteria
var Watcher = (function () {
'use strict';
// ----------------------------------------------------
var Css;
(function (Css) {
Css.Inverse = 'color: white; background: black';
Css.Error = 'font-weight: bold; color: #f4f';
Css.Link = 'color: #05f; font-weight: normal; text-decoration: underline';
Css.Bold = 'font-weight: bold';
Css.Blue = 'color: #05f';
Css.Kw = 'color: #35b; font-weight: bold; font-style: normal; text-decoration: none';
Css.Attr = 'color: #563; font-weight: normal; font-style: italic; text-decoration: none';
Css.Val = 'color: #c36; font-weight: normal; font-style: normal; text-decoration: none';
})(Css || (Css = {}));
// ----------------------------------------------------
// ----------------------------------------------------
var WatchEvents;
(function (WatchEvents) {
WatchEvents[WatchEvents["ElementsAdded"] = 1] = "ElementsAdded";
WatchEvents[WatchEvents["ElementsRemoved"] = 2] = "ElementsRemoved";
WatchEvents[WatchEvents["AttributesChanged"] = 4] = "AttributesChanged";
WatchEvents[WatchEvents["TextChanged"] = 8] = "TextChanged";
WatchEvents[WatchEvents["ElementsChanged"] = 3] = "ElementsChanged";
WatchEvents[WatchEvents["AllChanges"] = 15] = "AllChanges";
})(WatchEvents || (WatchEvents = {}));
// ----------------------------------------------------
// ----------------------------------------------------
class WatchResult {
constructor() {
this.added = new Array();
this.removed = new Array();
this.attributeChanges = new Array();
this.textChanges = new Array();
}
}
class ElementSet extends Set {
// get [Symbol.toStringTag]: string () {
// return 'ElementSet'
// }
// ----------------------------------------------------
addAll(elements) {
for (const element of elements) {
super.add(element);
}
return this;
}
// ----------------------------------------------------
toArray() {
return Array.from(this);
}
}
// ----------------------------------------------------
function getSelectorFunction(selector) {
return function (element) {
const matches = [];
if (element.matches(selector)) {
matches.push(element);
}
return matches.concat(Array.from(element.querySelectorAll(selector)));
};
}
// ----------------------------------------------------
function getElementNodesFromNodeList(nodes) {
return getElementNodes(Array.from(nodes));
}
// ----------------------------------------------------
function getElementNodes(nodes) {
return nodes.filter(node => node instanceof HTMLElement);
}
// ----------------------------------------------------------
class Watch {
// ----------------------------------------------------
constructor(options, callback) {
this.options = options;
this.callback = callback;
this.attributes = new Set();
this.selector = this.options.selector || '*';
this.selectorFunction = getSelectorFunction(this.selector);
this.findExisting = typeof options.findExisting === 'boolean'
? options.findExisting
: true;
this.events = options.events || WatchEvents.ElementsChanged;
if (options.attributes) {
this.attributes = new Set(options.attributes);
}
else if (options.attribute) {
this.attributes.add(options.attribute);
}
}
// ----------------------------------------------------
get [Symbol.toStringTag]() {
return 'Watch';
}
// ----------------------------------------------------
processSummary(summary, debug = false) {
const addedElements = getElementNodesFromNodeList(summary.addedNodes);
const removedElements = getElementNodesFromNodeList(summary.removedNodes);
const matchingAddedElements = this.processElements(addedElements);
const matchingRemovedElements = this.processElements(removedElements);
if (debug) {
console.groupCollapsed(`%cWatch.processSummary(%ctype=%c${summary.type}%c)`, Css.Kw, Css.Attr, Css.Val, Css.Kw);
if (addedElements.length) {
console.group(`Added elements`);
console.dir(addedElements);
console.dir(matchingAddedElements);
console.groupEnd();
}
if (removedElements.length) {
console.group(`Removed elements`);
console.dir(removedElements);
console.dir(matchingRemovedElements);
console.groupEnd();
}
console.groupEnd();
}
this.invoke(matchingAddedElements.toArray(), matchingRemovedElements.toArray(), debug);
}
// ----------------------------------------------------
processElement(element) {
this.invoke(this.selectorFunction(element), []);
}
// ----------------------------------------------------
dump() {
console.groupCollapsed(`%cWatch(%cselector: %c"${this.options.selector}"%c)`, Css.Kw, Css.Attr, Css.Link, Css.Kw);
console.dir(this.options);
console.log(this.callback.toString());
console.groupEnd();
}
// ----------------------------------------------------
processElements(elements) {
return elements.reduce((matches, element) => matches.addAll(this.selectorFunction(element)), new ElementSet());
}
// ----------------------------------------------------
invoke(added, removed, debug = false) {
if (added.length > 0 || removed.length > 0) {
const result = new WatchResult();
result.added = added;
result.removed = removed;
if (debug) {
console.groupCollapsed(`%cWatch.invoke()`, Css.Kw);
console.dir(result);
console.groupEnd();
}
this.callback(result);
// this.callback.call(this.context, result)
}
}
}
// ----------------------------------------------------------
class Watcher {
// ----------------------------------------------------
constructor(root = document.body, debug = false) {
this.root = root;
this.debug = debug;
this.observer = null;
// readonly watcheMap: Map<string, Watch> = new Map()
this.watches = [];
if (!(root instanceof HTMLElement)) {
throw new TypeError('Watch root is not a valid HTML element!');
}
}
// ----------------------------------------------------
get [Symbol.toStringTag]() {
return 'Watcher';
}
add(options, callback) {
if (typeof options === 'string') {
options = {
selector: options
};
}
else if (typeof options === 'function') {
callback = options;
options = {};
}
if (!callback) {
throw new Error('No callback function specified when calling Watcher.add()');
}
if (this.debug) {
console.groupCollapsed(`%cWatcher.add(selector: %c${options.selector}%c, %c${this.watchCount} watches%c)`, Css.Kw, Css.Link, Css.Kw, Css.Val, Css.Kw);
console.log(callback.toString());
if (options) {
console.dir(options);
}
console.groupEnd();
}
const watch = new Watch(options, callback);
this.watches.push(watch);
return watch;
}
// ----------------------------------------------------
get observing() {
return !!this.observer;
}
// ----------------------------------------------------
get watchCount() {
return this.watches.length;
}
// ----------------------------------------------------
// get watches (): Watch[] {
// return [ ...this.watchMap.values() ]
// }
// ----------------------------------------------------
processSummary(summary) {
if (this.debug) {
console.groupCollapsed(`%cWatcher.processSummary(%ctype=%c${summary.type}%c)`, Css.Kw, Css.Attr, Css.Val, Css.Kw);
console.dir(summary);
console.groupEnd();
}
for (const watch of this.watches) {
watch.processSummary(summary, this.debug);
}
}
// ----------------------------------------------------
start() {
if (!this.watchCount) {
throw new Error('Cannot start Watcher without any watches!');
}
if (this.debug) {
console.info(`%cWatcher.start(%cenabled = %c${this.observing ? 'true' : 'false'}%c, %c${this.watchCount} watches%c)`, Css.Kw, Css.Attr, Css.Val, Css.Kw, Css.Val, Css.Kw);
}
if (!this.observer) {
// Check for existing elements, pass to callback
for (const watch of this.watches) {
if (watch.findExisting && watch.events & WatchEvents.ElementsAdded) {
watch.processElement(this.root);
}
}
this.observer = new MutationObserver(summaries => {
summaries.forEach(summary => this.processSummary(summary));
});
this.observer.observe(this.root, {
childList: true,
// attributes: true,
subtree: true
});
}
return this;
}
// ----------------------------------------------------
stop() {
if (this.observer) {
this.observer.takeRecords().forEach(summary => this.processSummary(summary));
this.observer.disconnect();
this.observer = null;
}
return this;
}
}
return Watcher;
}());
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2hlci5qcyIsInNvdXJjZXMiOlsiLi4vc3JjL2ludGVyZmFjZXMudHMiLCIuLi9zcmMvd2F0Y2gtb3B0aW9ucy50cyIsIi4uL3NyYy93YXRjaC1yZXN1bHQudHMiLCIuLi9zcmMvZWxlbWVudC1zZXQudHMiLCIuLi9zcmMvdXRpbHMvZG9tLnRzIiwiLi4vc3JjL3dhdGNoLnRzIiwiLi4vc3JjL3dhdGNoZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiXHJcbmludGVyZmFjZSBDb25zb2xlIHtcclxuICBncm91cCAoZ3JvdXBUaXRsZT86IHN0cmluZywgLi4ub3B0aW9uYWxQYXJhbXM6IGFueVtdKTogdm9pZFxyXG59XHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgbmFtZXNwYWNlIENzcyB7XHJcbiAgZXhwb3J0IGNvbnN0IEludmVyc2UgPSAnY29sb3I6IHdoaXRlOyBiYWNrZ3JvdW5kOiBibGFjaydcclxuICBleHBvcnQgY29uc3QgRXJyb3IgPSAnZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjZjRmJ1xyXG4gIGV4cG9ydCBjb25zdCBMaW5rID0gJ2NvbG9yOiAjMDVmOyBmb250LXdlaWdodDogbm9ybWFsOyB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZSdcclxuICBleHBvcnQgY29uc3QgQm9sZCA9ICdmb250LXdlaWdodDogYm9sZCdcclxuICBleHBvcnQgY29uc3QgQmx1ZSA9ICdjb2xvcjogIzA1ZidcclxuICBleHBvcnQgY29uc3QgS3cgPSAnY29sb3I6ICMzNWI7IGZvbnQtd2VpZ2h0OiBib2xkOyBmb250LXN0eWxlOiBub3JtYWw7IHRleHQtZGVjb3JhdGlvbjogbm9uZSdcclxuICBleHBvcnQgY29uc3QgQXR0ciA9ICdjb2xvcjogIzU2MzsgZm9udC13ZWlnaHQ6IG5vcm1hbDsgZm9udC1zdHlsZTogaXRhbGljOyB0ZXh0LWRlY29yYXRpb246IG5vbmUnXHJcbiAgZXhwb3J0IGNvbnN0IFZhbCA9ICdjb2xvcjogI2MzNjsgZm9udC13ZWlnaHQ6IG5vcm1hbDsgZm9udC1zdHlsZTogbm9ybWFsOyB0ZXh0LWRlY29yYXRpb246IG5vbmUnXHJcbn1cclxuXHJcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgSU5vZGUge1xyXG4gIG5vZGVzPzogSU5vZGVbXVxyXG59XHJcbiIsIlxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFdhdGNoT3B0aW9ucyB7XHJcbiAgc2VsZWN0b3I/OiBzdHJpbmdcclxuICBmaW5kRXhpc3Rpbmc/OiBib29sZWFuXHJcbiAgZXZlbnRzPzogV2F0Y2hFdmVudHNcclxuICBhdHRyaWJ1dGU/OiBzdHJpbmdcclxuICBhdHRyaWJ1dGVzPzogc3RyaW5nW11cclxufVxyXG5cclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuZXhwb3J0IGVudW0gV2F0Y2hFdmVudHMge1xyXG4gIEVsZW1lbnRzQWRkZWQgPSAxLFxyXG4gIEVsZW1lbnRzUmVtb3ZlZCA9IDIsXHJcbiAgQXR0cmlidXRlc0NoYW5nZWQgPSA0LFxyXG4gIFRleHRDaGFuZ2VkID0gOCxcclxuXHJcbiAgRWxlbWVudHNDaGFuZ2VkID0gRWxlbWVudHNBZGRlZCB8IEVsZW1lbnRzUmVtb3ZlZCxcclxuICBBbGxDaGFuZ2VzID0gRWxlbWVudHNBZGRlZCB8IEVsZW1lbnRzUmVtb3ZlZCB8IEF0dHJpYnV0ZXNDaGFuZ2VkIHwgVGV4dENoYW5nZWRcclxufVxyXG4iLCJcclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBBdHRyaWJ1dGVDaGFuZ2Uge1xyXG4gIHRhcmdldDogSFRNTEVsZW1lbnRcclxuICBhdHRyaWJ1dGU6IHN0cmluZ1xyXG4gIHZhbHVlOiBzdHJpbmcgfCBTVkdOdW1iZXJMaXN0XHJcbn1cclxuXHJcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgVGV4dENoYW5nZSB7XHJcbiAgdGFyZ2V0OiBIVE1MRWxlbWVudFxyXG4gIHRleHQ6IHN0cmluZ1xyXG59XHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgY2xhc3MgV2F0Y2hSZXN1bHQge1xyXG4gIGFkZGVkOiBBcnJheTxIVE1MRWxlbWVudD4gPSBuZXcgQXJyYXk8SFRNTEVsZW1lbnQ+KClcclxuICByZW1vdmVkOiBBcnJheTxIVE1MRWxlbWVudD4gPSBuZXcgQXJyYXk8SFRNTEVsZW1lbnQ+KClcclxuICBhdHRyaWJ1dGVDaGFuZ2VzOiBBcnJheTxBdHRyaWJ1dGVDaGFuZ2U+ID0gbmV3IEFycmF5PEF0dHJpYnV0ZUNoYW5nZT4oKVxyXG4gIHRleHRDaGFuZ2VzOiBBcnJheTxUZXh0Q2hhbmdlPiA9IG5ldyBBcnJheTxUZXh0Q2hhbmdlPigpXHJcbn1cclxuIiwiXHJcbmV4cG9ydCBjbGFzcyBFbGVtZW50U2V0IGV4dGVuZHMgU2V0PEhUTUxFbGVtZW50PiB7XHJcbiAgLy8gZ2V0IFtTeW1ib2wudG9TdHJpbmdUYWddOiBzdHJpbmcgKCkge1xyXG4gIC8vICAgcmV0dXJuICdFbGVtZW50U2V0J1xyXG4gIC8vIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICBhZGRBbGwgKGVsZW1lbnRzOiBIVE1MRWxlbWVudFtdIHwgRWxlbWVudFNldCk6IHRoaXMge1xyXG4gICAgZm9yIChjb25zdCBlbGVtZW50IG9mIGVsZW1lbnRzKSB7XHJcbiAgICAgIHN1cGVyLmFkZChlbGVtZW50KVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzXHJcbiAgfVxyXG5cclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG4gIHRvQXJyYXkgKCk6IEhUTUxFbGVtZW50W10ge1xyXG4gICAgcmV0dXJuIEFycmF5LmZyb20odGhpcylcclxuICB9XHJcbn1cclxuIiwiaW1wb3J0IHsgU2VsZWN0b3JGdW5jIH0gZnJvbSAnLi4vd2F0Y2gnXHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VsZWN0b3JGdW5jdGlvbiAoc2VsZWN0b3I6IHN0cmluZyk6IFNlbGVjdG9yRnVuYyB7XHJcbiAgcmV0dXJuIGZ1bmN0aW9uIChlbGVtZW50OiBIVE1MRWxlbWVudCk6IEhUTUxFbGVtZW50W10ge1xyXG4gICAgY29uc3QgbWF0Y2hlczogSFRNTEVsZW1lbnRbXSA9IFtdXHJcblxyXG4gICAgaWYgKGVsZW1lbnQubWF0Y2hlcyhzZWxlY3RvcikpIHtcclxuICAgICAgbWF0Y2hlcy5wdXNoKGVsZW1lbnQpXHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIG1hdGNoZXMuY29uY2F0KEFycmF5LmZyb20oZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKHNlbGVjdG9yKSkpXHJcbiAgfVxyXG59XHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudE5vZGVzRnJvbU5vZGVMaXN0IChub2RlczogTm9kZUxpc3QpOiBIVE1MRWxlbWVudFtdIHtcclxuICByZXR1cm4gZ2V0RWxlbWVudE5vZGVzKEFycmF5LmZyb20obm9kZXMpKVxyXG59XHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudE5vZGVzIChub2RlczogTm9kZVtdKTogSFRNTEVsZW1lbnRbXSB7XHJcbiAgcmV0dXJuIG5vZGVzLmZpbHRlcihub2RlID0+IG5vZGUgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCkgYXMgSFRNTEVsZW1lbnRbXVxyXG59XHJcbiIsImltcG9ydCB7IENzcyB9IGZyb20gJy4vaW50ZXJmYWNlcydcclxuXHJcbmltcG9ydCB7IFdhdGNoT3B0aW9ucywgV2F0Y2hFdmVudHMgfSBmcm9tICcuL3dhdGNoLW9wdGlvbnMnXHJcbmltcG9ydCB7IFdhdGNoUmVzdWx0IH0gZnJvbSAnLi93YXRjaC1yZXN1bHQnXHJcbmltcG9ydCB7IEVsZW1lbnRTZXQgfSBmcm9tICcuL2VsZW1lbnQtc2V0J1xyXG5cclxuaW1wb3J0IHsgZ2V0U2VsZWN0b3JGdW5jdGlvbiwgZ2V0RWxlbWVudE5vZGVzRnJvbU5vZGVMaXN0IH0gZnJvbSAnLi91dGlscy9kb20nXHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgdHlwZSBTZWxlY3RvckZ1bmMgPSAoZWxlbWVudDogSFRNTEVsZW1lbnQpID0+IEhUTUxFbGVtZW50W11cclxuXHJcbmV4cG9ydCB0eXBlIFdhdGNoQ2FsbGJhY2sgPSAocmVzdWx0OiBXYXRjaFJlc3VsdCkgPT4gdm9pZFxyXG5cclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuZXhwb3J0IGNsYXNzIFdhdGNoIHtcclxuICBwdWJsaWMgc2VsZWN0b3I6IHN0cmluZ1xyXG4gIHB1YmxpYyBzZWxlY3RvckZ1bmN0aW9uOiBTZWxlY3RvckZ1bmNcclxuXHJcbiAgcHVibGljIGZpbmRFeGlzdGluZzogYm9vbGVhblxyXG5cclxuICBwdWJsaWMgZXZlbnRzOiBXYXRjaEV2ZW50c1xyXG4gIHB1YmxpYyBhdHRyaWJ1dGVzOiBTZXQ8c3RyaW5nPiA9IG5ldyBTZXQoKVxyXG5cclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG4gIGdldCBbU3ltYm9sLnRvU3RyaW5nVGFnXSAoKSB7XHJcbiAgICByZXR1cm4gJ1dhdGNoJ1xyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICBjb25zdHJ1Y3RvciAoXHJcbiAgICBwdWJsaWMgcmVhZG9ubHkgb3B0aW9uczogV2F0Y2hPcHRpb25zLFxyXG4gICAgcHVibGljIHJlYWRvbmx5IGNhbGxiYWNrOiBXYXRjaENhbGxiYWNrXHJcbiAgKSB7XHJcbiAgICB0aGlzLnNlbGVjdG9yID0gdGhpcy5vcHRpb25zLnNlbGVjdG9yIHx8ICcqJ1xyXG4gICAgdGhpcy5zZWxlY3RvckZ1bmN0aW9uID0gZ2V0U2VsZWN0b3JGdW5jdGlvbih0aGlzLnNlbGVjdG9yKVxyXG5cclxuICAgIHRoaXMuZmluZEV4aXN0aW5nID0gdHlwZW9mIG9wdGlvbnMuZmluZEV4aXN0aW5nID09PSAnYm9vbGVhbidcclxuICAgICAgPyBvcHRpb25zLmZpbmRFeGlzdGluZ1xyXG4gICAgICA6IHRydWVcclxuXHJcbiAgICB0aGlzLmV2ZW50cyA9IG9wdGlvbnMuZXZlbnRzIHx8IFdhdGNoRXZlbnRzLkVsZW1lbnRzQ2hhbmdlZFxyXG5cclxuICAgIGlmIChvcHRpb25zLmF0dHJpYnV0ZXMpIHtcclxuICAgICAgdGhpcy5hdHRyaWJ1dGVzID0gbmV3IFNldChvcHRpb25zLmF0dHJpYnV0ZXMpXHJcbiAgICB9IGVsc2UgaWYgKG9wdGlvbnMuYXR0cmlidXRlKSB7XHJcbiAgICAgIHRoaXMuYXR0cmlidXRlcy5hZGQob3B0aW9ucy5hdHRyaWJ1dGUpXHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG4gIHByb2Nlc3NTdW1tYXJ5IChzdW1tYXJ5OiBNdXRhdGlvblJlY29yZCwgZGVidWc6IGJvb2xlYW4gPSBmYWxzZSk6IHZvaWQge1xyXG4gICAgY29uc3QgYWRkZWRFbGVtZW50cyA9IGdldEVsZW1lbnROb2Rlc0Zyb21Ob2RlTGlzdChzdW1tYXJ5LmFkZGVkTm9kZXMpXHJcbiAgICBjb25zdCByZW1vdmVkRWxlbWVudHMgPSBnZXRFbGVtZW50Tm9kZXNGcm9tTm9kZUxpc3Qoc3VtbWFyeS5yZW1vdmVkTm9kZXMpXHJcblxyXG4gICAgY29uc3QgbWF0Y2hpbmdBZGRlZEVsZW1lbnRzOiBFbGVtZW50U2V0ID0gdGhpcy5wcm9jZXNzRWxlbWVudHMoYWRkZWRFbGVtZW50cylcclxuICAgIGNvbnN0IG1hdGNoaW5nUmVtb3ZlZEVsZW1lbnRzOiBFbGVtZW50U2V0ID0gdGhpcy5wcm9jZXNzRWxlbWVudHMocmVtb3ZlZEVsZW1lbnRzKVxyXG5cclxuICAgIGlmIChkZWJ1Zykge1xyXG4gICAgICBjb25zb2xlLmdyb3VwQ29sbGFwc2VkKGAlY1dhdGNoLnByb2Nlc3NTdW1tYXJ5KCVjdHlwZT0lYyR7c3VtbWFyeS50eXBlfSVjKWAsIENzcy5LdywgQ3NzLkF0dHIsIENzcy5WYWwsIENzcy5LdylcclxuXHJcbiAgICAgIGlmIChhZGRlZEVsZW1lbnRzLmxlbmd0aCkge1xyXG4gICAgICAgIGNvbnNvbGUuZ3JvdXAoYEFkZGVkIGVsZW1lbnRzYClcclxuICAgICAgICBjb25zb2xlLmRpcihhZGRlZEVsZW1lbnRzKVxyXG4gICAgICAgIGNvbnNvbGUuZGlyKG1hdGNoaW5nQWRkZWRFbGVtZW50cylcclxuICAgICAgICBjb25zb2xlLmdyb3VwRW5kKClcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKHJlbW92ZWRFbGVtZW50cy5sZW5ndGgpIHtcclxuICAgICAgICBjb25zb2xlLmdyb3VwKGBSZW1vdmVkIGVsZW1lbnRzYClcclxuICAgICAgICBjb25zb2xlLmRpcihyZW1vdmVkRWxlbWVudHMpXHJcbiAgICAgICAgY29uc29sZS5kaXIobWF0Y2hpbmdSZW1vdmVkRWxlbWVudHMpXHJcbiAgICAgICAgY29uc29sZS5ncm91cEVuZCgpXHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGNvbnNvbGUuZ3JvdXBFbmQoKVxyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuaW52b2tlKG1hdGNoaW5nQWRkZWRFbGVtZW50cy50b0FycmF5KCksIG1hdGNoaW5nUmVtb3ZlZEVsZW1lbnRzLnRvQXJyYXkoKSwgZGVidWcpXHJcbiAgfVxyXG5cclxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG4gIHByb2Nlc3NFbGVtZW50IChlbGVtZW50OiBIVE1MRWxlbWVudCk6IHZvaWQge1xyXG4gICAgdGhpcy5pbnZva2UodGhpcy5zZWxlY3RvckZ1bmN0aW9uKGVsZW1lbnQpLCBbXSlcclxuICB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbiAgZHVtcCAoKSB7XHJcbiAgICBjb25zb2xlLmdyb3VwQ29sbGFwc2VkKGAlY1dhdGNoKCVjc2VsZWN0b3I6ICVjXCIke3RoaXMub3B0aW9ucy5zZWxlY3Rvcn1cIiVjKWAsIENzcy5LdywgQ3NzLkF0dHIsIENzcy5MaW5rLCBDc3MuS3cpXHJcbiAgICBjb25zb2xlLmRpcih0aGlzLm9wdGlvbnMpXHJcbiAgICBjb25zb2xlLmxvZyh0aGlzLmNhbGxiYWNrLnRvU3RyaW5nKCkpXHJcbiAgICBjb25zb2xlLmdyb3VwRW5kKClcclxuICB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbiAgcHJpdmF0ZSBwcm9jZXNzRWxlbWVudHMgKGVsZW1lbnRzOiBIVE1MRWxlbWVudFtdKTogRWxlbWVudFNldCB7XHJcbiAgICByZXR1cm4gZWxlbWVudHMucmVkdWNlKChtYXRjaGVzOiBFbGVtZW50U2V0LCBlbGVtZW50OiBIVE1MRWxlbWVudCkgPT4gbWF0Y2hlcy5hZGRBbGwodGhpcy5zZWxlY3RvckZ1bmN0aW9uKGVsZW1lbnQpKSwgbmV3IEVsZW1lbnRTZXQoKSlcclxuICB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbiAgcHJpdmF0ZSBpbnZva2UgKGFkZGVkOiBIVE1MRWxlbWVudFtdLCByZW1vdmVkOiBIVE1MRWxlbWVudFtdLCBkZWJ1ZzogYm9vbGVhbiA9IGZhbHNlKSB7XHJcbiAgICBpZiAoYWRkZWQubGVuZ3RoID4gMCB8fCByZW1vdmVkLmxlbmd0aCA+IDApIHtcclxuICAgICAgY29uc3QgcmVzdWx0ID0gbmV3IFdhdGNoUmVzdWx0KClcclxuICAgICAgcmVzdWx0LmFkZGVkID0gYWRkZWRcclxuICAgICAgcmVzdWx0LnJlbW92ZWQgPSByZW1vdmVkXHJcblxyXG4gICAgICBpZiAoZGVidWcpIHtcclxuICAgICAgICBjb25zb2xlLmdyb3VwQ29sbGFwc2VkKGAlY1dhdGNoLmludm9rZSgpYCwgQ3NzLkt3KVxyXG4gICAgICAgIGNvbnNvbGUuZGlyKHJlc3VsdClcclxuICAgICAgICBjb25zb2xlLmdyb3VwRW5kKClcclxuICAgICAgfVxyXG5cclxuICAgICAgdGhpcy5jYWxsYmFjayhyZXN1bHQpXHJcbiAgICAgIC8vIHRoaXMuY2FsbGJhY2suY2FsbCh0aGlzLmNvbnRleHQsIHJlc3VsdClcclxuICAgIH1cclxuICB9XHJcbn1cclxuIiwiaW1wb3J0IHsgQ3NzIH0gZnJvbSAnLi9pbnRlcmZhY2VzJ1xyXG5cclxuaW1wb3J0IHsgV2F0Y2hDYWxsYmFjaywgV2F0Y2ggfSBmcm9tICcuL3dhdGNoJ1xyXG5pbXBvcnQgeyBXYXRjaE9wdGlvbnMsIFdhdGNoRXZlbnRzIH0gZnJvbSAnLi93YXRjaC1vcHRpb25zJ1xyXG5cclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgV2F0Y2hlciB7XHJcblxyXG4gIG9ic2VydmVyOiBNdXRhdGlvbk9ic2VydmVyIHwgbnVsbCA9IG51bGxcclxuXHJcbiAgLy8gcmVhZG9ubHkgd2F0Y2hlTWFwOiBNYXA8c3RyaW5nLCBXYXRjaD4gPSBuZXcgTWFwKClcclxuICByZWFkb25seSB3YXRjaGVzOiBXYXRjaFtdID0gW11cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICBnZXQgW1N5bWJvbC50b1N0cmluZ1RhZ10gKCkge1xyXG4gICAgcmV0dXJuICdXYXRjaGVyJ1xyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICBjb25zdHJ1Y3RvciAoXHJcbiAgICBwdWJsaWMgcmVhZG9ubHkgcm9vdDogSFRNTEVsZW1lbnQgPSBkb2N1bWVudC5ib2R5LFxyXG4gICAgcHVibGljIHJlYWRvbmx5IGRlYnVnOiBib29sZWFuID0gZmFsc2VcclxuICApIHtcclxuICAgIGlmICghKHJvb3QgaW5zdGFuY2VvZiBIVE1MRWxlbWVudCkpIHtcclxuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignV2F0Y2ggcm9vdCBpcyBub3QgYSB2YWxpZCBIVE1MIGVsZW1lbnQhJylcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbiAgYWRkIChjYWxsYmFjazogV2F0Y2hDYWxsYmFjayk6IFdhdGNoXHJcbiAgYWRkIChvcHRpb25zOiBzdHJpbmcgfCBXYXRjaE9wdGlvbnMsIGNhbGxiYWNrOiBXYXRjaENhbGxiYWNrKTogV2F0Y2hcclxuXHJcbiAgYWRkIChvcHRpb25zOiBzdHJpbmcgfCBXYXRjaE9wdGlvbnMgfCBXYXRjaENhbGxiYWNrLCBjYWxsYmFjaz86IFdhdGNoQ2FsbGJhY2spOiBXYXRjaCB7XHJcbiAgICBpZiAodHlwZW9mIG9wdGlvbnMgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIG9wdGlvbnMgPSB7XHJcbiAgICAgICAgc2VsZWN0b3I6IG9wdGlvbnNcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ2Z1bmN0aW9uJykge1xyXG4gICAgICBjYWxsYmFjayA9IG9wdGlvbnNcclxuICAgICAgb3B0aW9ucyA9IHt9XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFjYWxsYmFjaykge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIGNhbGxiYWNrIGZ1bmN0aW9uIHNwZWNpZmllZCB3aGVuIGNhbGxpbmcgV2F0Y2hlci5hZGQoKScpXHJcbiAgICB9XHJcblxyXG4gICAgaWYgKHRoaXMuZGVidWcpIHtcclxuICAgICAgY29uc29sZS5ncm91cENvbGxhcHNlZChgJWNXYXRjaGVyLmFkZChzZWxlY3RvcjogJWMke29wdGlvbnMuc2VsZWN0b3J9JWMsICVjJHt0aGlzLndhdGNoQ291bnR9IHdhdGNoZXMlYylgLCBDc3MuS3csIENzcy5MaW5rLCBDc3MuS3csIENzcy5WYWwsIENzcy5LdylcclxuICAgICAgY29uc29sZS5sb2coY2FsbGJhY2sudG9TdHJpbmcoKSlcclxuICAgICAgaWYgKG9wdGlvbnMpIHtcclxuICAgICAgICBjb25zb2xlLmRpcihvcHRpb25zKVxyXG4gICAgICB9XHJcbiAgICAgIGNvbnNvbGUuZ3JvdXBFbmQoKVxyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHdhdGNoID0gbmV3IFdhdGNoKG9wdGlvbnMsIGNhbGxiYWNrKVxyXG5cclxuICAgIHRoaXMud2F0Y2hlcy5wdXNoKHdhdGNoKVxyXG5cclxuICAgIHJldHVybiB3YXRjaFxyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICBnZXQgb2JzZXJ2aW5nICgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiAhIXRoaXMub2JzZXJ2ZXJcclxuICB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbiAgZ2V0IHdhdGNoQ291bnQgKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy53YXRjaGVzLmxlbmd0aFxyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICAvLyBnZXQgd2F0Y2hlcyAoKTogV2F0Y2hbXSB7XHJcbiAgLy8gICByZXR1cm4gWyAuLi50aGlzLndhdGNoTWFwLnZhbHVlcygpIF1cclxuICAvLyB9XHJcblxyXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbiAgcHJvY2Vzc1N1bW1hcnkgKHN1bW1hcnk6IE11dGF0aW9uUmVjb3JkKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy5kZWJ1Zykge1xyXG4gICAgICBjb25zb2xlLmdyb3VwQ29sbGFwc2VkKGAlY1dhdGNoZXIucHJvY2Vzc1N1bW1hcnkoJWN0eXBlPSVjJHtzdW1tYXJ5LnR5cGV9JWMpYCwgQ3NzLkt3LCBDc3MuQXR0ciwgQ3NzLlZhbCwgQ3NzLkt3KVxyXG4gICAgICBjb25zb2xlLmRpcihzdW1tYXJ5KVxyXG4gICAgICBjb25zb2xlLmdyb3VwRW5kKClcclxuICAgIH1cclxuXHJcbiAgICBmb3IgKGNvbnN0IHdhdGNoIG9mIHRoaXMud2F0Y2hlcykge1xyXG4gICAgICB3YXRjaC5wcm9jZXNzU3VtbWFyeShzdW1tYXJ5LCB0aGlzLmRlYnVnKVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICBzdGFydCAoKTogdGhpcyB7XHJcbiAgICBpZiAoIXRoaXMud2F0Y2hDb3VudCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBzdGFydCBXYXRjaGVyIHdpdGhvdXQgYW55IHdhdGNoZXMhJylcclxuICAgIH1cclxuXHJcbiAgICBpZiAodGhpcy5kZWJ1Zykge1xyXG4gICAgICBjb25zb2xlLmluZm8oYCVjV2F0Y2hlci5zdGFydCglY2VuYWJsZWQgPSAlYyR7dGhpcy5vYnNlcnZpbmcgPyAndHJ1ZScgOiAnZmFsc2UnfSVjLCAlYyR7dGhpcy53YXRjaENvdW50fSB3YXRjaGVzJWMpYCwgQ3NzLkt3LCBDc3MuQXR0ciwgQ3NzLlZhbCwgQ3NzLkt3LCBDc3MuVmFsLCBDc3MuS3cpXHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCF0aGlzLm9ic2VydmVyKSB7XHJcbiAgICAgIC8vIENoZWNrIGZvciBleGlzdGluZyBlbGVtZW50cywgcGFzcyB0byBjYWxsYmFja1xyXG4gICAgICBmb3IgKGNvbnN0IHdhdGNoIG9mIHRoaXMud2F0Y2hlcykge1xyXG4gICAgICAgIGlmICh3YXRjaC5maW5kRXhpc3RpbmcgJiYgd2F0Y2guZXZlbnRzICYgV2F0Y2hFdmVudHMuRWxlbWVudHNBZGRlZCkge1xyXG4gICAgICAgICAgd2F0Y2gucHJvY2Vzc0VsZW1lbnQodGhpcy5yb290KVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgdGhpcy5vYnNlcnZlciA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKHN1bW1hcmllcyA9PiB7XHJcbiAgICAgICAgc3VtbWFyaWVzLmZvckVhY2goc3VtbWFyeSA9PiB0aGlzLnByb2Nlc3NTdW1tYXJ5KHN1bW1hcnkpKVxyXG4gICAgICB9KVxyXG5cclxuICAgICAgdGhpcy5vYnNlcnZlci5vYnNlcnZlKHRoaXMucm9vdCwge1xyXG4gICAgICAgIGNoaWxkTGlzdDogdHJ1ZSxcclxuICAgICAgICAvLyBhdHRyaWJ1dGVzOiB0cnVlLFxyXG4gICAgICAgIHN1YnRyZWU6IHRydWVcclxuICAgICAgfSlcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gdGhpc1xyXG4gIH1cclxuXHJcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuICBzdG9wICgpOiB0aGlzIHtcclxuICAgIGlmICh0aGlzLm9ic2VydmVyKSB7XHJcbiAgICAgIHRoaXMub2JzZXJ2ZXIudGFrZVJlY29yZHMoKS5mb3JFYWNoKHN1bW1hcnkgPT4gdGhpcy5wcm9jZXNzU3VtbWFyeShzdW1tYXJ5KSlcclxuXHJcbiAgICAgIHRoaXMub2JzZXJ2ZXIuZGlzY29ubmVjdCgpXHJcbiAgICAgIHRoaXMub2JzZXJ2ZXIgPSBudWxsXHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXNcclxuICB9XHJcbn1cclxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUtBO0FBRUEsSUFBaUIsR0FBRyxDQVNuQjtBQVRELFdBQWlCLEdBQUc7SUFDTCxXQUFPLEdBQUcsaUNBQWlDLENBQUE7SUFDM0MsU0FBSyxHQUFHLGdDQUFnQyxDQUFBO0lBQ3hDLFFBQUksR0FBRyw4REFBOEQsQ0FBQTtJQUNyRSxRQUFJLEdBQUcsbUJBQW1CLENBQUE7SUFDMUIsUUFBSSxHQUFHLGFBQWEsQ0FBQTtJQUNwQixNQUFFLEdBQUcsMkVBQTJFLENBQUE7SUFDaEYsUUFBSSxHQUFHLDZFQUE2RSxDQUFBO0lBQ3BGLE9BQUcsR0FBRyw2RUFBNkUsQ0FBQTtDQUNqRyxFQVRnQixHQUFHLEtBQUgsR0FBRyxRQVNuQjs7QUNmRDs7QUFZQSxBQUFBLElBQVksV0FRWDtBQVJELFdBQVksV0FBVztJQUNyQiwr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
@spiralx
Copy link
Author

spiralx commented Jul 4, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment