dygraphs
Version:
dygraphs is a fast, flexible open source JavaScript charting library.
455 lines (427 loc) • 61.1 kB
JavaScript
"use strict";
/**
* @license
* Copyright 2013 Dan Vanderkam (danvdk@gmail.com)
* MIT-licenced: https://opensource.org/licenses/MIT
*
* Note: This plugin requires jQuery and jQuery UI Draggable.
*
* See high-level documentation at ../../docs/hairlines-annotations.pdf
*/
/* loader wrapper to allow browser use and ES6 imports */
(function _extras_superAnnotations_wrapper() {
'use strict';
var Dygraph;
if (window.Dygraph) {
Dygraph = window.Dygraph;
} else if (typeof module !== 'undefined') {
Dygraph = require('../dygraph');
if (typeof Dygraph.NAME === 'undefined' && typeof Dygraph["default"] !== 'undefined') Dygraph = Dygraph["default"];
}
/* end of loader wrapper header */
Dygraph.Plugins.SuperAnnotations = function _extras_superAnnotations_closure() {
"use strict";
/**
* These are just the basic requirements -- annotations can have whatever other
* properties the code that displays them wants them to have.
*
* @typedef {
* xval: number, // x-value (i.e. millis or a raw number)
* series: string, // series name
* yFrac: ?number, // y-positioning. Default is a few px above the point.
* lineDiv: !Element // vertical div connecting point to info div.
* infoDiv: !Element // div containing info about the annotation.
* } Annotation
*/
var annotations = function annotations(opt_options) {
/* @type {!Array.<!Annotation>} */
this.annotations_ = [];
// Used to detect resizes (which require the divs to be repositioned).
this.lastWidth_ = -1;
this.lastHeight = -1;
this.dygraph_ = null;
opt_options = opt_options || {};
this.defaultAnnotationProperties_ = $.extend({
'text': 'Description'
}, opt_options['defaultAnnotationProperties']);
};
annotations.prototype.toString = function toString() {
return "SuperAnnotations Plugin";
};
annotations.prototype.activate = function activate(g) {
this.dygraph_ = g;
this.annotations_ = [];
return {
didDrawChart: this.didDrawChart,
pointClick: this.pointClick // TODO(danvk): implement in dygraphs
};
};
annotations.prototype.detachLabels = function detachLabels() {
for (var i = 0; i < this.annotations_.length; i++) {
var a = this.annotations_[i];
$(a.lineDiv).remove();
$(a.infoDiv).remove();
this.annotations_[i] = null;
}
this.annotations_ = [];
};
annotations.prototype.annotationWasDragged = function annotationWasDragged(a, event, ui) {
var g = this.dygraph_;
var area = g.getArea();
var oldYFrac = a.yFrac;
var infoDiv = a.infoDiv;
var newYFrac = (infoDiv.offsetTop + infoDiv.offsetHeight - area.y) / area.h;
if (newYFrac == oldYFrac) return;
a.yFrac = newYFrac;
this.moveAnnotationToTop(a);
this.updateAnnotationDivPositions();
this.updateAnnotationInfo();
$(this).triggerHandler('annotationMoved', {
annotation: a,
oldYFrac: oldYFrac,
newYFrac: a.yFrac
});
$(this).triggerHandler('annotationsChanged', {});
};
annotations.prototype.makeAnnotationEditable = function makeAnnotationEditable(a) {
if (a.editable == true) return;
this.moveAnnotationToTop(a);
// Note: we have to fill out the HTML ourselves because
// updateAnnotationInfo() won't touch editable annotations.
a.editable = true;
var editableTemplateDiv = $('#annotation-editable-template').get(0);
a.infoDiv.innerHTML = this.getTemplateHTML(editableTemplateDiv, a);
$(a.infoDiv).toggleClass('editable', !!a.editable);
$(this).triggerHandler('beganEditAnnotation', a);
};
// This creates the hairline object and returns it.
// It does not position it and does not attach it to the chart.
annotations.prototype.createAnnotation = function createAnnotation(a) {
var self = this;
var color = this.getColorForSeries_(a.series);
var $lineDiv = $('<div />').css({
'width': '1px',
'left': '3px',
'background': 'black',
'height': '100%',
'position': 'absolute',
// TODO(danvk): use border-color here for consistency?
'background-color': color,
'z-index': 10
}).addClass('dygraph-annotation-line');
var $infoDiv = $('#annotation-template').clone().removeAttr('id').css({
'position': 'absolute',
'border-color': color,
'z-index': 10
}).show();
$.extend(a, {
lineDiv: $lineDiv.get(0),
infoDiv: $infoDiv.get(0)
});
var that = this;
$infoDiv.draggable({
'start': function draggableStart(event, ui) {
$(this).css({
'bottom': ''
});
a.isDragging = true;
},
'drag': function draggableDrag(event, ui) {
self.annotationWasDragged(a, event, ui);
},
'stop': function draggableStop(event, ui) {
$(this).css({
'top': ''
});
a.isDragging = false;
self.updateAnnotationDivPositions();
},
'axis': 'y',
'containment': 'parent'
});
// TODO(danvk): use 'on' instead of delegate/dblclick
$infoDiv.on('click', '.annotation-kill-button', function clickKill() {
that.removeAnnotation(a);
$(that).triggerHandler('annotationDeleted', a);
$(that).triggerHandler('annotationsChanged', {});
});
$infoDiv.on('dblclick', function dblclick() {
that.makeAnnotationEditable(a);
});
$infoDiv.on('click', '.annotation-update', function clickUpdate() {
self.extractUpdatedProperties_($infoDiv.get(0), a);
a.editable = false;
self.updateAnnotationInfo();
$(that).triggerHandler('annotationEdited', a);
$(that).triggerHandler('annotationsChanged', {});
});
$infoDiv.on('click', '.annotation-cancel', function clickCancel() {
a.editable = false;
self.updateAnnotationInfo();
$(that).triggerHandler('cancelEditAnnotation', a);
});
return a;
};
// Find the index of a point in a series.
// Returns a 2-element array, [row, col], which can be used with
// dygraph.getValue() to get the value at this point.
// Returns null if there's no match.
annotations.prototype.findPointIndex_ = function findPointIndex_(series, xval) {
var col = this.dygraph_.getLabels().indexOf(series);
if (col == -1) return null;
var lowIdx = 0,
highIdx = this.dygraph_.numRows() - 1;
while (lowIdx <= highIdx) {
var idx = Math.floor((lowIdx + highIdx) / 2);
var xAtIdx = this.dygraph_.getValue(idx, 0);
if (xAtIdx == xval) {
return [idx, col];
} else if (xAtIdx < xval) {
lowIdx = idx + 1;
} else {
highIdx = idx - 1;
}
}
return null;
};
annotations.prototype.getColorForSeries_ = function getColorForSeries_(series) {
var colors = this.dygraph_.getColors();
var col = this.dygraph_.getLabels().indexOf(series);
if (col == -1) return null;
return colors[(col - 1) % colors.length];
};
// Moves a hairline's divs to the top of the z-ordering.
annotations.prototype.moveAnnotationToTop = function moveAnnotationToTop(a) {
var div = this.dygraph_.graphDiv;
$(a.infoDiv).appendTo(div);
$(a.lineDiv).appendTo(div);
var idx = this.annotations_.indexOf(a);
this.annotations_.splice(idx, 1);
this.annotations_.push(a);
};
// Positions existing hairline divs.
annotations.prototype.updateAnnotationDivPositions = function updateAnnotationDivPositions() {
var layout = this.dygraph_.getArea();
var chartLeft = layout.x,
chartRight = layout.x + layout.w;
var chartTop = layout.y,
chartBottom = layout.y + layout.h;
var div = this.dygraph_.graphDiv;
var pos = Dygraph.findPos(div);
var box = [layout.x + pos.x, layout.y + pos.y];
box.push(box[0] + layout.w);
box.push(box[1] + layout.h);
var g = this.dygraph_;
var that = this;
$.each(this.annotations_, function annotationsLoop_(idx, a) {
var row_col = that.findPointIndex_(a.series, a.xval);
if (row_col == null) {
$([a.lineDiv, a.infoDiv]).hide();
return;
} else {
// TODO(danvk): only do this if they're invisible?
$([a.lineDiv, a.infoDiv]).show();
}
var xy = g.toDomCoords(a.xval, g.getValue(row_col[0], row_col[1]));
var x = xy[0],
pointY = xy[1];
var lineHeight = 6; // TODO(danvk): option?
var y = pointY;
if (a.yFrac !== undefined) {
y = layout.y + layout.h * a.yFrac;
} else {
y -= lineHeight;
}
var lineHeight = y < pointY ? pointY - y : y - pointY - a.infoDiv.offsetHeight;
$(a.lineDiv).css({
'left': x + 'px',
'top': Math.min(y, pointY) + 'px',
'height': lineHeight + 'px'
});
$(a.infoDiv).css({
'left': x + 'px'
});
if (!a.isDragging) {
// jQuery UI draggable likes to set 'top', whereas superannotations sets
// 'bottom'. Setting both will make the annotation grow and contract as
// the user drags it, which looks bad.
$(a.infoDiv).css({
'bottom': div.offsetHeight - y + 'px'
}); //.draggable("option", "containment", box);
var visible = x >= chartLeft && x <= chartRight && pointY >= chartTop && pointY <= chartBottom;
$([a.infoDiv, a.lineDiv]).toggle(visible);
}
});
};
// Fills out the info div based on current coordinates.
annotations.prototype.updateAnnotationInfo = function updateAnnotationInfo() {
var g = this.dygraph_;
var that = this;
var templateDiv = $('#annotation-template').get(0);
$.each(this.annotations_, function annotationsLoop_(idx, a) {
// We should never update an editable div -- doing so may kill unsaved
// edits to an annotation.
$(a.infoDiv).toggleClass('editable', !!a.editable);
if (a.editable) return;
a.infoDiv.innerHTML = that.getTemplateHTML(templateDiv, a);
});
};
/**
* @param {!Annotation} a Internal annotation
* @return {!PublicAnnotation} a view of the annotation for the public API.
*/
annotations.prototype.createPublicAnnotation_ = function createPublicAnnotation_(a, opt_props) {
var displayAnnotation = $.extend({}, a, opt_props);
delete displayAnnotation['infoDiv'];
delete displayAnnotation['lineDiv'];
delete displayAnnotation['isDragging'];
delete displayAnnotation['editable'];
return displayAnnotation;
};
// Fill out a div using the values in the annotation object.
// The div's html is expected to have text of the form "{{key}}"
annotations.prototype.getTemplateHTML = function getTemplateHTML(div, a) {
var g = this.dygraph_;
var row_col = this.findPointIndex_(a.series, a.xval);
if (row_col == null) return; // perhaps it's no longer a real point?
var row = row_col[0];
var col = row_col[1];
var yOptView = g.optionsViewForAxis_('y1'); // TODO: support secondary, too
var xOptView = g.optionsViewForAxis_('x');
var xvf = g.getOptionForAxis('valueFormatter', 'x');
var x = xvf.call(g, a.xval, xOptView);
var y = g.getOption('valueFormatter', a.series).call(g, g.getValue(row, col), yOptView);
var displayAnnotation = this.createPublicAnnotation_(a, {
x: x,
y: y
});
var html = div.innerHTML;
for (var k in displayAnnotation) {
var v = displayAnnotation[k];
if (typeof v == 'object') continue; // e.g. infoDiv or lineDiv
html = html.replace(new RegExp('\{\{' + k + '\}\}', 'g'), v);
}
return html;
};
// Update the annotation object by looking for elements with a 'dg-ann-field'
// attribute. For example, <input type='text' dg-ann-field='text' /> will have
// its value placed in the 'text' attribute of the annotation.
annotations.prototype.extractUpdatedProperties_ = function extractUpdatedProperties_(div, a) {
$(div).find('[dg-ann-field]').each(function fieldLoop_(idx, el) {
var k = $(el).attr('dg-ann-field');
var v = $(el).val();
a[k] = v;
});
};
// After a resize, the hairline divs can get dettached from the chart.
// This reattaches them.
annotations.prototype.attachAnnotationsToChart_ = function attachAnnotationsToChart_() {
var div = this.dygraph_.graphDiv;
$.each(this.annotations_, function annotationsLoop_(idx, a) {
// Re-attaching an editable div to the DOM can clear its focus.
// This makes typing really difficult!
if (a.editable) return;
$([a.lineDiv, a.infoDiv]).appendTo(div);
});
};
// Deletes a hairline and removes it from the chart.
annotations.prototype.removeAnnotation = function removeAnnotation(a) {
var idx = this.annotations_.indexOf(a);
if (idx >= 0) {
this.annotations_.splice(idx, 1);
$([a.lineDiv, a.infoDiv]).remove();
} else {
Dygraph.warn('Tried to remove non-existent annotation.');
}
};
annotations.prototype.didDrawChart = function didDrawChart(e) {
var g = e.dygraph;
// Early out in the (common) case of zero annotations.
if (this.annotations_.length === 0) return;
this.updateAnnotationDivPositions();
this.attachAnnotationsToChart_();
this.updateAnnotationInfo();
};
annotations.prototype.pointClick = function pointClick(e) {
// Prevent any other behavior based on this click, e.g. creation of a hairline.
e.preventDefault();
var a = $.extend({}, this.defaultAnnotationProperties_, {
series: e.point.name,
xval: e.point.xval
});
this.annotations_.push(this.createAnnotation(a));
this.updateAnnotationDivPositions();
this.updateAnnotationInfo();
this.attachAnnotationsToChart_();
$(this).triggerHandler('annotationCreated', a);
$(this).triggerHandler('annotationsChanged', {});
// Annotations should begin life editable.
this.makeAnnotationEditable(a);
};
annotations.prototype.destroy = function destroy() {
this.detachLabels();
};
// Public API
/**
* This is a restricted view of this.annotations_ which doesn't expose
* implementation details like the line / info divs.
*
* @typedef {
* xval: number, // x-value (i.e. millis or a raw number)
* series: string, // series name
* } PublicAnnotation
*/
/**
* @return {!Array.<!PublicAnnotation>} The current set of annotations, ordered
* from back to front.
*/
annotations.prototype.get = function get() {
var result = [];
for (var i = 0; i < this.annotations_.length; i++) {
result.push(this.createPublicAnnotation_(this.annotations_[i]));
}
return result;
};
/**
* Calling this will result in an annotationsChanged event being triggered, no
* matter whether it consists of additions, deletions, moves or no changes at
* all.
*
* @param {!Array.<!PublicAnnotation>} annotations The new set of annotations,
* ordered from back to front.
*/
annotations.prototype.set = function set(annotations) {
// Re-use divs from the old annotations array so far as we can.
// They're already correctly z-ordered.
var anyCreated = false;
for (var i = 0; i < annotations.length; i++) {
var a = annotations[i];
if (this.annotations_.length > i) {
// Only the divs need to be preserved.
var oldA = this.annotations_[i];
this.annotations_[i] = $.extend({
infoDiv: oldA.infoDiv,
lineDiv: oldA.lineDiv
}, a);
} else {
this.annotations_.push(this.createAnnotation(a));
anyCreated = true;
}
}
// If there are any remaining annotations, destroy them.
while (annotations.length < this.annotations_.length) {
this.removeAnnotation(this.annotations_[annotations.length]);
}
this.updateAnnotationDivPositions();
this.updateAnnotationInfo();
if (anyCreated) {
this.attachAnnotationsToChart_();
}
$(this).triggerHandler('annotationsChanged', {});
};
return annotations;
}();
/* loader wrapper */
Dygraph._require.add('dygraphs/src/extras/super-annotations.js', /* exports */{});
})();
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZXh0cmFzX3N1cGVyQW5ub3RhdGlvbnNfd3JhcHBlciIsIkR5Z3JhcGgiLCJ3aW5kb3ciLCJtb2R1bGUiLCJyZXF1aXJlIiwiTkFNRSIsIlBsdWdpbnMiLCJTdXBlckFubm90YXRpb25zIiwiX2V4dHJhc19zdXBlckFubm90YXRpb25zX2Nsb3N1cmUiLCJhbm5vdGF0aW9ucyIsIm9wdF9vcHRpb25zIiwiYW5ub3RhdGlvbnNfIiwibGFzdFdpZHRoXyIsImxhc3RIZWlnaHQiLCJkeWdyYXBoXyIsImRlZmF1bHRBbm5vdGF0aW9uUHJvcGVydGllc18iLCIkIiwiZXh0ZW5kIiwicHJvdG90eXBlIiwidG9TdHJpbmciLCJhY3RpdmF0ZSIsImciLCJkaWREcmF3Q2hhcnQiLCJwb2ludENsaWNrIiwiZGV0YWNoTGFiZWxzIiwiaSIsImxlbmd0aCIsImEiLCJsaW5lRGl2IiwicmVtb3ZlIiwiaW5mb0RpdiIsImFubm90YXRpb25XYXNEcmFnZ2VkIiwiZXZlbnQiLCJ1aSIsImFyZWEiLCJnZXRBcmVhIiwib2xkWUZyYWMiLCJ5RnJhYyIsIm5ld1lGcmFjIiwib2Zmc2V0VG9wIiwib2Zmc2V0SGVpZ2h0IiwieSIsImgiLCJtb3ZlQW5ub3RhdGlvblRvVG9wIiwidXBkYXRlQW5ub3RhdGlvbkRpdlBvc2l0aW9ucyIsInVwZGF0ZUFubm90YXRpb25JbmZvIiwidHJpZ2dlckhhbmRsZXIiLCJhbm5vdGF0aW9uIiwibWFrZUFubm90YXRpb25FZGl0YWJsZSIsImVkaXRhYmxlIiwiZWRpdGFibGVUZW1wbGF0ZURpdiIsImdldCIsImlubmVySFRNTCIsImdldFRlbXBsYXRlSFRNTCIsInRvZ2dsZUNsYXNzIiwiY3JlYXRlQW5ub3RhdGlvbiIsInNlbGYiLCJjb2xvciIsImdldENvbG9yRm9yU2VyaWVzXyIsInNlcmllcyIsIiRsaW5lRGl2IiwiY3NzIiwiYWRkQ2xhc3MiLCIkaW5mb0RpdiIsImNsb25lIiwicmVtb3ZlQXR0ciIsInNob3ciLCJ0aGF0IiwiZHJhZ2dhYmxlIiwiZHJhZ2dhYmxlU3RhcnQiLCJpc0RyYWdnaW5nIiwiZHJhZ2dhYmxlRHJhZyIsImRyYWdnYWJsZVN0b3AiLCJvbiIsImNsaWNrS2lsbCIsInJlbW92ZUFubm90YXRpb24iLCJkYmxjbGljayIsImNsaWNrVXBkYXRlIiwiZXh0cmFjdFVwZGF0ZWRQcm9wZXJ0aWVzXyIsImNsaWNrQ2FuY2VsIiwiZmluZFBvaW50SW5kZXhfIiwieHZhbCIsImNvbCIsImdldExhYmVscyIsImluZGV4T2YiLCJsb3dJZHgiLCJoaWdoSWR4IiwibnVtUm93cyIsImlkeCIsIk1hdGgiLCJmbG9vciIsInhBdElkeCIsImdldFZhbHVlIiwiY29sb3JzIiwiZ2V0Q29sb3JzIiwiZGl2IiwiZ3JhcGhEaXYiLCJhcHBlbmRUbyIsInNwbGljZSIsInB1c2giLCJsYXlvdXQiLCJjaGFydExlZnQiLCJ4IiwiY2hhcnRSaWdodCIsInciLCJjaGFydFRvcCIsImNoYXJ0Qm90dG9tIiwicG9zIiwiZmluZFBvcyIsImJveCIsImVhY2giLCJhbm5vdGF0aW9uc0xvb3BfIiwicm93X2NvbCIsImhpZGUiLCJ4eSIsInRvRG9tQ29vcmRzIiwicG9pbnRZIiwibGluZUhlaWdodCIsInVuZGVmaW5lZCIsIm1pbiIsInZpc2libGUiLCJ0b2dnbGUiLCJ0ZW1wbGF0ZURpdiIsImNyZWF0ZVB1YmxpY0Fubm90YXRpb25fIiwib3B0X3Byb3BzIiwiZGlzcGxheUFubm90YXRpb24iLCJyb3ciLCJ5T3B0VmlldyIsIm9wdGlvbnNWaWV3Rm9yQXhpc18iLCJ4T3B0VmlldyIsInh2ZiIsImdldE9wdGlvbkZvckF4aXMiLCJjYWxsIiwiZ2V0T3B0aW9uIiwiaHRtbCIsImsiLCJ2IiwicmVwbGFjZSIsIlJlZ0V4cCIsImZpbmQiLCJmaWVsZExvb3BfIiwiZWwiLCJhdHRyIiwidmFsIiwiYXR0YWNoQW5ub3RhdGlvbnNUb0NoYXJ0XyIsIndhcm4iLCJlIiwiZHlncmFwaCIsInByZXZlbnREZWZhdWx0IiwicG9pbnQiLCJuYW1lIiwiZGVzdHJveSIsInJlc3VsdCIsInNldCIsImFueUNyZWF0ZWQiLCJvbGRBIiwiX3JlcXVpcmUiLCJhZGQiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvZXh0cmFzL3N1cGVyLWFubm90YXRpb25zLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCAyMDEzIERhbiBWYW5kZXJrYW0gKGRhbnZka0BnbWFpbC5jb20pXG4gKiBNSVQtbGljZW5jZWQ6IGh0dHBzOi8vb3BlbnNvdXJjZS5vcmcvbGljZW5zZXMvTUlUXG4gKlxuICogTm90ZTogVGhpcyBwbHVnaW4gcmVxdWlyZXMgalF1ZXJ5IGFuZCBqUXVlcnkgVUkgRHJhZ2dhYmxlLlxuICpcbiAqIFNlZSBoaWdoLWxldmVsIGRvY3VtZW50YXRpb24gYXQgLi4vLi4vZG9jcy9oYWlybGluZXMtYW5ub3RhdGlvbnMucGRmXG4gKi9cblxuLyogbG9hZGVyIHdyYXBwZXIgdG8gYWxsb3cgYnJvd3NlciB1c2UgYW5kIEVTNiBpbXBvcnRzICovXG4oZnVuY3Rpb24gX2V4dHJhc19zdXBlckFubm90YXRpb25zX3dyYXBwZXIoKSB7XG4ndXNlIHN0cmljdCc7XG52YXIgRHlncmFwaDtcbmlmICh3aW5kb3cuRHlncmFwaCkge1xuICBEeWdyYXBoID0gd2luZG93LkR5Z3JhcGg7XG59IGVsc2UgaWYgKHR5cGVvZihtb2R1bGUpICE9PSAndW5kZWZpbmVkJykge1xuICBEeWdyYXBoID0gcmVxdWlyZSgnLi4vZHlncmFwaCcpO1xuICBpZiAodHlwZW9mKER5Z3JhcGguTkFNRSkgPT09ICd1bmRlZmluZWQnICYmIHR5cGVvZihEeWdyYXBoLmRlZmF1bHQpICE9PSAndW5kZWZpbmVkJylcbiAgICBEeWdyYXBoID0gRHlncmFwaC5kZWZhdWx0O1xufVxuLyogZW5kIG9mIGxvYWRlciB3cmFwcGVyIGhlYWRlciAqL1xuXG5EeWdyYXBoLlBsdWdpbnMuU3VwZXJBbm5vdGF0aW9ucyA9IChmdW5jdGlvbiBfZXh0cmFzX3N1cGVyQW5ub3RhdGlvbnNfY2xvc3VyZSgpIHtcblxuXCJ1c2Ugc3RyaWN0XCI7XG5cbi8qKlxuICogVGhlc2UgYXJlIGp1c3QgdGhlIGJhc2ljIHJlcXVpcmVtZW50cyAtLSBhbm5vdGF0aW9ucyBjYW4gaGF2ZSB3aGF0ZXZlciBvdGhlclxuICogcHJvcGVydGllcyB0aGUgY29kZSB0aGF0IGRpc3BsYXlzIHRoZW0gd2FudHMgdGhlbSB0byBoYXZlLlxuICpcbiAqIEB0eXBlZGVmIHtcbiAqICAgeHZhbDogIG51bWJlciwgICAgICAvLyB4LXZhbHVlIChpLmUuIG1pbGxpcyBvciBhIHJhdyBudW1iZXIpXG4gKiAgIHNlcmllczogc3RyaW5nLCAgICAgLy8gc2VyaWVzIG5hbWVcbiAqICAgeUZyYWM6ID9udW1iZXIsICAgICAvLyB5LXBvc2l0aW9uaW5nLiBEZWZhdWx0IGlzIGEgZmV3IHB4IGFib3ZlIHRoZSBwb2ludC5cbiAqICAgbGluZURpdjogIUVsZW1lbnQgICAvLyB2ZXJ0aWNhbCBkaXYgY29ubmVjdGluZyBwb2ludCB0byBpbmZvIGRpdi5cbiAqICAgaW5mb0RpdjogIUVsZW1lbnQgICAvLyBkaXYgY29udGFpbmluZyBpbmZvIGFib3V0IHRoZSBhbm5vdGF0aW9uLlxuICogfSBBbm5vdGF0aW9uXG4gKi9cblxudmFyIGFubm90YXRpb25zID0gZnVuY3Rpb24gYW5ub3RhdGlvbnMob3B0X29wdGlvbnMpIHtcbiAgLyogQHR5cGUgeyFBcnJheS48IUFubm90YXRpb24+fSAqL1xuICB0aGlzLmFubm90YXRpb25zXyA9IFtdO1xuICAvLyBVc2VkIHRvIGRldGVjdCByZXNpemVzICh3aGljaCByZXF1aXJlIHRoZSBkaXZzIHRvIGJlIHJlcG9zaXRpb25lZCkuXG4gIHRoaXMubGFzdFdpZHRoXyA9IC0xO1xuICB0aGlzLmxhc3RIZWlnaHQgPSAtMTtcbiAgdGhpcy5keWdyYXBoXyA9IG51bGw7XG5cbiAgb3B0X29wdGlvbnMgPSBvcHRfb3B0aW9ucyB8fCB7fTtcbiAgdGhpcy5kZWZhdWx0QW5ub3RhdGlvblByb3BlcnRpZXNfID0gJC5leHRlbmQoe1xuICAgICd0ZXh0JzogJ0Rlc2NyaXB0aW9uJ1xuICB9LCBvcHRfb3B0aW9uc1snZGVmYXVsdEFubm90YXRpb25Qcm9wZXJ0aWVzJ10pO1xufTtcblxuYW5ub3RhdGlvbnMucHJvdG90eXBlLnRvU3RyaW5nID0gZnVuY3Rpb24gdG9TdHJpbmcoKSB7XG4gIHJldHVybiBcIlN1cGVyQW5ub3RhdGlvbnMgUGx1Z2luXCI7XG59O1xuXG5hbm5vdGF0aW9ucy5wcm90b3R5cGUuYWN0aXZhdGUgPSBmdW5jdGlvbiBhY3RpdmF0ZShnKSB7XG4gIHRoaXMuZHlncmFwaF8gPSBnO1xuICB0aGlzLmFubm90YXRpb25zXyA9IFtdO1xuXG4gIHJldHVybiB7XG4gICAgZGlkRHJhd0NoYXJ0OiB0aGlzLmRpZERyYXdDaGFydCxcbiAgICBwb2ludENsaWNrOiB0aGlzLnBvaW50Q2xpY2sgIC8vIFRPRE8oZGFudmspOiBpbXBsZW1lbnQgaW4gZHlncmFwaHNcbiAgfTtcbn07XG5cbmFubm90YXRpb25zLnByb3RvdHlwZS5kZXRhY2hMYWJlbHMgPSBmdW5jdGlvbiBkZXRhY2hMYWJlbHMoKSB7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5hbm5vdGF0aW9uc18ubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgYSA9IHRoaXMuYW5ub3RhdGlvbnNfW2ldO1xuICAgICQoYS5saW5lRGl2KS5yZW1vdmUoKTtcbiAgICAkKGEuaW5mb0RpdikucmVtb3ZlKCk7XG4gICAgdGhpcy5hbm5vdGF0aW9uc19baV0gPSBudWxsO1xuICB9XG4gIHRoaXMuYW5ub3RhdGlvbnNfID0gW107XG59O1xuXG5hbm5vdGF0aW9ucy5wcm90b3R5cGUuYW5ub3RhdGlvbldhc0RyYWdnZWQgPSBmdW5jdGlvbiBhbm5vdGF0aW9uV2FzRHJhZ2dlZChhLCBldmVudCwgdWkpIHtcbiAgdmFyIGcgPSB0aGlzLmR5Z3JhcGhfO1xuICB2YXIgYXJlYSA9IGcuZ2V0QXJlYSgpO1xuICB2YXIgb2xkWUZyYWMgPSBhLnlGcmFjO1xuXG4gIHZhciBpbmZvRGl2ID0gYS5pbmZvRGl2O1xuICB2YXIgbmV3WUZyYWMgPSAoKGluZm9EaXYub2Zmc2V0VG9wICsgaW5mb0Rpdi5vZmZzZXRIZWlnaHQpIC0gYXJlYS55KSAvIGFyZWEuaDtcbiAgaWYgKG5ld1lGcmFjID09IG9sZFlGcmFjKSByZXR1cm47XG5cbiAgYS55RnJhYyA9IG5ld1lGcmFjO1xuXG4gIHRoaXMubW92ZUFubm90YXRpb25Ub1RvcChhKTtcbiAgdGhpcy51cGRhdGVBbm5vdGF0aW9uRGl2UG9zaXRpb25zKCk7XG4gIHRoaXMudXBkYXRlQW5ub3RhdGlvbkluZm8oKTtcbiAgJCh0aGlzKS50cmlnZ2VySGFuZGxlcignYW5ub3RhdGlvbk1vdmVkJywge1xuICAgIGFubm90YXRpb246IGEsXG4gICAgb2xkWUZyYWM6IG9sZFlGcmFjLFxuICAgIG5ld1lGcmFjOiBhLnlGcmFjXG4gIH0pO1xuICAkKHRoaXMpLnRyaWdnZXJIYW5kbGVyKCdhbm5vdGF0aW9uc0NoYW5nZWQnLCB7fSk7XG59O1xuXG5hbm5vdGF0aW9ucy5wcm90b3R5cGUubWFrZUFubm90YXRpb25FZGl0YWJsZSA9IGZ1bmN0aW9uIG1ha2VBbm5vdGF0aW9uRWRpdGFibGUoYSkge1xuICBpZiAoYS5lZGl0YWJsZSA9PSB0cnVlKSByZXR1cm47XG4gIHRoaXMubW92ZUFubm90YXRpb25Ub1RvcChhKTtcblxuICAvLyBOb3RlOiB3ZSBoYXZlIHRvIGZpbGwgb3V0IHRoZSBIVE1MIG91cnNlbHZlcyBiZWNhdXNlXG4gIC8vIHVwZGF0ZUFubm90YXRpb25JbmZvKCkgd29uJ3QgdG91Y2ggZWRpdGFibGUgYW5ub3RhdGlvbnMuXG4gIGEuZWRpdGFibGUgPSB0cnVlO1xuICB2YXIgZWRpdGFibGVUZW1wbGF0ZURpdiA9ICQoJyNhbm5vdGF0aW9uLWVkaXRhYmxlLXRlbXBsYXRlJykuZ2V0KDApO1xuICBhLmluZm9EaXYuaW5uZXJIVE1MID0gdGhpcy5nZXRUZW1wbGF0ZUhUTUwoZWRpdGFibGVUZW1wbGF0ZURpdiwgYSk7XG4gICQoYS5pbmZvRGl2KS50b2dnbGVDbGFzcygnZWRpdGFibGUnLCAhIWEuZWRpdGFibGUpO1xuICAkKHRoaXMpLnRyaWdnZXJIYW5kbGVyKCdiZWdhbkVkaXRBbm5vdGF0aW9uJywgYSk7XG59O1xuXG4vLyBUaGlzIGNyZWF0ZXMgdGhlIGhhaXJsaW5lIG9iamVjdCBhbmQgcmV0dXJucyBpdC5cbi8vIEl0IGRvZXMgbm90IHBvc2l0aW9uIGl0IGFuZCBkb2VzIG5vdCBhdHRhY2ggaXQgdG8gdGhlIGNoYXJ0LlxuYW5ub3RhdGlvbnMucHJvdG90eXBlLmNyZWF0ZUFubm90YXRpb24gPSBmdW5jdGlvbiBjcmVhdGVBbm5vdGF0aW9uKGEpIHtcbiAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gIHZhciBjb2xvciA9IHRoaXMuZ2V0Q29sb3JGb3JTZXJpZXNfKGEuc2VyaWVzKTtcblxuICB2YXIgJGxpbmVEaXYgPSAkKCc8ZGl2IC8+JykuY3NzKHtcbiAgICAnd2lkdGgnOiAnMXB4JyxcbiAgICAnbGVmdCc6ICczcHgnLFxuICAgICdiYWNrZ3JvdW5kJzogJ2JsYWNrJyxcbiAgICAnaGVpZ2h0JzogJzEwMCUnLFxuICAgICdwb3NpdGlvbic6ICdhYnNvbHV0ZScsXG4gICAgLy8gVE9ETyhkYW52ayk6IHVzZSBib3JkZXItY29sb3IgaGVyZSBmb3IgY29uc2lzdGVuY3k/XG4gICAgJ2JhY2tncm91bmQtY29sb3InOiBjb2xvcixcbiAgICAnei1pbmRleCc6IDEwXG4gIH0pLmFkZENsYXNzKCdkeWdyYXBoLWFubm90YXRpb24tbGluZScpO1xuXG4gIHZhciAkaW5mb0RpdiA9ICQoJyNhbm5vdGF0aW9uLXRlbXBsYXRlJykuY2xvbmUoKS5yZW1vdmVBdHRyKCdpZCcpLmNzcyh7XG4gICAgICAncG9zaXRpb24nOiAnYWJzb2x1dGUnLFxuICAgICAgJ2JvcmRlci1jb2xvcic6IGNvbG9yLFxuICAgICAgJ3otaW5kZXgnOiAxMFxuICAgIH0pXG4gICAgLnNob3coKTtcblxuICAkLmV4dGVuZChhLCB7XG4gICAgbGluZURpdjogJGxpbmVEaXYuZ2V0KDApLFxuICAgIGluZm9EaXY6ICRpbmZvRGl2LmdldCgwKVxuICB9KTtcblxuICB2YXIgdGhhdCA9IHRoaXM7XG5cbiAgJGluZm9EaXYuZHJhZ2dhYmxlKHtcbiAgICAnc3RhcnQnOiBmdW5jdGlvbiBkcmFnZ2FibGVTdGFydChldmVudCwgdWkpIHtcbiAgICAgICQodGhpcykuY3NzKHsnYm90dG9tJzogJyd9KTtcbiAgICAgIGEuaXNEcmFnZ2luZyA9IHRydWU7XG4gICAgfSxcbiAgICAnZHJhZyc6IGZ1bmN0aW9uIGRyYWdnYWJsZURyYWcoZXZlbnQsIHVpKSB7XG4gICAgICBzZWxmLmFubm90YXRpb25XYXNEcmFnZ2VkKGEsIGV2ZW50LCB1aSk7XG4gICAgfSxcbiAgICAnc3RvcCc6IGZ1bmN0aW9uIGRyYWdnYWJsZVN0b3AoZXZlbnQsIHVpKSB7XG4gICAgICAkKHRoaXMpLmNzcyh7J3RvcCc6ICcnfSk7XG4gICAgICBhLmlzRHJhZ2dpbmcgPSBmYWxzZTtcbiAgICAgIHNlbGYudXBkYXRlQW5ub3RhdGlvbkRpdlBvc2l0aW9ucygpO1xuICAgIH0sXG4gICAgJ2F4aXMnOiAneScsXG4gICAgJ2NvbnRhaW5tZW50JzogJ3BhcmVudCdcbiAgfSk7XG5cbiAgLy8gVE9ETyhkYW52ayk6IHVzZSAnb24nIGluc3RlYWQgb2YgZGVsZWdhdGUvZGJsY2xpY2tcbiAgJGluZm9EaXYub24oJ2NsaWNrJywgJy5hbm5vdGF0aW9uLWtpbGwtYnV0dG9uJywgZnVuY3Rpb24gY2xpY2tLaWxsKCkge1xuICAgIHRoYXQucmVtb3ZlQW5ub3RhdGlvbihhKTtcbiAgICAkKHRoYXQpLnRyaWdnZXJIYW5kbGVyKCdhbm5vdGF0aW9uRGVsZXRlZCcsIGEpO1xuICAgICQodGhhdCkudHJpZ2dlckhhbmRsZXIoJ2Fubm90YXRpb25zQ2hhbmdlZCcsIHt9KTtcbiAgfSk7XG5cbiAgJGluZm9EaXYub24oJ2RibGNsaWNrJywgZnVuY3Rpb24gZGJsY2xpY2soKSB7XG4gICAgdGhhdC5tYWtlQW5ub3RhdGlvbkVkaXRhYmxlKGEpO1xuICB9KTtcbiAgJGluZm9EaXYub24oJ2NsaWNrJywgJy5hbm5vdGF0aW9uLXVwZGF0ZScsIGZ1bmN0aW9uIGNsaWNrVXBkYXRlKCkge1xuICAgIHNlbGYuZXh0cmFjdFVwZGF0ZWRQcm9wZXJ0aWVzXygkaW5mb0Rpdi5nZXQoMCksIGEpO1xuICAgIGEuZWRpdGFibGUgPSBmYWxzZTtcbiAgICBzZWxmLnVwZGF0ZUFubm90YXRpb25JbmZvKCk7XG4gICAgJCh0aGF0KS50cmlnZ2VySGFuZGxlcignYW5ub3RhdGlvbkVkaXRlZCcsIGEpO1xuICAgICQodGhhdCkudHJpZ2dlckhhbmRsZXIoJ2Fubm90YXRpb25zQ2hhbmdlZCcsIHt9KTtcbiAgfSk7XG4gICRpbmZvRGl2Lm9uKCdjbGljaycsICcuYW5ub3RhdGlvbi1jYW5jZWwnLCBmdW5jdGlvbiBjbGlja0NhbmNlbCgpIHtcbiAgICBhLmVkaXRhYmxlID0gZmFsc2U7XG4gICAgc2VsZi51cGRhdGVBbm5vdGF0aW9uSW5mbygpO1xuICAgICQodGhhdCkudHJpZ2dlckhhbmRsZXIoJ2NhbmNlbEVkaXRBbm5vdGF0aW9uJywgYSk7XG4gIH0pO1xuXG4gIHJldHVybiBhO1xufTtcblxuLy8gRmluZCB0aGUgaW5kZXggb2YgYSBwb2ludCBpbiBhIHNlcmllcy5cbi8vIFJldHVybnMgYSAyLWVsZW1lbnQgYXJyYXksIFtyb3csIGNvbF0sIHdoaWNoIGNhbiBiZSB1c2VkIHdpdGhcbi8vIGR5Z3JhcGguZ2V0VmFsdWUoKSB0byBnZXQgdGhlIHZhbHVlIGF0IHRoaXMgcG9pbnQuXG4vLyBSZXR1cm5zIG51bGwgaWYgdGhlcmUncyBubyBtYXRjaC5cbmFubm90YXRpb25zLnByb3RvdHlwZS5maW5kUG9pbnRJbmRleF8gPSBmdW5jdGlvbiBmaW5kUG9pbnRJbmRleF8oc2VyaWVzLCB4dmFsKSB7XG4gIHZhciBjb2wgPSB0aGlzLmR5Z3JhcGhfLmdldExhYmVscygpLmluZGV4T2Yoc2VyaWVzKTtcbiAgaWYgKGNvbCA9PSAtMSkgcmV0dXJuIG51bGw7XG5cbiAgdmFyIGxvd0lkeCA9IDAsIGhpZ2hJZHggPSB0aGlzLmR5Z3JhcGhfLm51bVJvd3MoKSAtIDE7XG4gIHdoaWxlIChsb3dJZHggPD0gaGlnaElkeCkge1xuICAgIHZhciBpZHggPSBNYXRoLmZsb29yKChsb3dJZHggKyBoaWdoSWR4KSAvIDIpO1xuICAgIHZhciB4QXRJZHggPSB0aGlzLmR5Z3JhcGhfLmdldFZhbHVlKGlkeCwgMCk7XG4gICAgaWYgKHhBdElkeCA9PSB4dmFsKSB7XG4gICAgICByZXR1cm4gW2lkeCwgY29sXTtcbiAgICB9IGVsc2UgaWYgKHhBdElkeCA8IHh2YWwpIHtcbiAgICAgIGxvd0lkeCA9IGlkeCArIDE7XG4gICAgfSBlbHNlIHtcbiAgICAgIGhpZ2hJZHggPSBpZHggLSAxO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbnVsbDtcbn07XG5cbmFubm90YXRpb25zLnByb3RvdHlwZS5nZXRDb2xvckZvclNlcmllc18gPSBmdW5jdGlvbiBnZXRDb2xvckZvclNlcmllc18oc2VyaWVzKSB7XG4gIHZhciBjb2xvcnMgPSB0aGlzLmR5Z3JhcGhfLmdldENvbG9ycygpO1xuICB2YXIgY29sID0gdGhpcy5keWdyYXBoXy5nZXRMYWJlbHMoKS5pbmRleE9mKHNlcmllcyk7XG4gIGlmIChjb2wgPT0gLTEpIHJldHVybiBudWxsO1xuXG4gIHJldHVybiBjb2xvcnNbKGNvbCAtIDEpICUgY29sb3JzLmxlbmd0aF07XG59O1xuXG4vLyBNb3ZlcyBhIGhhaXJsaW5lJ3MgZGl2cyB0byB0aGUgdG9wIG9mIHRoZSB6LW9yZGVyaW5nLlxuYW5ub3RhdGlvbnMucHJvdG90eXBlLm1vdmVBbm5vdGF0aW9uVG9Ub3AgPSBmdW5jdGlvbiBtb3ZlQW5ub3RhdGlvblRvVG9wKGEpIHtcbiAgdmFyIGRpdiA9IHRoaXMuZHlncmFwaF8uZ3JhcGhEaXY7XG4gICQoYS5pbmZvRGl2KS5hcHBlbmRUbyhkaXYpO1xuICAkKGEubGluZURpdikuYXBwZW5kVG8oZGl2KTtcblxuICB2YXIgaWR4ID0gdGhpcy5hbm5vdGF0aW9uc18uaW5kZXhPZihhKTtcbiAgdGhpcy5hbm5vdGF0aW9uc18uc3BsaWNlKGlkeCwgMSk7XG4gIHRoaXMuYW5ub3RhdGlvbnNfLnB1c2goYSk7XG59O1xuXG4vLyBQb3NpdGlvbnMgZXhpc3RpbmcgaGFpcmxpbmUgZGl2cy5cbmFubm90YXRpb25zLnByb3RvdHlwZS51cGRhdGVBbm5vdGF0aW9uRGl2UG9zaXRpb25zID0gZnVuY3Rpb24gdXBkYXRlQW5ub3RhdGlvbkRpdlBvc2l0aW9ucygpIHtcbiAgdmFyIGxheW91dCA9IHRoaXMuZHlncmFwaF8uZ2V0QXJlYSgpO1xuICB2YXIgY2hhcnRMZWZ0ID0gbGF5b3V0LngsIGNoYXJ0UmlnaHQgPSBsYXlvdXQueCArIGxheW91dC53O1xuICB2YXIgY2hhcnRUb3AgPSBsYXlvdXQueSwgY2hhcnRCb3R0b20gPSBsYXlvdXQueSArIGxheW91dC5oO1xuICB2YXIgZGl2ID0gdGhpcy5keWdyYXBoXy5ncmFwaERpdjtcbiAgdmFyIHBvcyA9IER5Z3JhcGguZmluZFBvcyhkaXYpO1xuICB2YXIgYm94ID0gW2xheW91dC54ICsgcG9zLngsIGxheW91dC55ICsgcG9zLnldO1xuICBib3gucHVzaChib3hbMF0gKyBsYXlvdXQudyk7XG4gIGJveC5wdXNoKGJveFsxXSArIGxheW91dC5oKTtcblxuICB2YXIgZyA9IHRoaXMuZHlncmFwaF87XG5cbiAgdmFyIHRoYXQgPSB0aGlzO1xuICAkLmVhY2godGhpcy5hbm5vdGF0aW9uc18sIGZ1bmN0aW9uIGFubm90YXRpb25zTG9vcF8oaWR4LCBhKSB7XG4gICAgdmFyIHJvd19jb2wgPSB0aGF0LmZpbmRQb2ludEluZGV4XyhhLnNlcmllcywgYS54dmFsKTtcbiAgICBpZiAocm93X2NvbCA9PSBudWxsKSB7XG4gICAgICAkKFthLmxpbmVEaXYsIGEuaW5mb0Rpdl0pLmhpZGUoKTtcbiAgICAgIHJldHVybjtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gVE9ETyhkYW52ayk6IG9ubHkgZG8gdGhpcyBpZiB0aGV5J3JlIGludmlzaWJsZT9cbiAgICAgICQoW2EubGluZURpdiwgYS5pbmZvRGl2XSkuc2hvdygpO1xuICAgIH1cbiAgICB2YXIgeHkgPSBnLnRvRG9tQ29vcmRzKGEueHZhbCwgZy5nZXRWYWx1ZShyb3dfY29sWzBdLCByb3dfY29sWzFdKSk7XG4gICAgdmFyIHggPSB4eVswXSwgcG9pbnRZID0geHlbMV07XG5cbiAgICB2YXIgbGluZUhlaWdodCA9IDY7ICAvLyBUT0RPKGRhbnZrKTogb3B0aW9uP1xuXG4gICAgdmFyIHkgPSBwb2ludFk7XG4gICAgaWYgKGEueUZyYWMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgeSA9IGxheW91dC55ICsgbGF5b3V0LmggKiBhLnlGcmFjO1xuICAgIH0gZWxzZSB7XG4gICAgICB5IC09IGxpbmVIZWlnaHQ7XG4gICAgfVxuXG4gICAgdmFyIGxpbmVIZWlnaHQgPSB5IDwgcG9pbnRZID8gKHBvaW50WSAtIHkpIDogKHkgLSBwb2ludFkgLSBhLmluZm9EaXYub2Zmc2V0SGVpZ2h0KTtcbiAgICAkKGEubGluZURpdikuY3NzKHtcbiAgICAgICdsZWZ0JzogeCArICdweCcsXG4gICAgICAndG9wJzogTWF0aC5taW4oeSwgcG9pbnRZKSArICdweCcsXG4gICAgICAnaGVpZ2h0JzogbGluZUhlaWdodCArICdweCdcbiAgICB9KTtcbiAgICAkKGEuaW5mb0RpdikuY3NzKHtcbiAgICAgICdsZWZ0JzogeCArICdweCcsXG4gICAgfSk7XG4gICAgaWYgKCFhLmlzRHJhZ2dpbmcpIHtcbiAgICAgIC8vIGpRdWVyeSBVSSBkcmFnZ2FibGUgbGlrZXMgdG8gc2V0ICd0b3AnLCB3aGVyZWFzIHN1cGVyYW5ub3RhdGlvbnMgc2V0c1xuICAgICAgLy8gJ2JvdHRvbScuIFNldHRpbmcgYm90aCB3aWxsIG1ha2UgdGhlIGFubm90YXRpb24gZ3JvdyBhbmQgY29udHJhY3QgYXNcbiAgICAgIC8vIHRoZSB1c2VyIGRyYWdzIGl0LCB3aGljaCBsb29rcyBiYWQuXG4gICAgICAkKGEuaW5mb0RpdikuY3NzKHtcbiAgICAgICAgJ2JvdHRvbSc6IChkaXYub2Zmc2V0SGVpZ2h0IC0geSkgKyAncHgnXG4gICAgICB9KSAgLy8uZHJhZ2dhYmxlKFwib3B0aW9uXCIsIFwiY29udGFpbm1lbnRcIiwgYm94KTtcblxuICAgICAgdmFyIHZpc2libGUgPSAoeCA+PSBjaGFydExlZnQgJiYgeCA8PSBjaGFydFJpZ2h0KSAmJlxuICAgICAgICAgICAgICAgICAgICAocG9pbnRZID49IGNoYXJ0VG9wICYmIHBvaW50WSA8PSBjaGFydEJvdHRvbSk7XG4gICAgICAkKFthLmluZm9EaXYsIGEubGluZURpdl0pLnRvZ2dsZSh2aXNpYmxlKTtcbiAgICB9XG4gIH0pO1xufTtcblxuLy8gRmlsbHMgb3V0IHRoZSBpbmZvIGRpdiBiYXNlZCBvbiBjdXJyZW50IGNvb3JkaW5hdGVzLlxuYW5ub3RhdGlvbnMucHJvdG90eXBlLnVwZGF0ZUFubm90YXRpb25JbmZvID0gZnVuY3Rpb24gdXBkYXRlQW5ub3RhdGlvbkluZm8oKSB7XG4gIHZhciBnID0gdGhpcy5keWdyYXBoXztcblxuICB2YXIgdGhhdCA9IHRoaXM7XG4gIHZhciB0ZW1wbGF0ZURpdiA9ICQoJyNhbm5vdGF0aW9uLXRlbXBsYXRlJykuZ2V0KDApO1xuICAkLmVhY2godGhpcy5hbm5vdGF0aW9uc18sIGZ1bmN0aW9uIGFubm90YXRpb25zTG9vcF8oaWR4LCBhKSB7XG4gICAgLy8gV2Ugc2hvdWxkIG5ldmVyIHVwZGF0ZSBhbiBlZGl0YWJsZSBkaXYgLS0gZG9pbmcgc28gbWF5IGtpbGwgdW5zYXZlZFxuICAgIC8vIGVkaXRzIHRvIGFuIGFubm90YXRpb24uXG4gICAgJChhLmluZm9EaXYpLnRvZ2dsZUNsYXNzKCdlZGl0YWJsZScsICEhYS5lZGl0YWJsZSk7XG4gICAgaWYgKGEuZWRpdGFibGUpIHJldHVybjtcbiAgICBhLmluZm9EaXYuaW5uZXJIVE1MID0gdGhhdC5nZXRUZW1wbGF0ZUhUTUwodGVtcGxhdGVEaXYsIGEpO1xuICB9KTtcbn07XG5cbi8qKlxuICogQHBhcmFtIHshQW5ub3RhdGlvbn0gYSBJbnRlcm5hbCBhbm5vdGF0aW9uXG4gKiBAcmV0dXJuIHshUHVibGljQW5ub3RhdGlvbn0gYSB2aWV3IG9mIHRoZSBhbm5vdGF0aW9uIGZvciB0aGUgcHVibGljIEFQSS5cbiAqL1xuYW5ub3RhdGlvbnMucHJvdG90eXBlLmNyZWF0ZVB1YmxpY0Fubm90YXRpb25fID0gZnVuY3Rpb24gY3JlYXRlUHVibGljQW5ub3RhdGlvbl8oYSwgb3B0X3Byb3BzKSB7XG4gIHZhciBkaXNwbGF5QW5ub3RhdGlvbiA9ICQuZXh0ZW5kKHt9LCBhLCBvcHRfcHJvcHMpO1xuICBkZWxldGUgZGlzcGxheUFubm90YXRpb25bJ2luZm9EaXYnXTtcbiAgZGVsZXRlIGRpc3BsYXlBbm5vdGF0aW9uWydsaW5lRGl2J107XG4gIGRlbGV0ZSBkaXNwbGF5QW5ub3RhdGlvblsnaXNEcmFnZ2luZyddO1xuICBkZWxldGUgZGlzcGxheUFubm90YXRpb25bJ2VkaXRhYmxlJ107XG4gIHJldHVybiBkaXNwbGF5QW5ub3RhdGlvbjtcbn07XG5cbi8vIEZpbGwgb3V0IGEgZGl2IHVzaW5nIHRoZSB2YWx1ZXMgaW4gdGhlIGFubm90YXRpb24gb2JqZWN0LlxuLy8gVGhlIGRpdidzIGh0bWwgaXMgZXhwZWN0ZWQgdG8gaGF2ZSB0ZXh0IG9mIHRoZSBmb3JtIFwie3trZXl9fVwiXG5hbm5vdGF0aW9ucy5wcm90b3R5cGUuZ2V0VGVtcGxhdGVIVE1MID0gZnVuY3Rpb24gZ2V0VGVtcGxhdGVIVE1MKGRpdiwgYSkge1xuICB2YXIgZyA9IHRoaXMuZHlncmFwaF87XG4gIHZhciByb3dfY29sID0gdGhpcy5maW5kUG9pbnRJbmRleF8oYS5zZXJpZXMsIGEueHZhbCk7XG4gIGlmIChyb3dfY29sID09IG51bGwpIHJldHVybjsgIC8vIHBlcmhhcHMgaXQncyBubyBsb25nZXIgYSByZWFsIHBvaW50P1xuICB2YXIgcm93ID0gcm93X2NvbFswXTtcbiAgdmFyIGNvbCA9IHJvd19jb2xbMV07XG5cbiAgdmFyIHlPcHRWaWV3ID0gZy5vcHRpb25zVmlld0ZvckF4aXNfKCd5MScpOyAgLy8gVE9ETzogc3VwcG9ydCBzZWNvbmRhcnksIHRvb1xuICB2YXIgeE9wdFZpZXcgPSBnLm9wdGlvbnNWaWV3Rm9yQXhpc18oJ3gnKTtcbiAgdmFyIHh2ZiA9IGcuZ2V0T3B0aW9uRm9yQXhpcygndmFsdWVGb3JtYXR0ZXInLCAneCcpO1xuXG4gIHZhciB4ID0geHZmLmNhbGwoZywgYS54dmFsLCB4T3B0Vmlldyk7XG4gIHZhciB5ID0gZy5nZXRPcHRpb24oJ3ZhbHVlRm9ybWF0dGVyJywgYS5zZXJpZXMpLmNhbGwoXG4gICAgICBnLCBnLmdldFZhbHVlKHJvdywgY29sKSwgeU9wdFZpZXcpO1xuXG4gIHZhciBkaXNwbGF5QW5ub3RhdGlvbiA9IHRoaXMuY3JlYXRlUHVibGljQW5ub3RhdGlvbl8oYSwge3g6eCwgeTp5fSk7XG4gIHZhciBodG1sID0gZGl2LmlubmVySFRNTDtcbiAgZm9yICh2YXIgayBpbiBkaXNwbGF5QW5ub3RhdGlvbikge1xuICAgIHZhciB2ID0gZGlzcGxheUFubm90YXRpb25ba107XG4gICAgaWYgKHR5cGVvZih2KSA9PSAnb2JqZWN0JykgY29udGludWU7ICAvLyBlLmcuIGluZm9EaXYgb3IgbGluZURpdlxuICAgIGh0bWwgPSBodG1sLnJlcGxhY2UobmV3IFJlZ0V4cCgnXFx7XFx7JyArIGsgKyAnXFx9XFx9JywgJ2cnKSwgdik7XG4gIH1cbiAgcmV0dXJuIGh0bWw7XG59O1xuXG4vLyBVcGRhdGUgdGhlIGFubm90YXRpb24gb2JqZWN0IGJ5IGxvb2tpbmcgZm9yIGVsZW1lbnRzIHdpdGggYSAnZGctYW5uLWZpZWxkJ1xuLy8gYXR0cmlidXRlLiBGb3IgZXhhbXBsZSwgPGlucHV0IHR5cGU9J3RleHQnIGRnLWFubi1maWVsZD0ndGV4dCcgLz4gd2lsbCBoYXZlXG4vLyBpdHMgdmFsdWUgcGxhY2VkIGluIHRoZSAndGV4dCcgYXR0cmlidXRlIG9mIHRoZSBhbm5vdGF0aW9uLlxuYW5ub3RhdGlvbnMucHJvdG90eXBlLmV4dHJhY3RVcGRhdGVkUHJvcGVydGllc18gPSBmdW5jdGlvbiBleHRyYWN0VXBkYXRlZFByb3BlcnRpZXNfKGRpdiwgYSkge1xuICAkKGRpdikuZmluZCgnW2RnLWFubi1maWVsZF0nKS5lYWNoKGZ1bmN0aW9uIGZpZWxkTG9vcF8oaWR4LCBlbCkge1xuICAgIHZhciBrID0gJChlbCkuYXR0cignZGctYW5uLWZpZWxkJyk7XG4gICAgdmFyIHYgPSAkKGVsKS52YWwoKTtcbiAgICBhW2tdID0gdjtcbiAgfSk7XG59O1xuXG4vLyBBZnRlciBhIHJlc2l6ZSwgdGhlIGhhaXJsaW5lIGRpdnMgY2FuIGdldCBkZXR0YWNoZWQgZnJvbSB0aGUgY2hhcnQuXG4vLyBUaGlzIHJlYXR0YWNoZXMgdGhlbS5cbmFubm90YXRpb25zLnByb3RvdHlwZS5hdHRhY2hBbm5vdGF0aW9uc1RvQ2hhcnRfID0gZnVuY3Rpb24gYXR0YWNoQW5ub3RhdGlvbnNUb0NoYXJ0XygpIHtcbiAgdmFyIGRpdiA9IHRoaXMuZHlncmFwaF8uZ3JhcGhEaXY7XG4gICQuZWFjaCh0aGlzLmFubm90YXRpb25zXywgZnVuY3Rpb24gYW5ub3RhdGlvbnNMb29wXyhpZHgsIGEpIHtcbiAgICAvLyBSZS1hdHRhY2hpbmcgYW4gZWRpdGFibGUgZGl2IHRvIHRoZSBET00gY2FuIGNsZWFyIGl0cyBmb2N1cy5cbiAgICAvLyBUaGlzIG1ha2VzIHR5cGluZyByZWFsbHkgZGlmZmljdWx0IVxuICAgIGlmIChhLmVkaXRhYmxlKSByZXR1cm47XG5cbiAgICAkKFthLmxpbmVEaXYsIGEuaW5mb0Rpdl0pLmFwcGVuZFRvKGRpdik7XG4gIH0pO1xufTtcblxuLy8gRGVsZXRlcyBhIGhhaXJsaW5lIGFuZCByZW1vdmVzIGl0IGZyb20gdGhlIGNoYXJ0LlxuYW5ub3RhdGlvbnMucHJvdG90eXBlLnJlbW92ZUFubm90YXRpb24gPSBmdW5jdGlvbiByZW1vdmVBbm5vdGF0aW9uKGEpIHtcbiAgdmFyIGlkeCA9IHRoaXMuYW5ub3RhdGlvbnNfLmluZGV4T2YoYSk7XG4gIGlmIChpZHggPj0gMCkge1xuICAgIHRoaXMuYW5ub3RhdGlvbnNfLnNwbGljZShpZHgsIDEpO1xuICAgICQoW2EubGluZURpdiwgYS5pbmZvRGl2XSkucmVtb3ZlKCk7XG4gIH0gZWxzZSB7XG4gICAgRHlncmFwaC53YXJuKCdUcmllZCB0byByZW1vdmUgbm9uLWV4aXN0ZW50IGFubm90YXRpb24uJyk7XG4gIH1cbn07XG5cbmFubm90YXRpb25zLnByb3RvdHlwZS5kaWREcmF3Q2hhcnQgPSBmdW5jdGlvbiBkaWREcmF3Q2hhcnQoZSkge1xuICB2YXIgZyA9IGUuZHlncmFwaDtcblxuICAvLyBFYXJseSBvdXQgaW4gdGhlIChjb21tb24pIGNhc2Ugb2YgemVybyBhbm5vdGF0aW9ucy5cbiAgaWYgKHRoaXMuYW5ub3RhdGlvbnNfLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuXG4gIHRoaXMudXBkYXRlQW5ub3RhdGlvbkRpdlBvc2l0aW9ucygpO1xuICB0aGlzLmF0dGFjaEFubm90YXRpb25zVG9DaGFydF8oKTtcbiAgdGhpcy51cGRhdGVBbm5vdGF0aW9uSW5mbygpO1xufTtcblxuYW5ub3RhdGlvbnMucHJvdG90eXBlLnBvaW50Q2xpY2sgPSBmdW5jdGlvbiBwb2ludENsaWNrKGUpIHtcbiAgLy8gUHJldmVudCBhbnkgb3RoZXIgYmVoYXZpb3IgYmFzZWQgb24gdGhpcyBjbGljaywgZS5nLiBjcmVhdGlvbiBvZiBhIGhhaXJsaW5lLlxuICBlLnByZXZlbnREZWZhdWx0KCk7XG5cbiAgdmFyIGEgPSAkLmV4dGVuZCh7fSwgdGhpcy5kZWZhdWx0QW5ub3RhdGlvblByb3BlcnRpZXNfLCB7XG4gICAgc2VyaWVzOiBlLnBvaW50Lm5hbWUsXG4gICAgeHZhbDogZS5wb2ludC54dmFsXG4gIH0pO1xuICB0aGlzLmFubm90YXRpb25zXy5wdXNoKHRoaXMuY3JlYXRlQW5ub3RhdGlvbihhKSk7XG5cbiAgdGhpcy51cGRhdGVBbm5vdGF0aW9uRGl2UG9zaXRpb25zKCk7XG4gIHRoaXMudXBkYXRlQW5ub3RhdGlvbkluZm8oKTtcbiAgdGhpcy5hdHRhY2hBbm5vdGF0aW9uc1RvQ2hhcnRfKCk7XG5cbiAgJCh0aGlzKS50cmlnZ2VySGFuZGxlcignYW5ub3RhdGlvbkNyZWF0ZWQnLCBhKTtcbiAgJCh0aGlzKS50cmlnZ2VySGFuZGxlcignYW5ub3RhdGlvbnNDaGFuZ2VkJywge30pO1xuXG4gIC8vIEFubm90YXRpb25zIHNob3VsZCBiZWdpbiBsaWZlIGVkaXRhYmxlLlxuICB0aGlzLm1ha2VBbm5vdGF0aW9uRWRpdGFibGUoYSk7XG59O1xuXG5hbm5vdGF0aW9ucy5wcm90b3R5cGUuZGVzdHJveSA9IGZ1bmN0aW9uIGRlc3Ryb3koKSB7XG4gIHRoaXMuZGV0YWNoTGFiZWxzKCk7XG59O1xuXG4vLyBQdWJsaWMgQVBJXG5cbi8qKlxuICogVGhpcyBpcyBhIHJlc3RyaWN0ZWQgdmlldyBvZiB0aGlzLmFubm90YXRpb25zXyB3aGljaCBkb2Vzbid0IGV4cG9zZVxuICogaW1wbGVtZW50YXRpb24gZGV0YWlscyBsaWtlIHRoZSBsaW5lIC8gaW5mbyBkaXZzLlxuICpcbiAqIEB0eXBlZGVmIHtcbiAqICAgeHZhbDogIG51bWJlciwgICAgICAvLyB4LXZhbHVlIChpLmUuIG1pbGxpcyBvciBhIHJhdyBudW1iZXIpXG4gKiAgIHNlcmllczogc3RyaW5nLCAgICAgLy8gc2VyaWVzIG5hbWVcbiAqIH0gUHVibGljQW5ub3RhdGlvblxuICovXG5cbi8qKlxuICogQHJldHVybiB7IUFycmF5LjwhUHVibGljQW5ub3RhdGlvbj59IFRoZSBjdXJyZW50IHNldCBvZiBhbm5vdGF0aW9ucywgb3JkZXJlZFxuICogICAgIGZyb20gYmFjayB0byBmcm9udC5cbiAqL1xuYW5ub3RhdGlvbnMucHJvdG90eXBlLmdldCA9IGZ1bmN0aW9uIGdldCgpIHtcbiAgdmFyIHJlc3VsdCA9IFtdO1xuICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMuYW5ub3RhdGlvbnNfLmxlbmd0aDsgaSsrKSB7XG4gICAgcmVzdWx0LnB1c2godGhpcy5jcmVhdGVQdWJsaWNBbm5vdGF0aW9uXyh0aGlzLmFubm90YXRpb25zX1tpXSkpO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIENhbGxpbmcgdGhpcyB3aWxsIHJlc3VsdCBpbiBhbiBhbm5vdGF0aW9uc0NoYW5nZWQgZXZlbnQgYmVpbmcgdHJpZ2dlcmVkLCBub1xuICogbWF0dGVyIHdoZXRoZXIgaXQgY29uc2lzdHMgb2YgYWRkaXRpb25zLCBkZWxldGlvbnMsIG1vdmVzIG9yIG5vIGNoYW5nZXMgYXRcbiAqIGFsbC5cbiAqXG4gKiBAcGFyYW0geyFBcnJheS48IVB1YmxpY0Fubm90YXRpb24+fSBhbm5vdGF0aW9ucyBUaGUgbmV3IHNldCBvZiBhbm5vdGF0aW9ucyxcbiAqICAgICBvcmRlcmVkIGZyb20gYmFjayB0byBmcm9udC5cbiAqL1xuYW5ub3RhdGlvbnMucHJvdG90eXBlLnNldCA9IGZ1bmN0aW9uIHNldChhbm5vdGF0aW9ucykge1xuICAvLyBSZS11c2UgZGl2cyBmcm9tIHRoZSBvbGQgYW5ub3RhdGlvbnMgYXJyYXkgc28gZmFyIGFzIHdlIGNhbi5cbiAgLy8gVGhleSdyZSBhbHJlYWR5IGNvcnJlY3RseSB6LW9yZGVyZWQuXG4gIHZhciBhbnlDcmVhdGVkID0gZmFsc2U7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgYW5ub3RhdGlvbnMubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgYSA9IGFubm90YXRpb25zW2ldO1xuXG4gICAgaWYgKHRoaXMuYW5ub3RhdGlvbnNfLmxlbmd0aCA+IGkpIHtcbiAgICAgIC8vIE9ubHkgdGhlIGRpdnMgbmVlZCB0byBiZSBwcmVzZXJ2ZWQuXG4gICAgICB2YXIgb2xkQSA9IHRoaXMuYW5ub3RhdGlvbnNfW2ldO1xuICAgICAgdGhpcy5hbm5vdGF0aW9uc19baV0gPSAkLmV4dGVuZCh7XG4gICAgICAgIGluZm9EaXY6IG9sZEEuaW5mb0RpdixcbiAgICAgICAgbGluZURpdjogb2xkQS5saW5lRGl2XG4gICAgICB9LCBhKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5hbm5vdGF0aW9uc18ucHVzaCh0aGlzLmNyZWF0ZUFubm90YXRpb24oYSkpO1xuICAgICAgYW55Q3JlYXRlZCA9IHRydWU7XG4gICAgfVxuICB9XG5cbiAgLy8gSWYgdGhlcmUgYXJlIGFueSByZW1haW5pbmcgYW5ub3RhdGlvbnMsIGRlc3Ryb3kgdGhlbS5cbiAgd2hpbGUgKGFubm90YXRpb25zLmxlbmd0aCA8IHRoaXMuYW5ub3RhdGlvbnNfLmxlbmd0aCkge1xuICAgIHRoaXMucmVtb3ZlQW5ub3RhdGlvbih0aGlzLmFubm90YXRpb25zX1thbm5vdGF0aW9ucy5sZW5ndGhdKTtcbiAgfVxuXG4gIHRoaXMudXBkYXRlQW5ub3RhdGlvbkRpdlBvc2l0aW9ucygpO1xuICB0aGlzLnVwZGF0ZUFubm90YXRpb25JbmZvKCk7XG4gIGlmIChhbnlDcmVhdGVkKSB7XG4gICAgdGhpcy5hdHRhY2hBbm5vdGF0aW9uc1RvQ2hhcnRfKCk7XG4gIH1cblxuICAkKHRoaXMpLnRyaWdnZXJIYW5kbGVyKCdhbm5vdGF0aW9uc0NoYW5nZWQnLCB7fSk7XG59O1xuXG5yZXR1cm4gYW5ub3RhdGlvbnM7XG5cbn0pKCk7XG5cbi8qIGxvYWRlciB3cmFwcGVyICovXG5EeWdyYXBoLl9yZXF1aXJlLmFkZCgnZHlncmFwaHMvc3JjL2V4dHJhcy9zdXBlci1hbm5vdGF0aW9ucy5qcycsIC8qIGV4cG9ydHMgKi8ge30pO1xufSkoKTtcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxDQUFDLFNBQVNBLGdDQUFnQyxHQUFHO0VBQzdDLFlBQVk7O0VBQ1osSUFBSUMsT0FBTztFQUNYLElBQUlDLE1BQU0sQ0FBQ0QsT0FBTyxFQUFFO0lBQ2xCQSxPQUFPLEdBQUdDLE1BQU0sQ0FBQ0QsT0FBTztFQUMxQixDQUFDLE1BQU0sSUFBSSxPQUFPRSxNQUFPLEtBQUssV0FBVyxFQUFFO0lBQ3pDRixPQUFPLEdB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