UNPKG

@qogni/dygraphs

Version:

dygraphs is a fast, flexible open source JavaScript charting library.

454 lines (427 loc) 61.4 kB
"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+IGkpIHtcbiAgICAgIC8vIE9ubHkgdGhlIGRpdnMgbmVlZCB0byBiZSBwcmVzZXJ2ZWQuXG4gICAgICB2YXIgb2xkQSA9IHRoaXMuYW5ub3RhdGlvbnNfW2ldO1xuICAgICAgdGhpcy5hbm5vdGF0aW9uc19baV0gPSAkLmV4dGVuZCh7XG4gICAgICAgIGluZm9EaXY6IG9sZEEuaW5mb0RpdixcbiAgICAgICAgbGluZURpdjogb2xkQS5saW5lRGl2XG4gICAgICB9LCBhKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5hbm5vdGF0aW9uc18ucHVzaCh0aGlzLmNyZWF0ZUFubm90YXRpb24oYSkpO1xuICAgICAgYW55Q3JlYXRlZCA9IHRydWU7XG4gICAgfVxuICB9XG5cbiAgLy8gSWYgdGhlcmUgYXJlIGFueSByZW1haW5pbmcgYW5ub3RhdGlvbnMsIGRlc3Ryb3kgdGhlbS5cbiAgd2hpbGUgKGFubm90YXRpb25zLmxlbmd0aCA8IHRoaXMuYW5ub3RhdGlvbnNfLmxlbmd0aCkge1xuICAgIHRoaXMucmVtb3ZlQW5ub3RhdGlvbih0aGlzLmFubm90YXRpb25zX1thbm5vdGF0aW9ucy5sZW5ndGhdKTtcbiAgfVxuXG4gIHRoaXMudXBkYXRlQW5ub3RhdGlvbkRpdlBvc2l0aW9ucygpO1xuICB0aGlzLnVwZGF0ZUFubm90YXRpb25JbmZvKCk7XG4gIGlmIChhbnlDcmVhdGVkKSB7XG4gICAgdGhpcy5hdHRhY2hBbm5vdGF0aW9uc1RvQ2hhcnRfKCk7XG4gIH1cblxuICAkKHRoaXMpLnRyaWdnZXJIYW5kbGVyKCdhbm5vdGF0aW9uc0NoYW5nZWQnLCB7fSk7XG59O1xuXG5yZXR1cm4gYW5ub3RhdGlvbnM7XG5cbn0pKCk7XG5cbi8qIGxvYWRlciB3cmFwcGVyICovXG5EeWdyYXBoLl9yZXF1aXJlLmFkZCgnZHlncmFwaHMvc3JjL2V4dHJhcy9zdXBlci1hbm5vdGF0aW9ucy5qcycsIC8qIGV4cG9ydHMgKi8ge30pO1xufSkoKTtcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxDQUFDLFNBQVNBLGdDQUFnQ0EsQ0FBQSxFQUFHO0VBQzdDLFlBQVk7O0VBQ1osSUFBSUMsT0FBTztFQUNYLElBQUlDLE1BQU0sQ0FBQ0QsT0FBTyxFQUFFO0lBQ2xCQSxPQUFPLEdBQUdDLE1BQU0sQ0FBQ0QsT0FBTztFQUMxQixDQUFDLE1BQU0sSUFBSSxPQUFPRSxNQUFPLEtBQUssV0FBVyxFQUFFO0lBQ3pDRixP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